1 | /*
|
---|
2 | * Copyright (c) 1998, 2005, Oracle and/or its affiliates. All rights reserved.
|
---|
3 | * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
|
---|
4 | *
|
---|
5 | * This code is free software; you can redistribute it and/or modify it
|
---|
6 | * under the terms of the GNU General Public License version 2 only, as
|
---|
7 | * published by the Free Software Foundation. Oracle designates this
|
---|
8 | * particular file as subject to the "Classpath" exception as provided
|
---|
9 | * by Oracle in the LICENSE file that accompanied this code.
|
---|
10 | *
|
---|
11 | * This code is distributed in the hope that it will be useful, but WITHOUT
|
---|
12 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
---|
13 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
|
---|
14 | * version 2 for more details (a copy is included in the LICENSE file that
|
---|
15 | * accompanied this code).
|
---|
16 | *
|
---|
17 | * You should have received a copy of the GNU General Public License version
|
---|
18 | * 2 along with this work; if not, write to the Free Software Foundation,
|
---|
19 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
|
---|
20 | *
|
---|
21 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
|
---|
22 | * or visit www.oracle.com if you need additional information or have any
|
---|
23 | * questions.
|
---|
24 | */
|
---|
25 |
|
---|
26 | #include <ctype.h>
|
---|
27 |
|
---|
28 | #include "util.h"
|
---|
29 | #include "commonRef.h"
|
---|
30 | #include "debugDispatch.h"
|
---|
31 | #include "eventHandler.h"
|
---|
32 | #include "eventHelper.h"
|
---|
33 | #include "threadControl.h"
|
---|
34 | #include "stepControl.h"
|
---|
35 | #include "transport.h"
|
---|
36 | #include "classTrack.h"
|
---|
37 | #include "debugLoop.h"
|
---|
38 | #include "bag.h"
|
---|
39 | #include "invoker.h"
|
---|
40 |
|
---|
41 | /* How the options get to OnLoad: */
|
---|
42 | #define XDEBUG "-Xdebug"
|
---|
43 | #define XRUN "-Xrunjdwp"
|
---|
44 | #define AGENTLIB "-agentlib:jdwp"
|
---|
45 |
|
---|
46 | /* Debug version defaults */
|
---|
47 | #ifdef DEBUG
|
---|
48 | #define DEFAULT_ASSERT_ON JNI_TRUE
|
---|
49 | #define DEFAULT_ASSERT_FATAL JNI_TRUE
|
---|
50 | #define DEFAULT_LOGFILE "jdwp.log"
|
---|
51 | #else
|
---|
52 | #define DEFAULT_ASSERT_ON JNI_FALSE
|
---|
53 | #define DEFAULT_ASSERT_FATAL JNI_FALSE
|
---|
54 | #define DEFAULT_LOGFILE NULL
|
---|
55 | #endif
|
---|
56 |
|
---|
57 | static jboolean vmInitialized;
|
---|
58 | static jrawMonitorID initMonitor;
|
---|
59 | static jboolean initComplete;
|
---|
60 | static jbyte currentSessionID;
|
---|
61 |
|
---|
62 | /*
|
---|
63 | * Options set through the OnLoad options string. All of these values
|
---|
64 | * are set once at VM startup and never reset.
|
---|
65 | */
|
---|
66 | static jboolean isServer = JNI_FALSE; /* Listens for connecting debuggers? */
|
---|
67 | static jboolean isStrict = JNI_FALSE; /* Unused */
|
---|
68 | static jboolean useStandardAlloc = JNI_FALSE; /* Use standard malloc/free? */
|
---|
69 | static struct bag *transports; /* of TransportSpec */
|
---|
70 |
|
---|
71 | static jboolean initOnStartup = JNI_TRUE; /* init immediately */
|
---|
72 | static char *initOnException = NULL; /* init when this exception thrown */
|
---|
73 | static jboolean initOnUncaught = JNI_FALSE; /* init when uncaught exc thrown */
|
---|
74 |
|
---|
75 | static char *launchOnInit = NULL; /* launch this app during init */
|
---|
76 | static jboolean suspendOnInit = JNI_TRUE; /* suspend all app threads after init */
|
---|
77 | static jboolean dopause = JNI_FALSE; /* pause for debugger attach */
|
---|
78 | static jboolean docoredump = JNI_FALSE; /* core dump on exit */
|
---|
79 | static char *logfile = NULL; /* Name of logfile (if logging) */
|
---|
80 | static unsigned logflags = 0; /* Log flags */
|
---|
81 |
|
---|
82 | static char *names; /* strings derived from OnLoad options */
|
---|
83 |
|
---|
84 | /*
|
---|
85 | * Elements of the transports bag
|
---|
86 | */
|
---|
87 | typedef struct TransportSpec {
|
---|
88 | char *name;
|
---|
89 | char *address;
|
---|
90 | long timeout;
|
---|
91 | } TransportSpec;
|
---|
92 |
|
---|
93 | /*
|
---|
94 | * Forward Refs
|
---|
95 | */
|
---|
96 | static void JNICALL cbEarlyVMInit(jvmtiEnv*, JNIEnv *, jthread);
|
---|
97 | static void JNICALL cbEarlyVMDeath(jvmtiEnv*, JNIEnv *);
|
---|
98 | static void JNICALL cbEarlyException(jvmtiEnv*, JNIEnv *,
|
---|
99 | jthread, jmethodID, jlocation, jobject, jmethodID, jlocation);
|
---|
100 |
|
---|
101 | static void initialize(JNIEnv *env, jthread thread, EventIndex triggering_ei);
|
---|
102 | static jboolean parseOptions(char *str);
|
---|
103 |
|
---|
104 | /*
|
---|
105 | * Phase 1: Initial load.
|
---|
106 | *
|
---|
107 | * OnLoad is called by the VM immediately after the back-end
|
---|
108 | * library is loaded. We can do very little in this function since
|
---|
109 | * the VM has not completed initialization. So, we parse the JDWP
|
---|
110 | * options and set up a simple initial event callbacks for JVMTI events.
|
---|
111 | * When a triggering event occurs, that callback will begin debugger initialization.
|
---|
112 | */
|
---|
113 |
|
---|
114 | /* Get a static area to hold the Global Data */
|
---|
115 | static BackendGlobalData *
|
---|
116 | get_gdata(void)
|
---|
117 | {
|
---|
118 | static BackendGlobalData s;
|
---|
119 | (void)memset(&s, 0, sizeof(BackendGlobalData));
|
---|
120 | return &s;
|
---|
121 | }
|
---|
122 |
|
---|
123 | static jvmtiError
|
---|
124 | set_event_notification(jvmtiEventMode mode, EventIndex ei)
|
---|
125 | {
|
---|
126 | jvmtiError error;
|
---|
127 | error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventNotificationMode)
|
---|
128 | (gdata->jvmti, mode, eventIndex2jvmti(ei), NULL);
|
---|
129 | if (error != JVMTI_ERROR_NONE) {
|
---|
130 | ERROR_MESSAGE(("JDWP unable to configure initial JVMTI event %s: %s(%d)",
|
---|
131 | eventText(ei), jvmtiErrorText(error), error));
|
---|
132 | }
|
---|
133 | return error;
|
---|
134 | }
|
---|
135 |
|
---|
136 | /* Logic to determine JVMTI version compatibility */
|
---|
137 | static jboolean
|
---|
138 | compatible_versions(jint major_runtime, jint minor_runtime,
|
---|
139 | jint major_compiletime, jint minor_compiletime)
|
---|
140 | {
|
---|
141 | #if 1 /* FIXUP: We allow version 0 to be compatible with anything */
|
---|
142 | /* Special check for FCS of 1.0. */
|
---|
143 | if ( major_runtime == 0 || major_compiletime == 0 ) {
|
---|
144 | return JNI_TRUE;
|
---|
145 | }
|
---|
146 | #endif
|
---|
147 | /* Runtime major version must match. */
|
---|
148 | if ( major_runtime != major_compiletime ) {
|
---|
149 | return JNI_FALSE;
|
---|
150 | }
|
---|
151 | /* Runtime minor version must be >= the version compiled with. */
|
---|
152 | if ( minor_runtime < minor_compiletime ) {
|
---|
153 | return JNI_FALSE;
|
---|
154 | }
|
---|
155 | /* Assumed compatible */
|
---|
156 | return JNI_TRUE;
|
---|
157 | }
|
---|
158 |
|
---|
159 | /* OnLoad startup:
|
---|
160 | * Returning JNI_ERR will cause the java_g VM to core dump, be careful.
|
---|
161 | */
|
---|
162 | JNIEXPORT jint JNICALL
|
---|
163 | Agent_OnLoad(JavaVM *vm, char *options, void *reserved)
|
---|
164 | {
|
---|
165 | jvmtiError error;
|
---|
166 | jvmtiCapabilities needed_capabilities;
|
---|
167 | jvmtiCapabilities potential_capabilities;
|
---|
168 | jint jvmtiCompileTimeMajorVersion;
|
---|
169 | jint jvmtiCompileTimeMinorVersion;
|
---|
170 | jint jvmtiCompileTimeMicroVersion;
|
---|
171 |
|
---|
172 | /* See if it's already loaded */
|
---|
173 | if ( gdata!=NULL && gdata->isLoaded==JNI_TRUE ) {
|
---|
174 | ERROR_MESSAGE(("Cannot load this JVM TI agent twice, check your java command line for duplicate jdwp options."));
|
---|
175 | return JNI_ERR;
|
---|
176 | }
|
---|
177 |
|
---|
178 | /* If gdata is defined and the VM died, why are we here? */
|
---|
179 | if ( gdata!=NULL && gdata->vmDead ) {
|
---|
180 | ERROR_MESSAGE(("JDWP unable to load, VM died"));
|
---|
181 | return JNI_ERR;
|
---|
182 | }
|
---|
183 |
|
---|
184 | /* Get global data area */
|
---|
185 | gdata = get_gdata();
|
---|
186 | if (gdata == NULL) {
|
---|
187 | ERROR_MESSAGE(("JDWP unable to allocate memory"));
|
---|
188 | return JNI_ERR;
|
---|
189 | }
|
---|
190 | gdata->isLoaded = JNI_TRUE;
|
---|
191 |
|
---|
192 | /* Start filling in gdata */
|
---|
193 | gdata->jvm = vm;
|
---|
194 | vmInitialized = JNI_FALSE;
|
---|
195 | gdata->vmDead = JNI_FALSE;
|
---|
196 |
|
---|
197 | /* Npt and Utf function init */
|
---|
198 | NPT_INITIALIZE(&(gdata->npt), NPT_VERSION, NULL);
|
---|
199 | if (gdata->npt == NULL) {
|
---|
200 | ERROR_MESSAGE(("JDWP: unable to initialize NPT library"));
|
---|
201 | return JNI_ERR;
|
---|
202 | }
|
---|
203 | gdata->npt->utf = (gdata->npt->utfInitialize)(NULL);
|
---|
204 | if (gdata->npt->utf == NULL) {
|
---|
205 | ERROR_MESSAGE(("JDWP: UTF function initialization failed"));
|
---|
206 | return JNI_ERR;
|
---|
207 | }
|
---|
208 |
|
---|
209 | /* Get the JVMTI Env, IMPORTANT: Do this first! For jvmtiAllocate(). */
|
---|
210 | error = JVM_FUNC_PTR(vm,GetEnv)
|
---|
211 | (vm, (void **)&(gdata->jvmti), JVMTI_VERSION_1);
|
---|
212 | if (error != JNI_OK) {
|
---|
213 | ERROR_MESSAGE(("JDWP unable to access JVMTI Version 1 (0x%x),"
|
---|
214 | " is your J2SE a 1.5 or newer version?"
|
---|
215 | " JNIEnv's GetEnv() returned %d",
|
---|
216 | JVMTI_VERSION_1, error));
|
---|
217 | forceExit(1); /* Kill entire process, no core dump */
|
---|
218 | }
|
---|
219 |
|
---|
220 | /* Check to make sure the version of jvmti.h we compiled with
|
---|
221 | * matches the runtime version we are using.
|
---|
222 | */
|
---|
223 | jvmtiCompileTimeMajorVersion = ( JVMTI_VERSION & JVMTI_VERSION_MASK_MAJOR )
|
---|
224 | >> JVMTI_VERSION_SHIFT_MAJOR;
|
---|
225 | jvmtiCompileTimeMinorVersion = ( JVMTI_VERSION & JVMTI_VERSION_MASK_MINOR )
|
---|
226 | >> JVMTI_VERSION_SHIFT_MINOR;
|
---|
227 | jvmtiCompileTimeMicroVersion = ( JVMTI_VERSION & JVMTI_VERSION_MASK_MICRO )
|
---|
228 | >> JVMTI_VERSION_SHIFT_MICRO;
|
---|
229 |
|
---|
230 | /* Check for compatibility */
|
---|
231 | if ( !compatible_versions(jvmtiMajorVersion(), jvmtiMinorVersion(),
|
---|
232 | jvmtiCompileTimeMajorVersion, jvmtiCompileTimeMinorVersion) ) {
|
---|
233 |
|
---|
234 | ERROR_MESSAGE(("This jdwp native library will not work with this VM's "
|
---|
235 | "version of JVMTI (%d.%d.%d), it needs JVMTI %d.%d[.%d].",
|
---|
236 | jvmtiMajorVersion(),
|
---|
237 | jvmtiMinorVersion(),
|
---|
238 | jvmtiMicroVersion(),
|
---|
239 | jvmtiCompileTimeMajorVersion,
|
---|
240 | jvmtiCompileTimeMinorVersion,
|
---|
241 | jvmtiCompileTimeMicroVersion));
|
---|
242 |
|
---|
243 | /* Do not let VM get a fatal error, we don't want a core dump here. */
|
---|
244 | forceExit(1); /* Kill entire process, no core dump wanted */
|
---|
245 | }
|
---|
246 |
|
---|
247 | /* Parse input options */
|
---|
248 | if (!parseOptions(options)) {
|
---|
249 | /* No message necessary, should have been printed out already */
|
---|
250 | /* Do not let VM get a fatal error, we don't want a core dump here. */
|
---|
251 | forceExit(1); /* Kill entire process, no core dump wanted */
|
---|
252 | }
|
---|
253 |
|
---|
254 | LOG_MISC(("Onload: %s", options));
|
---|
255 |
|
---|
256 | /* Get potential capabilities */
|
---|
257 | (void)memset(&potential_capabilities,0,sizeof(potential_capabilities));
|
---|
258 | error = JVMTI_FUNC_PTR(gdata->jvmti,GetPotentialCapabilities)
|
---|
259 | (gdata->jvmti, &potential_capabilities);
|
---|
260 | if (error != JVMTI_ERROR_NONE) {
|
---|
261 | ERROR_MESSAGE(("JDWP unable to get potential JVMTI capabilities: %s(%d)",
|
---|
262 | jvmtiErrorText(error), error));
|
---|
263 | return JNI_ERR;
|
---|
264 | }
|
---|
265 |
|
---|
266 | /* Fill in ones that we must have */
|
---|
267 | (void)memset(&needed_capabilities,0,sizeof(needed_capabilities));
|
---|
268 | needed_capabilities.can_access_local_variables = 1;
|
---|
269 | needed_capabilities.can_generate_single_step_events = 1;
|
---|
270 | needed_capabilities.can_generate_exception_events = 1;
|
---|
271 | needed_capabilities.can_generate_frame_pop_events = 1;
|
---|
272 | needed_capabilities.can_generate_breakpoint_events = 1;
|
---|
273 | needed_capabilities.can_suspend = 1;
|
---|
274 | needed_capabilities.can_generate_method_entry_events = 1;
|
---|
275 | needed_capabilities.can_generate_method_exit_events = 1;
|
---|
276 | needed_capabilities.can_generate_garbage_collection_events = 1;
|
---|
277 | needed_capabilities.can_maintain_original_method_order = 1;
|
---|
278 | needed_capabilities.can_generate_monitor_events = 1;
|
---|
279 | needed_capabilities.can_tag_objects = 1;
|
---|
280 |
|
---|
281 | /* And what potential ones that would be nice to have */
|
---|
282 | needed_capabilities.can_force_early_return
|
---|
283 | = potential_capabilities.can_force_early_return;
|
---|
284 | needed_capabilities.can_generate_field_modification_events
|
---|
285 | = potential_capabilities.can_generate_field_modification_events;
|
---|
286 | needed_capabilities.can_generate_field_access_events
|
---|
287 | = potential_capabilities.can_generate_field_access_events;
|
---|
288 | needed_capabilities.can_get_bytecodes
|
---|
289 | = potential_capabilities.can_get_bytecodes;
|
---|
290 | needed_capabilities.can_get_synthetic_attribute
|
---|
291 | = potential_capabilities.can_get_synthetic_attribute;
|
---|
292 | needed_capabilities.can_get_owned_monitor_info
|
---|
293 | = potential_capabilities.can_get_owned_monitor_info;
|
---|
294 | needed_capabilities.can_get_current_contended_monitor
|
---|
295 | = potential_capabilities.can_get_current_contended_monitor;
|
---|
296 | needed_capabilities.can_get_monitor_info
|
---|
297 | = potential_capabilities.can_get_monitor_info;
|
---|
298 | needed_capabilities.can_pop_frame
|
---|
299 | = potential_capabilities.can_pop_frame;
|
---|
300 | needed_capabilities.can_redefine_classes
|
---|
301 | = potential_capabilities.can_redefine_classes;
|
---|
302 | needed_capabilities.can_redefine_any_class
|
---|
303 | = potential_capabilities.can_redefine_any_class;
|
---|
304 | needed_capabilities.can_get_owned_monitor_stack_depth_info
|
---|
305 | = potential_capabilities.can_get_owned_monitor_stack_depth_info;
|
---|
306 | needed_capabilities.can_get_constant_pool
|
---|
307 | = potential_capabilities.can_get_constant_pool;
|
---|
308 | {
|
---|
309 | needed_capabilities.can_get_source_debug_extension = 1;
|
---|
310 | needed_capabilities.can_get_source_file_name = 1;
|
---|
311 | needed_capabilities.can_get_line_numbers = 1;
|
---|
312 | needed_capabilities.can_signal_thread
|
---|
313 | = potential_capabilities.can_signal_thread;
|
---|
314 | }
|
---|
315 |
|
---|
316 | /* Add the capabilities */
|
---|
317 | error = JVMTI_FUNC_PTR(gdata->jvmti,AddCapabilities)
|
---|
318 | (gdata->jvmti, &needed_capabilities);
|
---|
319 | if (error != JVMTI_ERROR_NONE) {
|
---|
320 | ERROR_MESSAGE(("JDWP unable to get necessary JVMTI capabilities."));
|
---|
321 | forceExit(1); /* Kill entire process, no core dump wanted */
|
---|
322 | }
|
---|
323 |
|
---|
324 | /* Initialize event number mapping tables */
|
---|
325 | eventIndexInit();
|
---|
326 |
|
---|
327 | /* Set the initial JVMTI event notifications */
|
---|
328 | error = set_event_notification(JVMTI_ENABLE, EI_VM_DEATH);
|
---|
329 | if (error != JVMTI_ERROR_NONE) {
|
---|
330 | return JNI_ERR;
|
---|
331 | }
|
---|
332 | error = set_event_notification(JVMTI_ENABLE, EI_VM_INIT);
|
---|
333 | if (error != JVMTI_ERROR_NONE) {
|
---|
334 | return JNI_ERR;
|
---|
335 | }
|
---|
336 | if (initOnUncaught || (initOnException != NULL)) {
|
---|
337 | error = set_event_notification(JVMTI_ENABLE, EI_EXCEPTION);
|
---|
338 | if (error != JVMTI_ERROR_NONE) {
|
---|
339 | return JNI_ERR;
|
---|
340 | }
|
---|
341 | }
|
---|
342 |
|
---|
343 | /* Set callbacks just for 3 functions */
|
---|
344 | (void)memset(&(gdata->callbacks),0,sizeof(gdata->callbacks));
|
---|
345 | gdata->callbacks.VMInit = &cbEarlyVMInit;
|
---|
346 | gdata->callbacks.VMDeath = &cbEarlyVMDeath;
|
---|
347 | gdata->callbacks.Exception = &cbEarlyException;
|
---|
348 | error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventCallbacks)
|
---|
349 | (gdata->jvmti, &(gdata->callbacks), sizeof(gdata->callbacks));
|
---|
350 | if (error != JVMTI_ERROR_NONE) {
|
---|
351 | ERROR_MESSAGE(("JDWP unable to set JVMTI event callbacks: %s(%d)",
|
---|
352 | jvmtiErrorText(error), error));
|
---|
353 | return JNI_ERR;
|
---|
354 | }
|
---|
355 |
|
---|
356 | LOG_MISC(("OnLoad: DONE"));
|
---|
357 | return JNI_OK;
|
---|
358 | }
|
---|
359 |
|
---|
360 | JNIEXPORT void JNICALL
|
---|
361 | Agent_OnUnload(JavaVM *vm)
|
---|
362 | {
|
---|
363 |
|
---|
364 | gdata->isLoaded = JNI_FALSE;
|
---|
365 |
|
---|
366 | /* Cleanup, but make sure VM is alive before using JNI, and
|
---|
367 | * make sure JVMTI environment is ok before deallocating
|
---|
368 | * memory allocated through JVMTI, which all of it is.
|
---|
369 | */
|
---|
370 |
|
---|
371 | /*
|
---|
372 | * Close transport before exit
|
---|
373 | */
|
---|
374 | if (transport_is_open()) {
|
---|
375 | transport_close();
|
---|
376 | }
|
---|
377 | }
|
---|
378 |
|
---|
379 | /*
|
---|
380 | * Phase 2: Initial events. Phase 2 consists of waiting for the
|
---|
381 | * event that triggers full initialization. Under normal circumstances
|
---|
382 | * (initOnStartup == TRUE) this is the JVMTI_EVENT_VM_INIT event.
|
---|
383 | * Otherwise, we delay initialization until the app throws a
|
---|
384 | * particular exception. The triggering event invokes
|
---|
385 | * the bulk of the initialization, including creation of threads and
|
---|
386 | * monitors, transport setup, and installation of a new event callback which
|
---|
387 | * handles the complete set of events.
|
---|
388 | *
|
---|
389 | * Since the triggering event comes in on an application thread, some of the
|
---|
390 | * initialization is difficult to do here. Specifically, this thread along
|
---|
391 | * with all other app threads may need to be suspended until a debugger
|
---|
392 | * connects. These kinds of tasks are left to the third phase which is
|
---|
393 | * invoked by one of the spawned debugger threads, the event handler.
|
---|
394 | */
|
---|
395 |
|
---|
396 | /*
|
---|
397 | * Wait for a triggering event; then kick off debugger
|
---|
398 | * initialization. A different event callback will be installed by
|
---|
399 | * debugger initialization, and this function will not be called
|
---|
400 | * again.
|
---|
401 | */
|
---|
402 |
|
---|
403 | /*
|
---|
404 | * TO DO: Decide whether we need to protect this code with
|
---|
405 | * a lock. It might be too early to create a monitor safely (?).
|
---|
406 | */
|
---|
407 |
|
---|
408 | static void JNICALL
|
---|
409 | cbEarlyVMInit(jvmtiEnv *jvmti_env, JNIEnv *env, jthread thread)
|
---|
410 | {
|
---|
411 | LOG_CB(("cbEarlyVMInit"));
|
---|
412 | if ( gdata->vmDead ) {
|
---|
413 | EXIT_ERROR(AGENT_ERROR_INTERNAL,"VM dead at VM_INIT time");
|
---|
414 | }
|
---|
415 | if (initOnStartup)
|
---|
416 | initialize(env, thread, EI_VM_INIT);
|
---|
417 | vmInitialized = JNI_TRUE;
|
---|
418 | LOG_MISC(("END cbEarlyVMInit"));
|
---|
419 | }
|
---|
420 |
|
---|
421 | static void
|
---|
422 | disposeEnvironment(jvmtiEnv *jvmti_env)
|
---|
423 | {
|
---|
424 | jvmtiError error;
|
---|
425 |
|
---|
426 | error = JVMTI_FUNC_PTR(jvmti_env,DisposeEnvironment)(jvmti_env);
|
---|
427 | if ( error == JVMTI_ERROR_MUST_POSSESS_CAPABILITY )
|
---|
428 | error = JVMTI_ERROR_NONE; /* Hack! FIXUP when JVMTI has disposeEnv */
|
---|
429 | /* What should error return say? */
|
---|
430 | if (error != JVMTI_ERROR_NONE) {
|
---|
431 | ERROR_MESSAGE(("JDWP unable to dispose of JVMTI environment: %s(%d)",
|
---|
432 | jvmtiErrorText(error), error));
|
---|
433 | }
|
---|
434 | gdata->jvmti = NULL;
|
---|
435 | }
|
---|
436 |
|
---|
437 | static void JNICALL
|
---|
438 | cbEarlyVMDeath(jvmtiEnv *jvmti_env, JNIEnv *env)
|
---|
439 | {
|
---|
440 | LOG_CB(("cbEarlyVMDeath"));
|
---|
441 | if ( gdata->vmDead ) {
|
---|
442 | EXIT_ERROR(AGENT_ERROR_INTERNAL,"VM died more than once");
|
---|
443 | }
|
---|
444 | disposeEnvironment(jvmti_env);
|
---|
445 | gdata->jvmti = NULL;
|
---|
446 | gdata->jvm = NULL;
|
---|
447 | gdata->vmDead = JNI_TRUE;
|
---|
448 | LOG_MISC(("END cbEarlyVMDeath"));
|
---|
449 | }
|
---|
450 |
|
---|
451 | static void JNICALL
|
---|
452 | cbEarlyException(jvmtiEnv *jvmti_env, JNIEnv *env,
|
---|
453 | jthread thread, jmethodID method, jlocation location,
|
---|
454 | jobject exception,
|
---|
455 | jmethodID catch_method, jlocation catch_location)
|
---|
456 | {
|
---|
457 | jvmtiError error;
|
---|
458 | jthrowable currentException;
|
---|
459 |
|
---|
460 | LOG_CB(("cbEarlyException: thread=%p", thread));
|
---|
461 |
|
---|
462 | if ( gdata->vmDead ) {
|
---|
463 | EXIT_ERROR(AGENT_ERROR_INTERNAL,"VM dead at initial Exception event");
|
---|
464 | }
|
---|
465 | if (!vmInitialized) {
|
---|
466 | LOG_MISC(("VM is not initialized yet"));
|
---|
467 | return;
|
---|
468 | }
|
---|
469 |
|
---|
470 | /*
|
---|
471 | * We want to preserve any current exception that might get wiped
|
---|
472 | * out during event handling (e.g. JNI calls). We have to rely on
|
---|
473 | * space for the local reference on the current frame because
|
---|
474 | * doing a PushLocalFrame here might itself generate an exception.
|
---|
475 | */
|
---|
476 |
|
---|
477 | currentException = JNI_FUNC_PTR(env,ExceptionOccurred)(env);
|
---|
478 | JNI_FUNC_PTR(env,ExceptionClear)(env);
|
---|
479 |
|
---|
480 | if (initOnUncaught && catch_method == NULL) {
|
---|
481 |
|
---|
482 | LOG_MISC(("Initializing on uncaught exception"));
|
---|
483 | initialize(env, thread, EI_EXCEPTION);
|
---|
484 |
|
---|
485 | } else if (initOnException != NULL) {
|
---|
486 |
|
---|
487 | jclass clazz;
|
---|
488 |
|
---|
489 | /* Get class of exception thrown */
|
---|
490 | clazz = JNI_FUNC_PTR(env,GetObjectClass)(env, exception);
|
---|
491 | if ( clazz != NULL ) {
|
---|
492 | char *signature = NULL;
|
---|
493 | /* initing on throw, check */
|
---|
494 | error = classSignature(clazz, &signature, NULL);
|
---|
495 | LOG_MISC(("Checking specific exception: looking for %s, got %s",
|
---|
496 | initOnException, signature));
|
---|
497 | if ( (error==JVMTI_ERROR_NONE) &&
|
---|
498 | (strcmp(signature, initOnException) == 0)) {
|
---|
499 | LOG_MISC(("Initializing on specific exception"));
|
---|
500 | initialize(env, thread, EI_EXCEPTION);
|
---|
501 | } else {
|
---|
502 | error = AGENT_ERROR_INTERNAL; /* Just to cause restore */
|
---|
503 | }
|
---|
504 | if ( signature != NULL ) {
|
---|
505 | jvmtiDeallocate(signature);
|
---|
506 | }
|
---|
507 | } else {
|
---|
508 | error = AGENT_ERROR_INTERNAL; /* Just to cause restore */
|
---|
509 | }
|
---|
510 |
|
---|
511 | /* If initialize didn't happen, we need to restore things */
|
---|
512 | if ( error != JVMTI_ERROR_NONE ) {
|
---|
513 | /*
|
---|
514 | * Restore exception state from before callback call
|
---|
515 | */
|
---|
516 | LOG_MISC(("No initialization, didn't find right exception"));
|
---|
517 | if (currentException != NULL) {
|
---|
518 | JNI_FUNC_PTR(env,Throw)(env, currentException);
|
---|
519 | } else {
|
---|
520 | JNI_FUNC_PTR(env,ExceptionClear)(env);
|
---|
521 | }
|
---|
522 | }
|
---|
523 |
|
---|
524 | }
|
---|
525 |
|
---|
526 | LOG_MISC(("END cbEarlyException"));
|
---|
527 |
|
---|
528 | }
|
---|
529 |
|
---|
530 | typedef struct EnumerateArg {
|
---|
531 | jboolean isServer;
|
---|
532 | jdwpError error;
|
---|
533 | jint startCount;
|
---|
534 | } EnumerateArg;
|
---|
535 |
|
---|
536 | static jboolean
|
---|
537 | startTransport(void *item, void *arg)
|
---|
538 | {
|
---|
539 | TransportSpec *transport = item;
|
---|
540 | EnumerateArg *enumArg = arg;
|
---|
541 | jdwpError serror;
|
---|
542 |
|
---|
543 | LOG_MISC(("Begin startTransport"));
|
---|
544 | serror = transport_startTransport(enumArg->isServer, transport->name,
|
---|
545 | transport->address, transport->timeout);
|
---|
546 | if (serror != JDWP_ERROR(NONE)) {
|
---|
547 | ERROR_MESSAGE(("JDWP Transport %s failed to initialize, %s(%d)",
|
---|
548 | transport->name, jdwpErrorText(serror), serror));
|
---|
549 | enumArg->error = serror;
|
---|
550 | } else {
|
---|
551 | /* (Don't overwrite any previous error) */
|
---|
552 |
|
---|
553 | enumArg->startCount++;
|
---|
554 | }
|
---|
555 |
|
---|
556 | LOG_MISC(("End startTransport"));
|
---|
557 |
|
---|
558 | return JNI_TRUE; /* Always continue, even if there was an error */
|
---|
559 | }
|
---|
560 |
|
---|
561 | static void
|
---|
562 | signalInitComplete(void)
|
---|
563 | {
|
---|
564 | /*
|
---|
565 | * Initialization is complete
|
---|
566 | */
|
---|
567 | LOG_MISC(("signal initialization complete"));
|
---|
568 | debugMonitorEnter(initMonitor);
|
---|
569 | initComplete = JNI_TRUE;
|
---|
570 | debugMonitorNotifyAll(initMonitor);
|
---|
571 | debugMonitorExit(initMonitor);
|
---|
572 | }
|
---|
573 |
|
---|
574 | /*
|
---|
575 | * Determine if initialization is complete.
|
---|
576 | */
|
---|
577 | jboolean
|
---|
578 | debugInit_isInitComplete(void)
|
---|
579 | {
|
---|
580 | return initComplete;
|
---|
581 | }
|
---|
582 |
|
---|
583 | /*
|
---|
584 | * Wait for all initialization to complete.
|
---|
585 | */
|
---|
586 | void
|
---|
587 | debugInit_waitInitComplete(void)
|
---|
588 | {
|
---|
589 | debugMonitorEnter(initMonitor);
|
---|
590 | while (!initComplete) {
|
---|
591 | debugMonitorWait(initMonitor);
|
---|
592 | }
|
---|
593 | debugMonitorExit(initMonitor);
|
---|
594 | }
|
---|
595 |
|
---|
596 | /* All process exit() calls come from here */
|
---|
597 | void
|
---|
598 | forceExit(int exit_code)
|
---|
599 | {
|
---|
600 | /* make sure the transport is closed down before we exit() */
|
---|
601 | transport_close();
|
---|
602 | exit(exit_code);
|
---|
603 | }
|
---|
604 |
|
---|
605 | /* All JVM fatal error exits lead here (e.g. we need to kill the VM). */
|
---|
606 | static void
|
---|
607 | jniFatalError(JNIEnv *env, const char *msg, jvmtiError error, int exit_code)
|
---|
608 | {
|
---|
609 | JavaVM *vm;
|
---|
610 | char buf[512];
|
---|
611 |
|
---|
612 | gdata->vmDead = JNI_TRUE;
|
---|
613 | if ( msg==NULL )
|
---|
614 | msg = "UNKNOWN REASON";
|
---|
615 | vm = gdata->jvm;
|
---|
616 | if ( env==NULL && vm!=NULL ) {
|
---|
617 | jint rc = (*((*vm)->GetEnv))(vm, (void **)&env, JNI_VERSION_1_2);
|
---|
618 | if (rc != JNI_OK ) {
|
---|
619 | env = NULL;
|
---|
620 | }
|
---|
621 | }
|
---|
622 | if ( error != JVMTI_ERROR_NONE ) {
|
---|
623 | (void)snprintf(buf, sizeof(buf), "JDWP %s, jvmtiError=%s(%d)",
|
---|
624 | msg, jvmtiErrorText(error), error);
|
---|
625 | } else {
|
---|
626 | (void)snprintf(buf, sizeof(buf), "JDWP %s", buf);
|
---|
627 | }
|
---|
628 | if (env != NULL) {
|
---|
629 | (*((*env)->FatalError))(env, buf);
|
---|
630 | } else {
|
---|
631 | /* Should rarely ever reach here, means VM is really dead */
|
---|
632 | print_message(stderr, "ERROR: JDWP: ", "\n",
|
---|
633 | "Can't call JNI FatalError(NULL, \"%s\")", buf);
|
---|
634 | }
|
---|
635 | forceExit(exit_code);
|
---|
636 | }
|
---|
637 |
|
---|
638 | /*
|
---|
639 | * Initialize debugger back end modules
|
---|
640 | */
|
---|
641 | static void
|
---|
642 | initialize(JNIEnv *env, jthread thread, EventIndex triggering_ei)
|
---|
643 | {
|
---|
644 | jvmtiError error;
|
---|
645 | EnumerateArg arg;
|
---|
646 | jbyte suspendPolicy;
|
---|
647 |
|
---|
648 | LOG_MISC(("Begin initialize()"));
|
---|
649 | currentSessionID = 0;
|
---|
650 | initComplete = JNI_FALSE;
|
---|
651 |
|
---|
652 | if ( gdata->vmDead ) {
|
---|
653 | EXIT_ERROR(AGENT_ERROR_INTERNAL,"VM dead at initialize() time");
|
---|
654 | }
|
---|
655 |
|
---|
656 | /* Turn off the initial JVMTI event notifications */
|
---|
657 | error = set_event_notification(JVMTI_DISABLE, EI_EXCEPTION);
|
---|
658 | if (error != JVMTI_ERROR_NONE) {
|
---|
659 | EXIT_ERROR(error, "unable to disable JVMTI event notification");
|
---|
660 | }
|
---|
661 | error = set_event_notification(JVMTI_DISABLE, EI_VM_INIT);
|
---|
662 | if (error != JVMTI_ERROR_NONE) {
|
---|
663 | EXIT_ERROR(error, "unable to disable JVMTI event notification");
|
---|
664 | }
|
---|
665 | error = set_event_notification(JVMTI_DISABLE, EI_VM_DEATH);
|
---|
666 | if (error != JVMTI_ERROR_NONE) {
|
---|
667 | EXIT_ERROR(error, "unable to disable JVMTI event notification");
|
---|
668 | }
|
---|
669 |
|
---|
670 | /* Remove initial event callbacks */
|
---|
671 | (void)memset(&(gdata->callbacks),0,sizeof(gdata->callbacks));
|
---|
672 | error = JVMTI_FUNC_PTR(gdata->jvmti,SetEventCallbacks)
|
---|
673 | (gdata->jvmti, &(gdata->callbacks), sizeof(gdata->callbacks));
|
---|
674 | if (error != JVMTI_ERROR_NONE) {
|
---|
675 | EXIT_ERROR(error, "unable to clear JVMTI callbacks");
|
---|
676 | }
|
---|
677 |
|
---|
678 | commonRef_initialize();
|
---|
679 | util_initialize(env);
|
---|
680 | threadControl_initialize();
|
---|
681 | stepControl_initialize();
|
---|
682 | invoker_initialize();
|
---|
683 | debugDispatch_initialize();
|
---|
684 | classTrack_initialize(env);
|
---|
685 | debugLoop_initialize();
|
---|
686 |
|
---|
687 | initMonitor = debugMonitorCreate("JDWP Initialization Monitor");
|
---|
688 |
|
---|
689 |
|
---|
690 | /*
|
---|
691 | * Initialize transports
|
---|
692 | */
|
---|
693 | arg.isServer = isServer;
|
---|
694 | arg.error = JDWP_ERROR(NONE);
|
---|
695 | arg.startCount = 0;
|
---|
696 |
|
---|
697 | transport_initialize();
|
---|
698 | (void)bagEnumerateOver(transports, startTransport, &arg);
|
---|
699 |
|
---|
700 | /*
|
---|
701 | * Exit with an error only if
|
---|
702 | * 1) none of the transports was successfully started, and
|
---|
703 | * 2) the application has not yet started running
|
---|
704 | */
|
---|
705 | if ((arg.error != JDWP_ERROR(NONE)) &&
|
---|
706 | (arg.startCount == 0) &&
|
---|
707 | initOnStartup) {
|
---|
708 | EXIT_ERROR(map2jvmtiError(arg.error), "No transports initialized");
|
---|
709 | }
|
---|
710 |
|
---|
711 | eventHandler_initialize(currentSessionID);
|
---|
712 |
|
---|
713 | signalInitComplete();
|
---|
714 |
|
---|
715 | transport_waitForConnection();
|
---|
716 |
|
---|
717 | suspendPolicy = suspendOnInit ? JDWP_SUSPEND_POLICY(ALL)
|
---|
718 | : JDWP_SUSPEND_POLICY(NONE);
|
---|
719 | if (triggering_ei == EI_VM_INIT) {
|
---|
720 | LOG_MISC(("triggering_ei == EI_VM_INIT"));
|
---|
721 | eventHelper_reportVMInit(env, currentSessionID, thread, suspendPolicy);
|
---|
722 | } else {
|
---|
723 | /*
|
---|
724 | * TO DO: Kludgy way of getting the triggering event to the
|
---|
725 | * just-attached debugger. It would be nice to make this a little
|
---|
726 | * cleaner. There is also a race condition where other events
|
---|
727 | * can get in the queue (from other not-yet-suspended threads)
|
---|
728 | * before this one does. (Also need to handle allocation error below?)
|
---|
729 | */
|
---|
730 | EventInfo info;
|
---|
731 | struct bag *initEventBag;
|
---|
732 | LOG_MISC(("triggering_ei != EI_VM_INIT"));
|
---|
733 | initEventBag = eventHelper_createEventBag();
|
---|
734 | (void)memset(&info,0,sizeof(info));
|
---|
735 | info.ei = triggering_ei;
|
---|
736 | eventHelper_recordEvent(&info, 0, suspendPolicy, initEventBag);
|
---|
737 | (void)eventHelper_reportEvents(currentSessionID, initEventBag);
|
---|
738 | bagDestroyBag(initEventBag);
|
---|
739 | }
|
---|
740 |
|
---|
741 | if ( gdata->vmDead ) {
|
---|
742 | EXIT_ERROR(AGENT_ERROR_INTERNAL,"VM dead before initialize() completes");
|
---|
743 | }
|
---|
744 | LOG_MISC(("End initialize()"));
|
---|
745 | }
|
---|
746 |
|
---|
747 | /*
|
---|
748 | * Restore all static data to the initialized state so that another
|
---|
749 | * debugger can connect properly later.
|
---|
750 | */
|
---|
751 | void
|
---|
752 | debugInit_reset(JNIEnv *env)
|
---|
753 | {
|
---|
754 | EnumerateArg arg;
|
---|
755 |
|
---|
756 | LOG_MISC(("debugInit_reset() beginning"));
|
---|
757 |
|
---|
758 | currentSessionID++;
|
---|
759 | initComplete = JNI_FALSE;
|
---|
760 |
|
---|
761 | eventHandler_reset(currentSessionID);
|
---|
762 | transport_reset();
|
---|
763 | debugDispatch_reset();
|
---|
764 | invoker_reset();
|
---|
765 | stepControl_reset();
|
---|
766 | threadControl_reset();
|
---|
767 | util_reset();
|
---|
768 | commonRef_reset(env);
|
---|
769 | classTrack_reset();
|
---|
770 |
|
---|
771 | /*
|
---|
772 | * If this is a server, we are now ready to accept another connection.
|
---|
773 | * If it's a client, then we've cleaned up some (more should be added
|
---|
774 | * later) and we're done.
|
---|
775 | */
|
---|
776 | if (isServer) {
|
---|
777 | arg.isServer = JNI_TRUE;
|
---|
778 | arg.error = JDWP_ERROR(NONE);
|
---|
779 | arg.startCount = 0;
|
---|
780 | (void)bagEnumerateOver(transports, startTransport, &arg);
|
---|
781 |
|
---|
782 | signalInitComplete();
|
---|
783 |
|
---|
784 | transport_waitForConnection();
|
---|
785 | } else {
|
---|
786 | signalInitComplete(); /* Why? */
|
---|
787 | }
|
---|
788 |
|
---|
789 | LOG_MISC(("debugInit_reset() completed."));
|
---|
790 | }
|
---|
791 |
|
---|
792 |
|
---|
793 | char *
|
---|
794 | debugInit_launchOnInit(void)
|
---|
795 | {
|
---|
796 | return launchOnInit;
|
---|
797 | }
|
---|
798 |
|
---|
799 | jboolean
|
---|
800 | debugInit_suspendOnInit(void)
|
---|
801 | {
|
---|
802 | return suspendOnInit;
|
---|
803 | }
|
---|
804 |
|
---|
805 | /*
|
---|
806 | * code below is shamelessly swiped from hprof.
|
---|
807 | */
|
---|
808 |
|
---|
809 | static int
|
---|
810 | get_tok(char **src, char *buf, int buflen, char sep)
|
---|
811 | {
|
---|
812 | int i;
|
---|
813 | char *p = *src;
|
---|
814 | for (i = 0; i < buflen; i++) {
|
---|
815 | if (p[i] == 0 || p[i] == sep) {
|
---|
816 | buf[i] = 0;
|
---|
817 | if (p[i] == sep) {
|
---|
818 | i++;
|
---|
819 | }
|
---|
820 | *src += i;
|
---|
821 | return i;
|
---|
822 | }
|
---|
823 | buf[i] = p[i];
|
---|
824 | }
|
---|
825 | /* overflow */
|
---|
826 | return 0;
|
---|
827 | }
|
---|
828 |
|
---|
829 | static void
|
---|
830 | printUsage(void)
|
---|
831 | {
|
---|
832 | TTY_MESSAGE((
|
---|
833 | " Java Debugger JDWP Agent Library\n"
|
---|
834 | " --------------------------------\n"
|
---|
835 | "\n"
|
---|
836 | " (see http://java.sun.com/products/jpda for more information)\n"
|
---|
837 | "\n"
|
---|
838 | "jdwp usage: java " AGENTLIB "=[help]|[<option>=<value>, ...]\n"
|
---|
839 | "\n"
|
---|
840 | "Option Name and Value Description Default\n"
|
---|
841 | "--------------------- ----------- -------\n"
|
---|
842 | "suspend=y|n wait on startup? y\n"
|
---|
843 | "transport=<name> transport spec none\n"
|
---|
844 | "address=<listen/attach address> transport spec \"\"\n"
|
---|
845 | "server=y|n listen for debugger? n\n"
|
---|
846 | "launch=<command line> run debugger on event none\n"
|
---|
847 | "onthrow=<exception name> debug on throw none\n"
|
---|
848 | "onuncaught=y|n debug on any uncaught? n\n"
|
---|
849 | "timeout=<timeout value> for listen/attach in milliseconds n\n"
|
---|
850 | "mutf8=y|n output modified utf-8 n\n"
|
---|
851 | "quiet=y|n control over terminal messages n\n"
|
---|
852 | "\n"
|
---|
853 | "Obsolete Options\n"
|
---|
854 | "----------------\n"
|
---|
855 | "strict=y|n\n"
|
---|
856 | "stdalloc=y|n\n"
|
---|
857 | "\n"
|
---|
858 | "Examples\n"
|
---|
859 | "--------\n"
|
---|
860 | " - Using sockets connect to a debugger at a specific address:\n"
|
---|
861 | " java " AGENTLIB "=transport=dt_socket,address=localhost:8000 ...\n"
|
---|
862 | " - Using sockets listen for a debugger to attach:\n"
|
---|
863 | " java " AGENTLIB "=transport=dt_socket,server=y,suspend=y ...\n"
|
---|
864 | "\n"
|
---|
865 | "Notes\n"
|
---|
866 | "-----\n"
|
---|
867 | " - A timeout value of 0 (the default) is no timeout.\n"
|
---|
868 | "\n"
|
---|
869 | "Warnings\n"
|
---|
870 | "--------\n"
|
---|
871 | " - The older " XRUN " interface can still be used, but will be removed in\n"
|
---|
872 | " a future release, for example:\n"
|
---|
873 | " java " XDEBUG " " XRUN ":[help]|[<option>=<value>, ...]\n"
|
---|
874 | ));
|
---|
875 |
|
---|
876 | #ifdef DEBUG
|
---|
877 |
|
---|
878 | TTY_MESSAGE((
|
---|
879 | "\n"
|
---|
880 | "Debugging Options Description Default\n"
|
---|
881 | "----------------- ----------- -------\n"
|
---|
882 | "pause=y|n pause to debug PID n\n"
|
---|
883 | "coredump=y|n coredump at exit n\n"
|
---|
884 | "errorexit=y|n exit on any error n\n"
|
---|
885 | "logfile=filename name of log file none\n"
|
---|
886 | "logflags=flags log flags (bitmask) none\n"
|
---|
887 | " JVM calls = 0x001\n"
|
---|
888 | " JNI calls = 0x002\n"
|
---|
889 | " JVMTI calls = 0x004\n"
|
---|
890 | " misc events = 0x008\n"
|
---|
891 | " step logs = 0x010\n"
|
---|
892 | " locations = 0x020\n"
|
---|
893 | " callbacks = 0x040\n"
|
---|
894 | " errors = 0x080\n"
|
---|
895 | " everything = 0xfff\n"
|
---|
896 | "debugflags=flags debug flags (bitmask) none\n"
|
---|
897 | " USE_ITERATE_THROUGH_HEAP 0x01\n"
|
---|
898 | "\n"
|
---|
899 | "Environment Variables\n"
|
---|
900 | "---------------------\n"
|
---|
901 | "_JAVA_JDWP_OPTIONS\n"
|
---|
902 | " Options can be added externally via this environment variable.\n"
|
---|
903 | " Anything contained in it will get a comma prepended to it (if needed),\n"
|
---|
904 | " then it will be added to the end of the options supplied via the\n"
|
---|
905 | " " XRUN " or " AGENTLIB " command line option.\n"
|
---|
906 | ));
|
---|
907 |
|
---|
908 | #endif
|
---|
909 |
|
---|
910 |
|
---|
911 |
|
---|
912 | }
|
---|
913 |
|
---|
914 | static jboolean checkAddress(void *bagItem, void *arg)
|
---|
915 | {
|
---|
916 | TransportSpec *spec = (TransportSpec *)bagItem;
|
---|
917 | if (spec->address == NULL) {
|
---|
918 | ERROR_MESSAGE(("JDWP Non-server transport %s must have a connection "
|
---|
919 | "address specified through the 'address=' option",
|
---|
920 | spec->name));
|
---|
921 | return JNI_FALSE;
|
---|
922 | } else {
|
---|
923 | return JNI_TRUE;
|
---|
924 | }
|
---|
925 | }
|
---|
926 |
|
---|
927 | static char *
|
---|
928 | add_to_options(char *options, char *new_options)
|
---|
929 | {
|
---|
930 | size_t originalLength;
|
---|
931 | char *combinedOptions;
|
---|
932 |
|
---|
933 | /*
|
---|
934 | * Allocate enough space for both strings and
|
---|
935 | * comma in between.
|
---|
936 | */
|
---|
937 | originalLength = strlen(options);
|
---|
938 | combinedOptions = jvmtiAllocate((jint)originalLength + 1 +
|
---|
939 | (jint)strlen(new_options) + 1);
|
---|
940 | if (combinedOptions == NULL) {
|
---|
941 | return NULL;
|
---|
942 | }
|
---|
943 |
|
---|
944 | (void)strcpy(combinedOptions, options);
|
---|
945 | (void)strcat(combinedOptions, ",");
|
---|
946 | (void)strcat(combinedOptions, new_options);
|
---|
947 |
|
---|
948 | return combinedOptions;
|
---|
949 | }
|
---|
950 |
|
---|
951 | static jboolean
|
---|
952 | get_boolean(char **pstr, jboolean *answer)
|
---|
953 | {
|
---|
954 | char buf[80];
|
---|
955 | *answer = JNI_FALSE;
|
---|
956 | /*LINTED*/
|
---|
957 | if (get_tok(pstr, buf, (int)sizeof(buf), ',')) {
|
---|
958 | if (strcmp(buf, "y") == 0) {
|
---|
959 | *answer = JNI_TRUE;
|
---|
960 | return JNI_TRUE;
|
---|
961 | } else if (strcmp(buf, "n") == 0) {
|
---|
962 | *answer = JNI_FALSE;
|
---|
963 | return JNI_TRUE;
|
---|
964 | }
|
---|
965 | }
|
---|
966 | return JNI_FALSE;
|
---|
967 | }
|
---|
968 |
|
---|
969 | /* atexit() callback */
|
---|
970 | static void
|
---|
971 | atexit_finish_logging(void)
|
---|
972 | {
|
---|
973 | /* Normal exit(0) (not _exit()) may only reach here */
|
---|
974 | finish_logging(0); /* Only first call matters */
|
---|
975 | }
|
---|
976 |
|
---|
977 | static jboolean
|
---|
978 | parseOptions(char *options)
|
---|
979 | {
|
---|
980 | TransportSpec *currentTransport = NULL;
|
---|
981 | char *end;
|
---|
982 | char *current;
|
---|
983 | int length;
|
---|
984 | char *str;
|
---|
985 | char *errmsg;
|
---|
986 |
|
---|
987 | /* Set defaults */
|
---|
988 | gdata->assertOn = DEFAULT_ASSERT_ON;
|
---|
989 | gdata->assertFatal = DEFAULT_ASSERT_FATAL;
|
---|
990 | logfile = DEFAULT_LOGFILE;
|
---|
991 |
|
---|
992 | /* Options being NULL will end up being an error. */
|
---|
993 | if (options == NULL) {
|
---|
994 | options = "";
|
---|
995 | }
|
---|
996 |
|
---|
997 | /* Check for "help" BEFORE we add any environmental settings */
|
---|
998 | if ((strcmp(options, "help")) == 0) {
|
---|
999 | printUsage();
|
---|
1000 | forceExit(0); /* Kill entire process, no core dump wanted */
|
---|
1001 | }
|
---|
1002 |
|
---|
1003 | /* These buffers are never freed */
|
---|
1004 | {
|
---|
1005 | char *envOptions;
|
---|
1006 |
|
---|
1007 | /*
|
---|
1008 | * Add environmentally specified options.
|
---|
1009 | */
|
---|
1010 | envOptions = getenv("_JAVA_JDWP_OPTIONS");
|
---|
1011 | if (envOptions != NULL) {
|
---|
1012 | options = add_to_options(options, envOptions);
|
---|
1013 | if ( options==NULL ) {
|
---|
1014 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"options");
|
---|
1015 | }
|
---|
1016 | }
|
---|
1017 |
|
---|
1018 | /*
|
---|
1019 | * Allocate a buffer for names derived from option strings. It should
|
---|
1020 | * never be longer than the original options string itself.
|
---|
1021 | * Also keep a copy of the options in gdata->options.
|
---|
1022 | */
|
---|
1023 | length = (int)strlen(options);
|
---|
1024 | gdata->options = jvmtiAllocate(length + 1);
|
---|
1025 | if (gdata->options == NULL) {
|
---|
1026 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"options");
|
---|
1027 | }
|
---|
1028 | (void)strcpy(gdata->options, options);
|
---|
1029 | names = jvmtiAllocate(length + 1);
|
---|
1030 | if (names == NULL) {
|
---|
1031 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"options");
|
---|
1032 | }
|
---|
1033 |
|
---|
1034 | transports = bagCreateBag(sizeof(TransportSpec), 3);
|
---|
1035 | if (transports == NULL) {
|
---|
1036 | EXIT_ERROR(AGENT_ERROR_OUT_OF_MEMORY,"transports");
|
---|
1037 | }
|
---|
1038 |
|
---|
1039 | }
|
---|
1040 |
|
---|
1041 | current = names;
|
---|
1042 | end = names + length;
|
---|
1043 | str = options;
|
---|
1044 |
|
---|
1045 | while (*str) {
|
---|
1046 | char buf[100];
|
---|
1047 | /*LINTED*/
|
---|
1048 | if (!get_tok(&str, buf, (int)sizeof(buf), '=')) {
|
---|
1049 | goto syntax_error;
|
---|
1050 | }
|
---|
1051 | if (strcmp(buf, "transport") == 0) {
|
---|
1052 | currentTransport = bagAdd(transports);
|
---|
1053 | /*LINTED*/
|
---|
1054 | if (!get_tok(&str, current, (int)(end - current), ',')) {
|
---|
1055 | goto syntax_error;
|
---|
1056 | }
|
---|
1057 | currentTransport->name = current;
|
---|
1058 | current += strlen(current) + 1;
|
---|
1059 | } else if (strcmp(buf, "address") == 0) {
|
---|
1060 | if (currentTransport == NULL) {
|
---|
1061 | errmsg = "address specified without transport";
|
---|
1062 | goto bad_option_with_errmsg;
|
---|
1063 | }
|
---|
1064 | /*LINTED*/
|
---|
1065 | if (!get_tok(&str, current, (int)(end - current), ',')) {
|
---|
1066 | goto syntax_error;
|
---|
1067 | }
|
---|
1068 | currentTransport->address = current;
|
---|
1069 | current += strlen(current) + 1;
|
---|
1070 | } else if (strcmp(buf, "timeout") == 0) {
|
---|
1071 | if (currentTransport == NULL) {
|
---|
1072 | errmsg = "timeout specified without transport";
|
---|
1073 | goto bad_option_with_errmsg;
|
---|
1074 | }
|
---|
1075 | /*LINTED*/
|
---|
1076 | if (!get_tok(&str, current, (int)(end - current), ',')) {
|
---|
1077 | goto syntax_error;
|
---|
1078 | }
|
---|
1079 | currentTransport->timeout = atol(current);
|
---|
1080 | current += strlen(current) + 1;
|
---|
1081 | } else if (strcmp(buf, "launch") == 0) {
|
---|
1082 | /*LINTED*/
|
---|
1083 | if (!get_tok(&str, current, (int)(end - current), ',')) {
|
---|
1084 | goto syntax_error;
|
---|
1085 | }
|
---|
1086 | launchOnInit = current;
|
---|
1087 | current += strlen(current) + 1;
|
---|
1088 | } else if (strcmp(buf, "onthrow") == 0) {
|
---|
1089 | /* Read class name and convert in place to a signature */
|
---|
1090 | *current = 'L';
|
---|
1091 | /*LINTED*/
|
---|
1092 | if (!get_tok(&str, current + 1, (int)(end - current - 1), ',')) {
|
---|
1093 | goto syntax_error;
|
---|
1094 | }
|
---|
1095 | initOnException = current;
|
---|
1096 | while (*current != '\0') {
|
---|
1097 | if (*current == '.') {
|
---|
1098 | *current = '/';
|
---|
1099 | }
|
---|
1100 | current++;
|
---|
1101 | }
|
---|
1102 | *current++ = ';';
|
---|
1103 | *current++ = '\0';
|
---|
1104 | } else if (strcmp(buf, "assert") == 0) {
|
---|
1105 | /*LINTED*/
|
---|
1106 | if (!get_tok(&str, current, (int)(end - current), ',')) {
|
---|
1107 | goto syntax_error;
|
---|
1108 | }
|
---|
1109 | if (strcmp(current, "y") == 0) {
|
---|
1110 | gdata->assertOn = JNI_TRUE;
|
---|
1111 | gdata->assertFatal = JNI_FALSE;
|
---|
1112 | } else if (strcmp(current, "fatal") == 0) {
|
---|
1113 | gdata->assertOn = JNI_TRUE;
|
---|
1114 | gdata->assertFatal = JNI_TRUE;
|
---|
1115 | } else if (strcmp(current, "n") == 0) {
|
---|
1116 | gdata->assertOn = JNI_FALSE;
|
---|
1117 | gdata->assertFatal = JNI_FALSE;
|
---|
1118 | } else {
|
---|
1119 | goto syntax_error;
|
---|
1120 | }
|
---|
1121 | current += strlen(current) + 1;
|
---|
1122 | } else if (strcmp(buf, "pause") == 0) {
|
---|
1123 | if ( !get_boolean(&str, &dopause) ) {
|
---|
1124 | goto syntax_error;
|
---|
1125 | }
|
---|
1126 | if ( dopause ) {
|
---|
1127 | do_pause();
|
---|
1128 | }
|
---|
1129 | } else if (strcmp(buf, "coredump") == 0) {
|
---|
1130 | if ( !get_boolean(&str, &docoredump) ) {
|
---|
1131 | goto syntax_error;
|
---|
1132 | }
|
---|
1133 | } else if (strcmp(buf, "errorexit") == 0) {
|
---|
1134 | if ( !get_boolean(&str, &(gdata->doerrorexit)) ) {
|
---|
1135 | goto syntax_error;
|
---|
1136 | }
|
---|
1137 | } else if (strcmp(buf, "exitpause") == 0) {
|
---|
1138 | errmsg = "The exitpause option removed, use -XX:OnError";
|
---|
1139 | goto bad_option_with_errmsg;
|
---|
1140 | } else if (strcmp(buf, "precrash") == 0) {
|
---|
1141 | errmsg = "The precrash option removed, use -XX:OnError";
|
---|
1142 | goto bad_option_with_errmsg;
|
---|
1143 | } else if (strcmp(buf, "logfile") == 0) {
|
---|
1144 | /*LINTED*/
|
---|
1145 | if (!get_tok(&str, current, (int)(end - current), ',')) {
|
---|
1146 | goto syntax_error;
|
---|
1147 | }
|
---|
1148 | logfile = current;
|
---|
1149 | current += strlen(current) + 1;
|
---|
1150 | } else if (strcmp(buf, "logflags") == 0) {
|
---|
1151 | /*LINTED*/
|
---|
1152 | if (!get_tok(&str, current, (int)(end - current), ',')) {
|
---|
1153 | goto syntax_error;
|
---|
1154 | }
|
---|
1155 | /*LINTED*/
|
---|
1156 | logflags = (unsigned)strtol(current, NULL, 0);
|
---|
1157 | } else if (strcmp(buf, "debugflags") == 0) {
|
---|
1158 | /*LINTED*/
|
---|
1159 | if (!get_tok(&str, current, (int)(end - current), ',')) {
|
---|
1160 | goto syntax_error;
|
---|
1161 | }
|
---|
1162 | /*LINTED*/
|
---|
1163 | gdata->debugflags = (unsigned)strtol(current, NULL, 0);
|
---|
1164 | } else if ( strcmp(buf, "suspend")==0 ) {
|
---|
1165 | if ( !get_boolean(&str, &suspendOnInit) ) {
|
---|
1166 | goto syntax_error;
|
---|
1167 | }
|
---|
1168 | } else if ( strcmp(buf, "server")==0 ) {
|
---|
1169 | if ( !get_boolean(&str, &isServer) ) {
|
---|
1170 | goto syntax_error;
|
---|
1171 | }
|
---|
1172 | } else if ( strcmp(buf, "strict")==0 ) { /* Obsolete, but accept it */
|
---|
1173 | if ( !get_boolean(&str, &isStrict) ) {
|
---|
1174 | goto syntax_error;
|
---|
1175 | }
|
---|
1176 | } else if ( strcmp(buf, "quiet")==0 ) {
|
---|
1177 | if ( !get_boolean(&str, &(gdata->quiet)) ) {
|
---|
1178 | goto syntax_error;
|
---|
1179 | }
|
---|
1180 | } else if ( strcmp(buf, "onuncaught")==0 ) {
|
---|
1181 | if ( !get_boolean(&str, &initOnUncaught) ) {
|
---|
1182 | goto syntax_error;
|
---|
1183 | }
|
---|
1184 | } else if ( strcmp(buf, "mutf8")==0 ) {
|
---|
1185 | if ( !get_boolean(&str, &(gdata->modifiedUtf8)) ) {
|
---|
1186 | goto syntax_error;
|
---|
1187 | }
|
---|
1188 | } else if ( strcmp(buf, "stdalloc")==0 ) { /* Obsolete, but accept it */
|
---|
1189 | if ( !get_boolean(&str, &useStandardAlloc) ) {
|
---|
1190 | goto syntax_error;
|
---|
1191 | }
|
---|
1192 | } else {
|
---|
1193 | goto syntax_error;
|
---|
1194 | }
|
---|
1195 | }
|
---|
1196 |
|
---|
1197 | /* Setup logging now */
|
---|
1198 | if ( logfile!=NULL ) {
|
---|
1199 | setup_logging(logfile, logflags);
|
---|
1200 | (void)atexit(&atexit_finish_logging);
|
---|
1201 | }
|
---|
1202 |
|
---|
1203 | if (bagSize(transports) == 0) {
|
---|
1204 | errmsg = "no transport specified";
|
---|
1205 | goto bad_option_with_errmsg;
|
---|
1206 | }
|
---|
1207 |
|
---|
1208 | /*
|
---|
1209 | * TO DO: Remove when multiple transports are allowed. (replace with
|
---|
1210 | * check below.
|
---|
1211 | */
|
---|
1212 | if (bagSize(transports) > 1) {
|
---|
1213 | errmsg = "multiple transports are not supported in this release";
|
---|
1214 | goto bad_option_with_errmsg;
|
---|
1215 | }
|
---|
1216 |
|
---|
1217 |
|
---|
1218 | if (!isServer) {
|
---|
1219 | jboolean specified = bagEnumerateOver(transports, checkAddress, NULL);
|
---|
1220 | if (!specified) {
|
---|
1221 | /* message already printed */
|
---|
1222 | goto bad_option_no_msg;
|
---|
1223 | }
|
---|
1224 | }
|
---|
1225 |
|
---|
1226 | /*
|
---|
1227 | * The user has selected to wait for an exception before init happens
|
---|
1228 | */
|
---|
1229 | if ((initOnException != NULL) || (initOnUncaught)) {
|
---|
1230 | initOnStartup = JNI_FALSE;
|
---|
1231 |
|
---|
1232 | if (launchOnInit == NULL) {
|
---|
1233 | /*
|
---|
1234 | * These rely on the launch=/usr/bin/foo
|
---|
1235 | * suboption, so it is an error if user did not
|
---|
1236 | * provide one.
|
---|
1237 | */
|
---|
1238 | errmsg = "Specify launch=<command line> when using onthrow or onuncaught suboption";
|
---|
1239 | goto bad_option_with_errmsg;
|
---|
1240 | }
|
---|
1241 | }
|
---|
1242 |
|
---|
1243 | return JNI_TRUE;
|
---|
1244 |
|
---|
1245 | syntax_error:
|
---|
1246 | ERROR_MESSAGE(("JDWP option syntax error: %s=%s", AGENTLIB, options));
|
---|
1247 | return JNI_FALSE;
|
---|
1248 |
|
---|
1249 | bad_option_with_errmsg:
|
---|
1250 | ERROR_MESSAGE(("JDWP %s: %s=%s", errmsg, AGENTLIB, options));
|
---|
1251 | return JNI_FALSE;
|
---|
1252 |
|
---|
1253 | bad_option_no_msg:
|
---|
1254 | ERROR_MESSAGE(("JDWP %s: %s=%s", "invalid option", AGENTLIB, options));
|
---|
1255 | return JNI_FALSE;
|
---|
1256 | }
|
---|
1257 |
|
---|
1258 | /* All normal exit doors lead here */
|
---|
1259 | void
|
---|
1260 | debugInit_exit(jvmtiError error, const char *msg)
|
---|
1261 | {
|
---|
1262 | int exit_code = 0;
|
---|
1263 |
|
---|
1264 | /* Pick an error code */
|
---|
1265 | if ( error != JVMTI_ERROR_NONE ) {
|
---|
1266 | exit_code = 1;
|
---|
1267 | if ( docoredump ) {
|
---|
1268 | finish_logging(exit_code);
|
---|
1269 | abort();
|
---|
1270 | }
|
---|
1271 | }
|
---|
1272 | if ( msg==NULL ) {
|
---|
1273 | msg = "";
|
---|
1274 | }
|
---|
1275 |
|
---|
1276 | LOG_MISC(("Exiting with error %s(%d): %s", jvmtiErrorText(error), error, msg));
|
---|
1277 |
|
---|
1278 | gdata->vmDead = JNI_TRUE;
|
---|
1279 |
|
---|
1280 | /* Let's try and cleanup the JVMTI, if we even have one */
|
---|
1281 | if ( gdata->jvmti != NULL ) {
|
---|
1282 | /* Dispose of jvmti (gdata->jvmti becomes NULL) */
|
---|
1283 | disposeEnvironment(gdata->jvmti);
|
---|
1284 | }
|
---|
1285 |
|
---|
1286 | /* Finish up logging. We reach here if JDWP is doing the exiting. */
|
---|
1287 | finish_logging(exit_code); /* Only first call matters */
|
---|
1288 |
|
---|
1289 | /* Let's give the JNI a FatalError if non-exit 0, which is historic way */
|
---|
1290 | if ( exit_code != 0 ) {
|
---|
1291 | JNIEnv *env = NULL;
|
---|
1292 | jniFatalError(env, msg, error, exit_code);
|
---|
1293 | }
|
---|
1294 |
|
---|
1295 | /* Last chance to die, this kills the entire process. */
|
---|
1296 | forceExit(exit_code);
|
---|
1297 | }
|
---|