1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/src/share/vm/jfr/recorder/checkpoint/types/jfrThreadGroup.cpp Mon Aug 12 18:30:40 2019 +0300 1.3 @@ -0,0 +1,414 @@ 1.4 +/* 1.5 + * Copyright (c) 2016, 2018, Oracle and/or its affiliates. All rights reserved. 1.6 + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 1.7 + * 1.8 + * This code is free software; you can redistribute it and/or modify it 1.9 + * under the terms of the GNU General Public License version 2 only, as 1.10 + * published by the Free Software Foundation. 1.11 + * 1.12 + * This code is distributed in the hope that it will be useful, but WITHOUT 1.13 + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 1.14 + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 1.15 + * version 2 for more details (a copy is included in the LICENSE file that 1.16 + * accompanied this code). 1.17 + * 1.18 + * You should have received a copy of the GNU General Public License version 1.19 + * 2 along with this work; if not, write to the Free Software Foundation, 1.20 + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 1.21 + * 1.22 + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 1.23 + * or visit www.oracle.com if you need additional information or have any 1.24 + * questions. 1.25 + * 1.26 + */ 1.27 + 1.28 +#include "precompiled.hpp" 1.29 +#include "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" 1.30 +#include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp" 1.31 +#include "jfr/utilities/jfrResourceManager.hpp" 1.32 +#include "jfr/utilities/jfrTypes.hpp" 1.33 +#include "runtime/handles.inline.hpp" 1.34 +#include "runtime/jniHandles.hpp" 1.35 +#include "runtime/safepoint.hpp" 1.36 +#include "runtime/semaphore.hpp" 1.37 +#include "utilities/growableArray.hpp" 1.38 + 1.39 +class ThreadGroupExclusiveAccess : public StackObj { 1.40 + private: 1.41 + static Semaphore _mutex_semaphore; 1.42 + public: 1.43 + ThreadGroupExclusiveAccess() { _mutex_semaphore.wait(); } 1.44 + ~ThreadGroupExclusiveAccess() { _mutex_semaphore.signal(); } 1.45 +}; 1.46 + 1.47 +Semaphore ThreadGroupExclusiveAccess::_mutex_semaphore(1); 1.48 +JfrThreadGroup* JfrThreadGroup::_instance = NULL; 1.49 + 1.50 +class JfrThreadGroupPointers : public ResourceObj { 1.51 + private: 1.52 + const Handle _thread_group_handle; 1.53 + jweak _thread_group_weak_ref; 1.54 + public: 1.55 + JfrThreadGroupPointers(Handle thread_group_handle, jweak thread_group_weak_ref); 1.56 + Handle thread_group_handle() const; 1.57 + jweak thread_group_weak_ref() const; 1.58 + oopDesc* const thread_group_oop() const; 1.59 + jweak transfer_weak_global_handle_ownership(); 1.60 + void clear_weak_ref(); 1.61 +}; 1.62 + 1.63 +JfrThreadGroupPointers::JfrThreadGroupPointers(Handle thread_group_handle, jweak thread_group_weak_ref) : 1.64 + _thread_group_handle(thread_group_handle), 1.65 + _thread_group_weak_ref(thread_group_weak_ref) {} 1.66 + 1.67 +Handle JfrThreadGroupPointers::thread_group_handle() const { 1.68 + return _thread_group_handle; 1.69 +} 1.70 + 1.71 +jweak JfrThreadGroupPointers::thread_group_weak_ref() const { 1.72 + return _thread_group_weak_ref; 1.73 +} 1.74 + 1.75 +oopDesc* const JfrThreadGroupPointers::thread_group_oop() const { 1.76 + assert(_thread_group_weak_ref == NULL || 1.77 + JNIHandles::resolve_non_null(_thread_group_weak_ref) == _thread_group_handle(), "invariant"); 1.78 + return _thread_group_handle(); 1.79 +} 1.80 + 1.81 +jweak JfrThreadGroupPointers::transfer_weak_global_handle_ownership() { 1.82 + jweak temp = _thread_group_weak_ref; 1.83 + _thread_group_weak_ref = NULL; 1.84 + return temp; 1.85 +} 1.86 + 1.87 +void JfrThreadGroupPointers::clear_weak_ref() { 1.88 + if (NULL != _thread_group_weak_ref) { 1.89 + JNIHandles::destroy_weak_global(_thread_group_weak_ref); 1.90 + } 1.91 +} 1.92 + 1.93 +class JfrThreadGroupsHelper : public ResourceObj { 1.94 + private: 1.95 + static const int invalid_iterator_pos = -1; 1.96 + GrowableArray<JfrThreadGroupPointers*>* _thread_group_hierarchy; 1.97 + int _current_iterator_pos; 1.98 + 1.99 + int populate_thread_group_hierarchy(const JavaThread* jt, Thread* current); 1.100 + JfrThreadGroupPointers& at(int index); 1.101 + 1.102 + public: 1.103 + JfrThreadGroupsHelper(const JavaThread* jt, Thread* current); 1.104 + ~JfrThreadGroupsHelper(); 1.105 + JfrThreadGroupPointers& next(); 1.106 + bool is_valid() const; 1.107 + bool has_next() const; 1.108 +}; 1.109 + 1.110 +JfrThreadGroupsHelper::JfrThreadGroupsHelper(const JavaThread* jt, Thread* current) { 1.111 + _thread_group_hierarchy = new GrowableArray<JfrThreadGroupPointers*>(10, false, mtTracing); 1.112 + _current_iterator_pos = populate_thread_group_hierarchy(jt, current) - 1; 1.113 +} 1.114 + 1.115 +JfrThreadGroupsHelper::~JfrThreadGroupsHelper() { 1.116 + assert(_current_iterator_pos == invalid_iterator_pos, "invariant"); 1.117 + for (int i = 0; i < _thread_group_hierarchy->length(); ++i) { 1.118 + _thread_group_hierarchy->at(i)->clear_weak_ref(); 1.119 + } 1.120 +} 1.121 + 1.122 +JfrThreadGroupPointers& JfrThreadGroupsHelper::at(int index) { 1.123 + assert(_thread_group_hierarchy != NULL, "invariant"); 1.124 + assert(index > invalid_iterator_pos && index < _thread_group_hierarchy->length(), "invariant"); 1.125 + return *(_thread_group_hierarchy->at(index)); 1.126 +} 1.127 + 1.128 +bool JfrThreadGroupsHelper::has_next() const { 1.129 + return _current_iterator_pos > invalid_iterator_pos; 1.130 +} 1.131 + 1.132 +bool JfrThreadGroupsHelper::is_valid() const { 1.133 + return (_thread_group_hierarchy != NULL && _thread_group_hierarchy->length() > 0); 1.134 +} 1.135 + 1.136 +JfrThreadGroupPointers& JfrThreadGroupsHelper::next() { 1.137 + assert(is_valid(), "invariant"); 1.138 + return at(_current_iterator_pos--); 1.139 +} 1.140 + 1.141 +/* 1.142 + * If not at a safepoint, we create global weak references for 1.143 + * all reachable threadgroups for this thread. 1.144 + * If we are at a safepoint, the caller is the VMThread during 1.145 + * JFR checkpointing. It can use naked oops, because nothing 1.146 + * will move before the list of threadgroups is cleared and 1.147 + * mutator threads restarted. The threadgroup list is cleared 1.148 + * later by the VMThread as one of the final steps in JFR checkpointing 1.149 + * (not here). 1.150 + */ 1.151 +int JfrThreadGroupsHelper::populate_thread_group_hierarchy(const JavaThread* jt, Thread* current) { 1.152 + assert(jt != NULL && jt->is_Java_thread(), "invariant"); 1.153 + assert(current != NULL, "invariant"); 1.154 + assert(_thread_group_hierarchy != NULL, "invariant"); 1.155 + 1.156 + // immediate thread group 1.157 + Handle thread_group_handle(current, java_lang_Thread::threadGroup(jt->threadObj())); 1.158 + if (thread_group_handle == NULL) { 1.159 + return 0; 1.160 + } 1.161 + 1.162 + const bool use_weak_handles = !SafepointSynchronize::is_at_safepoint(); 1.163 + jweak thread_group_weak_ref = use_weak_handles ? JNIHandles::make_weak_global(thread_group_handle) : NULL; 1.164 + 1.165 + JfrThreadGroupPointers* thread_group_pointers = new JfrThreadGroupPointers(thread_group_handle, thread_group_weak_ref); 1.166 + _thread_group_hierarchy->append(thread_group_pointers); 1.167 + // immediate parent thread group 1.168 + oop parent_thread_group_obj = java_lang_ThreadGroup::parent(thread_group_handle()); 1.169 + Handle parent_thread_group_handle(current, parent_thread_group_obj); 1.170 + 1.171 + // and check parents parents... 1.172 + while (!(parent_thread_group_handle == NULL)) { 1.173 + const jweak parent_group_weak_ref = use_weak_handles ? JNIHandles::make_weak_global(parent_thread_group_handle) : NULL; 1.174 + thread_group_pointers = new JfrThreadGroupPointers(parent_thread_group_handle, parent_group_weak_ref); 1.175 + _thread_group_hierarchy->append(thread_group_pointers); 1.176 + parent_thread_group_obj = java_lang_ThreadGroup::parent(parent_thread_group_handle()); 1.177 + parent_thread_group_handle = Handle(current, parent_thread_group_obj); 1.178 + } 1.179 + return _thread_group_hierarchy->length(); 1.180 +} 1.181 + 1.182 +static traceid next_id() { 1.183 + static traceid _current_threadgroup_id = 0; 1.184 + return ++_current_threadgroup_id; 1.185 +} 1.186 + 1.187 +class JfrThreadGroup::JfrThreadGroupEntry : public JfrCHeapObj { 1.188 + friend class JfrThreadGroup; 1.189 + private: 1.190 + traceid _thread_group_id; 1.191 + traceid _parent_group_id; 1.192 + char* _thread_group_name; // utf8 format 1.193 + // If an entry is created during a safepoint, the 1.194 + // _thread_group_oop contains a direct oop to 1.195 + // the java.lang.ThreadGroup object. 1.196 + // If an entry is created on javathread exit time (not at safepoint), 1.197 + // _thread_group_weak_ref contains a JNI weak global handle 1.198 + // indirection to the java.lang.ThreadGroup object. 1.199 + // Note: we cannot use a union here since CHECK_UNHANDLED_OOPS makes oop have 1.200 + // a ctor which isn't allowed in a union by the SunStudio compiler 1.201 + oop _thread_group_oop; 1.202 + jweak _thread_group_weak_ref; 1.203 + 1.204 + JfrThreadGroupEntry(const char* tgstr, JfrThreadGroupPointers& ptrs); 1.205 + ~JfrThreadGroupEntry(); 1.206 + 1.207 + traceid thread_group_id() const { return _thread_group_id; } 1.208 + void set_thread_group_id(traceid tgid) { _thread_group_id = tgid; } 1.209 + 1.210 + const char* const thread_group_name() const { return _thread_group_name; } 1.211 + void set_thread_group_name(const char* tgname); 1.212 + 1.213 + traceid parent_group_id() const { return _parent_group_id; } 1.214 + void set_parent_group_id(traceid pgid) { _parent_group_id = pgid; } 1.215 + 1.216 + void set_thread_group(JfrThreadGroupPointers& ptrs); 1.217 + bool is_equal(const JfrThreadGroupPointers& ptrs) const; 1.218 + const oop thread_group() const; 1.219 +}; 1.220 + 1.221 +JfrThreadGroup::JfrThreadGroupEntry::JfrThreadGroupEntry(const char* tgname, JfrThreadGroupPointers& ptrs) : 1.222 + _thread_group_id(0), 1.223 + _parent_group_id(0), 1.224 + _thread_group_name(NULL), 1.225 + _thread_group_oop(NULL), 1.226 + _thread_group_weak_ref(NULL) { 1.227 + set_thread_group_name(tgname); 1.228 + set_thread_group(ptrs); 1.229 +} 1.230 + 1.231 +JfrThreadGroup::JfrThreadGroupEntry::~JfrThreadGroupEntry() { 1.232 + if (_thread_group_name != NULL) { 1.233 + JfrCHeapObj::free(_thread_group_name, strlen(_thread_group_name) + 1); 1.234 + } 1.235 + if (_thread_group_weak_ref != NULL) { 1.236 + JNIHandles::destroy_weak_global(_thread_group_weak_ref); 1.237 + } 1.238 +} 1.239 + 1.240 +void JfrThreadGroup::JfrThreadGroupEntry::set_thread_group_name(const char* tgname) { 1.241 + assert(_thread_group_name == NULL, "invariant"); 1.242 + if (tgname != NULL) { 1.243 + size_t len = strlen(tgname); 1.244 + _thread_group_name = JfrCHeapObj::new_array<char>(len+1); 1.245 + strncpy(_thread_group_name, tgname, len); 1.246 + _thread_group_name[len] = '\0'; 1.247 + } 1.248 +} 1.249 + 1.250 +const oop JfrThreadGroup::JfrThreadGroupEntry::thread_group() const { 1.251 + return _thread_group_weak_ref != NULL ? JNIHandles::resolve(_thread_group_weak_ref) : _thread_group_oop; 1.252 +} 1.253 + 1.254 +void JfrThreadGroup::JfrThreadGroupEntry::set_thread_group(JfrThreadGroupPointers& ptrs) { 1.255 + _thread_group_weak_ref = ptrs.transfer_weak_global_handle_ownership(); 1.256 + if (_thread_group_weak_ref == NULL) { 1.257 + _thread_group_oop = ptrs.thread_group_oop(); 1.258 + assert(_thread_group_oop != NULL, "invariant"); 1.259 + } else { 1.260 + _thread_group_oop = NULL; 1.261 + } 1.262 +} 1.263 + 1.264 +JfrThreadGroup::JfrThreadGroup() : _list(NULL) { 1.265 + _list = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<JfrThreadGroupEntry*>(30, true); 1.266 +} 1.267 + 1.268 +JfrThreadGroup::~JfrThreadGroup() { 1.269 + assert(SafepointSynchronize::is_at_safepoint(), "invariant"); 1.270 + if (_list != NULL) { 1.271 + for (int i = 0; i < _list->length(); i++) { 1.272 + JfrThreadGroupEntry* e = _list->at(i); 1.273 + delete e; 1.274 + } 1.275 + delete _list; 1.276 + } 1.277 +} 1.278 + 1.279 +JfrThreadGroup* JfrThreadGroup::instance() { 1.280 + return _instance; 1.281 +} 1.282 + 1.283 +void JfrThreadGroup::set_instance(JfrThreadGroup* new_instance) { 1.284 + _instance = new_instance; 1.285 +} 1.286 + 1.287 +traceid JfrThreadGroup::thread_group_id(const JavaThread* jt, Thread* current) { 1.288 + ResourceMark rm(current); 1.289 + HandleMark hm(current); 1.290 + JfrThreadGroupsHelper helper(jt, current); 1.291 + return helper.is_valid() ? thread_group_id_internal(helper) : 0; 1.292 +} 1.293 + 1.294 +traceid JfrThreadGroup::thread_group_id(JavaThread* jt) { 1.295 + assert(!JfrStream_lock->owned_by_self(), "holding stream lock but should not hold it here"); 1.296 + return thread_group_id(jt, jt); 1.297 +} 1.298 + 1.299 +traceid JfrThreadGroup::thread_group_id_internal(JfrThreadGroupsHelper& helper) { 1.300 + ThreadGroupExclusiveAccess lock; 1.301 + JfrThreadGroup* tg_instance = instance(); 1.302 + if (tg_instance == NULL) { 1.303 + tg_instance = new JfrThreadGroup(); 1.304 + if (tg_instance == NULL) { 1.305 + return 0; 1.306 + } 1.307 + set_instance(tg_instance); 1.308 + } 1.309 + 1.310 + JfrThreadGroupEntry* tge = NULL; 1.311 + int parent_thread_group_id = 0; 1.312 + while (helper.has_next()) { 1.313 + JfrThreadGroupPointers& ptrs = helper.next(); 1.314 + tge = tg_instance->find_entry(ptrs); 1.315 + if (NULL == tge) { 1.316 + tge = tg_instance->new_entry(ptrs); 1.317 + assert(tge != NULL, "invariant"); 1.318 + tge->set_parent_group_id(parent_thread_group_id); 1.319 + } 1.320 + parent_thread_group_id = tge->thread_group_id(); 1.321 + } 1.322 + // the last entry in the hierarchy is the immediate thread group 1.323 + return tge->thread_group_id(); 1.324 +} 1.325 + 1.326 +bool JfrThreadGroup::JfrThreadGroupEntry::is_equal(const JfrThreadGroupPointers& ptrs) const { 1.327 + return ptrs.thread_group_oop() == thread_group(); 1.328 +} 1.329 + 1.330 +JfrThreadGroup::JfrThreadGroupEntry* 1.331 +JfrThreadGroup::find_entry(const JfrThreadGroupPointers& ptrs) const { 1.332 + for (int index = 0; index < _list->length(); ++index) { 1.333 + JfrThreadGroupEntry* curtge = _list->at(index); 1.334 + if (curtge->is_equal(ptrs)) { 1.335 + return curtge; 1.336 + } 1.337 + } 1.338 + return (JfrThreadGroupEntry*) NULL; 1.339 +} 1.340 + 1.341 +// Assumes you already searched for the existence 1.342 +// of a corresponding entry in find_entry(). 1.343 +JfrThreadGroup::JfrThreadGroupEntry* 1.344 +JfrThreadGroup::new_entry(JfrThreadGroupPointers& ptrs) { 1.345 + typeArrayOop tg_name = java_lang_ThreadGroup::name(ptrs.thread_group_oop()); 1.346 + JfrThreadGroupEntry* const tge = 1.347 + new JfrThreadGroupEntry(UNICODE::as_utf8((jchar*) tg_name->base(T_CHAR), tg_name->length()), ptrs); 1.348 + add_entry(tge); 1.349 + return tge; 1.350 +} 1.351 + 1.352 +int JfrThreadGroup::add_entry(JfrThreadGroupEntry* tge) { 1.353 + assert(tge != NULL, "attempting to add a null entry!"); 1.354 + assert(0 == tge->thread_group_id(), "id must be unassigned!"); 1.355 + tge->set_thread_group_id(next_id()); 1.356 + return _list->append(tge); 1.357 +} 1.358 + 1.359 +void JfrThreadGroup::write_thread_group_entries(JfrCheckpointWriter& writer) const { 1.360 + assert(_list != NULL && !_list->is_empty(), "should not need be here!"); 1.361 + const int number_of_tg_entries = _list->length(); 1.362 + writer.write_count(number_of_tg_entries); 1.363 + for (int index = 0; index < number_of_tg_entries; ++index) { 1.364 + const JfrThreadGroupEntry* const curtge = _list->at(index); 1.365 + writer.write_key(curtge->thread_group_id()); 1.366 + writer.write(curtge->parent_group_id()); 1.367 + writer.write(curtge->thread_group_name()); 1.368 + } 1.369 +} 1.370 + 1.371 +void JfrThreadGroup::write_selective_thread_group(JfrCheckpointWriter* writer, traceid thread_group_id) const { 1.372 + assert(writer != NULL, "invariant"); 1.373 + assert(_list != NULL && !_list->is_empty(), "should not need be here!"); 1.374 + const int number_of_tg_entries = _list->length(); 1.375 + 1.376 + // save context 1.377 + const JfrCheckpointContext ctx = writer->context(); 1.378 + writer->write_type(TYPE_THREADGROUP); 1.379 + const jlong count_offset = writer->reserve(sizeof(u4)); // Don't know how many yet 1.380 + int number_of_entries_written = 0; 1.381 + for (int index = number_of_tg_entries - 1; index >= 0; --index) { 1.382 + const JfrThreadGroupEntry* const curtge = _list->at(index); 1.383 + if (thread_group_id == curtge->thread_group_id()) { 1.384 + writer->write_key(curtge->thread_group_id()); 1.385 + writer->write(curtge->parent_group_id()); 1.386 + writer->write(curtge->thread_group_name()); 1.387 + ++number_of_entries_written; 1.388 + thread_group_id = curtge->parent_group_id(); 1.389 + } 1.390 + } 1.391 + if (number_of_entries_written == 0) { 1.392 + // nothing to write, restore context 1.393 + writer->set_context(ctx); 1.394 + return; 1.395 + } 1.396 + assert(number_of_entries_written > 0, "invariant"); 1.397 + writer->write_count(number_of_entries_written, count_offset); 1.398 +} 1.399 + 1.400 +// Write out JfrThreadGroup instance and then delete it 1.401 +void JfrThreadGroup::serialize(JfrCheckpointWriter& writer) { 1.402 + ThreadGroupExclusiveAccess lock; 1.403 + JfrThreadGroup* tg_instance = instance(); 1.404 + assert(tg_instance != NULL, "invariant"); 1.405 + ResourceManager<JfrThreadGroup> tg_handle(tg_instance); 1.406 + set_instance(NULL); 1.407 + tg_handle->write_thread_group_entries(writer); 1.408 +} 1.409 + 1.410 +// for writing a particular thread group 1.411 +void JfrThreadGroup::serialize(JfrCheckpointWriter* writer, traceid thread_group_id) { 1.412 + assert(writer != NULL, "invariant"); 1.413 + ThreadGroupExclusiveAccess lock; 1.414 + JfrThreadGroup* const tg_instance = instance(); 1.415 + assert(tg_instance != NULL, "invariant"); 1.416 + tg_instance->write_selective_thread_group(writer, thread_group_id); 1.417 +}