< prev index next >

src/hotspot/share/code/codeHeapState.cpp

Print this page
rev 54097 : 8216314: SIGILL in CodeHeapState::print_names()
Reviewed-by: thartmann, kvn

*** 1,8 **** /* * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. ! * Copyright (c) 2018 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation. --- 1,8 ---- /* * Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved. ! * Copyright (c) 2018, 2019 SAP SE. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License version 2 only, as * published by the Free Software Foundation.
*** 38,56 **** // Aggregation condenses the information of a piece of the CodeHeap // (4096 bytes by default) into an analysis granule. These granules // contain enough detail to gain initial insight while keeping the // internal structure sizes in check. // - // The CodeHeap is a living thing. Therefore, the aggregate is collected - // under the CodeCache_lock. The subsequent print steps are only locked - // against concurrent aggregations. That keeps the impact on - // "normal operation" (JIT compiler and sweeper activity) to a minimum. - // // The second part, which consists of several, independent steps, // prints the previously collected information with emphasis on // various aspects. // // Data collection and printing is done on an "on request" basis. // While no request is being processed, there is no impact on performance. // The CodeHeap state analytics do have some memory footprint. // The "aggregate" step allocates some data structures to hold the aggregated // information for later output. These data structures live until they are --- 38,58 ---- // Aggregation condenses the information of a piece of the CodeHeap // (4096 bytes by default) into an analysis granule. These granules // contain enough detail to gain initial insight while keeping the // internal structure sizes in check. // // The second part, which consists of several, independent steps, // prints the previously collected information with emphasis on // various aspects. // + // The CodeHeap is a living thing. Therefore, protection against concurrent + // modification (by acquiring the CodeCache_lock) is necessary. It has + // to be provided by the caller of the analysis functions. + // If the CodeCache_lock is not held, the analysis functions may print + // less detailed information or may just do nothing. It is by intention + // that an unprotected invocation is not abnormally terminated. + // // Data collection and printing is done on an "on request" basis. // While no request is being processed, there is no impact on performance. // The CodeHeap state analytics do have some memory footprint. // The "aggregate" step allocates some data structures to hold the aggregated // information for later output. These data structures live until they are
*** 454,464 **** unsigned long total_iterations = 0; bool done = false; const int min_granules = 256; const int max_granules = 512*K; // limits analyzable CodeHeap (with segment_granules) to 32M..128M ! // results in StatArray size of 20M (= max_granules * 40 Bytes per element) // For a 1GB CodeHeap, the granule size must be at least 2kB to not violate the max_granles limit. const char* heapName = get_heapName(heap); STRINGSTREAM_DECL(ast, out) if (!initialization_complete) { --- 456,466 ---- unsigned long total_iterations = 0; bool done = false; const int min_granules = 256; const int max_granules = 512*K; // limits analyzable CodeHeap (with segment_granules) to 32M..128M ! // results in StatArray size of 24M (= max_granules * 48 Bytes per element) // For a 1GB CodeHeap, the granule size must be at least 2kB to not violate the max_granles limit. const char* heapName = get_heapName(heap); STRINGSTREAM_DECL(ast, out) if (!initialization_complete) {
*** 493,502 **** --- 495,510 ---- printBox(ast, '-', "Heap not fully initialized yet, segment size is zero for segment ", heapName); STRINGSTREAM_FLUSH("") return; } + if (!CodeCache_lock->owned_by_self()) { + printBox(ast, '-', "aggregate function called without holding the CodeCache_lock for ", heapName); + STRINGSTREAM_FLUSH("") + return; + } + // Calculate granularity of analysis (and output). // The CodeHeap is managed (allocated) in segments (units) of CodeCacheSegmentSize. // The CodeHeap can become fairly large, in particular in productive real-life systems. // // It is often neither feasible nor desirable to aggregate the data with the highest possible
*** 1027,1036 **** --- 1035,1045 ---- ast->print_cr("No hotness data available"); } STRINGSTREAM_FLUSH("\n") // This loop is intentionally printing directly to "out". + // It should not print anything, anyway. out->print("Verifying collected data..."); size_t granule_segs = granule_size>>log2_seg_size; for (unsigned int ix = 0; ix < granules; ix++) { if (StatArray[ix].t1_count > granule_segs) { out->print_cr("t1_count[%d] = %d", ix, StatArray[ix].t1_count);
*** 1070,1079 **** --- 1079,1089 ---- out->print_cr("t1_space[%d] = %d, t2_space[%d] = %d, tx_space[%d] = %d, stub_space[%d] = %d", ix, StatArray[ix].t1_space, ix, StatArray[ix].t2_space, ix, StatArray[ix].tx_space, ix, StatArray[ix].stub_space); } } // This loop is intentionally printing directly to "out". + // It should not print anything, anyway. if (used_topSizeBlocks > 0) { unsigned int j = 0; if (TopSizeArray[0].len != currMax) { out->print_cr("currMax(%d) differs from TopSizeArray[0].len(%d)", currMax, TopSizeArray[0].len); }
*** 1165,1174 **** --- 1175,1185 ---- } //---< calculate and fill remaining fields >--- if (FreeArray != NULL) { // This loop is intentionally printing directly to "out". + // It should not print anything, anyway. for (unsigned int ix = 0; ix < alloc_freeBlocks-1; ix++) { size_t lenSum = 0; FreeArray[ix].gap = (unsigned int)((address)FreeArray[ix+1].start - ((address)FreeArray[ix].start + FreeArray[ix].len)); for (HeapBlock *h = heap->next_block(FreeArray[ix].start); (h != NULL) && (h != FreeArray[ix+1].start); h = heap->next_block(h)) { CodeBlob *cb = (CodeBlob*)(heap->find_start(h));
*** 1222,1231 **** --- 1233,1243 ---- //---------------------------- //-- Print Top Used Blocks -- //---------------------------- { char* low_bound = heap->low_boundary(); + bool have_CodeCache_lock = CodeCache_lock->owned_by_self(); printBox(ast, '-', "Largest Used Blocks in ", heapName); print_blobType_legend(ast); ast->fill_to(51);
*** 1240,1255 **** //---< print Top Ten Used Blocks >--- if (used_topSizeBlocks > 0) { unsigned int printed_topSizeBlocks = 0; for (unsigned int i = 0; i != tsbStopper; i = TopSizeArray[i].index) { printed_topSizeBlocks++; - CodeBlob* this_blob = (CodeBlob*)(heap->find_start(TopSizeArray[i].start)); nmethod* nm = NULL; ! const char* blob_name = "unnamed blob"; ! if (this_blob != NULL) { blob_name = this_blob->name(); nm = this_blob->as_nmethod_or_null(); //---< blob address >--- ast->print(INTPTR_FORMAT, p2i(this_blob)); ast->fill_to(19); //---< blob offset from CodeHeap begin >--- ast->print("(+" PTR32_FORMAT ")", (unsigned int)((char*)this_blob-low_bound)); --- 1252,1274 ---- //---< print Top Ten Used Blocks >--- if (used_topSizeBlocks > 0) { unsigned int printed_topSizeBlocks = 0; for (unsigned int i = 0; i != tsbStopper; i = TopSizeArray[i].index) { printed_topSizeBlocks++; nmethod* nm = NULL; ! const char* blob_name = "unnamed blob or blob name unavailable"; ! // heap->find_start() is safe. Only works on _segmap. ! // Returns NULL or void*. Returned CodeBlob may be uninitialized. ! HeapBlock* heapBlock = TopSizeArray[i].start; ! CodeBlob* this_blob = (CodeBlob*)(heap->find_start(heapBlock)); ! bool blob_is_safe = blob_access_is_safe(this_blob, NULL); ! if (blob_is_safe) { ! //---< access these fields only if we own the CodeCache_lock >--- ! if (have_CodeCache_lock) { blob_name = this_blob->name(); nm = this_blob->as_nmethod_or_null(); + } //---< blob address >--- ast->print(INTPTR_FORMAT, p2i(this_blob)); ast->fill_to(19); //---< blob offset from CodeHeap begin >--- ast->print("(+" PTR32_FORMAT ")", (unsigned int)((char*)this_blob-low_bound));
*** 1261,1274 **** //---< block offset from CodeHeap begin >--- ast->print("(+" PTR32_FORMAT ")", (unsigned int)((char*)TopSizeArray[i].start-low_bound)); ast->fill_to(33); } - //---< print size, name, and signature (for nMethods) >--- ! if ((nm != NULL) && (nm->method() != NULL)) { ResourceMark rm; //---< nMethod size in hex >--- unsigned int total_size = nm->total_size(); ast->print(PTR32_FORMAT, total_size); ast->print("(" SIZE_FORMAT_W(4) "K)", total_size/K); ast->fill_to(51); --- 1280,1301 ---- //---< block offset from CodeHeap begin >--- ast->print("(+" PTR32_FORMAT ")", (unsigned int)((char*)TopSizeArray[i].start-low_bound)); ast->fill_to(33); } //---< print size, name, and signature (for nMethods) >--- ! // access nmethod and Method fields only if we own the CodeCache_lock. ! // This fact is implicitly transported via nm != NULL. ! if (CompiledMethod::nmethod_access_is_safe(nm)) { ResourceMark rm; + Method* method = nm->method(); + if (nm->is_in_use()) { + blob_name = method->name_and_sig_as_C_string(); + } + if (nm->is_not_entrant()) { + blob_name = method->name_and_sig_as_C_string(); + } //---< nMethod size in hex >--- unsigned int total_size = nm->total_size(); ast->print(PTR32_FORMAT, total_size); ast->print("(" SIZE_FORMAT_W(4) "K)", total_size/K); ast->fill_to(51);
*** 1279,1292 **** //---< method temperature >--- ast->fill_to(67); ast->print("%5d", nm->hotness_counter()); //---< name and signature >--- ast->fill_to(67+6); ! if (nm->is_in_use()) {blob_name = nm->method()->name_and_sig_as_C_string(); } ! if (nm->is_not_entrant()) {blob_name = nm->method()->name_and_sig_as_C_string(); } ! if (nm->is_not_installed()) {ast->print("%s", " not (yet) installed method "); } ! if (nm->is_zombie()) {ast->print("%s", " zombie method "); } ast->print("%s", blob_name); } else { //---< block size in hex >--- ast->print(PTR32_FORMAT, (unsigned int)(TopSizeArray[i].len<<log2_seg_size)); ast->print("(" SIZE_FORMAT_W(4) "K)", (TopSizeArray[i].len<<log2_seg_size)/K); --- 1306,1321 ---- //---< method temperature >--- ast->fill_to(67); ast->print("%5d", nm->hotness_counter()); //---< name and signature >--- ast->fill_to(67+6); ! if (nm->is_not_installed()) { ! ast->print(" not (yet) installed method "); ! } ! if (nm->is_zombie()) { ! ast->print(" zombie method "); ! } ast->print("%s", blob_name); } else { //---< block size in hex >--- ast->print(PTR32_FORMAT, (unsigned int)(TopSizeArray[i].len<<log2_seg_size)); ast->print("(" SIZE_FORMAT_W(4) "K)", (TopSizeArray[i].len<<log2_seg_size)/K);
*** 2086,2095 **** --- 2115,2125 ---- unsigned int granules_per_line = 128; char* low_bound = heap->low_boundary(); CodeBlob* last_blob = NULL; bool name_in_addr_range = true; + bool have_CodeCache_lock = CodeCache_lock->owned_by_self(); //---< print at least 128K per block (i.e. between headers) >--- if (granules_per_line*granule_size < 128*K) { granules_per_line = (unsigned int)((128*K)/granule_size); }
*** 2120,2153 **** // Only check granule if it contains at least one blob. unsigned int nBlobs = StatArray[ix].t1_count + StatArray[ix].t2_count + StatArray[ix].tx_count + StatArray[ix].stub_count + StatArray[ix].dead_count; if (nBlobs > 0 ) { for (unsigned int is = 0; is < granule_size; is+=(unsigned int)seg_size) { ! // heap->find_start() is safe. Only working with _segmap. Returns NULL or void*. Returned CodeBlob may be uninitialized. ! CodeBlob* this_blob = (CodeBlob *)(heap->find_start(low_bound+ix*granule_size+is)); ! bool blob_initialized = (this_blob != NULL) && (this_blob->header_size() >= 0) && (this_blob->relocation_size() >= 0) && ! ((address)this_blob + this_blob->header_size() == (address)(this_blob->relocation_begin())) && ! ((address)this_blob + CodeBlob::align_code_offset(this_blob->header_size() + this_blob->relocation_size()) == (address)(this_blob->content_begin())) && ! os::is_readable_pointer((address)(this_blob->relocation_begin())) && ! os::is_readable_pointer(this_blob->content_begin()); // blob could have been flushed, freed, and merged. // this_blob < last_blob is an indicator for that. ! if (blob_initialized && (this_blob > last_blob)) { last_blob = this_blob; //---< get type and name >--- blobType cbType = noType; if (segment_granules) { cbType = (blobType)StatArray[ix].type; } else { cbType = get_cbType(this_blob); } // this_blob->name() could return NULL if no name was given to CTOR. Inlined, maybe invisible on stack - const char* blob_name = this_blob->name(); if ((blob_name == NULL) || !os::is_readable_pointer(blob_name)) { blob_name = "<unavailable>"; } //---< print table header for new print range >--- if (!name_in_addr_range) { name_in_addr_range = true; ast->fill_to(51); --- 2150,2191 ---- // Only check granule if it contains at least one blob. unsigned int nBlobs = StatArray[ix].t1_count + StatArray[ix].t2_count + StatArray[ix].tx_count + StatArray[ix].stub_count + StatArray[ix].dead_count; if (nBlobs > 0 ) { for (unsigned int is = 0; is < granule_size; is+=(unsigned int)seg_size) { ! // heap->find_start() is safe. Only works on _segmap. ! // Returns NULL or void*. Returned CodeBlob may be uninitialized. ! char* this_seg = low_bound + ix*granule_size + is; ! CodeBlob* this_blob = (CodeBlob*)(heap->find_start(this_seg)); ! bool blob_is_safe = blob_access_is_safe(this_blob, NULL); // blob could have been flushed, freed, and merged. // this_blob < last_blob is an indicator for that. ! if (blob_is_safe && (this_blob > last_blob)) { last_blob = this_blob; //---< get type and name >--- blobType cbType = noType; if (segment_granules) { cbType = (blobType)StatArray[ix].type; } else { + //---< access these fields only if we own the CodeCache_lock >--- + if (have_CodeCache_lock) { cbType = get_cbType(this_blob); } + } + + //---< access these fields only if we own the CodeCache_lock >--- + const char* blob_name = "<unavailable>"; + nmethod* nm = NULL; + if (have_CodeCache_lock) { + blob_name = this_blob->name(); + nm = this_blob->as_nmethod_or_null(); // this_blob->name() could return NULL if no name was given to CTOR. Inlined, maybe invisible on stack if ((blob_name == NULL) || !os::is_readable_pointer(blob_name)) { blob_name = "<unavailable>"; } + } //---< print table header for new print range >--- if (!name_in_addr_range) { name_in_addr_range = true; ast->fill_to(51);
*** 2162,2173 **** ast->print(INTPTR_FORMAT, p2i(this_blob)); ast->fill_to(19); ast->print("(+" PTR32_FORMAT ")", (unsigned int)((char*)this_blob-low_bound)); ast->fill_to(33); ! // this_blob->as_nmethod_or_null() is safe. Inlined, maybe invisible on stack. ! nmethod* nm = this_blob->as_nmethod_or_null(); if (CompiledMethod::nmethod_access_is_safe(nm)) { Method* method = nm->method(); ResourceMark rm; //---< collect all data to locals as quickly as possible >--- unsigned int total_size = nm->total_size(); --- 2200,2211 ---- ast->print(INTPTR_FORMAT, p2i(this_blob)); ast->fill_to(19); ast->print("(+" PTR32_FORMAT ")", (unsigned int)((char*)this_blob-low_bound)); ast->fill_to(33); ! // access nmethod and Method fields only if we own the CodeCache_lock. ! // This fact is implicitly transported via nm != NULL. if (CompiledMethod::nmethod_access_is_safe(nm)) { Method* method = nm->method(); ResourceMark rm; //---< collect all data to locals as quickly as possible >--- unsigned int total_size = nm->total_size();
*** 2200,2217 **** ast->print("%s", methNameS); ast->print("%s", methSigS); } else { ast->print("%s", blob_name); } ! } else { ast->fill_to(62+6); ast->print("%s", blobTypeName[cbType]); ast->fill_to(82+6); ast->print("%s", blob_name); } STRINGSTREAM_FLUSH_LOCKED("\n") ! } else if (!blob_initialized && (this_blob != last_blob) && (this_blob != NULL)) { last_blob = this_blob; STRINGSTREAM_FLUSH_LOCKED("\n") } } } // nBlobs > 0 --- 2238,2258 ---- ast->print("%s", methNameS); ast->print("%s", methSigS); } else { ast->print("%s", blob_name); } ! } else if (blob_is_safe) { ast->fill_to(62+6); ast->print("%s", blobTypeName[cbType]); ast->fill_to(82+6); ast->print("%s", blob_name); + } else { + ast->fill_to(62+6); + ast->print("<stale blob>"); } STRINGSTREAM_FLUSH_LOCKED("\n") ! } else if (!blob_is_safe && (this_blob != last_blob) && (this_blob != NULL)) { last_blob = this_blob; STRINGSTREAM_FLUSH_LOCKED("\n") } } } // nBlobs > 0
*** 2374,2383 **** --- 2415,2427 ---- if (cb->is_safepoint_stub()) return safepointStub; if (cb->is_adapter_blob()) return adapterBlob; if (cb->is_method_handles_adapter_blob()) return mh_adapterBlob; if (cb->is_buffer_blob()) return bufferBlob; + //---< access these fields only if we own the CodeCache_lock >--- + // Should be ensured by caller. aggregate() amd print_names() do that. + if (CodeCache_lock->owned_by_self()) { nmethod* nm = cb->as_nmethod_or_null(); if (nm != NULL) { // no is_readable check required, nm = (nmethod*)cb. if (nm->is_not_installed()) return nMethod_inconstruction; if (nm->is_zombie()) return nMethod_dead; if (nm->is_unloaded()) return nMethod_unloaded;
*** 2385,2391 **** --- 2429,2447 ---- if (nm->is_alive() && !(nm->is_not_entrant())) return nMethod_notused; if (nm->is_alive()) return nMethod_alive; return nMethod_dead; } } + } return noType; } + + bool CodeHeapState::blob_access_is_safe(CodeBlob* this_blob, CodeBlob* prev_blob) { + return (this_blob != NULL) && // a blob must have been found, obviously + ((this_blob == prev_blob) || (prev_blob == NULL)) && // when re-checking, the same blob must have been found + (this_blob->header_size() >= 0) && + (this_blob->relocation_size() >= 0) && + ((address)this_blob + this_blob->header_size() == (address)(this_blob->relocation_begin())) && + ((address)this_blob + CodeBlob::align_code_offset(this_blob->header_size() + this_blob->relocation_size()) == (address)(this_blob->content_begin())) && + os::is_readable_pointer((address)(this_blob->relocation_begin())) && + os::is_readable_pointer(this_blob->content_begin()); + }
< prev index next >