File: | jdk/src/hotspot/share/services/attachListener.cpp |
Warning: | line 313, column 8 Although the value stored to 'name' is used in the enclosing expression, the value is never actually read from 'name' |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
1 | /* |
2 | * Copyright (c) 2005, 2021, 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. |
8 | * |
9 | * This code is distributed in the hope that it will be useful, but WITHOUT |
10 | * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or |
11 | * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License |
12 | * version 2 for more details (a copy is included in the LICENSE file that |
13 | * accompanied this code). |
14 | * |
15 | * You should have received a copy of the GNU General Public License version |
16 | * 2 along with this work; if not, write to the Free Software Foundation, |
17 | * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. |
18 | * |
19 | * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA |
20 | * or visit www.oracle.com if you need additional information or have any |
21 | * questions. |
22 | * |
23 | */ |
24 | |
25 | #include "precompiled.hpp" |
26 | #include "classfile/javaClasses.hpp" |
27 | #include "classfile/systemDictionary.hpp" |
28 | #include "classfile/vmClasses.hpp" |
29 | #include "gc/shared/gcVMOperations.hpp" |
30 | #include "memory/resourceArea.hpp" |
31 | #include "memory/universe.hpp" |
32 | #include "oops/oop.inline.hpp" |
33 | #include "oops/typeArrayOop.inline.hpp" |
34 | #include "prims/jvmtiExport.hpp" |
35 | #include "runtime/arguments.hpp" |
36 | #include "runtime/flags/jvmFlag.hpp" |
37 | #include "runtime/globals.hpp" |
38 | #include "runtime/handles.inline.hpp" |
39 | #include "runtime/java.hpp" |
40 | #include "runtime/javaCalls.hpp" |
41 | #include "runtime/os.hpp" |
42 | #include "runtime/vmOperations.hpp" |
43 | #include "services/attachListener.hpp" |
44 | #include "services/diagnosticCommand.hpp" |
45 | #include "services/heapDumper.hpp" |
46 | #include "services/writeableFlags.hpp" |
47 | #include "utilities/debug.hpp" |
48 | #include "utilities/formatBuffer.hpp" |
49 | |
50 | volatile AttachListenerState AttachListener::_state = AL_NOT_INITIALIZED; |
51 | |
52 | // Implementation of "properties" command. |
53 | // |
54 | // Invokes VMSupport.serializePropertiesToByteArray to serialize |
55 | // the system properties into a byte array. |
56 | |
57 | static InstanceKlass* load_and_initialize_klass(Symbol* sh, TRAPSJavaThread* __the_thread__) { |
58 | Klass* k = SystemDictionary::resolve_or_fail(sh, true, CHECK_NULL__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return __null; (void)(0); |
59 | InstanceKlass* ik = InstanceKlass::cast(k); |
60 | if (ik->should_be_initialized()) { |
61 | ik->initialize(CHECK_NULL__the_thread__); if ((((ThreadShadow*)__the_thread__)->has_pending_exception ())) return __null; (void)(0); |
62 | } |
63 | return ik; |
64 | } |
65 | |
66 | static jint get_properties(AttachOperation* op, outputStream* out, Symbol* serializePropertiesMethod) { |
67 | JavaThread* THREAD__the_thread__ = JavaThread::current(); // For exception macros. |
68 | HandleMark hm(THREAD__the_thread__); |
69 | |
70 | // load VMSupport |
71 | Symbol* klass = vmSymbols::jdk_internal_vm_VMSupport(); |
72 | InstanceKlass* k = load_and_initialize_klass(klass, THREAD__the_thread__); |
73 | if (HAS_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->has_pending_exception())) { |
74 | java_lang_Throwable::print(PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->pending_exception()), out); |
75 | CLEAR_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->clear_pending_exception( )); |
76 | return JNI_ERR(-1); |
77 | } |
78 | |
79 | // invoke the serializePropertiesToByteArray method |
80 | JavaValue result(T_OBJECT); |
81 | JavaCallArguments args; |
82 | |
83 | |
84 | Symbol* signature = vmSymbols::serializePropertiesToByteArray_signature(); |
85 | JavaCalls::call_static(&result, |
86 | k, |
87 | serializePropertiesMethod, |
88 | signature, |
89 | &args, |
90 | THREAD__the_thread__); |
91 | if (HAS_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->has_pending_exception())) { |
92 | java_lang_Throwable::print(PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->pending_exception()), out); |
93 | CLEAR_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->clear_pending_exception( )); |
94 | return JNI_ERR(-1); |
95 | } |
96 | |
97 | // The result should be a [B |
98 | oop res = result.get_oop(); |
99 | assert(res->is_typeArray(), "just checking")do { if (!(res->is_typeArray())) { (*g_assert_poison) = 'X' ;; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/services/attachListener.cpp" , 99, "assert(" "res->is_typeArray()" ") failed", "just checking" ); ::breakpoint(); } } while (0); |
100 | assert(TypeArrayKlass::cast(res->klass())->element_type() == T_BYTE, "just checking")do { if (!(TypeArrayKlass::cast(res->klass())->element_type () == T_BYTE)) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/services/attachListener.cpp" , 100, "assert(" "TypeArrayKlass::cast(res->klass())->element_type() == T_BYTE" ") failed", "just checking"); ::breakpoint(); } } while (0); |
101 | |
102 | // copy the bytes to the output stream |
103 | typeArrayOop ba = typeArrayOop(res); |
104 | jbyte* addr = typeArrayOop(res)->byte_at_addr(0); |
105 | out->print_raw((const char*)addr, ba->length()); |
106 | |
107 | return JNI_OK0; |
108 | } |
109 | |
110 | // Implementation of "load" command. |
111 | static jint load_agent(AttachOperation* op, outputStream* out) { |
112 | // get agent name and options |
113 | const char* agent = op->arg(0); |
114 | const char* absParam = op->arg(1); |
115 | const char* options = op->arg(2); |
116 | |
117 | // If loading a java agent then need to ensure that the java.instrument module is loaded |
118 | if (strcmp(agent, "instrument") == 0) { |
119 | JavaThread* THREAD__the_thread__ = JavaThread::current(); // For exception macros. |
120 | ResourceMark rm(THREAD__the_thread__); |
121 | HandleMark hm(THREAD__the_thread__); |
122 | JavaValue result(T_OBJECT); |
123 | Handle h_module_name = java_lang_String::create_from_str("java.instrument", THREAD__the_thread__); |
124 | JavaCalls::call_static(&result, |
125 | vmClasses::module_Modules_klass(), |
126 | vmSymbols::loadModule_name(), |
127 | vmSymbols::loadModule_signature(), |
128 | h_module_name, |
129 | THREAD__the_thread__); |
130 | if (HAS_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->has_pending_exception())) { |
131 | java_lang_Throwable::print(PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->pending_exception()), out); |
132 | CLEAR_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->clear_pending_exception( )); |
133 | return JNI_ERR(-1); |
134 | } |
135 | } |
136 | |
137 | return JvmtiExport::load_agent_library(agent, absParam, options, out); |
138 | } |
139 | |
140 | // Implementation of "properties" command. |
141 | // See also: PrintSystemPropertiesDCmd class |
142 | static jint get_system_properties(AttachOperation* op, outputStream* out) { |
143 | return get_properties(op, out, vmSymbols::serializePropertiesToByteArray_name()); |
144 | } |
145 | |
146 | // Implementation of "agent_properties" command. |
147 | static jint get_agent_properties(AttachOperation* op, outputStream* out) { |
148 | return get_properties(op, out, vmSymbols::serializeAgentPropertiesToByteArray_name()); |
149 | } |
150 | |
151 | // Implementation of "datadump" command. |
152 | // |
153 | // Raises a SIGBREAK signal so that VM dump threads, does deadlock detection, |
154 | // etc. In theory this command should only post a DataDumpRequest to any |
155 | // JVMTI environment that has enabled this event. However it's useful to |
156 | // trigger the SIGBREAK handler. |
157 | |
158 | static jint data_dump(AttachOperation* op, outputStream* out) { |
159 | if (!ReduceSignalUsage) { |
160 | AttachListener::pd_data_dump(); |
161 | } else { |
162 | if (JvmtiExport::should_post_data_dump()) { |
163 | JvmtiExport::post_data_dump(); |
164 | } |
165 | } |
166 | return JNI_OK0; |
167 | } |
168 | |
169 | // Implementation of "threaddump" command - essentially a remote ctrl-break |
170 | // See also: ThreadDumpDCmd class |
171 | // |
172 | static jint thread_dump(AttachOperation* op, outputStream* out) { |
173 | bool print_concurrent_locks = false; |
174 | bool print_extended_info = false; |
175 | if (op->arg(0) != NULL__null) { |
176 | for (int i = 0; op->arg(0)[i] != 0; ++i) { |
177 | if (op->arg(0)[i] == 'l') { |
178 | print_concurrent_locks = true; |
179 | } |
180 | if (op->arg(0)[i] == 'e') { |
181 | print_extended_info = true; |
182 | } |
183 | } |
184 | } |
185 | |
186 | // thread stacks and JNI global handles |
187 | VM_PrintThreads op1(out, print_concurrent_locks, print_extended_info, true /* print JNI handle info */); |
188 | VMThread::execute(&op1); |
189 | |
190 | // Deadlock detection |
191 | VM_FindDeadlocks op2(out); |
192 | VMThread::execute(&op2); |
193 | |
194 | return JNI_OK0; |
195 | } |
196 | |
197 | // A jcmd attach operation request was received, which will now |
198 | // dispatch to the diagnostic commands used for serviceability functions. |
199 | static jint jcmd(AttachOperation* op, outputStream* out) { |
200 | JavaThread* THREAD__the_thread__ = JavaThread::current(); // For exception macros. |
201 | // All the supplied jcmd arguments are stored as a single |
202 | // string (op->arg(0)). This is parsed by the Dcmd framework. |
203 | DCmd::parse_and_execute(DCmd_Source_AttachAPI, out, op->arg(0), ' ', THREAD__the_thread__); |
204 | if (HAS_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->has_pending_exception())) { |
205 | java_lang_Throwable::print(PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->pending_exception()), out); |
206 | out->cr(); |
207 | CLEAR_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->clear_pending_exception( )); |
208 | return JNI_ERR(-1); |
209 | } |
210 | return JNI_OK0; |
211 | } |
212 | |
213 | // Implementation of "dumpheap" command. |
214 | // See also: HeapDumpDCmd class |
215 | // |
216 | // Input arguments :- |
217 | // arg0: Name of the dump file |
218 | // arg1: "-live" or "-all" |
219 | // arg2: Compress level |
220 | jint dump_heap(AttachOperation* op, outputStream* out) { |
221 | const char* path = op->arg(0); |
222 | if (path == NULL__null || path[0] == '\0') { |
223 | out->print_cr("No dump file specified"); |
224 | } else { |
225 | bool live_objects_only = true; // default is true to retain the behavior before this change is made |
226 | const char* arg1 = op->arg(1); |
227 | if (arg1 != NULL__null && (strlen(arg1) > 0)) { |
228 | if (strcmp(arg1, "-all") != 0 && strcmp(arg1, "-live") != 0) { |
229 | out->print_cr("Invalid argument to dumpheap operation: %s", arg1); |
230 | return JNI_ERR(-1); |
231 | } |
232 | live_objects_only = strcmp(arg1, "-live") == 0; |
233 | } |
234 | |
235 | const char* num_str = op->arg(2); |
236 | uintx level = 0; |
237 | if (num_str != NULL__null && num_str[0] != '\0') { |
238 | if (!Arguments::parse_uintx(num_str, &level, 0)) { |
239 | out->print_cr("Invalid compress level: [%s]", num_str); |
240 | return JNI_ERR(-1); |
241 | } else if (level < 1 || level > 9) { |
242 | out->print_cr("Compression level out of range (1-9): " UINTX_FORMAT"%" "l" "u", level); |
243 | return JNI_ERR(-1); |
244 | } |
245 | } |
246 | // Parallel thread number for heap dump, initialize based on active processor count. |
247 | // Note the real number of threads used is also determined by active workers and compression |
248 | // backend thread number. See heapDumper.cpp. |
249 | uint parallel_thread_num = MAX2<uint>(1, (uint)os::initial_active_processor_count() * 3 / 8); |
250 | // Request a full GC before heap dump if live_objects_only = true |
251 | // This helps reduces the amount of unreachable objects in the dump |
252 | // and makes it easier to browse. |
253 | HeapDumper dumper(live_objects_only /* request GC */); |
254 | dumper.dump(path, out, (int)level, false, (uint)parallel_thread_num); |
255 | } |
256 | return JNI_OK0; |
257 | } |
258 | |
259 | // Implementation of "inspectheap" command |
260 | // See also: ClassHistogramDCmd class |
261 | // |
262 | // Input arguments :- |
263 | // arg0: "-live" or "-all" |
264 | // arg1: Name of the dump file or NULL |
265 | // arg2: parallel thread number |
266 | static jint heap_inspection(AttachOperation* op, outputStream* out) { |
267 | bool live_objects_only = true; // default is true to retain the behavior before this change is made |
268 | outputStream* os = out; // if path not specified or path is NULL, use out |
269 | fileStream* fs = NULL__null; |
270 | const char* arg0 = op->arg(0); |
271 | uint parallel_thread_num = MAX2<uint>(1, (uint)os::initial_active_processor_count() * 3 / 8); |
272 | if (arg0 != NULL__null && (strlen(arg0) > 0)) { |
273 | if (strcmp(arg0, "-all") != 0 && strcmp(arg0, "-live") != 0) { |
274 | out->print_cr("Invalid argument to inspectheap operation: %s", arg0); |
275 | return JNI_ERR(-1); |
276 | } |
277 | live_objects_only = strcmp(arg0, "-live") == 0; |
278 | } |
279 | |
280 | const char* path = op->arg(1); |
281 | if (path != NULL__null && path[0] != '\0') { |
282 | // create file |
283 | fs = new (ResourceObj::C_HEAP, mtInternal) fileStream(path); |
284 | if (fs == NULL__null) { |
285 | out->print_cr("Failed to allocate space for file: %s", path); |
286 | } |
287 | os = fs; |
288 | } |
289 | |
290 | const char* num_str = op->arg(2); |
291 | if (num_str != NULL__null && num_str[0] != '\0') { |
292 | uintx num; |
293 | if (!Arguments::parse_uintx(num_str, &num, 0)) { |
294 | out->print_cr("Invalid parallel thread number: [%s]", num_str); |
295 | return JNI_ERR(-1); |
296 | } |
297 | parallel_thread_num = num == 0 ? parallel_thread_num : (uint)num; |
298 | } |
299 | |
300 | VM_GC_HeapInspection heapop(os, live_objects_only /* request full gc */, parallel_thread_num); |
301 | VMThread::execute(&heapop); |
302 | if (os != NULL__null && os != out) { |
303 | out->print_cr("Heap inspection file created: %s", path); |
304 | delete fs; |
305 | } |
306 | return JNI_OK0; |
307 | } |
308 | |
309 | // Implementation of "setflag" command |
310 | static jint set_flag(AttachOperation* op, outputStream* out) { |
311 | |
312 | const char* name = NULL__null; |
313 | if ((name = op->arg(0)) == NULL__null) { |
Although the value stored to 'name' is used in the enclosing expression, the value is never actually read from 'name' | |
314 | out->print_cr("flag name is missing"); |
315 | return JNI_ERR(-1); |
316 | } |
317 | |
318 | FormatBuffer<80> err_msg("%s", ""); |
319 | |
320 | int ret = WriteableFlags::set_flag(op->arg(0), op->arg(1), JVMFlagOrigin::ATTACH_ON_DEMAND, err_msg); |
321 | if (ret != JVMFlag::SUCCESS) { |
322 | if (ret == JVMFlag::NON_WRITABLE) { |
323 | // if the flag is not manageable try to change it through |
324 | // the platform dependent implementation |
325 | return AttachListener::pd_set_flag(op, out); |
326 | } else { |
327 | out->print_cr("%s", err_msg.buffer()); |
328 | } |
329 | |
330 | return JNI_ERR(-1); |
331 | } |
332 | return JNI_OK0; |
333 | } |
334 | |
335 | // Implementation of "printflag" command |
336 | // See also: PrintVMFlagsDCmd class |
337 | static jint print_flag(AttachOperation* op, outputStream* out) { |
338 | const char* name = NULL__null; |
339 | if ((name = op->arg(0)) == NULL__null) { |
340 | out->print_cr("flag name is missing"); |
341 | return JNI_ERR(-1); |
342 | } |
343 | JVMFlag* f = JVMFlag::find_flag(name); |
344 | if (f) { |
345 | f->print_as_flag(out); |
346 | out->cr(); |
347 | } else { |
348 | out->print_cr("no such flag '%s'", name); |
349 | } |
350 | return JNI_OK0; |
351 | } |
352 | |
353 | // Table to map operation names to functions. |
354 | |
355 | // names must be of length <= AttachOperation::name_length_max |
356 | static AttachOperationFunctionInfo funcs[] = { |
357 | { "agentProperties", get_agent_properties }, |
358 | { "datadump", data_dump }, |
359 | { "dumpheap", dump_heap }, |
360 | { "load", load_agent }, |
361 | { "properties", get_system_properties }, |
362 | { "threaddump", thread_dump }, |
363 | { "inspectheap", heap_inspection }, |
364 | { "setflag", set_flag }, |
365 | { "printflag", print_flag }, |
366 | { "jcmd", jcmd }, |
367 | { NULL__null, NULL__null } |
368 | }; |
369 | |
370 | |
371 | |
372 | // The Attach Listener threads services a queue. It dequeues an operation |
373 | // from the queue, examines the operation name (command), and dispatches |
374 | // to the corresponding function to perform the operation. |
375 | |
376 | static void attach_listener_thread_entry(JavaThread* thread, TRAPSJavaThread* __the_thread__) { |
377 | os::set_priority(thread, NearMaxPriority); |
378 | |
379 | assert(thread == Thread::current(), "Must be")do { if (!(thread == Thread::current())) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/services/attachListener.cpp" , 379, "assert(" "thread == Thread::current()" ") failed", "Must be" ); ::breakpoint(); } } while (0); |
380 | assert(thread->stack_base() != NULL && thread->stack_size() > 0,do { if (!(thread->stack_base() != __null && thread ->stack_size() > 0)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/services/attachListener.cpp" , 381, "assert(" "thread->stack_base() != __null && thread->stack_size() > 0" ") failed", "Should already be setup"); ::breakpoint(); } } while (0) |
381 | "Should already be setup")do { if (!(thread->stack_base() != __null && thread ->stack_size() > 0)) { (*g_assert_poison) = 'X';; report_vm_error ("/home/daniel/Projects/java/jdk/src/hotspot/share/services/attachListener.cpp" , 381, "assert(" "thread->stack_base() != __null && thread->stack_size() > 0" ") failed", "Should already be setup"); ::breakpoint(); } } while (0); |
382 | |
383 | if (AttachListener::pd_init() != 0) { |
384 | AttachListener::set_state(AL_NOT_INITIALIZED); |
385 | return; |
386 | } |
387 | AttachListener::set_initialized(); |
388 | |
389 | for (;;) { |
390 | AttachOperation* op = AttachListener::dequeue(); |
391 | if (op == NULL__null) { |
392 | AttachListener::set_state(AL_NOT_INITIALIZED); |
393 | return; // dequeue failed or shutdown |
394 | } |
395 | |
396 | ResourceMark rm; |
397 | bufferedStream st; |
398 | jint res = JNI_OK0; |
399 | |
400 | // handle special detachall operation |
401 | if (strcmp(op->name(), AttachOperation::detachall_operation_name()) == 0) { |
402 | AttachListener::detachall(); |
403 | } else if (!EnableDynamicAgentLoading && strcmp(op->name(), "load") == 0) { |
404 | st.print("Dynamic agent loading is not enabled. " |
405 | "Use -XX:+EnableDynamicAgentLoading to launch target VM."); |
406 | res = JNI_ERR(-1); |
407 | } else { |
408 | // find the function to dispatch too |
409 | AttachOperationFunctionInfo* info = NULL__null; |
410 | for (int i=0; funcs[i].name != NULL__null; i++) { |
411 | const char* name = funcs[i].name; |
412 | assert(strlen(name) <= AttachOperation::name_length_max, "operation <= name_length_max")do { if (!(strlen(name) <= AttachOperation::name_length_max )) { (*g_assert_poison) = 'X';; report_vm_error("/home/daniel/Projects/java/jdk/src/hotspot/share/services/attachListener.cpp" , 412, "assert(" "strlen(name) <= AttachOperation::name_length_max" ") failed", "operation <= name_length_max"); ::breakpoint (); } } while (0); |
413 | if (strcmp(op->name(), name) == 0) { |
414 | info = &(funcs[i]); |
415 | break; |
416 | } |
417 | } |
418 | |
419 | // check for platform dependent attach operation |
420 | if (info == NULL__null) { |
421 | info = AttachListener::pd_find_operation(op->name()); |
422 | } |
423 | |
424 | if (info != NULL__null) { |
425 | // dispatch to the function that implements this operation |
426 | res = (info->func)(op, &st); |
427 | } else { |
428 | st.print("Operation %s not recognized!", op->name()); |
429 | res = JNI_ERR(-1); |
430 | } |
431 | } |
432 | |
433 | // operation complete - send result and output to client |
434 | op->complete(res, &st); |
435 | } |
436 | |
437 | ShouldNotReachHere()do { (*g_assert_poison) = 'X';; report_should_not_reach_here( "/home/daniel/Projects/java/jdk/src/hotspot/share/services/attachListener.cpp" , 437); ::breakpoint(); } while (0); |
438 | } |
439 | |
440 | bool AttachListener::has_init_error(TRAPSJavaThread* __the_thread__) { |
441 | if (HAS_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->has_pending_exception())) { |
442 | tty->print_cr("Exception in VM (AttachListener::init) : "); |
443 | java_lang_Throwable::print(PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->pending_exception()), tty); |
444 | tty->cr(); |
445 | |
446 | CLEAR_PENDING_EXCEPTION(((ThreadShadow*)__the_thread__)->clear_pending_exception( )); |
447 | |
448 | return true; |
449 | } else { |
450 | return false; |
451 | } |
452 | } |
453 | |
454 | // Starts the Attach Listener thread |
455 | void AttachListener::init() { |
456 | EXCEPTION_MARKExceptionMark __em; JavaThread* __the_thread__ = __em.thread( );; |
457 | |
458 | const char* name = "Attach Listener"; |
459 | Handle thread_oop = JavaThread::create_system_thread_object(name, true /* visible */, THREAD__the_thread__); |
460 | if (has_init_error(THREAD__the_thread__)) { |
461 | set_state(AL_NOT_INITIALIZED); |
462 | return; |
463 | } |
464 | |
465 | JavaThread* thread = new JavaThread(&attach_listener_thread_entry); |
466 | JavaThread::vm_exit_on_osthread_failure(thread); |
467 | |
468 | JavaThread::start_internal_daemon(THREAD__the_thread__, thread, thread_oop, NoPriority); |
469 | } |
470 | |
471 | // Performs clean-up tasks on platforms where we can detect that the last |
472 | // client has detached |
473 | void AttachListener::detachall() { |
474 | // call the platform dependent clean-up |
475 | pd_detachall(); |
476 | } |