Path: blob/master/src/hotspot/share/code/codeHeapState.cpp
40930 views
/*1* Copyright (c) 2018, 2019, Oracle and/or its affiliates. All rights reserved.2* Copyright (c) 2018, 2019 SAP SE. 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 it6* under the terms of the GNU General Public License version 2 only, as7* published by the Free Software Foundation.8*9* This code is distributed in the hope that it will be useful, but WITHOUT10* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or11* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License12* version 2 for more details (a copy is included in the LICENSE file that13* accompanied this code).14*15* You should have received a copy of the GNU General Public License version16* 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 USA20* or visit www.oracle.com if you need additional information or have any21* questions.22*23*/2425#include "precompiled.hpp"26#include "code/codeHeapState.hpp"27#include "compiler/compileBroker.hpp"28#include "runtime/safepoint.hpp"29#include "runtime/sweeper.hpp"30#include "utilities/powerOfTwo.hpp"3132// -------------------------33// | General Description |34// -------------------------35// The CodeHeap state analytics are divided in two parts.36// The first part examines the entire CodeHeap and aggregates all37// information that is believed useful/important.38//39// Aggregation condenses the information of a piece of the CodeHeap40// (4096 bytes by default) into an analysis granule. These granules41// contain enough detail to gain initial insight while keeping the42// internal structure sizes in check.43//44// The second part, which consists of several, independent steps,45// prints the previously collected information with emphasis on46// various aspects.47//48// The CodeHeap is a living thing. Therefore, protection against concurrent49// modification (by acquiring the CodeCache_lock) is necessary. It has50// to be provided by the caller of the analysis functions.51// If the CodeCache_lock is not held, the analysis functions may print52// less detailed information or may just do nothing. It is by intention53// that an unprotected invocation is not abnormally terminated.54//55// Data collection and printing is done on an "on request" basis.56// While no request is being processed, there is no impact on performance.57// The CodeHeap state analytics do have some memory footprint.58// The "aggregate" step allocates some data structures to hold the aggregated59// information for later output. These data structures live until they are60// explicitly discarded (function "discard") or until the VM terminates.61// There is one exception: the function "all" does not leave any data62// structures allocated.63//64// Requests for real-time, on-the-fly analysis can be issued via65// jcmd <pid> Compiler.CodeHeap_Analytics [<function>] [<granularity>]66//67// If you are (only) interested in how the CodeHeap looks like after running68// a sample workload, you can use the command line option69// -XX:+PrintCodeHeapAnalytics70// It will cause a full analysis to be written to tty. In addition, a full71// analysis will be written the first time a "CodeCache full" condition is72// detected.73//74// The command line option produces output identical to the jcmd function75// jcmd <pid> Compiler.CodeHeap_Analytics all 409676// ---------------------------------------------------------------------------------7778// With this declaration macro, it is possible to switch between79// - direct output into an argument-passed outputStream and80// - buffered output into a bufferedStream with subsequent flush81// of the filled buffer to the outputStream.82#define USE_BUFFEREDSTREAM8384// There are instances when composing an output line or a small set of85// output lines out of many tty->print() calls creates significant overhead.86// Writing to a bufferedStream buffer first has a significant advantage:87// It uses noticeably less cpu cycles and reduces (when writing to a88// network file) the required bandwidth by at least a factor of ten. Observed on MacOS.89// That clearly makes up for the increased code complexity.90//91// Conversion of existing code is easy and straightforward, if the code already92// uses a parameterized output destination, e.g. "outputStream st".93// - rename the formal parameter to any other name, e.g. out_st.94// - at a suitable place in your code, insert95// BUFFEREDSTEAM_DECL(buf_st, out_st)96// This will provide all the declarations necessary. After that, all97// buf_st->print() (and the like) calls will be directed to a bufferedStream object.98// Once a block of output (a line or a small set of lines) is composed, insert99// BUFFEREDSTREAM_FLUSH(termstring)100// to flush the bufferedStream to the final destination out_st. termstring is just101// an arbitrary string (e.g. "\n") which is appended to the bufferedStream before102// being written to out_st. Be aware that the last character written MUST be a '\n'.103// Otherwise, buf_st->position() does not correspond to out_st->position() any longer.104// BUFFEREDSTREAM_FLUSH_LOCKED(termstring)105// does the same thing, protected by the ttyLocker lock.106// BUFFEREDSTREAM_FLUSH_IF(termstring, remSize)107// does a flush only if the remaining buffer space is less than remSize.108//109// To activate, #define USE_BUFFERED_STREAM before including this header.110// If not activated, output will directly go to the originally used outputStream111// with no additional overhead.112//113#if defined(USE_BUFFEREDSTREAM)114// All necessary declarations to print via a bufferedStream115// This macro must be placed before any other BUFFEREDSTREAM*116// macro in the function.117#define BUFFEREDSTREAM_DECL_SIZE(_anyst, _outst, _capa) \118ResourceMark _rm; \119/* _anyst name of the stream as used in the code */ \120/* _outst stream where final output will go to */ \121/* _capa allocated capacity of stream buffer */ \122size_t _nflush = 0; \123size_t _nforcedflush = 0; \124size_t _nsavedflush = 0; \125size_t _nlockedflush = 0; \126size_t _nflush_bytes = 0; \127size_t _capacity = _capa; \128bufferedStream _sstobj(_capa); \129bufferedStream* _sstbuf = &_sstobj; \130outputStream* _outbuf = _outst; \131bufferedStream* _anyst = &_sstobj; /* any stream. Use this to just print - no buffer flush. */132133// Same as above, but with fixed buffer size.134#define BUFFEREDSTREAM_DECL(_anyst, _outst) \135BUFFEREDSTREAM_DECL_SIZE(_anyst, _outst, 4*K);136137// Flush the buffer contents unconditionally.138// No action if the buffer is empty.139#define BUFFEREDSTREAM_FLUSH(_termString) \140if (((_termString) != NULL) && (strlen(_termString) > 0)){\141_sstbuf->print("%s", _termString); \142} \143if (_sstbuf != _outbuf) { \144if (_sstbuf->size() != 0) { \145_nforcedflush++; _nflush_bytes += _sstbuf->size(); \146_outbuf->print("%s", _sstbuf->as_string()); \147_sstbuf->reset(); \148} \149}150151// Flush the buffer contents if the remaining capacity is152// less than the given threshold.153#define BUFFEREDSTREAM_FLUSH_IF(_termString, _remSize) \154if (((_termString) != NULL) && (strlen(_termString) > 0)){\155_sstbuf->print("%s", _termString); \156} \157if (_sstbuf != _outbuf) { \158if ((_capacity - _sstbuf->size()) < (size_t)(_remSize)){\159_nflush++; _nforcedflush--; \160BUFFEREDSTREAM_FLUSH("") \161} else { \162_nsavedflush++; \163} \164}165166// Flush the buffer contents if the remaining capacity is less167// than the calculated threshold (256 bytes + capacity/16)168// That should suffice for all reasonably sized output lines.169#define BUFFEREDSTREAM_FLUSH_AUTO(_termString) \170BUFFEREDSTREAM_FLUSH_IF(_termString, 256+(_capacity>>4))171172#define BUFFEREDSTREAM_FLUSH_LOCKED(_termString) \173{ ttyLocker ttyl;/* keep this output block together */ \174_nlockedflush++; \175BUFFEREDSTREAM_FLUSH(_termString) \176}177178// #define BUFFEREDSTREAM_FLUSH_STAT() \179// if (_sstbuf != _outbuf) { \180// _outbuf->print_cr("%ld flushes (buffer full), %ld forced, %ld locked, %ld bytes total, %ld flushes saved", _nflush, _nforcedflush, _nlockedflush, _nflush_bytes, _nsavedflush); \181// }182183#define BUFFEREDSTREAM_FLUSH_STAT()184#else185#define BUFFEREDSTREAM_DECL_SIZE(_anyst, _outst, _capa) \186size_t _capacity = _capa; \187outputStream* _outbuf = _outst; \188outputStream* _anyst = _outst; /* any stream. Use this to just print - no buffer flush. */189190#define BUFFEREDSTREAM_DECL(_anyst, _outst) \191BUFFEREDSTREAM_DECL_SIZE(_anyst, _outst, 4*K)192193#define BUFFEREDSTREAM_FLUSH(_termString) \194if (((_termString) != NULL) && (strlen(_termString) > 0)){\195_outbuf->print("%s", _termString); \196}197198#define BUFFEREDSTREAM_FLUSH_IF(_termString, _remSize) \199BUFFEREDSTREAM_FLUSH(_termString)200201#define BUFFEREDSTREAM_FLUSH_AUTO(_termString) \202BUFFEREDSTREAM_FLUSH(_termString)203204#define BUFFEREDSTREAM_FLUSH_LOCKED(_termString) \205BUFFEREDSTREAM_FLUSH(_termString)206207#define BUFFEREDSTREAM_FLUSH_STAT()208#endif209#define HEX32_FORMAT "0x%x" // just a helper format string used below multiple times210211const char blobTypeChar[] = {' ', 'C', 'N', 'I', 'X', 'Z', 'U', 'R', '?', 'D', 'T', 'E', 'S', 'A', 'M', 'B', 'L' };212const char* blobTypeName[] = {"noType"213, "nMethod (under construction), cannot be observed"214, "nMethod (active)"215, "nMethod (inactive)"216, "nMethod (deopt)"217, "nMethod (zombie)"218, "nMethod (unloaded)"219, "runtime stub"220, "ricochet stub"221, "deopt stub"222, "uncommon trap stub"223, "exception stub"224, "safepoint stub"225, "adapter blob"226, "MH adapter blob"227, "buffer blob"228, "lastType"229};230const char* compTypeName[] = { "none", "c1", "c2", "jvmci" };231232// Be prepared for ten different CodeHeap segments. Should be enough for a few years.233const unsigned int nSizeDistElements = 31; // logarithmic range growth, max size: 2**32234const unsigned int maxTopSizeBlocks = 100;235const unsigned int tsbStopper = 2 * maxTopSizeBlocks;236const unsigned int maxHeaps = 10;237static unsigned int nHeaps = 0;238static struct CodeHeapStat CodeHeapStatArray[maxHeaps];239240// static struct StatElement *StatArray = NULL;241static StatElement* StatArray = NULL;242static int log2_seg_size = 0;243static size_t seg_size = 0;244static size_t alloc_granules = 0;245static size_t granule_size = 0;246static bool segment_granules = false;247static unsigned int nBlocks_t1 = 0; // counting "in_use" nmethods only.248static unsigned int nBlocks_t2 = 0; // counting "in_use" nmethods only.249static unsigned int nBlocks_alive = 0; // counting "not_used" and "not_entrant" nmethods only.250static unsigned int nBlocks_dead = 0; // counting "zombie" and "unloaded" methods only.251static unsigned int nBlocks_unloaded = 0; // counting "unloaded" nmethods only. This is a transient state.252static unsigned int nBlocks_stub = 0;253254static struct FreeBlk* FreeArray = NULL;255static unsigned int alloc_freeBlocks = 0;256257static struct TopSizeBlk* TopSizeArray = NULL;258static unsigned int alloc_topSizeBlocks = 0;259static unsigned int used_topSizeBlocks = 0;260261static struct SizeDistributionElement* SizeDistributionArray = NULL;262263// nMethod temperature (hotness) indicators.264static int avgTemp = 0;265static int maxTemp = 0;266static int minTemp = 0;267268static unsigned int latest_compilation_id = 0;269static volatile bool initialization_complete = false;270271const char* CodeHeapState::get_heapName(CodeHeap* heap) {272if (SegmentedCodeCache) {273return heap->name();274} else {275return "CodeHeap";276}277}278279// returns the index for the heap being processed.280unsigned int CodeHeapState::findHeapIndex(outputStream* out, const char* heapName) {281if (heapName == NULL) {282return maxHeaps;283}284if (SegmentedCodeCache) {285// Search for a pre-existing entry. If found, return that index.286for (unsigned int i = 0; i < nHeaps; i++) {287if (CodeHeapStatArray[i].heapName != NULL && strcmp(heapName, CodeHeapStatArray[i].heapName) == 0) {288return i;289}290}291292// check if there are more code heap segments than we can handle.293if (nHeaps == maxHeaps) {294out->print_cr("Too many heap segments for current limit(%d).", maxHeaps);295return maxHeaps;296}297298// allocate new slot in StatArray.299CodeHeapStatArray[nHeaps].heapName = heapName;300return nHeaps++;301} else {302nHeaps = 1;303CodeHeapStatArray[0].heapName = heapName;304return 0; // This is the default index if CodeCache is not segmented.305}306}307308void CodeHeapState::get_HeapStatGlobals(outputStream* out, const char* heapName) {309unsigned int ix = findHeapIndex(out, heapName);310if (ix < maxHeaps) {311StatArray = CodeHeapStatArray[ix].StatArray;312seg_size = CodeHeapStatArray[ix].segment_size;313log2_seg_size = seg_size == 0 ? 0 : exact_log2(seg_size);314alloc_granules = CodeHeapStatArray[ix].alloc_granules;315granule_size = CodeHeapStatArray[ix].granule_size;316segment_granules = CodeHeapStatArray[ix].segment_granules;317nBlocks_t1 = CodeHeapStatArray[ix].nBlocks_t1;318nBlocks_t2 = CodeHeapStatArray[ix].nBlocks_t2;319nBlocks_alive = CodeHeapStatArray[ix].nBlocks_alive;320nBlocks_dead = CodeHeapStatArray[ix].nBlocks_dead;321nBlocks_unloaded = CodeHeapStatArray[ix].nBlocks_unloaded;322nBlocks_stub = CodeHeapStatArray[ix].nBlocks_stub;323FreeArray = CodeHeapStatArray[ix].FreeArray;324alloc_freeBlocks = CodeHeapStatArray[ix].alloc_freeBlocks;325TopSizeArray = CodeHeapStatArray[ix].TopSizeArray;326alloc_topSizeBlocks = CodeHeapStatArray[ix].alloc_topSizeBlocks;327used_topSizeBlocks = CodeHeapStatArray[ix].used_topSizeBlocks;328SizeDistributionArray = CodeHeapStatArray[ix].SizeDistributionArray;329avgTemp = CodeHeapStatArray[ix].avgTemp;330maxTemp = CodeHeapStatArray[ix].maxTemp;331minTemp = CodeHeapStatArray[ix].minTemp;332} else {333StatArray = NULL;334seg_size = 0;335log2_seg_size = 0;336alloc_granules = 0;337granule_size = 0;338segment_granules = false;339nBlocks_t1 = 0;340nBlocks_t2 = 0;341nBlocks_alive = 0;342nBlocks_dead = 0;343nBlocks_unloaded = 0;344nBlocks_stub = 0;345FreeArray = NULL;346alloc_freeBlocks = 0;347TopSizeArray = NULL;348alloc_topSizeBlocks = 0;349used_topSizeBlocks = 0;350SizeDistributionArray = NULL;351avgTemp = 0;352maxTemp = 0;353minTemp = 0;354}355}356357void CodeHeapState::set_HeapStatGlobals(outputStream* out, const char* heapName) {358unsigned int ix = findHeapIndex(out, heapName);359if (ix < maxHeaps) {360CodeHeapStatArray[ix].StatArray = StatArray;361CodeHeapStatArray[ix].segment_size = seg_size;362CodeHeapStatArray[ix].alloc_granules = alloc_granules;363CodeHeapStatArray[ix].granule_size = granule_size;364CodeHeapStatArray[ix].segment_granules = segment_granules;365CodeHeapStatArray[ix].nBlocks_t1 = nBlocks_t1;366CodeHeapStatArray[ix].nBlocks_t2 = nBlocks_t2;367CodeHeapStatArray[ix].nBlocks_alive = nBlocks_alive;368CodeHeapStatArray[ix].nBlocks_dead = nBlocks_dead;369CodeHeapStatArray[ix].nBlocks_unloaded = nBlocks_unloaded;370CodeHeapStatArray[ix].nBlocks_stub = nBlocks_stub;371CodeHeapStatArray[ix].FreeArray = FreeArray;372CodeHeapStatArray[ix].alloc_freeBlocks = alloc_freeBlocks;373CodeHeapStatArray[ix].TopSizeArray = TopSizeArray;374CodeHeapStatArray[ix].alloc_topSizeBlocks = alloc_topSizeBlocks;375CodeHeapStatArray[ix].used_topSizeBlocks = used_topSizeBlocks;376CodeHeapStatArray[ix].SizeDistributionArray = SizeDistributionArray;377CodeHeapStatArray[ix].avgTemp = avgTemp;378CodeHeapStatArray[ix].maxTemp = maxTemp;379CodeHeapStatArray[ix].minTemp = minTemp;380}381}382383//---< get a new statistics array >---384void CodeHeapState::prepare_StatArray(outputStream* out, size_t nElem, size_t granularity, const char* heapName) {385if (StatArray == NULL) {386StatArray = new StatElement[nElem];387//---< reset some counts >---388alloc_granules = nElem;389granule_size = granularity;390}391392if (StatArray == NULL) {393//---< just do nothing if allocation failed >---394out->print_cr("Statistics could not be collected for %s, probably out of memory.", heapName);395out->print_cr("Current granularity is " SIZE_FORMAT " bytes. Try a coarser granularity.", granularity);396alloc_granules = 0;397granule_size = 0;398} else {399//---< initialize statistics array >---400memset((void*)StatArray, 0, nElem*sizeof(StatElement));401}402}403404//---< get a new free block array >---405void CodeHeapState::prepare_FreeArray(outputStream* out, unsigned int nElem, const char* heapName) {406if (FreeArray == NULL) {407FreeArray = new FreeBlk[nElem];408//---< reset some counts >---409alloc_freeBlocks = nElem;410}411412if (FreeArray == NULL) {413//---< just do nothing if allocation failed >---414out->print_cr("Free space analysis cannot be done for %s, probably out of memory.", heapName);415alloc_freeBlocks = 0;416} else {417//---< initialize free block array >---418memset((void*)FreeArray, 0, alloc_freeBlocks*sizeof(FreeBlk));419}420}421422//---< get a new TopSizeArray >---423void CodeHeapState::prepare_TopSizeArray(outputStream* out, unsigned int nElem, const char* heapName) {424if (TopSizeArray == NULL) {425TopSizeArray = new TopSizeBlk[nElem];426//---< reset some counts >---427alloc_topSizeBlocks = nElem;428used_topSizeBlocks = 0;429}430431if (TopSizeArray == NULL) {432//---< just do nothing if allocation failed >---433out->print_cr("Top-%d list of largest CodeHeap blocks can not be collected for %s, probably out of memory.", nElem, heapName);434alloc_topSizeBlocks = 0;435} else {436//---< initialize TopSizeArray >---437memset((void*)TopSizeArray, 0, nElem*sizeof(TopSizeBlk));438used_topSizeBlocks = 0;439}440}441442//---< get a new SizeDistributionArray >---443void CodeHeapState::prepare_SizeDistArray(outputStream* out, unsigned int nElem, const char* heapName) {444if (SizeDistributionArray == NULL) {445SizeDistributionArray = new SizeDistributionElement[nElem];446}447448if (SizeDistributionArray == NULL) {449//---< just do nothing if allocation failed >---450out->print_cr("Size distribution can not be collected for %s, probably out of memory.", heapName);451} else {452//---< initialize SizeDistArray >---453memset((void*)SizeDistributionArray, 0, nElem*sizeof(SizeDistributionElement));454// Logarithmic range growth. First range starts at _segment_size.455SizeDistributionArray[log2_seg_size-1].rangeEnd = 1U;456for (unsigned int i = log2_seg_size; i < nElem; i++) {457SizeDistributionArray[i].rangeStart = 1U << (i - log2_seg_size);458SizeDistributionArray[i].rangeEnd = 1U << ((i+1) - log2_seg_size);459}460}461}462463//---< get a new SizeDistributionArray >---464void CodeHeapState::update_SizeDistArray(outputStream* out, unsigned int len) {465if (SizeDistributionArray != NULL) {466for (unsigned int i = log2_seg_size-1; i < nSizeDistElements; i++) {467if ((SizeDistributionArray[i].rangeStart <= len) && (len < SizeDistributionArray[i].rangeEnd)) {468SizeDistributionArray[i].lenSum += len;469SizeDistributionArray[i].count++;470break;471}472}473}474}475476void CodeHeapState::discard_StatArray(outputStream* out) {477if (StatArray != NULL) {478delete StatArray;479StatArray = NULL;480alloc_granules = 0;481granule_size = 0;482}483}484485void CodeHeapState::discard_FreeArray(outputStream* out) {486if (FreeArray != NULL) {487delete[] FreeArray;488FreeArray = NULL;489alloc_freeBlocks = 0;490}491}492493void CodeHeapState::discard_TopSizeArray(outputStream* out) {494if (TopSizeArray != NULL) {495for (unsigned int i = 0; i < alloc_topSizeBlocks; i++) {496if (TopSizeArray[i].blob_name != NULL) {497os::free((void*)TopSizeArray[i].blob_name);498}499}500delete[] TopSizeArray;501TopSizeArray = NULL;502alloc_topSizeBlocks = 0;503used_topSizeBlocks = 0;504}505}506507void CodeHeapState::discard_SizeDistArray(outputStream* out) {508if (SizeDistributionArray != NULL) {509delete[] SizeDistributionArray;510SizeDistributionArray = NULL;511}512}513514// Discard all allocated internal data structures.515// This should be done after an analysis session is completed.516void CodeHeapState::discard(outputStream* out, CodeHeap* heap) {517if (!initialization_complete) {518return;519}520521if (nHeaps > 0) {522for (unsigned int ix = 0; ix < nHeaps; ix++) {523get_HeapStatGlobals(out, CodeHeapStatArray[ix].heapName);524discard_StatArray(out);525discard_FreeArray(out);526discard_TopSizeArray(out);527discard_SizeDistArray(out);528set_HeapStatGlobals(out, CodeHeapStatArray[ix].heapName);529CodeHeapStatArray[ix].heapName = NULL;530}531nHeaps = 0;532}533}534535void CodeHeapState::aggregate(outputStream* out, CodeHeap* heap, size_t granularity) {536unsigned int nBlocks_free = 0;537unsigned int nBlocks_used = 0;538unsigned int nBlocks_zomb = 0;539unsigned int nBlocks_disconn = 0;540unsigned int nBlocks_notentr = 0;541542//---< max & min of TopSizeArray >---543// it is sufficient to have these sizes as 32bit unsigned ints.544// The CodeHeap is limited in size to 4GB. Furthermore, the sizes545// are stored in _segment_size units, scaling them down by a factor of 64 (at least).546unsigned int currMax = 0;547unsigned int currMin = 0;548unsigned int currMin_ix = 0;549unsigned long total_iterations = 0;550551bool done = false;552const int min_granules = 256;553const int max_granules = 512*K; // limits analyzable CodeHeap (with segment_granules) to 32M..128M554// results in StatArray size of 24M (= max_granules * 48 Bytes per element)555// For a 1GB CodeHeap, the granule size must be at least 2kB to not violate the max_granles limit.556const char* heapName = get_heapName(heap);557BUFFEREDSTREAM_DECL(ast, out)558559if (!initialization_complete) {560memset(CodeHeapStatArray, 0, sizeof(CodeHeapStatArray));561initialization_complete = true;562563printBox(ast, '=', "C O D E H E A P A N A L Y S I S (general remarks)", NULL);564ast->print_cr(" The code heap analysis function provides deep insights into\n"565" the inner workings and the internal state of the Java VM's\n"566" code cache - the place where all the JVM generated machine\n"567" code is stored.\n"568" \n"569" This function is designed and provided for support engineers\n"570" to help them understand and solve issues in customer systems.\n"571" It is not intended for use and interpretation by other persons.\n"572" \n");573BUFFEREDSTREAM_FLUSH("")574}575get_HeapStatGlobals(out, heapName);576577578// Since we are (and must be) analyzing the CodeHeap contents under the CodeCache_lock,579// all heap information is "constant" and can be safely extracted/calculated before we580// enter the while() loop. Actually, the loop will only be iterated once.581char* low_bound = heap->low_boundary();582size_t size = heap->capacity();583size_t res_size = heap->max_capacity();584seg_size = heap->segment_size();585log2_seg_size = seg_size == 0 ? 0 : exact_log2(seg_size); // This is a global static value.586587if (seg_size == 0) {588printBox(ast, '-', "Heap not fully initialized yet, segment size is zero for segment ", heapName);589BUFFEREDSTREAM_FLUSH("")590return;591}592593if (!holding_required_locks()) {594printBox(ast, '-', "Must be at safepoint or hold Compile_lock and CodeCache_lock when calling aggregate function for ", heapName);595BUFFEREDSTREAM_FLUSH("")596return;597}598599// Calculate granularity of analysis (and output).600// The CodeHeap is managed (allocated) in segments (units) of CodeCacheSegmentSize.601// The CodeHeap can become fairly large, in particular in productive real-life systems.602//603// It is often neither feasible nor desirable to aggregate the data with the highest possible604// level of detail, i.e. inspecting and printing each segment on its own.605//606// The granularity parameter allows to specify the level of detail available in the analysis.607// It must be a positive multiple of the segment size and should be selected such that enough608// detail is provided while, at the same time, the printed output does not explode.609//610// By manipulating the granularity value, we enforce that at least min_granules units611// of analysis are available. We also enforce an upper limit of max_granules units to612// keep the amount of allocated storage in check.613//614// Finally, we adjust the granularity such that each granule covers at most 64k-1 segments.615// This is necessary to prevent an unsigned short overflow while accumulating space information.616//617assert(granularity > 0, "granularity should be positive.");618619if (granularity > size) {620granularity = size;621}622if (size/granularity < min_granules) {623granularity = size/min_granules; // at least min_granules granules624}625granularity = granularity & (~(seg_size - 1)); // must be multiple of seg_size626if (granularity < seg_size) {627granularity = seg_size; // must be at least seg_size628}629if (size/granularity > max_granules) {630granularity = size/max_granules; // at most max_granules granules631}632granularity = granularity & (~(seg_size - 1)); // must be multiple of seg_size633if (granularity>>log2_seg_size >= (1L<<sizeof(unsigned short)*8)) {634granularity = ((1L<<(sizeof(unsigned short)*8))-1)<<log2_seg_size; // Limit: (64k-1) * seg_size635}636segment_granules = granularity == seg_size;637size_t granules = (size + (granularity-1))/granularity;638639printBox(ast, '=', "C O D E H E A P A N A L Y S I S (used blocks) for segment ", heapName);640ast->print_cr(" The aggregate step takes an aggregated snapshot of the CodeHeap.\n"641" Subsequent print functions create their output based on this snapshot.\n"642" The CodeHeap is a living thing, and every effort has been made for the\n"643" collected data to be consistent. Only the method names and signatures\n"644" are retrieved at print time. That may lead to rare cases where the\n"645" name of a method is no longer available, e.g. because it was unloaded.\n");646ast->print_cr(" CodeHeap committed size " SIZE_FORMAT "K (" SIZE_FORMAT "M), reserved size " SIZE_FORMAT "K (" SIZE_FORMAT "M), %d%% occupied.",647size/(size_t)K, size/(size_t)M, res_size/(size_t)K, res_size/(size_t)M, (unsigned int)(100.0*size/res_size));648ast->print_cr(" CodeHeap allocation segment size is " SIZE_FORMAT " bytes. This is the smallest possible granularity.", seg_size);649ast->print_cr(" CodeHeap (committed part) is mapped to " SIZE_FORMAT " granules of size " SIZE_FORMAT " bytes.", granules, granularity);650ast->print_cr(" Each granule takes " SIZE_FORMAT " bytes of C heap, that is " SIZE_FORMAT "K in total for statistics data.", sizeof(StatElement), (sizeof(StatElement)*granules)/(size_t)K);651ast->print_cr(" The number of granules is limited to %dk, requiring a granules size of at least %d bytes for a 1GB heap.", (unsigned int)(max_granules/K), (unsigned int)(G/max_granules));652BUFFEREDSTREAM_FLUSH("\n")653654655while (!done) {656//---< reset counters with every aggregation >---657nBlocks_t1 = 0;658nBlocks_t2 = 0;659nBlocks_alive = 0;660nBlocks_dead = 0;661nBlocks_unloaded = 0;662nBlocks_stub = 0;663664nBlocks_free = 0;665nBlocks_used = 0;666nBlocks_zomb = 0;667nBlocks_disconn = 0;668nBlocks_notentr = 0;669670//---< discard old arrays if size does not match >---671if (granules != alloc_granules) {672discard_StatArray(out);673discard_TopSizeArray(out);674}675676//---< allocate arrays if they don't yet exist, initialize >---677prepare_StatArray(out, granules, granularity, heapName);678if (StatArray == NULL) {679set_HeapStatGlobals(out, heapName);680return;681}682prepare_TopSizeArray(out, maxTopSizeBlocks, heapName);683prepare_SizeDistArray(out, nSizeDistElements, heapName);684685latest_compilation_id = CompileBroker::get_compilation_id();686unsigned int highest_compilation_id = 0;687size_t usedSpace = 0;688size_t t1Space = 0;689size_t t2Space = 0;690size_t aliveSpace = 0;691size_t disconnSpace = 0;692size_t notentrSpace = 0;693size_t deadSpace = 0;694size_t unloadedSpace = 0;695size_t stubSpace = 0;696size_t freeSpace = 0;697size_t maxFreeSize = 0;698HeapBlock* maxFreeBlock = NULL;699bool insane = false;700701int64_t hotnessAccumulator = 0;702unsigned int n_methods = 0;703avgTemp = 0;704minTemp = (int)(res_size > M ? (res_size/M)*2 : 1);705maxTemp = -minTemp;706707for (HeapBlock *h = heap->first_block(); h != NULL && !insane; h = heap->next_block(h)) {708unsigned int hb_len = (unsigned int)h->length(); // despite being size_t, length can never overflow an unsigned int.709size_t hb_bytelen = ((size_t)hb_len)<<log2_seg_size;710unsigned int ix_beg = (unsigned int)(((char*)h-low_bound)/granule_size);711unsigned int ix_end = (unsigned int)(((char*)h-low_bound+(hb_bytelen-1))/granule_size);712unsigned int compile_id = 0;713CompLevel comp_lvl = CompLevel_none;714compType cType = noComp;715blobType cbType = noType;716717//---< some sanity checks >---718// Do not assert here, just check, print error message and return.719// This is a diagnostic function. It is not supposed to tear down the VM.720if ((char*)h < low_bound) {721insane = true; ast->print_cr("Sanity check: HeapBlock @%p below low bound (%p)", (char*)h, low_bound);722}723if ((char*)h > (low_bound + res_size)) {724insane = true; ast->print_cr("Sanity check: HeapBlock @%p outside reserved range (%p)", (char*)h, low_bound + res_size);725}726if ((char*)h > (low_bound + size)) {727insane = true; ast->print_cr("Sanity check: HeapBlock @%p outside used range (%p)", (char*)h, low_bound + size);728}729if (ix_end >= granules) {730insane = true; ast->print_cr("Sanity check: end index (%d) out of bounds (" SIZE_FORMAT ")", ix_end, granules);731}732if (size != heap->capacity()) {733insane = true; ast->print_cr("Sanity check: code heap capacity has changed (" SIZE_FORMAT "K to " SIZE_FORMAT "K)", size/(size_t)K, heap->capacity()/(size_t)K);734}735if (ix_beg > ix_end) {736insane = true; ast->print_cr("Sanity check: end index (%d) lower than begin index (%d)", ix_end, ix_beg);737}738if (insane) {739BUFFEREDSTREAM_FLUSH("")740continue;741}742743if (h->free()) {744nBlocks_free++;745freeSpace += hb_bytelen;746if (hb_bytelen > maxFreeSize) {747maxFreeSize = hb_bytelen;748maxFreeBlock = h;749}750} else {751update_SizeDistArray(out, hb_len);752nBlocks_used++;753usedSpace += hb_bytelen;754CodeBlob* cb = (CodeBlob*)heap->find_start(h);755cbType = get_cbType(cb); // Will check for cb == NULL and other safety things.756if (cbType != noType) {757const char* blob_name = os::strdup(cb->name());758unsigned int nm_size = 0;759int temperature = 0;760nmethod* nm = cb->as_nmethod_or_null();761if (nm != NULL) { // no is_readable check required, nm = (nmethod*)cb.762ResourceMark rm;763Method* method = nm->method();764if (nm->is_in_use()) {765blob_name = os::strdup(method->name_and_sig_as_C_string());766}767if (nm->is_not_entrant()) {768blob_name = os::strdup(method->name_and_sig_as_C_string());769}770771nm_size = nm->total_size();772compile_id = nm->compile_id();773comp_lvl = (CompLevel)(nm->comp_level());774if (nm->is_compiled_by_c1()) {775cType = c1;776}777if (nm->is_compiled_by_c2()) {778cType = c2;779}780if (nm->is_compiled_by_jvmci()) {781cType = jvmci;782}783switch (cbType) {784case nMethod_inuse: { // only for executable methods!!!785// space for these cbs is accounted for later.786temperature = nm->hotness_counter();787hotnessAccumulator += temperature;788n_methods++;789maxTemp = (temperature > maxTemp) ? temperature : maxTemp;790minTemp = (temperature < minTemp) ? temperature : minTemp;791break;792}793case nMethod_notused:794nBlocks_alive++;795nBlocks_disconn++;796aliveSpace += hb_bytelen;797disconnSpace += hb_bytelen;798break;799case nMethod_notentrant: // equivalent to nMethod_alive800nBlocks_alive++;801nBlocks_notentr++;802aliveSpace += hb_bytelen;803notentrSpace += hb_bytelen;804break;805case nMethod_unloaded:806nBlocks_unloaded++;807unloadedSpace += hb_bytelen;808break;809case nMethod_dead:810nBlocks_dead++;811deadSpace += hb_bytelen;812break;813default:814break;815}816}817818//------------------------------------------819//---< register block in TopSizeArray >---820//------------------------------------------821if (alloc_topSizeBlocks > 0) {822if (used_topSizeBlocks == 0) {823TopSizeArray[0].start = h;824TopSizeArray[0].blob_name = blob_name;825TopSizeArray[0].len = hb_len;826TopSizeArray[0].index = tsbStopper;827TopSizeArray[0].nm_size = nm_size;828TopSizeArray[0].temperature = temperature;829TopSizeArray[0].compiler = cType;830TopSizeArray[0].level = comp_lvl;831TopSizeArray[0].type = cbType;832currMax = hb_len;833currMin = hb_len;834currMin_ix = 0;835used_topSizeBlocks++;836blob_name = NULL; // indicate blob_name was consumed837// This check roughly cuts 5000 iterations (JVM98, mixed, dbg, termination stats):838} else if ((used_topSizeBlocks < alloc_topSizeBlocks) && (hb_len < currMin)) {839//---< all blocks in list are larger, but there is room left in array >---840TopSizeArray[currMin_ix].index = used_topSizeBlocks;841TopSizeArray[used_topSizeBlocks].start = h;842TopSizeArray[used_topSizeBlocks].blob_name = blob_name;843TopSizeArray[used_topSizeBlocks].len = hb_len;844TopSizeArray[used_topSizeBlocks].index = tsbStopper;845TopSizeArray[used_topSizeBlocks].nm_size = nm_size;846TopSizeArray[used_topSizeBlocks].temperature = temperature;847TopSizeArray[used_topSizeBlocks].compiler = cType;848TopSizeArray[used_topSizeBlocks].level = comp_lvl;849TopSizeArray[used_topSizeBlocks].type = cbType;850currMin = hb_len;851currMin_ix = used_topSizeBlocks;852used_topSizeBlocks++;853blob_name = NULL; // indicate blob_name was consumed854} else {855// This check cuts total_iterations by a factor of 6 (JVM98, mixed, dbg, termination stats):856// We don't need to search the list if we know beforehand that the current block size is857// smaller than the currently recorded minimum and there is no free entry left in the list.858if (!((used_topSizeBlocks == alloc_topSizeBlocks) && (hb_len <= currMin))) {859if (currMax < hb_len) {860currMax = hb_len;861}862unsigned int i;863unsigned int prev_i = tsbStopper;864unsigned int limit_i = 0;865for (i = 0; i != tsbStopper; i = TopSizeArray[i].index) {866if (limit_i++ >= alloc_topSizeBlocks) {867insane = true; break; // emergency exit868}869if (i >= used_topSizeBlocks) {870insane = true; break; // emergency exit871}872total_iterations++;873if (TopSizeArray[i].len < hb_len) {874//---< We want to insert here, element <i> is smaller than the current one >---875if (used_topSizeBlocks < alloc_topSizeBlocks) { // still room for a new entry to insert876// old entry gets moved to the next free element of the array.877// That's necessary to keep the entry for the largest block at index 0.878// This move might cause the current minimum to be moved to another place879if (i == currMin_ix) {880assert(TopSizeArray[i].len == currMin, "sort error");881currMin_ix = used_topSizeBlocks;882}883memcpy((void*)&TopSizeArray[used_topSizeBlocks], (void*)&TopSizeArray[i], sizeof(TopSizeBlk));884TopSizeArray[i].start = h;885TopSizeArray[i].blob_name = blob_name;886TopSizeArray[i].len = hb_len;887TopSizeArray[i].index = used_topSizeBlocks;888TopSizeArray[i].nm_size = nm_size;889TopSizeArray[i].temperature = temperature;890TopSizeArray[i].compiler = cType;891TopSizeArray[i].level = comp_lvl;892TopSizeArray[i].type = cbType;893used_topSizeBlocks++;894blob_name = NULL; // indicate blob_name was consumed895} else { // no room for new entries, current block replaces entry for smallest block896//---< Find last entry (entry for smallest remembered block) >---897// We either want to insert right before the smallest entry, which is when <i>898// indexes the smallest entry. We then just overwrite the smallest entry.899// What's more likely:900// We want to insert somewhere in the list. The smallest entry (@<j>) then falls off the cliff.901// The element at the insert point <i> takes it's slot. The second-smallest entry now becomes smallest.902// Data of the current block is filled in at index <i>.903unsigned int j = i;904unsigned int prev_j = tsbStopper;905unsigned int limit_j = 0;906while (TopSizeArray[j].index != tsbStopper) {907if (limit_j++ >= alloc_topSizeBlocks) {908insane = true; break; // emergency exit909}910if (j >= used_topSizeBlocks) {911insane = true; break; // emergency exit912}913total_iterations++;914prev_j = j;915j = TopSizeArray[j].index;916}917if (!insane) {918if (TopSizeArray[j].blob_name != NULL) {919os::free((void*)TopSizeArray[j].blob_name);920}921if (prev_j == tsbStopper) {922//---< Above while loop did not iterate, we already are the min entry >---923//---< We have to just replace the smallest entry >---924currMin = hb_len;925currMin_ix = j;926TopSizeArray[j].start = h;927TopSizeArray[j].blob_name = blob_name;928TopSizeArray[j].len = hb_len;929TopSizeArray[j].index = tsbStopper; // already set!!930TopSizeArray[i].nm_size = nm_size;931TopSizeArray[i].temperature = temperature;932TopSizeArray[j].compiler = cType;933TopSizeArray[j].level = comp_lvl;934TopSizeArray[j].type = cbType;935} else {936//---< second-smallest entry is now smallest >---937TopSizeArray[prev_j].index = tsbStopper;938currMin = TopSizeArray[prev_j].len;939currMin_ix = prev_j;940//---< previously smallest entry gets overwritten >---941memcpy((void*)&TopSizeArray[j], (void*)&TopSizeArray[i], sizeof(TopSizeBlk));942TopSizeArray[i].start = h;943TopSizeArray[i].blob_name = blob_name;944TopSizeArray[i].len = hb_len;945TopSizeArray[i].index = j;946TopSizeArray[i].nm_size = nm_size;947TopSizeArray[i].temperature = temperature;948TopSizeArray[i].compiler = cType;949TopSizeArray[i].level = comp_lvl;950TopSizeArray[i].type = cbType;951}952blob_name = NULL; // indicate blob_name was consumed953} // insane954}955break;956}957prev_i = i;958}959if (insane) {960// Note: regular analysis could probably continue by resetting "insane" flag.961out->print_cr("Possible loop in TopSizeBlocks list detected. Analysis aborted.");962discard_TopSizeArray(out);963}964}965}966}967if (blob_name != NULL) {968os::free((void*)blob_name);969blob_name = NULL;970}971//----------------------------------------------972//---< END register block in TopSizeArray >---973//----------------------------------------------974} else {975nBlocks_zomb++;976}977978if (ix_beg == ix_end) {979StatArray[ix_beg].type = cbType;980switch (cbType) {981case nMethod_inuse:982highest_compilation_id = (highest_compilation_id >= compile_id) ? highest_compilation_id : compile_id;983if (comp_lvl < CompLevel_full_optimization) {984nBlocks_t1++;985t1Space += hb_bytelen;986StatArray[ix_beg].t1_count++;987StatArray[ix_beg].t1_space += (unsigned short)hb_len;988StatArray[ix_beg].t1_age = StatArray[ix_beg].t1_age < compile_id ? compile_id : StatArray[ix_beg].t1_age;989} else {990nBlocks_t2++;991t2Space += hb_bytelen;992StatArray[ix_beg].t2_count++;993StatArray[ix_beg].t2_space += (unsigned short)hb_len;994StatArray[ix_beg].t2_age = StatArray[ix_beg].t2_age < compile_id ? compile_id : StatArray[ix_beg].t2_age;995}996StatArray[ix_beg].level = comp_lvl;997StatArray[ix_beg].compiler = cType;998break;999case nMethod_alive:1000StatArray[ix_beg].tx_count++;1001StatArray[ix_beg].tx_space += (unsigned short)hb_len;1002StatArray[ix_beg].tx_age = StatArray[ix_beg].tx_age < compile_id ? compile_id : StatArray[ix_beg].tx_age;1003StatArray[ix_beg].level = comp_lvl;1004StatArray[ix_beg].compiler = cType;1005break;1006case nMethod_dead:1007case nMethod_unloaded:1008StatArray[ix_beg].dead_count++;1009StatArray[ix_beg].dead_space += (unsigned short)hb_len;1010break;1011default:1012// must be a stub, if it's not a dead or alive nMethod1013nBlocks_stub++;1014stubSpace += hb_bytelen;1015StatArray[ix_beg].stub_count++;1016StatArray[ix_beg].stub_space += (unsigned short)hb_len;1017break;1018}1019} else {1020unsigned int beg_space = (unsigned int)(granule_size - ((char*)h - low_bound - ix_beg*granule_size));1021unsigned int end_space = (unsigned int)(hb_bytelen - beg_space - (ix_end-ix_beg-1)*granule_size);1022beg_space = beg_space>>log2_seg_size; // store in units of _segment_size1023end_space = end_space>>log2_seg_size; // store in units of _segment_size1024StatArray[ix_beg].type = cbType;1025StatArray[ix_end].type = cbType;1026switch (cbType) {1027case nMethod_inuse:1028highest_compilation_id = (highest_compilation_id >= compile_id) ? highest_compilation_id : compile_id;1029if (comp_lvl < CompLevel_full_optimization) {1030nBlocks_t1++;1031t1Space += hb_bytelen;1032StatArray[ix_beg].t1_count++;1033StatArray[ix_beg].t1_space += (unsigned short)beg_space;1034StatArray[ix_beg].t1_age = StatArray[ix_beg].t1_age < compile_id ? compile_id : StatArray[ix_beg].t1_age;10351036StatArray[ix_end].t1_count++;1037StatArray[ix_end].t1_space += (unsigned short)end_space;1038StatArray[ix_end].t1_age = StatArray[ix_end].t1_age < compile_id ? compile_id : StatArray[ix_end].t1_age;1039} else {1040nBlocks_t2++;1041t2Space += hb_bytelen;1042StatArray[ix_beg].t2_count++;1043StatArray[ix_beg].t2_space += (unsigned short)beg_space;1044StatArray[ix_beg].t2_age = StatArray[ix_beg].t2_age < compile_id ? compile_id : StatArray[ix_beg].t2_age;10451046StatArray[ix_end].t2_count++;1047StatArray[ix_end].t2_space += (unsigned short)end_space;1048StatArray[ix_end].t2_age = StatArray[ix_end].t2_age < compile_id ? compile_id : StatArray[ix_end].t2_age;1049}1050StatArray[ix_beg].level = comp_lvl;1051StatArray[ix_beg].compiler = cType;1052StatArray[ix_end].level = comp_lvl;1053StatArray[ix_end].compiler = cType;1054break;1055case nMethod_alive:1056StatArray[ix_beg].tx_count++;1057StatArray[ix_beg].tx_space += (unsigned short)beg_space;1058StatArray[ix_beg].tx_age = StatArray[ix_beg].tx_age < compile_id ? compile_id : StatArray[ix_beg].tx_age;10591060StatArray[ix_end].tx_count++;1061StatArray[ix_end].tx_space += (unsigned short)end_space;1062StatArray[ix_end].tx_age = StatArray[ix_end].tx_age < compile_id ? compile_id : StatArray[ix_end].tx_age;10631064StatArray[ix_beg].level = comp_lvl;1065StatArray[ix_beg].compiler = cType;1066StatArray[ix_end].level = comp_lvl;1067StatArray[ix_end].compiler = cType;1068break;1069case nMethod_dead:1070case nMethod_unloaded:1071StatArray[ix_beg].dead_count++;1072StatArray[ix_beg].dead_space += (unsigned short)beg_space;1073StatArray[ix_end].dead_count++;1074StatArray[ix_end].dead_space += (unsigned short)end_space;1075break;1076default:1077// must be a stub, if it's not a dead or alive nMethod1078nBlocks_stub++;1079stubSpace += hb_bytelen;1080StatArray[ix_beg].stub_count++;1081StatArray[ix_beg].stub_space += (unsigned short)beg_space;1082StatArray[ix_end].stub_count++;1083StatArray[ix_end].stub_space += (unsigned short)end_space;1084break;1085}1086for (unsigned int ix = ix_beg+1; ix < ix_end; ix++) {1087StatArray[ix].type = cbType;1088switch (cbType) {1089case nMethod_inuse:1090if (comp_lvl < CompLevel_full_optimization) {1091StatArray[ix].t1_count++;1092StatArray[ix].t1_space += (unsigned short)(granule_size>>log2_seg_size);1093StatArray[ix].t1_age = StatArray[ix].t1_age < compile_id ? compile_id : StatArray[ix].t1_age;1094} else {1095StatArray[ix].t2_count++;1096StatArray[ix].t2_space += (unsigned short)(granule_size>>log2_seg_size);1097StatArray[ix].t2_age = StatArray[ix].t2_age < compile_id ? compile_id : StatArray[ix].t2_age;1098}1099StatArray[ix].level = comp_lvl;1100StatArray[ix].compiler = cType;1101break;1102case nMethod_alive:1103StatArray[ix].tx_count++;1104StatArray[ix].tx_space += (unsigned short)(granule_size>>log2_seg_size);1105StatArray[ix].tx_age = StatArray[ix].tx_age < compile_id ? compile_id : StatArray[ix].tx_age;1106StatArray[ix].level = comp_lvl;1107StatArray[ix].compiler = cType;1108break;1109case nMethod_dead:1110case nMethod_unloaded:1111StatArray[ix].dead_count++;1112StatArray[ix].dead_space += (unsigned short)(granule_size>>log2_seg_size);1113break;1114default:1115// must be a stub, if it's not a dead or alive nMethod1116StatArray[ix].stub_count++;1117StatArray[ix].stub_space += (unsigned short)(granule_size>>log2_seg_size);1118break;1119}1120}1121}1122}1123}1124done = true;11251126if (!insane) {1127// There is a risk for this block (because it contains many print statements) to get1128// interspersed with print data from other threads. We take this risk intentionally.1129// Getting stalled waiting for tty_lock while holding the CodeCache_lock is not desirable.1130printBox(ast, '-', "Global CodeHeap statistics for segment ", heapName);1131ast->print_cr("freeSpace = " SIZE_FORMAT_W(8) "k, nBlocks_free = %6d, %10.3f%% of capacity, %10.3f%% of max_capacity", freeSpace/(size_t)K, nBlocks_free, (100.0*freeSpace)/size, (100.0*freeSpace)/res_size);1132ast->print_cr("usedSpace = " SIZE_FORMAT_W(8) "k, nBlocks_used = %6d, %10.3f%% of capacity, %10.3f%% of max_capacity", usedSpace/(size_t)K, nBlocks_used, (100.0*usedSpace)/size, (100.0*usedSpace)/res_size);1133ast->print_cr(" Tier1 Space = " SIZE_FORMAT_W(8) "k, nBlocks_t1 = %6d, %10.3f%% of capacity, %10.3f%% of max_capacity", t1Space/(size_t)K, nBlocks_t1, (100.0*t1Space)/size, (100.0*t1Space)/res_size);1134ast->print_cr(" Tier2 Space = " SIZE_FORMAT_W(8) "k, nBlocks_t2 = %6d, %10.3f%% of capacity, %10.3f%% of max_capacity", t2Space/(size_t)K, nBlocks_t2, (100.0*t2Space)/size, (100.0*t2Space)/res_size);1135ast->print_cr(" Alive Space = " SIZE_FORMAT_W(8) "k, nBlocks_alive = %6d, %10.3f%% of capacity, %10.3f%% of max_capacity", aliveSpace/(size_t)K, nBlocks_alive, (100.0*aliveSpace)/size, (100.0*aliveSpace)/res_size);1136ast->print_cr(" disconnected = " SIZE_FORMAT_W(8) "k, nBlocks_disconn = %6d, %10.3f%% of capacity, %10.3f%% of max_capacity", disconnSpace/(size_t)K, nBlocks_disconn, (100.0*disconnSpace)/size, (100.0*disconnSpace)/res_size);1137ast->print_cr(" not entrant = " SIZE_FORMAT_W(8) "k, nBlocks_notentr = %6d, %10.3f%% of capacity, %10.3f%% of max_capacity", notentrSpace/(size_t)K, nBlocks_notentr, (100.0*notentrSpace)/size, (100.0*notentrSpace)/res_size);1138ast->print_cr(" unloadedSpace = " SIZE_FORMAT_W(8) "k, nBlocks_unloaded = %6d, %10.3f%% of capacity, %10.3f%% of max_capacity", unloadedSpace/(size_t)K, nBlocks_unloaded, (100.0*unloadedSpace)/size, (100.0*unloadedSpace)/res_size);1139ast->print_cr(" deadSpace = " SIZE_FORMAT_W(8) "k, nBlocks_dead = %6d, %10.3f%% of capacity, %10.3f%% of max_capacity", deadSpace/(size_t)K, nBlocks_dead, (100.0*deadSpace)/size, (100.0*deadSpace)/res_size);1140ast->print_cr(" stubSpace = " SIZE_FORMAT_W(8) "k, nBlocks_stub = %6d, %10.3f%% of capacity, %10.3f%% of max_capacity", stubSpace/(size_t)K, nBlocks_stub, (100.0*stubSpace)/size, (100.0*stubSpace)/res_size);1141ast->print_cr("ZombieBlocks = %8d. These are HeapBlocks which could not be identified as CodeBlobs.", nBlocks_zomb);1142ast->cr();1143ast->print_cr("Segment start = " INTPTR_FORMAT ", used space = " SIZE_FORMAT_W(8)"k", p2i(low_bound), size/K);1144ast->print_cr("Segment end (used) = " INTPTR_FORMAT ", remaining space = " SIZE_FORMAT_W(8)"k", p2i(low_bound) + size, (res_size - size)/K);1145ast->print_cr("Segment end (reserved) = " INTPTR_FORMAT ", reserved space = " SIZE_FORMAT_W(8)"k", p2i(low_bound) + res_size, res_size/K);1146ast->cr();1147ast->print_cr("latest allocated compilation id = %d", latest_compilation_id);1148ast->print_cr("highest observed compilation id = %d", highest_compilation_id);1149ast->print_cr("Building TopSizeList iterations = %ld", total_iterations);1150ast->cr();11511152int reset_val = NMethodSweeper::hotness_counter_reset_val();1153double reverse_free_ratio = (res_size > size) ? (double)res_size/(double)(res_size-size) : (double)res_size;1154printBox(ast, '-', "Method hotness information at time of this analysis", NULL);1155ast->print_cr("Highest possible method temperature: %12d", reset_val);1156ast->print_cr("Threshold for method to be considered 'cold': %12.3f", -reset_val + reverse_free_ratio * NmethodSweepActivity);1157if (n_methods > 0) {1158avgTemp = hotnessAccumulator/n_methods;1159ast->print_cr("min. hotness = %6d", minTemp);1160ast->print_cr("avg. hotness = %6d", avgTemp);1161ast->print_cr("max. hotness = %6d", maxTemp);1162} else {1163avgTemp = 0;1164ast->print_cr("No hotness data available");1165}1166BUFFEREDSTREAM_FLUSH("\n")11671168// This loop is intentionally printing directly to "out".1169// It should not print anything, anyway.1170out->print("Verifying collected data...");1171size_t granule_segs = granule_size>>log2_seg_size;1172for (unsigned int ix = 0; ix < granules; ix++) {1173if (StatArray[ix].t1_count > granule_segs) {1174out->print_cr("t1_count[%d] = %d", ix, StatArray[ix].t1_count);1175}1176if (StatArray[ix].t2_count > granule_segs) {1177out->print_cr("t2_count[%d] = %d", ix, StatArray[ix].t2_count);1178}1179if (StatArray[ix].tx_count > granule_segs) {1180out->print_cr("tx_count[%d] = %d", ix, StatArray[ix].tx_count);1181}1182if (StatArray[ix].stub_count > granule_segs) {1183out->print_cr("stub_count[%d] = %d", ix, StatArray[ix].stub_count);1184}1185if (StatArray[ix].dead_count > granule_segs) {1186out->print_cr("dead_count[%d] = %d", ix, StatArray[ix].dead_count);1187}1188if (StatArray[ix].t1_space > granule_segs) {1189out->print_cr("t1_space[%d] = %d", ix, StatArray[ix].t1_space);1190}1191if (StatArray[ix].t2_space > granule_segs) {1192out->print_cr("t2_space[%d] = %d", ix, StatArray[ix].t2_space);1193}1194if (StatArray[ix].tx_space > granule_segs) {1195out->print_cr("tx_space[%d] = %d", ix, StatArray[ix].tx_space);1196}1197if (StatArray[ix].stub_space > granule_segs) {1198out->print_cr("stub_space[%d] = %d", ix, StatArray[ix].stub_space);1199}1200if (StatArray[ix].dead_space > granule_segs) {1201out->print_cr("dead_space[%d] = %d", ix, StatArray[ix].dead_space);1202}1203// this cast is awful! I need it because NT/Intel reports a signed/unsigned mismatch.1204if ((size_t)(StatArray[ix].t1_count+StatArray[ix].t2_count+StatArray[ix].tx_count+StatArray[ix].stub_count+StatArray[ix].dead_count) > granule_segs) {1205out->print_cr("t1_count[%d] = %d, t2_count[%d] = %d, tx_count[%d] = %d, stub_count[%d] = %d", ix, StatArray[ix].t1_count, ix, StatArray[ix].t2_count, ix, StatArray[ix].tx_count, ix, StatArray[ix].stub_count);1206}1207if ((size_t)(StatArray[ix].t1_space+StatArray[ix].t2_space+StatArray[ix].tx_space+StatArray[ix].stub_space+StatArray[ix].dead_space) > granule_segs) {1208out->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);1209}1210}12111212// This loop is intentionally printing directly to "out".1213// It should not print anything, anyway.1214if (used_topSizeBlocks > 0) {1215unsigned int j = 0;1216if (TopSizeArray[0].len != currMax) {1217out->print_cr("currMax(%d) differs from TopSizeArray[0].len(%d)", currMax, TopSizeArray[0].len);1218}1219for (unsigned int i = 0; (TopSizeArray[i].index != tsbStopper) && (j++ < alloc_topSizeBlocks); i = TopSizeArray[i].index) {1220if (TopSizeArray[i].len < TopSizeArray[TopSizeArray[i].index].len) {1221out->print_cr("sort error at index %d: %d !>= %d", i, TopSizeArray[i].len, TopSizeArray[TopSizeArray[i].index].len);1222}1223}1224if (j >= alloc_topSizeBlocks) {1225out->print_cr("Possible loop in TopSizeArray chaining!\n allocBlocks = %d, usedBlocks = %d", alloc_topSizeBlocks, used_topSizeBlocks);1226for (unsigned int i = 0; i < alloc_topSizeBlocks; i++) {1227out->print_cr(" TopSizeArray[%d].index = %d, len = %d", i, TopSizeArray[i].index, TopSizeArray[i].len);1228}1229}1230}1231out->print_cr("...done\n\n");1232} else {1233// insane heap state detected. Analysis data incomplete. Just throw it away.1234discard_StatArray(out);1235discard_TopSizeArray(out);1236}1237}123812391240done = false;1241while (!done && (nBlocks_free > 0)) {12421243printBox(ast, '=', "C O D E H E A P A N A L Y S I S (free blocks) for segment ", heapName);1244ast->print_cr(" The aggregate step collects information about all free blocks in CodeHeap.\n"1245" Subsequent print functions create their output based on this snapshot.\n");1246ast->print_cr(" Free space in %s is distributed over %d free blocks.", heapName, nBlocks_free);1247ast->print_cr(" Each free block takes " SIZE_FORMAT " bytes of C heap for statistics data, that is " SIZE_FORMAT "K in total.", sizeof(FreeBlk), (sizeof(FreeBlk)*nBlocks_free)/K);1248BUFFEREDSTREAM_FLUSH("\n")12491250//----------------------------------------1251//-- Prepare the FreeArray of FreeBlks --1252//----------------------------------------12531254//---< discard old array if size does not match >---1255if (nBlocks_free != alloc_freeBlocks) {1256discard_FreeArray(out);1257}12581259prepare_FreeArray(out, nBlocks_free, heapName);1260if (FreeArray == NULL) {1261done = true;1262continue;1263}12641265//----------------------------------------1266//-- Collect all FreeBlks in FreeArray --1267//----------------------------------------12681269unsigned int ix = 0;1270FreeBlock* cur = heap->freelist();12711272while (cur != NULL) {1273if (ix < alloc_freeBlocks) { // don't index out of bounds if _freelist has more blocks than anticipated1274FreeArray[ix].start = cur;1275FreeArray[ix].len = (unsigned int)(cur->length()<<log2_seg_size);1276FreeArray[ix].index = ix;1277}1278cur = cur->link();1279ix++;1280}1281if (ix != alloc_freeBlocks) {1282ast->print_cr("Free block count mismatch. Expected %d free blocks, but found %d.", alloc_freeBlocks, ix);1283ast->print_cr("I will update the counter and retry data collection");1284BUFFEREDSTREAM_FLUSH("\n")1285nBlocks_free = ix;1286continue;1287}1288done = true;1289}12901291if (!done || (nBlocks_free == 0)) {1292if (nBlocks_free == 0) {1293printBox(ast, '-', "no free blocks found in ", heapName);1294} else if (!done) {1295ast->print_cr("Free block count mismatch could not be resolved.");1296ast->print_cr("Try to run \"aggregate\" function to update counters");1297}1298BUFFEREDSTREAM_FLUSH("")12991300//---< discard old array and update global values >---1301discard_FreeArray(out);1302set_HeapStatGlobals(out, heapName);1303return;1304}13051306//---< calculate and fill remaining fields >---1307if (FreeArray != NULL) {1308// This loop is intentionally printing directly to "out".1309// It should not print anything, anyway.1310for (unsigned int ix = 0; ix < alloc_freeBlocks-1; ix++) {1311size_t lenSum = 0;1312FreeArray[ix].gap = (unsigned int)((address)FreeArray[ix+1].start - ((address)FreeArray[ix].start + FreeArray[ix].len));1313for (HeapBlock *h = heap->next_block(FreeArray[ix].start); (h != NULL) && (h != FreeArray[ix+1].start); h = heap->next_block(h)) {1314CodeBlob *cb = (CodeBlob*)(heap->find_start(h));1315if ((cb != NULL) && !cb->is_nmethod()) { // checks equivalent to those in get_cbType()1316FreeArray[ix].stubs_in_gap = true;1317}1318FreeArray[ix].n_gapBlocks++;1319lenSum += h->length()<<log2_seg_size;1320if (((address)h < ((address)FreeArray[ix].start+FreeArray[ix].len)) || (h >= FreeArray[ix+1].start)) {1321out->print_cr("unsorted occupied CodeHeap block found @ %p, gap interval [%p, %p)", h, (address)FreeArray[ix].start+FreeArray[ix].len, FreeArray[ix+1].start);1322}1323}1324if (lenSum != FreeArray[ix].gap) {1325out->print_cr("Length mismatch for gap between FreeBlk[%d] and FreeBlk[%d]. Calculated: %d, accumulated: %d.", ix, ix+1, FreeArray[ix].gap, (unsigned int)lenSum);1326}1327}1328}1329set_HeapStatGlobals(out, heapName);13301331printBox(ast, '=', "C O D E H E A P A N A L Y S I S C O M P L E T E for segment ", heapName);1332BUFFEREDSTREAM_FLUSH("\n")1333}133413351336void CodeHeapState::print_usedSpace(outputStream* out, CodeHeap* heap) {1337if (!initialization_complete) {1338return;1339}13401341const char* heapName = get_heapName(heap);1342get_HeapStatGlobals(out, heapName);13431344if ((StatArray == NULL) || (TopSizeArray == NULL) || (used_topSizeBlocks == 0)) {1345return;1346}1347BUFFEREDSTREAM_DECL(ast, out)13481349{1350printBox(ast, '=', "U S E D S P A C E S T A T I S T I C S for ", heapName);1351ast->print_cr("Note: The Top%d list of the largest used blocks associates method names\n"1352" and other identifying information with the block size data.\n"1353"\n"1354" Method names are dynamically retrieved from the code cache at print time.\n"1355" Due to the living nature of the code cache and because the CodeCache_lock\n"1356" is not continuously held, the displayed name might be wrong or no name\n"1357" might be found at all. The likelihood for that to happen increases\n"1358" over time passed between analysis and print step.\n", used_topSizeBlocks);1359BUFFEREDSTREAM_FLUSH_LOCKED("\n")1360}13611362//----------------------------1363//-- Print Top Used Blocks --1364//----------------------------1365{1366char* low_bound = heap->low_boundary();13671368printBox(ast, '-', "Largest Used Blocks in ", heapName);1369print_blobType_legend(ast);13701371ast->fill_to(51);1372ast->print("%4s", "blob");1373ast->fill_to(56);1374ast->print("%9s", "compiler");1375ast->fill_to(66);1376ast->print_cr("%6s", "method");1377ast->print_cr("%18s %13s %17s %4s %9s %5s %s", "Addr(module) ", "offset", "size", "type", " type lvl", " temp", "Name");1378BUFFEREDSTREAM_FLUSH_LOCKED("")13791380//---< print Top Ten Used Blocks >---1381if (used_topSizeBlocks > 0) {1382unsigned int printed_topSizeBlocks = 0;1383for (unsigned int i = 0; i != tsbStopper; i = TopSizeArray[i].index) {1384printed_topSizeBlocks++;1385if (TopSizeArray[i].blob_name == NULL) {1386TopSizeArray[i].blob_name = os::strdup("unnamed blob or blob name unavailable");1387}1388// heap->find_start() is safe. Only works on _segmap.1389// Returns NULL or void*. Returned CodeBlob may be uninitialized.1390HeapBlock* heapBlock = TopSizeArray[i].start;1391CodeBlob* this_blob = (CodeBlob*)(heap->find_start(heapBlock));1392if (this_blob != NULL) {1393//---< access these fields only if we own the CodeCache_lock >---1394//---< blob address >---1395ast->print(INTPTR_FORMAT, p2i(this_blob));1396ast->fill_to(19);1397//---< blob offset from CodeHeap begin >---1398ast->print("(+" PTR32_FORMAT ")", (unsigned int)((char*)this_blob-low_bound));1399ast->fill_to(33);1400} else {1401//---< block address >---1402ast->print(INTPTR_FORMAT, p2i(TopSizeArray[i].start));1403ast->fill_to(19);1404//---< block offset from CodeHeap begin >---1405ast->print("(+" PTR32_FORMAT ")", (unsigned int)((char*)TopSizeArray[i].start-low_bound));1406ast->fill_to(33);1407}14081409//---< print size, name, and signature (for nMethods) >---1410bool is_nmethod = TopSizeArray[i].nm_size > 0;1411if (is_nmethod) {1412//---< nMethod size in hex >---1413ast->print(PTR32_FORMAT, TopSizeArray[i].nm_size);1414ast->print("(" SIZE_FORMAT_W(4) "K)", TopSizeArray[i].nm_size/K);1415ast->fill_to(51);1416ast->print(" %c", blobTypeChar[TopSizeArray[i].type]);1417//---< compiler information >---1418ast->fill_to(56);1419ast->print("%5s %3d", compTypeName[TopSizeArray[i].compiler], TopSizeArray[i].level);1420//---< method temperature >---1421ast->fill_to(67);1422ast->print("%5d", TopSizeArray[i].temperature);1423//---< name and signature >---1424ast->fill_to(67+6);1425if (TopSizeArray[i].type == nMethod_dead) {1426ast->print(" zombie method ");1427}1428ast->print("%s", TopSizeArray[i].blob_name);1429} else {1430//---< block size in hex >---1431ast->print(PTR32_FORMAT, (unsigned int)(TopSizeArray[i].len<<log2_seg_size));1432ast->print("(" SIZE_FORMAT_W(4) "K)", (TopSizeArray[i].len<<log2_seg_size)/K);1433//---< no compiler information >---1434ast->fill_to(56);1435//---< name and signature >---1436ast->fill_to(67+6);1437ast->print("%s", TopSizeArray[i].blob_name);1438}1439ast->cr();1440BUFFEREDSTREAM_FLUSH_AUTO("")1441}1442if (used_topSizeBlocks != printed_topSizeBlocks) {1443ast->print_cr("used blocks: %d, printed blocks: %d", used_topSizeBlocks, printed_topSizeBlocks);1444for (unsigned int i = 0; i < alloc_topSizeBlocks; i++) {1445ast->print_cr(" TopSizeArray[%d].index = %d, len = %d", i, TopSizeArray[i].index, TopSizeArray[i].len);1446BUFFEREDSTREAM_FLUSH_AUTO("")1447}1448}1449BUFFEREDSTREAM_FLUSH("\n\n")1450}1451}14521453//-----------------------------1454//-- Print Usage Histogram --1455//-----------------------------14561457if (SizeDistributionArray != NULL) {1458unsigned long total_count = 0;1459unsigned long total_size = 0;1460const unsigned long pctFactor = 200;14611462for (unsigned int i = 0; i < nSizeDistElements; i++) {1463total_count += SizeDistributionArray[i].count;1464total_size += SizeDistributionArray[i].lenSum;1465}14661467if ((total_count > 0) && (total_size > 0)) {1468printBox(ast, '-', "Block count histogram for ", heapName);1469ast->print_cr("Note: The histogram indicates how many blocks (as a percentage\n"1470" of all blocks) have a size in the given range.\n"1471" %ld characters are printed per percentage point.\n", pctFactor/100);1472ast->print_cr("total size of all blocks: %7ldM", (total_size<<log2_seg_size)/M);1473ast->print_cr("total number of all blocks: %7ld\n", total_count);1474BUFFEREDSTREAM_FLUSH_LOCKED("")14751476ast->print_cr("[Size Range)------avg.-size-+----count-+");1477for (unsigned int i = 0; i < nSizeDistElements; i++) {1478if (SizeDistributionArray[i].rangeStart<<log2_seg_size < K) {1479ast->print("[" SIZE_FORMAT_W(5) " .." SIZE_FORMAT_W(5) " ): "1480,(size_t)(SizeDistributionArray[i].rangeStart<<log2_seg_size)1481,(size_t)(SizeDistributionArray[i].rangeEnd<<log2_seg_size)1482);1483} else if (SizeDistributionArray[i].rangeStart<<log2_seg_size < M) {1484ast->print("[" SIZE_FORMAT_W(5) "K.." SIZE_FORMAT_W(5) "K): "1485,(SizeDistributionArray[i].rangeStart<<log2_seg_size)/K1486,(SizeDistributionArray[i].rangeEnd<<log2_seg_size)/K1487);1488} else {1489ast->print("[" SIZE_FORMAT_W(5) "M.." SIZE_FORMAT_W(5) "M): "1490,(SizeDistributionArray[i].rangeStart<<log2_seg_size)/M1491,(SizeDistributionArray[i].rangeEnd<<log2_seg_size)/M1492);1493}1494ast->print(" %8d | %8d |",1495SizeDistributionArray[i].count > 0 ? (SizeDistributionArray[i].lenSum<<log2_seg_size)/SizeDistributionArray[i].count : 0,1496SizeDistributionArray[i].count);14971498unsigned int percent = pctFactor*SizeDistributionArray[i].count/total_count;1499for (unsigned int j = 1; j <= percent; j++) {1500ast->print("%c", (j%((pctFactor/100)*10) == 0) ? ('0'+j/(((unsigned int)pctFactor/100)*10)) : '*');1501}1502ast->cr();1503BUFFEREDSTREAM_FLUSH_AUTO("")1504}1505ast->print_cr("----------------------------+----------+");1506BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")15071508printBox(ast, '-', "Contribution per size range to total size for ", heapName);1509ast->print_cr("Note: The histogram indicates how much space (as a percentage of all\n"1510" occupied space) is used by the blocks in the given size range.\n"1511" %ld characters are printed per percentage point.\n", pctFactor/100);1512ast->print_cr("total size of all blocks: %7ldM", (total_size<<log2_seg_size)/M);1513ast->print_cr("total number of all blocks: %7ld\n", total_count);1514BUFFEREDSTREAM_FLUSH_LOCKED("")15151516ast->print_cr("[Size Range)------avg.-size-+----count-+");1517for (unsigned int i = 0; i < nSizeDistElements; i++) {1518if (SizeDistributionArray[i].rangeStart<<log2_seg_size < K) {1519ast->print("[" SIZE_FORMAT_W(5) " .." SIZE_FORMAT_W(5) " ): "1520,(size_t)(SizeDistributionArray[i].rangeStart<<log2_seg_size)1521,(size_t)(SizeDistributionArray[i].rangeEnd<<log2_seg_size)1522);1523} else if (SizeDistributionArray[i].rangeStart<<log2_seg_size < M) {1524ast->print("[" SIZE_FORMAT_W(5) "K.." SIZE_FORMAT_W(5) "K): "1525,(SizeDistributionArray[i].rangeStart<<log2_seg_size)/K1526,(SizeDistributionArray[i].rangeEnd<<log2_seg_size)/K1527);1528} else {1529ast->print("[" SIZE_FORMAT_W(5) "M.." SIZE_FORMAT_W(5) "M): "1530,(SizeDistributionArray[i].rangeStart<<log2_seg_size)/M1531,(SizeDistributionArray[i].rangeEnd<<log2_seg_size)/M1532);1533}1534ast->print(" %8d | %8d |",1535SizeDistributionArray[i].count > 0 ? (SizeDistributionArray[i].lenSum<<log2_seg_size)/SizeDistributionArray[i].count : 0,1536SizeDistributionArray[i].count);15371538unsigned int percent = pctFactor*(unsigned long)SizeDistributionArray[i].lenSum/total_size;1539for (unsigned int j = 1; j <= percent; j++) {1540ast->print("%c", (j%((pctFactor/100)*10) == 0) ? ('0'+j/(((unsigned int)pctFactor/100)*10)) : '*');1541}1542ast->cr();1543BUFFEREDSTREAM_FLUSH_AUTO("")1544}1545ast->print_cr("----------------------------+----------+");1546BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")1547}1548}1549}155015511552void CodeHeapState::print_freeSpace(outputStream* out, CodeHeap* heap) {1553if (!initialization_complete) {1554return;1555}15561557const char* heapName = get_heapName(heap);1558get_HeapStatGlobals(out, heapName);15591560if ((StatArray == NULL) || (FreeArray == NULL) || (alloc_granules == 0)) {1561return;1562}1563BUFFEREDSTREAM_DECL(ast, out)15641565{1566printBox(ast, '=', "F R E E S P A C E S T A T I S T I C S for ", heapName);1567ast->print_cr("Note: in this context, a gap is the occupied space between two free blocks.\n"1568" Those gaps are of interest if there is a chance that they become\n"1569" unoccupied, e.g. by class unloading. Then, the two adjacent free\n"1570" blocks, together with the now unoccupied space, form a new, large\n"1571" free block.");1572BUFFEREDSTREAM_FLUSH_LOCKED("\n")1573}15741575{1576printBox(ast, '-', "List of all Free Blocks in ", heapName);15771578unsigned int ix = 0;1579for (ix = 0; ix < alloc_freeBlocks-1; ix++) {1580ast->print(INTPTR_FORMAT ": Len[%4d] = " HEX32_FORMAT ",", p2i(FreeArray[ix].start), ix, FreeArray[ix].len);1581ast->fill_to(38);1582ast->print("Gap[%4d..%4d]: " HEX32_FORMAT " bytes,", ix, ix+1, FreeArray[ix].gap);1583ast->fill_to(71);1584ast->print("block count: %6d", FreeArray[ix].n_gapBlocks);1585if (FreeArray[ix].stubs_in_gap) {1586ast->print(" !! permanent gap, contains stubs and/or blobs !!");1587}1588ast->cr();1589BUFFEREDSTREAM_FLUSH_AUTO("")1590}1591ast->print_cr(INTPTR_FORMAT ": Len[%4d] = " HEX32_FORMAT, p2i(FreeArray[ix].start), ix, FreeArray[ix].len);1592BUFFEREDSTREAM_FLUSH_LOCKED("\n\n")1593}159415951596//-----------------------------------------1597//-- Find and Print Top Ten Free Blocks --1598//-----------------------------------------15991600//---< find Top Ten Free Blocks >---1601const unsigned int nTop = 10;1602unsigned int currMax10 = 0;1603struct FreeBlk* FreeTopTen[nTop];1604memset(FreeTopTen, 0, sizeof(FreeTopTen));16051606for (unsigned int ix = 0; ix < alloc_freeBlocks; ix++) {1607if (FreeArray[ix].len > currMax10) { // larger than the ten largest found so far1608unsigned int currSize = FreeArray[ix].len;16091610unsigned int iy;1611for (iy = 0; iy < nTop && FreeTopTen[iy] != NULL; iy++) {1612if (FreeTopTen[iy]->len < currSize) {1613for (unsigned int iz = nTop-1; iz > iy; iz--) { // make room to insert new free block1614FreeTopTen[iz] = FreeTopTen[iz-1];1615}1616FreeTopTen[iy] = &FreeArray[ix]; // insert new free block1617if (FreeTopTen[nTop-1] != NULL) {1618currMax10 = FreeTopTen[nTop-1]->len;1619}1620break; // done with this, check next free block1621}1622}1623if (iy >= nTop) {1624ast->print_cr("Internal logic error. New Max10 = %d detected, but could not be merged. Old Max10 = %d",1625currSize, currMax10);1626continue;1627}1628if (FreeTopTen[iy] == NULL) {1629FreeTopTen[iy] = &FreeArray[ix];1630if (iy == (nTop-1)) {1631currMax10 = currSize;1632}1633}1634}1635}1636BUFFEREDSTREAM_FLUSH_AUTO("")16371638{1639printBox(ast, '-', "Top Ten Free Blocks in ", heapName);16401641//---< print Top Ten Free Blocks >---1642for (unsigned int iy = 0; (iy < nTop) && (FreeTopTen[iy] != NULL); iy++) {1643ast->print("Pos %3d: Block %4d - size " HEX32_FORMAT ",", iy+1, FreeTopTen[iy]->index, FreeTopTen[iy]->len);1644ast->fill_to(39);1645if (FreeTopTen[iy]->index == (alloc_freeBlocks-1)) {1646ast->print("last free block in list.");1647} else {1648ast->print("Gap (to next) " HEX32_FORMAT ",", FreeTopTen[iy]->gap);1649ast->fill_to(63);1650ast->print("#blocks (in gap) %d", FreeTopTen[iy]->n_gapBlocks);1651}1652ast->cr();1653BUFFEREDSTREAM_FLUSH_AUTO("")1654}1655}1656BUFFEREDSTREAM_FLUSH_LOCKED("\n\n")165716581659//--------------------------------------------------------1660//-- Find and Print Top Ten Free-Occupied-Free Triples --1661//--------------------------------------------------------16621663//---< find and print Top Ten Triples (Free-Occupied-Free) >---1664currMax10 = 0;1665struct FreeBlk *FreeTopTenTriple[nTop];1666memset(FreeTopTenTriple, 0, sizeof(FreeTopTenTriple));16671668for (unsigned int ix = 0; ix < alloc_freeBlocks-1; ix++) {1669// If there are stubs in the gap, this gap will never become completely free.1670// The triple will thus never merge to one free block.1671unsigned int lenTriple = FreeArray[ix].len + (FreeArray[ix].stubs_in_gap ? 0 : FreeArray[ix].gap + FreeArray[ix+1].len);1672FreeArray[ix].len = lenTriple;1673if (lenTriple > currMax10) { // larger than the ten largest found so far16741675unsigned int iy;1676for (iy = 0; (iy < nTop) && (FreeTopTenTriple[iy] != NULL); iy++) {1677if (FreeTopTenTriple[iy]->len < lenTriple) {1678for (unsigned int iz = nTop-1; iz > iy; iz--) {1679FreeTopTenTriple[iz] = FreeTopTenTriple[iz-1];1680}1681FreeTopTenTriple[iy] = &FreeArray[ix];1682if (FreeTopTenTriple[nTop-1] != NULL) {1683currMax10 = FreeTopTenTriple[nTop-1]->len;1684}1685break;1686}1687}1688if (iy == nTop) {1689ast->print_cr("Internal logic error. New Max10 = %d detected, but could not be merged. Old Max10 = %d",1690lenTriple, currMax10);1691continue;1692}1693if (FreeTopTenTriple[iy] == NULL) {1694FreeTopTenTriple[iy] = &FreeArray[ix];1695if (iy == (nTop-1)) {1696currMax10 = lenTriple;1697}1698}1699}1700}1701BUFFEREDSTREAM_FLUSH_AUTO("")17021703{1704printBox(ast, '-', "Top Ten Free-Occupied-Free Triples in ", heapName);1705ast->print_cr(" Use this information to judge how likely it is that a large(r) free block\n"1706" might get created by code cache sweeping.\n"1707" If all the occupied blocks can be swept, the three free blocks will be\n"1708" merged into one (much larger) free block. That would reduce free space\n"1709" fragmentation.\n");17101711//---< print Top Ten Free-Occupied-Free Triples >---1712for (unsigned int iy = 0; (iy < nTop) && (FreeTopTenTriple[iy] != NULL); iy++) {1713ast->print("Pos %3d: Block %4d - size " HEX32_FORMAT ",", iy+1, FreeTopTenTriple[iy]->index, FreeTopTenTriple[iy]->len);1714ast->fill_to(39);1715ast->print("Gap (to next) " HEX32_FORMAT ",", FreeTopTenTriple[iy]->gap);1716ast->fill_to(63);1717ast->print("#blocks (in gap) %d", FreeTopTenTriple[iy]->n_gapBlocks);1718ast->cr();1719BUFFEREDSTREAM_FLUSH_AUTO("")1720}1721}1722BUFFEREDSTREAM_FLUSH_LOCKED("\n\n")1723}172417251726void CodeHeapState::print_count(outputStream* out, CodeHeap* heap) {1727if (!initialization_complete) {1728return;1729}17301731const char* heapName = get_heapName(heap);1732get_HeapStatGlobals(out, heapName);17331734if ((StatArray == NULL) || (alloc_granules == 0)) {1735return;1736}1737BUFFEREDSTREAM_DECL(ast, out)17381739unsigned int granules_per_line = 32;1740char* low_bound = heap->low_boundary();17411742{1743printBox(ast, '=', "B L O C K C O U N T S for ", heapName);1744ast->print_cr(" Each granule contains an individual number of heap blocks. Large blocks\n"1745" may span multiple granules and are counted for each granule they touch.\n");1746if (segment_granules) {1747ast->print_cr(" You have selected granule size to be as small as segment size.\n"1748" As a result, each granule contains exactly one block (or a part of one block)\n"1749" or is displayed as empty (' ') if it's BlobType does not match the selection.\n"1750" Occupied granules show their BlobType character, see legend.\n");1751print_blobType_legend(ast);1752}1753BUFFEREDSTREAM_FLUSH_LOCKED("")1754}17551756{1757if (segment_granules) {1758printBox(ast, '-', "Total (all types) count for granule size == segment size", NULL);17591760granules_per_line = 128;1761for (unsigned int ix = 0; ix < alloc_granules; ix++) {1762print_line_delim(out, ast, low_bound, ix, granules_per_line);1763print_blobType_single(ast, StatArray[ix].type);1764}1765} else {1766printBox(ast, '-', "Total (all tiers) count, 0x1..0xf. '*' indicates >= 16 blocks, ' ' indicates empty", NULL);17671768granules_per_line = 128;1769for (unsigned int ix = 0; ix < alloc_granules; ix++) {1770print_line_delim(out, ast, low_bound, ix, granules_per_line);1771unsigned int count = StatArray[ix].t1_count + StatArray[ix].t2_count + StatArray[ix].tx_count1772+ StatArray[ix].stub_count + StatArray[ix].dead_count;1773print_count_single(ast, count);1774}1775}1776BUFFEREDSTREAM_FLUSH_LOCKED("|\n\n\n")1777}17781779{1780if (nBlocks_t1 > 0) {1781printBox(ast, '-', "Tier1 nMethod count only, 0x1..0xf. '*' indicates >= 16 blocks, ' ' indicates empty", NULL);17821783granules_per_line = 128;1784for (unsigned int ix = 0; ix < alloc_granules; ix++) {1785print_line_delim(out, ast, low_bound, ix, granules_per_line);1786if (segment_granules && StatArray[ix].t1_count > 0) {1787print_blobType_single(ast, StatArray[ix].type);1788} else {1789print_count_single(ast, StatArray[ix].t1_count);1790}1791}1792ast->print("|");1793} else {1794ast->print("No Tier1 nMethods found in CodeHeap.");1795}1796BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")1797}17981799{1800if (nBlocks_t2 > 0) {1801printBox(ast, '-', "Tier2 nMethod count only, 0x1..0xf. '*' indicates >= 16 blocks, ' ' indicates empty", NULL);18021803granules_per_line = 128;1804for (unsigned int ix = 0; ix < alloc_granules; ix++) {1805print_line_delim(out, ast, low_bound, ix, granules_per_line);1806if (segment_granules && StatArray[ix].t2_count > 0) {1807print_blobType_single(ast, StatArray[ix].type);1808} else {1809print_count_single(ast, StatArray[ix].t2_count);1810}1811}1812ast->print("|");1813} else {1814ast->print("No Tier2 nMethods found in CodeHeap.");1815}1816BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")1817}18181819{1820if (nBlocks_alive > 0) {1821printBox(ast, '-', "not_used/not_entrant/not_installed nMethod count only, 0x1..0xf. '*' indicates >= 16 blocks, ' ' indicates empty", NULL);18221823granules_per_line = 128;1824for (unsigned int ix = 0; ix < alloc_granules; ix++) {1825print_line_delim(out, ast, low_bound, ix, granules_per_line);1826if (segment_granules && StatArray[ix].tx_count > 0) {1827print_blobType_single(ast, StatArray[ix].type);1828} else {1829print_count_single(ast, StatArray[ix].tx_count);1830}1831}1832ast->print("|");1833} else {1834ast->print("No not_used/not_entrant nMethods found in CodeHeap.");1835}1836BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")1837}18381839{1840if (nBlocks_stub > 0) {1841printBox(ast, '-', "Stub & Blob count only, 0x1..0xf. '*' indicates >= 16 blocks, ' ' indicates empty", NULL);18421843granules_per_line = 128;1844for (unsigned int ix = 0; ix < alloc_granules; ix++) {1845print_line_delim(out, ast, low_bound, ix, granules_per_line);1846if (segment_granules && StatArray[ix].stub_count > 0) {1847print_blobType_single(ast, StatArray[ix].type);1848} else {1849print_count_single(ast, StatArray[ix].stub_count);1850}1851}1852ast->print("|");1853} else {1854ast->print("No Stubs and Blobs found in CodeHeap.");1855}1856BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")1857}18581859{1860if (nBlocks_dead > 0) {1861printBox(ast, '-', "Dead nMethod count only, 0x1..0xf. '*' indicates >= 16 blocks, ' ' indicates empty", NULL);18621863granules_per_line = 128;1864for (unsigned int ix = 0; ix < alloc_granules; ix++) {1865print_line_delim(out, ast, low_bound, ix, granules_per_line);1866if (segment_granules && StatArray[ix].dead_count > 0) {1867print_blobType_single(ast, StatArray[ix].type);1868} else {1869print_count_single(ast, StatArray[ix].dead_count);1870}1871}1872ast->print("|");1873} else {1874ast->print("No dead nMethods found in CodeHeap.");1875}1876BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")1877}18781879{1880if (!segment_granules) { // Prevent totally redundant printouts1881printBox(ast, '-', "Count by tier (combined, no dead blocks): <#t1>:<#t2>:<#s>, 0x0..0xf. '*' indicates >= 16 blocks", NULL);18821883granules_per_line = 24;1884for (unsigned int ix = 0; ix < alloc_granules; ix++) {1885print_line_delim(out, ast, low_bound, ix, granules_per_line);18861887print_count_single(ast, StatArray[ix].t1_count);1888ast->print(":");1889print_count_single(ast, StatArray[ix].t2_count);1890ast->print(":");1891if (segment_granules && StatArray[ix].stub_count > 0) {1892print_blobType_single(ast, StatArray[ix].type);1893} else {1894print_count_single(ast, StatArray[ix].stub_count);1895}1896ast->print(" ");1897}1898BUFFEREDSTREAM_FLUSH_LOCKED("|\n\n\n")1899}1900}1901}190219031904void CodeHeapState::print_space(outputStream* out, CodeHeap* heap) {1905if (!initialization_complete) {1906return;1907}19081909const char* heapName = get_heapName(heap);1910get_HeapStatGlobals(out, heapName);19111912if ((StatArray == NULL) || (alloc_granules == 0)) {1913return;1914}1915BUFFEREDSTREAM_DECL(ast, out)19161917unsigned int granules_per_line = 32;1918char* low_bound = heap->low_boundary();19191920{1921printBox(ast, '=', "S P A C E U S A G E & F R A G M E N T A T I O N for ", heapName);1922ast->print_cr(" The heap space covered by one granule is occupied to a various extend.\n"1923" The granule occupancy is displayed by one decimal digit per granule.\n");1924if (segment_granules) {1925ast->print_cr(" You have selected granule size to be as small as segment size.\n"1926" As a result, each granule contains exactly one block (or a part of one block)\n"1927" or is displayed as empty (' ') if it's BlobType does not match the selection.\n"1928" Occupied granules show their BlobType character, see legend.\n");1929print_blobType_legend(ast);1930} else {1931ast->print_cr(" These digits represent a fill percentage range (see legend).\n");1932print_space_legend(ast);1933}1934BUFFEREDSTREAM_FLUSH_LOCKED("")1935}19361937{1938if (segment_granules) {1939printBox(ast, '-', "Total (all types) space consumption for granule size == segment size", NULL);19401941granules_per_line = 128;1942for (unsigned int ix = 0; ix < alloc_granules; ix++) {1943print_line_delim(out, ast, low_bound, ix, granules_per_line);1944print_blobType_single(ast, StatArray[ix].type);1945}1946} else {1947printBox(ast, '-', "Total (all types) space consumption. ' ' indicates empty, '*' indicates full.", NULL);19481949granules_per_line = 128;1950for (unsigned int ix = 0; ix < alloc_granules; ix++) {1951print_line_delim(out, ast, low_bound, ix, granules_per_line);1952unsigned int space = StatArray[ix].t1_space + StatArray[ix].t2_space + StatArray[ix].tx_space1953+ StatArray[ix].stub_space + StatArray[ix].dead_space;1954print_space_single(ast, space);1955}1956}1957BUFFEREDSTREAM_FLUSH_LOCKED("|\n\n\n")1958}19591960{1961if (nBlocks_t1 > 0) {1962printBox(ast, '-', "Tier1 space consumption. ' ' indicates empty, '*' indicates full", NULL);19631964granules_per_line = 128;1965for (unsigned int ix = 0; ix < alloc_granules; ix++) {1966print_line_delim(out, ast, low_bound, ix, granules_per_line);1967if (segment_granules && StatArray[ix].t1_space > 0) {1968print_blobType_single(ast, StatArray[ix].type);1969} else {1970print_space_single(ast, StatArray[ix].t1_space);1971}1972}1973ast->print("|");1974} else {1975ast->print("No Tier1 nMethods found in CodeHeap.");1976}1977BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")1978}19791980{1981if (nBlocks_t2 > 0) {1982printBox(ast, '-', "Tier2 space consumption. ' ' indicates empty, '*' indicates full", NULL);19831984granules_per_line = 128;1985for (unsigned int ix = 0; ix < alloc_granules; ix++) {1986print_line_delim(out, ast, low_bound, ix, granules_per_line);1987if (segment_granules && StatArray[ix].t2_space > 0) {1988print_blobType_single(ast, StatArray[ix].type);1989} else {1990print_space_single(ast, StatArray[ix].t2_space);1991}1992}1993ast->print("|");1994} else {1995ast->print("No Tier2 nMethods found in CodeHeap.");1996}1997BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")1998}19992000{2001if (nBlocks_alive > 0) {2002printBox(ast, '-', "not_used/not_entrant/not_installed space consumption. ' ' indicates empty, '*' indicates full", NULL);20032004granules_per_line = 128;2005for (unsigned int ix = 0; ix < alloc_granules; ix++) {2006print_line_delim(out, ast, low_bound, ix, granules_per_line);2007if (segment_granules && StatArray[ix].tx_space > 0) {2008print_blobType_single(ast, StatArray[ix].type);2009} else {2010print_space_single(ast, StatArray[ix].tx_space);2011}2012}2013ast->print("|");2014} else {2015ast->print("No Tier2 nMethods found in CodeHeap.");2016}2017BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")2018}20192020{2021if (nBlocks_stub > 0) {2022printBox(ast, '-', "Stub and Blob space consumption. ' ' indicates empty, '*' indicates full", NULL);20232024granules_per_line = 128;2025for (unsigned int ix = 0; ix < alloc_granules; ix++) {2026print_line_delim(out, ast, low_bound, ix, granules_per_line);2027if (segment_granules && StatArray[ix].stub_space > 0) {2028print_blobType_single(ast, StatArray[ix].type);2029} else {2030print_space_single(ast, StatArray[ix].stub_space);2031}2032}2033ast->print("|");2034} else {2035ast->print("No Stubs and Blobs found in CodeHeap.");2036}2037BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")2038}20392040{2041if (nBlocks_dead > 0) {2042printBox(ast, '-', "Dead space consumption. ' ' indicates empty, '*' indicates full", NULL);20432044granules_per_line = 128;2045for (unsigned int ix = 0; ix < alloc_granules; ix++) {2046print_line_delim(out, ast, low_bound, ix, granules_per_line);2047print_space_single(ast, StatArray[ix].dead_space);2048}2049ast->print("|");2050} else {2051ast->print("No dead nMethods found in CodeHeap.");2052}2053BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")2054}20552056{2057if (!segment_granules) { // Prevent totally redundant printouts2058printBox(ast, '-', "Space consumption by tier (combined): <t1%>:<t2%>:<s%>. ' ' indicates empty, '*' indicates full", NULL);20592060granules_per_line = 24;2061for (unsigned int ix = 0; ix < alloc_granules; ix++) {2062print_line_delim(out, ast, low_bound, ix, granules_per_line);20632064if (segment_granules && StatArray[ix].t1_space > 0) {2065print_blobType_single(ast, StatArray[ix].type);2066} else {2067print_space_single(ast, StatArray[ix].t1_space);2068}2069ast->print(":");2070if (segment_granules && StatArray[ix].t2_space > 0) {2071print_blobType_single(ast, StatArray[ix].type);2072} else {2073print_space_single(ast, StatArray[ix].t2_space);2074}2075ast->print(":");2076if (segment_granules && StatArray[ix].stub_space > 0) {2077print_blobType_single(ast, StatArray[ix].type);2078} else {2079print_space_single(ast, StatArray[ix].stub_space);2080}2081ast->print(" ");2082}2083ast->print("|");2084BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")2085}2086}2087}20882089void CodeHeapState::print_age(outputStream* out, CodeHeap* heap) {2090if (!initialization_complete) {2091return;2092}20932094const char* heapName = get_heapName(heap);2095get_HeapStatGlobals(out, heapName);20962097if ((StatArray == NULL) || (alloc_granules == 0)) {2098return;2099}2100BUFFEREDSTREAM_DECL(ast, out)21012102unsigned int granules_per_line = 32;2103char* low_bound = heap->low_boundary();21042105{2106printBox(ast, '=', "M E T H O D A G E by CompileID for ", heapName);2107ast->print_cr(" The age of a compiled method in the CodeHeap is not available as a\n"2108" time stamp. Instead, a relative age is deducted from the method's compilation ID.\n"2109" Age information is available for tier1 and tier2 methods only. There is no\n"2110" age information for stubs and blobs, because they have no compilation ID assigned.\n"2111" Information for the youngest method (highest ID) in the granule is printed.\n"2112" Refer to the legend to learn how method age is mapped to the displayed digit.");2113print_age_legend(ast);2114BUFFEREDSTREAM_FLUSH_LOCKED("")2115}21162117{2118printBox(ast, '-', "Age distribution. '0' indicates youngest 1/256, '8': oldest half, ' ': no age information", NULL);21192120granules_per_line = 128;2121for (unsigned int ix = 0; ix < alloc_granules; ix++) {2122print_line_delim(out, ast, low_bound, ix, granules_per_line);2123unsigned int age1 = StatArray[ix].t1_age;2124unsigned int age2 = StatArray[ix].t2_age;2125unsigned int agex = StatArray[ix].tx_age;2126unsigned int age = age1 > age2 ? age1 : age2;2127age = age > agex ? age : agex;2128print_age_single(ast, age);2129}2130ast->print("|");2131BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")2132}21332134{2135if (nBlocks_t1 > 0) {2136printBox(ast, '-', "Tier1 age distribution. '0' indicates youngest 1/256, '8': oldest half, ' ': no age information", NULL);21372138granules_per_line = 128;2139for (unsigned int ix = 0; ix < alloc_granules; ix++) {2140print_line_delim(out, ast, low_bound, ix, granules_per_line);2141print_age_single(ast, StatArray[ix].t1_age);2142}2143ast->print("|");2144} else {2145ast->print("No Tier1 nMethods found in CodeHeap.");2146}2147BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")2148}21492150{2151if (nBlocks_t2 > 0) {2152printBox(ast, '-', "Tier2 age distribution. '0' indicates youngest 1/256, '8': oldest half, ' ': no age information", NULL);21532154granules_per_line = 128;2155for (unsigned int ix = 0; ix < alloc_granules; ix++) {2156print_line_delim(out, ast, low_bound, ix, granules_per_line);2157print_age_single(ast, StatArray[ix].t2_age);2158}2159ast->print("|");2160} else {2161ast->print("No Tier2 nMethods found in CodeHeap.");2162}2163BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")2164}21652166{2167if (nBlocks_alive > 0) {2168printBox(ast, '-', "not_used/not_entrant/not_installed age distribution. '0' indicates youngest 1/256, '8': oldest half, ' ': no age information", NULL);21692170granules_per_line = 128;2171for (unsigned int ix = 0; ix < alloc_granules; ix++) {2172print_line_delim(out, ast, low_bound, ix, granules_per_line);2173print_age_single(ast, StatArray[ix].tx_age);2174}2175ast->print("|");2176} else {2177ast->print("No Tier2 nMethods found in CodeHeap.");2178}2179BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")2180}21812182{2183if (!segment_granules) { // Prevent totally redundant printouts2184printBox(ast, '-', "age distribution by tier <a1>:<a2>. '0' indicates youngest 1/256, '8': oldest half, ' ': no age information", NULL);21852186granules_per_line = 32;2187for (unsigned int ix = 0; ix < alloc_granules; ix++) {2188print_line_delim(out, ast, low_bound, ix, granules_per_line);2189print_age_single(ast, StatArray[ix].t1_age);2190ast->print(":");2191print_age_single(ast, StatArray[ix].t2_age);2192ast->print(" ");2193}2194ast->print("|");2195BUFFEREDSTREAM_FLUSH_LOCKED("\n\n\n")2196}2197}2198}219922002201void CodeHeapState::print_names(outputStream* out, CodeHeap* heap) {2202if (!initialization_complete) {2203return;2204}22052206const char* heapName = get_heapName(heap);2207get_HeapStatGlobals(out, heapName);22082209if ((StatArray == NULL) || (alloc_granules == 0)) {2210return;2211}2212BUFFEREDSTREAM_DECL(ast, out)22132214unsigned int granules_per_line = 128;2215char* low_bound = heap->low_boundary();2216CodeBlob* last_blob = NULL;2217bool name_in_addr_range = true;2218bool have_locks = holding_required_locks();22192220//---< print at least 128K per block (i.e. between headers) >---2221if (granules_per_line*granule_size < 128*K) {2222granules_per_line = (unsigned int)((128*K)/granule_size);2223}22242225printBox(ast, '=', "M E T H O D N A M E S for ", heapName);2226ast->print_cr(" Method names are dynamically retrieved from the code cache at print time.\n"2227" Due to the living nature of the code heap and because the CodeCache_lock\n"2228" is not continuously held, the displayed name might be wrong or no name\n"2229" might be found at all. The likelihood for that to happen increases\n"2230" over time passed between aggregation and print steps.\n");2231BUFFEREDSTREAM_FLUSH_LOCKED("")22322233for (unsigned int ix = 0; ix < alloc_granules; ix++) {2234//---< print a new blob on a new line >---2235if (ix%granules_per_line == 0) {2236if (!name_in_addr_range) {2237ast->print_cr("No methods, blobs, or stubs found in this address range");2238}2239name_in_addr_range = false;22402241size_t end_ix = (ix+granules_per_line <= alloc_granules) ? ix+granules_per_line : alloc_granules;2242ast->cr();2243ast->print_cr("--------------------------------------------------------------------");2244ast->print_cr("Address range [" INTPTR_FORMAT "," INTPTR_FORMAT "), " SIZE_FORMAT "k", p2i(low_bound+ix*granule_size), p2i(low_bound + end_ix*granule_size), (end_ix - ix)*granule_size/(size_t)K);2245ast->print_cr("--------------------------------------------------------------------");2246BUFFEREDSTREAM_FLUSH_AUTO("")2247}2248// Only check granule if it contains at least one blob.2249unsigned int nBlobs = StatArray[ix].t1_count + StatArray[ix].t2_count + StatArray[ix].tx_count +2250StatArray[ix].stub_count + StatArray[ix].dead_count;2251if (nBlobs > 0 ) {2252for (unsigned int is = 0; is < granule_size; is+=(unsigned int)seg_size) {2253// heap->find_start() is safe. Only works on _segmap.2254// Returns NULL or void*. Returned CodeBlob may be uninitialized.2255char* this_seg = low_bound + ix*granule_size + is;2256CodeBlob* this_blob = (CodeBlob*)(heap->find_start(this_seg));2257bool blob_is_safe = blob_access_is_safe(this_blob);2258// blob could have been flushed, freed, and merged.2259// this_blob < last_blob is an indicator for that.2260if (blob_is_safe && (this_blob > last_blob)) {2261last_blob = this_blob;22622263//---< get type and name >---2264blobType cbType = noType;2265if (segment_granules) {2266cbType = (blobType)StatArray[ix].type;2267} else {2268//---< access these fields only if we own the CodeCache_lock >---2269if (have_locks) {2270cbType = get_cbType(this_blob);2271}2272}22732274//---< access these fields only if we own the CodeCache_lock >---2275const char* blob_name = "<unavailable>";2276nmethod* nm = NULL;2277if (have_locks) {2278blob_name = this_blob->name();2279nm = this_blob->as_nmethod_or_null();2280// this_blob->name() could return NULL if no name was given to CTOR. Inlined, maybe invisible on stack2281if (blob_name == NULL) {2282blob_name = "<unavailable>";2283}2284}22852286//---< print table header for new print range >---2287if (!name_in_addr_range) {2288name_in_addr_range = true;2289ast->fill_to(51);2290ast->print("%9s", "compiler");2291ast->fill_to(61);2292ast->print_cr("%6s", "method");2293ast->print_cr("%18s %13s %17s %9s %5s %18s %s", "Addr(module) ", "offset", "size", " type lvl", " temp", "blobType ", "Name");2294BUFFEREDSTREAM_FLUSH_AUTO("")2295}22962297//---< print line prefix (address and offset from CodeHeap start) >---2298ast->print(INTPTR_FORMAT, p2i(this_blob));2299ast->fill_to(19);2300ast->print("(+" PTR32_FORMAT ")", (unsigned int)((char*)this_blob-low_bound));2301ast->fill_to(33);23022303// access nmethod and Method fields only if we own the CodeCache_lock.2304// This fact is implicitly transported via nm != NULL.2305if (nmethod_access_is_safe(nm)) {2306Method* method = nm->method();2307ResourceMark rm;2308//---< collect all data to locals as quickly as possible >---2309unsigned int total_size = nm->total_size();2310int hotness = nm->hotness_counter();2311bool get_name = (cbType == nMethod_inuse) || (cbType == nMethod_notused);2312//---< nMethod size in hex >---2313ast->print(PTR32_FORMAT, total_size);2314ast->print("(" SIZE_FORMAT_W(4) "K)", total_size/K);2315//---< compiler information >---2316ast->fill_to(51);2317ast->print("%5s %3d", compTypeName[StatArray[ix].compiler], StatArray[ix].level);2318//---< method temperature >---2319ast->fill_to(62);2320ast->print("%5d", hotness);2321//---< name and signature >---2322ast->fill_to(62+6);2323ast->print("%s", blobTypeName[cbType]);2324ast->fill_to(82+6);2325if (cbType == nMethod_dead) {2326ast->print("%14s", " zombie method");2327}23282329if (get_name) {2330Symbol* methName = method->name();2331const char* methNameS = (methName == NULL) ? NULL : methName->as_C_string();2332methNameS = (methNameS == NULL) ? "<method name unavailable>" : methNameS;2333Symbol* methSig = method->signature();2334const char* methSigS = (methSig == NULL) ? NULL : methSig->as_C_string();2335methSigS = (methSigS == NULL) ? "<method signature unavailable>" : methSigS;2336ast->print("%s", methNameS);2337ast->print("%s", methSigS);2338} else {2339ast->print("%s", blob_name);2340}2341} else if (blob_is_safe) {2342ast->fill_to(62+6);2343ast->print("%s", blobTypeName[cbType]);2344ast->fill_to(82+6);2345ast->print("%s", blob_name);2346} else {2347ast->fill_to(62+6);2348ast->print("<stale blob>");2349}2350ast->cr();2351BUFFEREDSTREAM_FLUSH_AUTO("")2352} else if (!blob_is_safe && (this_blob != last_blob) && (this_blob != NULL)) {2353last_blob = this_blob;2354}2355}2356} // nBlobs > 02357}2358BUFFEREDSTREAM_FLUSH_LOCKED("\n\n")2359}236023612362void CodeHeapState::printBox(outputStream* ast, const char border, const char* text1, const char* text2) {2363unsigned int lineLen = 1 + 2 + 2 + 1;2364char edge, frame;23652366if (text1 != NULL) {2367lineLen += (unsigned int)strlen(text1); // text1 is much shorter than MAX_INT chars.2368}2369if (text2 != NULL) {2370lineLen += (unsigned int)strlen(text2); // text2 is much shorter than MAX_INT chars.2371}2372if (border == '-') {2373edge = '+';2374frame = '|';2375} else {2376edge = border;2377frame = border;2378}23792380ast->print("%c", edge);2381for (unsigned int i = 0; i < lineLen-2; i++) {2382ast->print("%c", border);2383}2384ast->print_cr("%c", edge);23852386ast->print("%c ", frame);2387if (text1 != NULL) {2388ast->print("%s", text1);2389}2390if (text2 != NULL) {2391ast->print("%s", text2);2392}2393ast->print_cr(" %c", frame);23942395ast->print("%c", edge);2396for (unsigned int i = 0; i < lineLen-2; i++) {2397ast->print("%c", border);2398}2399ast->print_cr("%c", edge);2400}24012402void CodeHeapState::print_blobType_legend(outputStream* out) {2403out->cr();2404printBox(out, '-', "Block types used in the following CodeHeap dump", NULL);2405for (int type = noType; type < lastType; type += 1) {2406out->print_cr(" %c - %s", blobTypeChar[type], blobTypeName[type]);2407}2408out->print_cr(" -----------------------------------------------------");2409out->cr();2410}24112412void CodeHeapState::print_space_legend(outputStream* out) {2413unsigned int indicator = 0;2414unsigned int age_range = 256;2415unsigned int range_beg = latest_compilation_id;2416out->cr();2417printBox(out, '-', "Space ranges, based on granule occupancy", NULL);2418out->print_cr(" - 0%% == occupancy");2419for (int i=0; i<=9; i++) {2420out->print_cr(" %d - %3d%% < occupancy < %3d%%", i, 10*i, 10*(i+1));2421}2422out->print_cr(" * - 100%% == occupancy");2423out->print_cr(" ----------------------------------------------");2424out->cr();2425}24262427void CodeHeapState::print_age_legend(outputStream* out) {2428unsigned int indicator = 0;2429unsigned int age_range = 256;2430unsigned int range_beg = latest_compilation_id;2431out->cr();2432printBox(out, '-', "Age ranges, based on compilation id", NULL);2433while (age_range > 0) {2434out->print_cr(" %d - %6d to %6d", indicator, range_beg, latest_compilation_id - latest_compilation_id/age_range);2435range_beg = latest_compilation_id - latest_compilation_id/age_range;2436age_range /= 2;2437indicator += 1;2438}2439out->print_cr(" -----------------------------------------");2440out->cr();2441}24422443void CodeHeapState::print_blobType_single(outputStream* out, u2 /* blobType */ type) {2444out->print("%c", blobTypeChar[type]);2445}24462447void CodeHeapState::print_count_single(outputStream* out, unsigned short count) {2448if (count >= 16) out->print("*");2449else if (count > 0) out->print("%1.1x", count);2450else out->print(" ");2451}24522453void CodeHeapState::print_space_single(outputStream* out, unsigned short space) {2454size_t space_in_bytes = ((unsigned int)space)<<log2_seg_size;2455char fraction = (space == 0) ? ' ' : (space_in_bytes >= granule_size-1) ? '*' : char('0'+10*space_in_bytes/granule_size);2456out->print("%c", fraction);2457}24582459void CodeHeapState::print_age_single(outputStream* out, unsigned int age) {2460unsigned int indicator = 0;2461unsigned int age_range = 256;2462if (age > 0) {2463while ((age_range > 0) && (latest_compilation_id-age > latest_compilation_id/age_range)) {2464age_range /= 2;2465indicator += 1;2466}2467out->print("%c", char('0'+indicator));2468} else {2469out->print(" ");2470}2471}24722473void CodeHeapState::print_line_delim(outputStream* out, outputStream* ast, char* low_bound, unsigned int ix, unsigned int gpl) {2474if (ix % gpl == 0) {2475if (ix > 0) {2476ast->print("|");2477}2478ast->cr();2479assert(out == ast, "must use the same stream!");24802481ast->print(INTPTR_FORMAT, p2i(low_bound + ix*granule_size));2482ast->fill_to(19);2483ast->print("(+" PTR32_FORMAT "): |", (unsigned int)(ix*granule_size));2484}2485}24862487void CodeHeapState::print_line_delim(outputStream* out, bufferedStream* ast, char* low_bound, unsigned int ix, unsigned int gpl) {2488assert(out != ast, "must not use the same stream!");2489if (ix % gpl == 0) {2490if (ix > 0) {2491ast->print("|");2492}2493ast->cr();24942495// can't use BUFFEREDSTREAM_FLUSH_IF("", 512) here.2496// can't use this expression. bufferedStream::capacity() does not exist.2497// if ((ast->capacity() - ast->size()) < 512) {2498// Assume instead that default bufferedStream capacity (4K) was used.2499if (ast->size() > 3*K) {2500ttyLocker ttyl;2501out->print("%s", ast->as_string());2502ast->reset();2503}25042505ast->print(INTPTR_FORMAT, p2i(low_bound + ix*granule_size));2506ast->fill_to(19);2507ast->print("(+" PTR32_FORMAT "): |", (unsigned int)(ix*granule_size));2508}2509}25102511// Find out which blob type we have at hand.2512// Return "noType" if anything abnormal is detected.2513CodeHeapState::blobType CodeHeapState::get_cbType(CodeBlob* cb) {2514if (cb != NULL) {2515if (cb->is_runtime_stub()) return runtimeStub;2516if (cb->is_deoptimization_stub()) return deoptimizationStub;2517if (cb->is_uncommon_trap_stub()) return uncommonTrapStub;2518if (cb->is_exception_stub()) return exceptionStub;2519if (cb->is_safepoint_stub()) return safepointStub;2520if (cb->is_adapter_blob()) return adapterBlob;2521if (cb->is_method_handles_adapter_blob()) return mh_adapterBlob;2522if (cb->is_buffer_blob()) return bufferBlob;25232524//---< access these fields only if we own CodeCache_lock and Compile_lock >---2525// Should be ensured by caller. aggregate() and print_names() do that.2526if (holding_required_locks()) {2527nmethod* nm = cb->as_nmethod_or_null();2528if (nm != NULL) { // no is_readable check required, nm = (nmethod*)cb.2529if (nm->is_zombie()) return nMethod_dead;2530if (nm->is_unloaded()) return nMethod_unloaded;2531if (nm->is_in_use()) return nMethod_inuse;2532if (nm->is_alive() && !(nm->is_not_entrant())) return nMethod_notused;2533if (nm->is_alive()) return nMethod_alive;2534return nMethod_dead;2535}2536}2537}2538return noType;2539}25402541// make sure the blob at hand is not garbage.2542bool CodeHeapState::blob_access_is_safe(CodeBlob* this_blob) {2543return (this_blob != NULL) && // a blob must have been found, obviously2544(this_blob->header_size() >= 0) &&2545(this_blob->relocation_size() >= 0) &&2546((address)this_blob + this_blob->header_size() == (address)(this_blob->relocation_begin())) &&2547((address)this_blob + CodeBlob::align_code_offset(this_blob->header_size() + this_blob->relocation_size()) == (address)(this_blob->content_begin()));2548}25492550// make sure the nmethod at hand (and the linked method) is not garbage.2551bool CodeHeapState::nmethod_access_is_safe(nmethod* nm) {2552Method* method = (nm == NULL) ? NULL : nm->method(); // nm->method() was found to be uninitialized, i.e. != NULL, but invalid.2553return (nm != NULL) && (method != NULL) && nm->is_alive() && (method->signature() != NULL);2554}25552556bool CodeHeapState::holding_required_locks() {2557return SafepointSynchronize::is_at_safepoint() ||2558(CodeCache_lock->owned_by_self() && Compile_lock->owned_by_self());2559}256025612562