1 /* 2 * Copyright (c) 2016, 2018, 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 "jfr/recorder/checkpoint/jfrCheckpointWriter.hpp" 27 #include "jfr/recorder/checkpoint/types/jfrThreadGroup.hpp" 28 #include "jfr/utilities/jfrResourceManager.hpp" 29 #include "jfr/utilities/jfrTypes.hpp" 30 #include "runtime/handles.inline.hpp" 31 #include "runtime/jniHandles.hpp" 32 #include "runtime/safepoint.hpp" 33 #include "runtime/semaphore.hpp" 34 #include "utilities/growableArray.hpp" 35 36 class ThreadGroupExclusiveAccess : public StackObj { 37 private: 38 static Semaphore _mutex_semaphore; 39 public: 40 ThreadGroupExclusiveAccess() { _mutex_semaphore.wait(); } 41 ~ThreadGroupExclusiveAccess() { _mutex_semaphore.signal(); } 42 }; 43 44 Semaphore ThreadGroupExclusiveAccess::_mutex_semaphore(1); 45 JfrThreadGroup* JfrThreadGroup::_instance = NULL; 46 47 class JfrThreadGroupPointers : public ResourceObj { 48 private: 49 const Handle _thread_group_handle; 50 jweak _thread_group_weak_ref; 51 public: 52 JfrThreadGroupPointers(Handle thread_group_handle, jweak thread_group_weak_ref); 53 Handle thread_group_handle() const; 54 jweak thread_group_weak_ref() const; 55 oopDesc* const thread_group_oop() const; 56 jweak transfer_weak_global_handle_ownership(); 57 void clear_weak_ref(); 58 }; 59 60 JfrThreadGroupPointers::JfrThreadGroupPointers(Handle thread_group_handle, jweak thread_group_weak_ref) : 61 _thread_group_handle(thread_group_handle), 62 _thread_group_weak_ref(thread_group_weak_ref) {} 63 64 Handle JfrThreadGroupPointers::thread_group_handle() const { 65 return _thread_group_handle; 66 } 67 68 jweak JfrThreadGroupPointers::thread_group_weak_ref() const { 69 return _thread_group_weak_ref; 70 } 71 72 oopDesc* const JfrThreadGroupPointers::thread_group_oop() const { 73 assert(_thread_group_weak_ref == NULL || 74 JNIHandles::resolve_non_null(_thread_group_weak_ref) == _thread_group_handle(), "invariant"); 75 return _thread_group_handle(); 76 } 77 78 jweak JfrThreadGroupPointers::transfer_weak_global_handle_ownership() { 79 jweak temp = _thread_group_weak_ref; 80 _thread_group_weak_ref = NULL; 81 return temp; 82 } 83 84 void JfrThreadGroupPointers::clear_weak_ref() { 85 if (NULL != _thread_group_weak_ref) { 86 JNIHandles::destroy_weak_global(_thread_group_weak_ref); 87 } 88 } 89 90 class JfrThreadGroupsHelper : public ResourceObj { 91 private: 92 static const int invalid_iterator_pos = -1; 93 GrowableArray<JfrThreadGroupPointers*>* _thread_group_hierarchy; 94 int _current_iterator_pos; 95 96 int populate_thread_group_hierarchy(const JavaThread* jt, Thread* current); 97 JfrThreadGroupPointers& at(int index); 98 99 public: 100 JfrThreadGroupsHelper(const JavaThread* jt, Thread* current); 101 ~JfrThreadGroupsHelper(); 102 JfrThreadGroupPointers& next(); 103 bool is_valid() const; 104 bool has_next() const; 105 }; 106 107 JfrThreadGroupsHelper::JfrThreadGroupsHelper(const JavaThread* jt, Thread* current) { 108 _thread_group_hierarchy = new GrowableArray<JfrThreadGroupPointers*>(10, false, mtTracing); 109 _current_iterator_pos = populate_thread_group_hierarchy(jt, current) - 1; 110 } 111 112 JfrThreadGroupsHelper::~JfrThreadGroupsHelper() { 113 assert(_current_iterator_pos == invalid_iterator_pos, "invariant"); 114 for (int i = 0; i < _thread_group_hierarchy->length(); ++i) { 115 _thread_group_hierarchy->at(i)->clear_weak_ref(); 116 } 117 } 118 119 JfrThreadGroupPointers& JfrThreadGroupsHelper::at(int index) { 120 assert(_thread_group_hierarchy != NULL, "invariant"); 121 assert(index > invalid_iterator_pos && index < _thread_group_hierarchy->length(), "invariant"); 122 return *(_thread_group_hierarchy->at(index)); 123 } 124 125 bool JfrThreadGroupsHelper::has_next() const { 126 return _current_iterator_pos > invalid_iterator_pos; 127 } 128 129 bool JfrThreadGroupsHelper::is_valid() const { 130 return (_thread_group_hierarchy != NULL && _thread_group_hierarchy->length() > 0); 131 } 132 133 JfrThreadGroupPointers& JfrThreadGroupsHelper::next() { 134 assert(is_valid(), "invariant"); 135 return at(_current_iterator_pos--); 136 } 137 138 /* 139 * If not at a safepoint, we create global weak references for 140 * all reachable threadgroups for this thread. 141 * If we are at a safepoint, the caller is the VMThread during 142 * JFR checkpointing. It can use naked oops, because nothing 143 * will move before the list of threadgroups is cleared and 144 * mutator threads restarted. The threadgroup list is cleared 145 * later by the VMThread as one of the final steps in JFR checkpointing 146 * (not here). 147 */ 148 int JfrThreadGroupsHelper::populate_thread_group_hierarchy(const JavaThread* jt, Thread* current) { 149 assert(jt != NULL && jt->is_Java_thread(), "invariant"); 150 assert(current != NULL, "invariant"); 151 assert(_thread_group_hierarchy != NULL, "invariant"); 152 153 // immediate thread group 154 Handle thread_group_handle(current, java_lang_Thread::threadGroup(jt->threadObj())); 155 if (thread_group_handle == NULL) { 156 return 0; 157 } 158 159 const bool use_weak_handles = !SafepointSynchronize::is_at_safepoint(); 160 jweak thread_group_weak_ref = use_weak_handles ? JNIHandles::make_weak_global(thread_group_handle) : NULL; 161 162 JfrThreadGroupPointers* thread_group_pointers = new JfrThreadGroupPointers(thread_group_handle, thread_group_weak_ref); 163 _thread_group_hierarchy->append(thread_group_pointers); 164 // immediate parent thread group 165 oop parent_thread_group_obj = java_lang_ThreadGroup::parent(thread_group_handle()); 166 Handle parent_thread_group_handle(current, parent_thread_group_obj); 167 168 // and check parents parents... 169 while (!(parent_thread_group_handle == NULL)) { 170 const jweak parent_group_weak_ref = use_weak_handles ? JNIHandles::make_weak_global(parent_thread_group_handle) : NULL; 171 thread_group_pointers = new JfrThreadGroupPointers(parent_thread_group_handle, parent_group_weak_ref); 172 _thread_group_hierarchy->append(thread_group_pointers); 173 parent_thread_group_obj = java_lang_ThreadGroup::parent(parent_thread_group_handle()); 174 parent_thread_group_handle = Handle(current, parent_thread_group_obj); 175 } 176 return _thread_group_hierarchy->length(); 177 } 178 179 static traceid next_id() { 180 static traceid _current_threadgroup_id = 0; 181 return ++_current_threadgroup_id; 182 } 183 184 class JfrThreadGroup::JfrThreadGroupEntry : public JfrCHeapObj { 185 friend class JfrThreadGroup; 186 private: 187 traceid _thread_group_id; 188 traceid _parent_group_id; 189 char* _thread_group_name; // utf8 format 190 // If an entry is created during a safepoint, the 191 // _thread_group_oop contains a direct oop to 192 // the java.lang.ThreadGroup object. 193 // If an entry is created on javathread exit time (not at safepoint), 194 // _thread_group_weak_ref contains a JNI weak global handle 195 // indirection to the java.lang.ThreadGroup object. 196 // Note: we cannot use a union here since CHECK_UNHANDLED_OOPS makes oop have 197 // a ctor which isn't allowed in a union by the SunStudio compiler 198 oop _thread_group_oop; 199 jweak _thread_group_weak_ref; 200 201 JfrThreadGroupEntry(const char* tgstr, JfrThreadGroupPointers& ptrs); 202 ~JfrThreadGroupEntry(); 203 204 traceid thread_group_id() const { return _thread_group_id; } 205 void set_thread_group_id(traceid tgid) { _thread_group_id = tgid; } 206 207 const char* const thread_group_name() const { return _thread_group_name; } 208 void set_thread_group_name(const char* tgname); 209 210 traceid parent_group_id() const { return _parent_group_id; } 211 void set_parent_group_id(traceid pgid) { _parent_group_id = pgid; } 212 213 void set_thread_group(JfrThreadGroupPointers& ptrs); 214 bool is_equal(const JfrThreadGroupPointers& ptrs) const; 215 const oop thread_group() const; 216 }; 217 218 JfrThreadGroup::JfrThreadGroupEntry::JfrThreadGroupEntry(const char* tgname, JfrThreadGroupPointers& ptrs) : 219 _thread_group_id(0), 220 _parent_group_id(0), 221 _thread_group_name(NULL), 222 _thread_group_oop(NULL), 223 _thread_group_weak_ref(NULL) { 224 set_thread_group_name(tgname); 225 set_thread_group(ptrs); 226 } 227 228 JfrThreadGroup::JfrThreadGroupEntry::~JfrThreadGroupEntry() { 229 if (_thread_group_name != NULL) { 230 JfrCHeapObj::free(_thread_group_name, strlen(_thread_group_name) + 1); 231 } 232 if (_thread_group_weak_ref != NULL) { 233 JNIHandles::destroy_weak_global(_thread_group_weak_ref); 234 } 235 } 236 237 void JfrThreadGroup::JfrThreadGroupEntry::set_thread_group_name(const char* tgname) { 238 assert(_thread_group_name == NULL, "invariant"); 239 if (tgname != NULL) { 240 size_t len = strlen(tgname); 241 _thread_group_name = JfrCHeapObj::new_array<char>(len+1); 242 strncpy(_thread_group_name, tgname, len); 243 _thread_group_name[len] = '\0'; 244 } 245 } 246 247 const oop JfrThreadGroup::JfrThreadGroupEntry::thread_group() const { 248 return _thread_group_weak_ref != NULL ? JNIHandles::resolve(_thread_group_weak_ref) : _thread_group_oop; 249 } 250 251 void JfrThreadGroup::JfrThreadGroupEntry::set_thread_group(JfrThreadGroupPointers& ptrs) { 252 _thread_group_weak_ref = ptrs.transfer_weak_global_handle_ownership(); 253 if (_thread_group_weak_ref == NULL) { 254 _thread_group_oop = ptrs.thread_group_oop(); 255 assert(_thread_group_oop != NULL, "invariant"); 256 } else { 257 _thread_group_oop = NULL; 258 } 259 } 260 261 JfrThreadGroup::JfrThreadGroup() : _list(NULL) { 262 _list = new (ResourceObj::C_HEAP, mtTracing) GrowableArray<JfrThreadGroupEntry*>(30, true); 263 } 264 265 JfrThreadGroup::~JfrThreadGroup() { 266 assert(SafepointSynchronize::is_at_safepoint(), "invariant"); 267 if (_list != NULL) { 268 for (int i = 0; i < _list->length(); i++) { 269 JfrThreadGroupEntry* e = _list->at(i); 270 delete e; 271 } 272 delete _list; 273 } 274 } 275 276 JfrThreadGroup* JfrThreadGroup::instance() { 277 return _instance; 278 } 279 280 void JfrThreadGroup::set_instance(JfrThreadGroup* new_instance) { 281 _instance = new_instance; 282 } 283 284 traceid JfrThreadGroup::thread_group_id(const JavaThread* jt, Thread* current) { 285 ResourceMark rm(current); 286 HandleMark hm(current); 287 JfrThreadGroupsHelper helper(jt, current); 288 return helper.is_valid() ? thread_group_id_internal(helper) : 0; 289 } 290 291 traceid JfrThreadGroup::thread_group_id(JavaThread* jt) { 292 assert(!JfrStream_lock->owned_by_self(), "holding stream lock but should not hold it here"); 293 return thread_group_id(jt, jt); 294 } 295 296 traceid JfrThreadGroup::thread_group_id_internal(JfrThreadGroupsHelper& helper) { 297 ThreadGroupExclusiveAccess lock; 298 JfrThreadGroup* tg_instance = instance(); 299 if (tg_instance == NULL) { 300 tg_instance = new JfrThreadGroup(); 301 if (tg_instance == NULL) { 302 return 0; 303 } 304 set_instance(tg_instance); 305 } 306 307 JfrThreadGroupEntry* tge = NULL; 308 int parent_thread_group_id = 0; 309 while (helper.has_next()) { 310 JfrThreadGroupPointers& ptrs = helper.next(); 311 tge = tg_instance->find_entry(ptrs); 312 if (NULL == tge) { 313 tge = tg_instance->new_entry(ptrs); 314 assert(tge != NULL, "invariant"); 315 tge->set_parent_group_id(parent_thread_group_id); 316 } 317 parent_thread_group_id = tge->thread_group_id(); 318 } 319 // the last entry in the hierarchy is the immediate thread group 320 return tge->thread_group_id(); 321 } 322 323 bool JfrThreadGroup::JfrThreadGroupEntry::is_equal(const JfrThreadGroupPointers& ptrs) const { 324 return ptrs.thread_group_oop() == thread_group(); 325 } 326 327 JfrThreadGroup::JfrThreadGroupEntry* 328 JfrThreadGroup::find_entry(const JfrThreadGroupPointers& ptrs) const { 329 for (int index = 0; index < _list->length(); ++index) { 330 JfrThreadGroupEntry* curtge = _list->at(index); 331 if (curtge->is_equal(ptrs)) { 332 return curtge; 333 } 334 } 335 return (JfrThreadGroupEntry*) NULL; 336 } 337 338 // Assumes you already searched for the existence 339 // of a corresponding entry in find_entry(). 340 JfrThreadGroup::JfrThreadGroupEntry* 341 JfrThreadGroup::new_entry(JfrThreadGroupPointers& ptrs) { 342 typeArrayOop tg_name = java_lang_ThreadGroup::name(ptrs.thread_group_oop()); 343 JfrThreadGroupEntry* const tge = 344 new JfrThreadGroupEntry(UNICODE::as_utf8((jchar*) tg_name->base(T_CHAR), tg_name->length()), ptrs); 345 add_entry(tge); 346 return tge; 347 } 348 349 int JfrThreadGroup::add_entry(JfrThreadGroupEntry* tge) { 350 assert(tge != NULL, "attempting to add a null entry!"); 351 assert(0 == tge->thread_group_id(), "id must be unassigned!"); 352 tge->set_thread_group_id(next_id()); 353 return _list->append(tge); 354 } 355 356 void JfrThreadGroup::write_thread_group_entries(JfrCheckpointWriter& writer) const { 357 assert(_list != NULL && !_list->is_empty(), "should not need be here!"); 358 const int number_of_tg_entries = _list->length(); 359 writer.write_count(number_of_tg_entries); 360 for (int index = 0; index < number_of_tg_entries; ++index) { 361 const JfrThreadGroupEntry* const curtge = _list->at(index); 362 writer.write_key(curtge->thread_group_id()); 363 writer.write(curtge->parent_group_id()); 364 writer.write(curtge->thread_group_name()); 365 } 366 } 367 368 void JfrThreadGroup::write_selective_thread_group(JfrCheckpointWriter* writer, traceid thread_group_id) const { 369 assert(writer != NULL, "invariant"); 370 assert(_list != NULL && !_list->is_empty(), "should not need be here!"); 371 const int number_of_tg_entries = _list->length(); 372 373 // save context 374 const JfrCheckpointContext ctx = writer->context(); 375 writer->write_type(TYPE_THREADGROUP); 376 const jlong count_offset = writer->reserve(sizeof(u4)); // Don't know how many yet 377 int number_of_entries_written = 0; 378 for (int index = number_of_tg_entries - 1; index >= 0; --index) { 379 const JfrThreadGroupEntry* const curtge = _list->at(index); 380 if (thread_group_id == curtge->thread_group_id()) { 381 writer->write_key(curtge->thread_group_id()); 382 writer->write(curtge->parent_group_id()); 383 writer->write(curtge->thread_group_name()); 384 ++number_of_entries_written; 385 thread_group_id = curtge->parent_group_id(); 386 } 387 } 388 if (number_of_entries_written == 0) { 389 // nothing to write, restore context 390 writer->set_context(ctx); 391 return; 392 } 393 assert(number_of_entries_written > 0, "invariant"); 394 writer->write_count(number_of_entries_written, count_offset); 395 } 396 397 // Write out JfrThreadGroup instance and then delete it 398 void JfrThreadGroup::serialize(JfrCheckpointWriter& writer) { 399 ThreadGroupExclusiveAccess lock; 400 JfrThreadGroup* tg_instance = instance(); 401 assert(tg_instance != NULL, "invariant"); 402 ResourceManager<JfrThreadGroup> tg_handle(tg_instance); 403 set_instance(NULL); 404 tg_handle->write_thread_group_entries(writer); 405 } 406 407 // for writing a particular thread group 408 void JfrThreadGroup::serialize(JfrCheckpointWriter* writer, traceid thread_group_id) { 409 assert(writer != NULL, "invariant"); 410 ThreadGroupExclusiveAccess lock; 411 JfrThreadGroup* const tg_instance = instance(); 412 assert(tg_instance != NULL, "invariant"); 413 tg_instance->write_selective_thread_group(writer, thread_group_id); 414 }