# HG changeset patch # User kamg # Date 1207922195 14400 # Node ID a49a647afe9a9089e08236d8c6d9e317052cead7 # Parent c6ff24ceec1c7abce53e5c54b2d9a9923d23707a# Parent 9f4457a14b586f37f55903271122afcf2b1cf54e Merge diff -r c6ff24ceec1c -r a49a647afe9a .hgignore --- a/.hgignore Thu Apr 10 15:49:16 2008 -0400 +++ b/.hgignore Fri Apr 11 09:56:35 2008 -0400 @@ -1,3 +1,4 @@ ^build/ ^dist/ ^nbproject/private/ +^src/share/tools/hsdis/bin/ diff -r c6ff24ceec1c -r a49a647afe9a make/linux/makefiles/vm.make --- a/make/linux/makefiles/vm.make Thu Apr 10 15:49:16 2008 -0400 +++ b/make/linux/makefiles/vm.make Fri Apr 11 09:56:35 2008 -0400 @@ -71,6 +71,7 @@ # The following variables are defined in the generated flags.make file. BUILD_VERSION = -DHOTSPOT_RELEASE_VERSION="\"$(HS_BUILD_VER)\"" JRE_VERSION = -DJRE_RELEASE_VERSION="\"$(JRE_RELEASE_VER)\"" +HS_LIB_ARCH = -DHOTSPOT_LIB_ARCH=\"$(LIBARCH)\" BUILD_TARGET = -DHOTSPOT_BUILD_TARGET="\"$(TARGET)\"" BUILD_USER = -DHOTSPOT_BUILD_USER="\"$(HOTSPOT_BUILD_USER)\"" VM_DISTRO = -DHOTSPOT_VM_DISTRO="\"$(HOTSPOT_VM_DISTRO)\"" @@ -81,6 +82,7 @@ ${BUILD_VERSION} \ ${BUILD_TARGET} \ ${BUILD_USER} \ + ${HS_LIB_ARCH} \ ${JRE_VERSION} \ ${VM_DISTRO} diff -r c6ff24ceec1c -r a49a647afe9a make/linux/platform_amd64 --- a/make/linux/platform_amd64 Thu Apr 10 15:49:16 2008 -0400 +++ b/make/linux/platform_amd64 Fri Apr 11 09:56:35 2008 -0400 @@ -12,6 +12,4 @@ compiler = gcc -gnu_dis_arch = amd64 - sysdefs = -DLINUX -D_GNU_SOURCE -DAMD64 diff -r c6ff24ceec1c -r a49a647afe9a make/linux/platform_i486 --- a/make/linux/platform_i486 Thu Apr 10 15:49:16 2008 -0400 +++ b/make/linux/platform_i486 Fri Apr 11 09:56:35 2008 -0400 @@ -12,6 +12,4 @@ compiler = gcc -gnu_dis_arch = i386 - sysdefs = -DLINUX -D_GNU_SOURCE -DIA32 diff -r c6ff24ceec1c -r a49a647afe9a make/linux/platform_sparc --- a/make/linux/platform_sparc Thu Apr 10 15:49:16 2008 -0400 +++ b/make/linux/platform_sparc Fri Apr 11 09:56:35 2008 -0400 @@ -12,6 +12,4 @@ compiler = gcc -gnu_dis_arch = sparc - sysdefs = -DLINUX -D_GNU_SOURCE -DSPARC diff -r c6ff24ceec1c -r a49a647afe9a make/solaris/makefiles/vm.make --- a/make/solaris/makefiles/vm.make Thu Apr 10 15:49:16 2008 -0400 +++ b/make/solaris/makefiles/vm.make Fri Apr 11 09:56:35 2008 -0400 @@ -63,6 +63,7 @@ # The following variables are defined in the generated flags.make file. BUILD_VERSION = -DHOTSPOT_RELEASE_VERSION="\"$(HS_BUILD_VER)\"" JRE_VERSION = -DJRE_RELEASE_VERSION="\"$(JRE_RELEASE_VER)\"" +HS_LIB_ARCH = -DHOTSPOT_LIB_ARCH=\"$(LIBARCH)\" BUILD_TARGET = -DHOTSPOT_BUILD_TARGET="\"$(TARGET)\"" BUILD_USER = -DHOTSPOT_BUILD_USER="\"$(HOTSPOT_BUILD_USER)\"" VM_DISTRO = -DHOTSPOT_VM_DISTRO="\"$(HOTSPOT_VM_DISTRO)\"" @@ -73,6 +74,7 @@ ${BUILD_VERSION} \ ${BUILD_TARGET} \ ${BUILD_USER} \ + ${HS_LIB_ARCH} \ ${JRE_VERSION} \ ${VM_DISTRO} diff -r c6ff24ceec1c -r a49a647afe9a make/solaris/platform_amd64 --- a/make/solaris/platform_amd64 Thu Apr 10 15:49:16 2008 -0400 +++ b/make/solaris/platform_amd64 Fri Apr 11 09:56:35 2008 -0400 @@ -12,6 +12,4 @@ compiler = sparcWorks -gnu_dis_arch = amd64 - sysdefs = -DSOLARIS -DSPARC_WORKS -DAMD64 diff -r c6ff24ceec1c -r a49a647afe9a make/solaris/platform_amd64.gcc --- a/make/solaris/platform_amd64.gcc Thu Apr 10 15:49:16 2008 -0400 +++ b/make/solaris/platform_amd64.gcc Fri Apr 11 09:56:35 2008 -0400 @@ -12,6 +12,4 @@ compiler = gcc -gnu_dis_arch = amd64 - sysdefs = -DSOLARIS -D_GNU_SOURCE -DAMD64 diff -r c6ff24ceec1c -r a49a647afe9a make/solaris/platform_i486 --- a/make/solaris/platform_i486 Thu Apr 10 15:49:16 2008 -0400 +++ b/make/solaris/platform_i486 Fri Apr 11 09:56:35 2008 -0400 @@ -12,6 +12,4 @@ compiler = sparcWorks -gnu_dis_arch = i386 - sysdefs = -DSOLARIS -DSPARC_WORKS -DIA32 diff -r c6ff24ceec1c -r a49a647afe9a make/solaris/platform_i486.gcc --- a/make/solaris/platform_i486.gcc Thu Apr 10 15:49:16 2008 -0400 +++ b/make/solaris/platform_i486.gcc Fri Apr 11 09:56:35 2008 -0400 @@ -12,6 +12,4 @@ compiler = gcc -gnu_dis_arch = i386 - sysdefs = -DSOLARIS -D_GNU_SOURCE -DIA32 diff -r c6ff24ceec1c -r a49a647afe9a make/solaris/platform_sparc --- a/make/solaris/platform_sparc Thu Apr 10 15:49:16 2008 -0400 +++ b/make/solaris/platform_sparc Fri Apr 11 09:56:35 2008 -0400 @@ -12,6 +12,4 @@ compiler = sparcWorks -gnu_dis_arch = sparc - sysdefs = -DSOLARIS -DSPARC_WORKS -DSPARC diff -r c6ff24ceec1c -r a49a647afe9a make/solaris/platform_sparc.gcc --- a/make/solaris/platform_sparc.gcc Thu Apr 10 15:49:16 2008 -0400 +++ b/make/solaris/platform_sparc.gcc Fri Apr 11 09:56:35 2008 -0400 @@ -12,6 +12,4 @@ compiler = gcc -gnu_dis_arch = sparc - sysdefs = -DSOLARIS -D_GNU_SOURCE -DSPARC diff -r c6ff24ceec1c -r a49a647afe9a make/solaris/platform_sparcv9 --- a/make/solaris/platform_sparcv9 Thu Apr 10 15:49:16 2008 -0400 +++ b/make/solaris/platform_sparcv9 Fri Apr 11 09:56:35 2008 -0400 @@ -8,10 +8,8 @@ os_arch_model = solaris_sparc -lib_arch = sparc +lib_arch = sparcv9 compiler = sparcWorks -gnu_dis_arch = sparc - sysdefs = -DSOLARIS -DSPARC_WORKS -DSPARC diff -r c6ff24ceec1c -r a49a647afe9a make/solaris/platform_sparcv9.gcc --- a/make/solaris/platform_sparcv9.gcc Thu Apr 10 15:49:16 2008 -0400 +++ b/make/solaris/platform_sparcv9.gcc Fri Apr 11 09:56:35 2008 -0400 @@ -8,10 +8,8 @@ os_arch_model = solaris_sparc -lib_arch = sparc +lib_arch = sparcv9 compiler = gcc -gnu_dis_arch = sparc - sysdefs = -DSOLARIS -D_GNU_SOURCE -DSPARC diff -r c6ff24ceec1c -r a49a647afe9a make/windows/makefiles/vm.make --- a/make/windows/makefiles/vm.make Thu Apr 10 15:49:16 2008 -0400 +++ b/make/windows/makefiles/vm.make Fri Apr 11 09:56:35 2008 -0400 @@ -58,6 +58,7 @@ # The following variables are defined in the generated local.make file. CPP_FLAGS=$(CPP_FLAGS) /D "HOTSPOT_RELEASE_VERSION=\"$(HS_BUILD_VER)\"" CPP_FLAGS=$(CPP_FLAGS) /D "JRE_RELEASE_VERSION=\"$(JRE_RELEASE_VER)\"" +CPP_FLAGS=$(CPP_FLAGS) /D "HOTSPOT_LIB_ARCH=\"$(BUILDARCH)\"" CPP_FLAGS=$(CPP_FLAGS) /D "HOTSPOT_BUILD_TARGET=\"$(BUILD_FLAVOR)\"" CPP_FLAGS=$(CPP_FLAGS) /D "HOTSPOT_BUILD_USER=\"$(BuildUser)\"" CPP_FLAGS=$(CPP_FLAGS) /D "HOTSPOT_VM_DISTRO=\"$(HOTSPOT_VM_DISTRO)\"" diff -r c6ff24ceec1c -r a49a647afe9a make/windows/platform_amd64 --- a/make/windows/platform_amd64 Thu Apr 10 15:49:16 2008 -0400 +++ b/make/windows/platform_amd64 Fri Apr 11 09:56:35 2008 -0400 @@ -10,6 +10,6 @@ os_arch_model = windows_x86_64 -compiler = visCPP +lib_arch = amd64 -gnu_dis_arch = amd64 +compiler = visCPP diff -r c6ff24ceec1c -r a49a647afe9a make/windows/platform_i486 --- a/make/windows/platform_i486 Thu Apr 10 15:49:16 2008 -0400 +++ b/make/windows/platform_i486 Fri Apr 11 09:56:35 2008 -0400 @@ -10,7 +10,6 @@ os_arch_model = windows_x86_32 -compiler = visCPP +lib_arch = i386 -gnu_dis_arch = i386 - +compiler = visCPP diff -r c6ff24ceec1c -r a49a647afe9a src/cpu/sparc/vm/disassembler_sparc.cpp --- a/src/cpu/sparc/vm/disassembler_sparc.cpp Thu Apr 10 15:49:16 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,230 +0,0 @@ -/* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - * - */ - -# include "incls/_precompiled.incl" -# include "incls/_disassembler_sparc.cpp.incl" - -#ifndef PRODUCT - -#define SPARC_VERSION (VM_Version::v9_instructions_work()? \ - (VM_Version::v8_instructions_work()? "" : "9") : "8") - -// This routine is in the shared library: -typedef unsigned char* print_insn_sparc_t(unsigned char* start, DisassemblerEnv* env, - const char* sparc_version); - -void* Disassembler::_library = NULL; -dll_func Disassembler::_print_insn_sparc = NULL; - -bool Disassembler::load_library() { - if (_library == NULL) { - char buf[1024]; - char ebuf[1024]; - sprintf(buf, "disassembler%s", os::dll_file_extension()); - _library = hpi::dll_load(buf, ebuf, sizeof ebuf); - if (_library != NULL) { - tty->print_cr("Loaded disassembler"); - _print_insn_sparc = CAST_TO_FN_PTR(dll_func, hpi::dll_lookup(_library, "print_insn_sparc")); - } - } - return (_library != NULL) && (_print_insn_sparc != NULL); -} - - -class sparc_env : public DisassemblerEnv { - private: - nmethod* code; - outputStream* output; - const char* version; - - static void print_address(address value, outputStream* st); - - public: - sparc_env(nmethod* rcode, outputStream* routput) { - code = rcode; - output = routput; - version = SPARC_VERSION; - } - const char* sparc_version() { return version; } - void print_label(intptr_t value); - void print_raw(char* str) { output->print_raw(str); } - void print(char* format, ...); - char* string_for_offset(intptr_t value); - char* string_for_constant(unsigned char* pc, intptr_t value, int is_decimal); -}; - - -void sparc_env::print_address(address adr, outputStream* st) { - if (!Universe::is_fully_initialized()) { - st->print(INTPTR_FORMAT, (intptr_t)adr); - return; - } - if (StubRoutines::contains(adr)) { - StubCodeDesc *desc = StubCodeDesc::desc_for(adr); - if (desc == NULL) - desc = StubCodeDesc::desc_for(adr + frame::pc_return_offset); - if (desc == NULL) - st->print("Unknown stub at " INTPTR_FORMAT, adr); - else { - st->print("Stub::%s", desc->name()); - if (desc->begin() != adr) - st->print("%+d 0x%p",adr - desc->begin(), adr); - else if (WizardMode) st->print(" " INTPTR_FORMAT, adr); - } - } else { - BarrierSet* bs = Universe::heap()->barrier_set(); - if (bs->kind() == BarrierSet::CardTableModRef && - adr == (address)((CardTableModRefBS*)(bs))->byte_map_base) { - st->print("word_map_base"); - if (WizardMode) st->print(" " INTPTR_FORMAT, (intptr_t)adr); - } else { - st->print(INTPTR_FORMAT, (intptr_t)adr); - } - } -} - - -// called by the disassembler to print out jump addresses -void sparc_env::print_label(intptr_t value) { - print_address((address) value, output); -} - -void sparc_env::print(char* format, ...) { - va_list ap; - va_start(ap, format); - output->vprint(format, ap); - va_end(ap); -} - -char* sparc_env::string_for_offset(intptr_t value) { - stringStream st; - print_address((address) value, &st); - return st.as_string(); -} - -char* sparc_env::string_for_constant(unsigned char* pc, intptr_t value, int is_decimal) { - stringStream st; - oop obj; - if (code && (obj = code->embeddedOop_at(pc)) != NULL) { - obj->print_value_on(&st); - } else - { - print_address((address) value, &st); - } - return st.as_string(); -} - - -address Disassembler::decode_instruction(address start, DisassemblerEnv* env) { - const char* version = ((sparc_env*)env)->sparc_version(); - return ((print_insn_sparc_t*) _print_insn_sparc)(start, env, version); -} - - -const int show_bytes = false; // for disassembler debugging - - -void Disassembler::decode(CodeBlob* cb, outputStream* st) { - st = st ? st : tty; - st->print_cr("Decoding CodeBlob " INTPTR_FORMAT, cb); - decode(cb->instructions_begin(), cb->instructions_end(), st); -} - - -void Disassembler::decode(u_char* begin, u_char* end, outputStream* st) { - assert ((((intptr_t)begin | (intptr_t)end) % sizeof(int) == 0), "misaligned insn addr"); - st = st ? st : tty; - if (!load_library()) { - st->print_cr("Could not load disassembler"); - return; - } - sparc_env env(NULL, st); - unsigned char* p = (unsigned char*) begin; - CodeBlob* cb = CodeCache::find_blob_unsafe(begin); - while (p < (unsigned char*) end && p) { - if (cb != NULL) { - cb->print_block_comment(st, (intptr_t)(p - cb->instructions_begin())); - } - - unsigned char* p0 = p; - st->print(INTPTR_FORMAT ": ", p); - p = decode_instruction(p, &env); - if (show_bytes && p) { - st->print("\t\t\t"); - while (p0 < p) { st->print("%08lx ", *(int*)p0); p0 += sizeof(int); } - } - st->cr(); - } -} - - -void Disassembler::decode(nmethod* nm, outputStream* st) { - st = st ? st : tty; - - st->print_cr("Decoding compiled method " INTPTR_FORMAT ":", nm); - st->print("Code:"); - st->cr(); - - if (!load_library()) { - st->print_cr("Could not load disassembler"); - return; - } - sparc_env env(nm, st); - unsigned char* p = nm->instructions_begin(); - unsigned char* end = nm->instructions_end(); - assert ((((intptr_t)p | (intptr_t)end) % sizeof(int) == 0), "misaligned insn addr"); - - unsigned char *p1 = p; - int total_bucket_count = 0; - while (p1 < end && p1) { - unsigned char *p0 = p1; - ++p1; - address bucket_pc = FlatProfiler::bucket_start_for(p1); - if (bucket_pc != NULL && bucket_pc > p0 && bucket_pc <= p1) - total_bucket_count += FlatProfiler::bucket_count_for(p0); - } - - while (p < end && p) { - if (p == nm->entry_point()) st->print_cr("[Entry Point]"); - if (p == nm->verified_entry_point()) st->print_cr("[Verified Entry Point]"); - if (p == nm->exception_begin()) st->print_cr("[Exception Handler]"); - if (p == nm->stub_begin()) st->print_cr("[Stub Code]"); - if (p == nm->consts_begin()) st->print_cr("[Constants]"); - nm->print_block_comment(st, (intptr_t)(p - nm->instructions_begin())); - unsigned char* p0 = p; - st->print(" " INTPTR_FORMAT ": ", p); - p = decode_instruction(p, &env); - nm->print_code_comment_on(st, 40, p0, p); - st->cr(); - // Output pc bucket ticks if we have any - address bucket_pc = FlatProfiler::bucket_start_for(p); - if (bucket_pc != NULL && bucket_pc > p0 && bucket_pc <= p) { - int bucket_count = FlatProfiler::bucket_count_for(p0); - tty->print_cr("%3.1f%% [%d]", bucket_count*100.0/total_bucket_count, bucket_count); - tty->cr(); - } - } -} - -#endif // PRODUCT diff -r c6ff24ceec1c -r a49a647afe9a src/cpu/sparc/vm/disassembler_sparc.hpp --- a/src/cpu/sparc/vm/disassembler_sparc.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/cpu/sparc/vm/disassembler_sparc.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -1,5 +1,5 @@ /* - * Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,30 +22,11 @@ * */ -// The disassembler prints out sparc code annotated -// with Java specific information. + static int pd_instruction_alignment() { + return sizeof(int); + } -class Disassembler { -#ifndef PRODUCT - private: - // points to the library. - static void* _library; - // points to the print_insn_sparc function. - static dll_func _print_insn_sparc; - // tries to load library and return whether it succedded. - static bool load_library(); - // decodes one instruction and return the start of the next instruction. - static address decode_instruction(address start, DisassemblerEnv* env); -#endif - public: - static void decode(CodeBlob *cb, outputStream* st = NULL) PRODUCT_RETURN; - static void decode(nmethod* nm, outputStream* st = NULL) PRODUCT_RETURN; - static void decode(u_char* begin, u_char* end, outputStream* st = NULL) PRODUCT_RETURN; -}; - -//Reconciliation History -// 1.9 98/04/29 10:45:51 disassembler_i486.hpp -// 1.10 98/05/11 16:47:20 disassembler_i486.hpp -// 1.12 99/06/22 16:37:37 disassembler_i486.hpp -// 1.13 99/08/06 10:09:04 disassembler_i486.hpp -//End + static const char* pd_cpu_opts() { + return (VM_Version::v9_instructions_work()? + (VM_Version::v8_instructions_work()? "" : "v9only") : "v8only"); + } diff -r c6ff24ceec1c -r a49a647afe9a src/cpu/sparc/vm/frame_sparc.cpp --- a/src/cpu/sparc/vm/frame_sparc.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/cpu/sparc/vm/frame_sparc.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -157,22 +157,158 @@ check_location_valid(); } +bool frame::safe_for_sender(JavaThread *thread) { -bool frame::safe_for_sender(JavaThread *thread) { - address sp = (address)_sp; - if (sp != NULL && - (sp <= thread->stack_base() && sp >= thread->stack_base() - thread->stack_size())) { - // Unfortunately we can only check frame complete for runtime stubs and nmethod - // other generic buffer blobs are more problematic so we just assume they are - // ok. adapter blobs never have a frame complete and are never ok. - if (_cb != NULL && !_cb->is_frame_complete_at(_pc)) { - if (_cb->is_nmethod() || _cb->is_adapter_blob() || _cb->is_runtime_stub()) { - return false; - } + address _SP = (address) sp(); + address _FP = (address) fp(); + address _UNEXTENDED_SP = (address) unextended_sp(); + // sp must be within the stack + bool sp_safe = (_SP <= thread->stack_base()) && + (_SP >= thread->stack_base() - thread->stack_size()); + + if (!sp_safe) { + return false; + } + + // unextended sp must be within the stack and above or equal sp + bool unextended_sp_safe = (_UNEXTENDED_SP <= thread->stack_base()) && + (_UNEXTENDED_SP >= _SP); + + if (!unextended_sp_safe) return false; + + // an fp must be within the stack and above (but not equal) sp + bool fp_safe = (_FP <= thread->stack_base()) && + (_FP > _SP); + + // We know sp/unextended_sp are safe only fp is questionable here + + // If the current frame is known to the code cache then we can attempt to + // to construct the sender and do some validation of it. This goes a long way + // toward eliminating issues when we get in frame construction code + + if (_cb != NULL ) { + + // First check if frame is complete and tester is reliable + // Unfortunately we can only check frame complete for runtime stubs and nmethod + // other generic buffer blobs are more problematic so we just assume they are + // ok. adapter blobs never have a frame complete and are never ok. + + if (!_cb->is_frame_complete_at(_pc)) { + if (_cb->is_nmethod() || _cb->is_adapter_blob() || _cb->is_runtime_stub()) { + return false; + } + } + + // Entry frame checks + if (is_entry_frame()) { + // an entry frame must have a valid fp. + + if (!fp_safe) { + return false; } - return true; + + // Validate the JavaCallWrapper an entry frame must have + + address jcw = (address)entry_frame_call_wrapper(); + + bool jcw_safe = (jcw <= thread->stack_base()) && ( jcw > _FP); + + return jcw_safe; + + } + + intptr_t* younger_sp = sp(); + intptr_t* _SENDER_SP = sender_sp(); // sender is actually just _FP + bool adjusted_stack = is_interpreted_frame(); + + address sender_pc = (address)younger_sp[I7->sp_offset_in_saved_window()] + pc_return_offset; + + + // We must always be able to find a recognizable pc + CodeBlob* sender_blob = CodeCache::find_blob_unsafe(sender_pc); + if (sender_pc == NULL || sender_blob == NULL) { + return false; + } + + // It should be safe to construct the sender though it might not be valid + + frame sender(_SENDER_SP, younger_sp, adjusted_stack); + + // Do we have a valid fp? + address sender_fp = (address) sender.fp(); + + // an fp must be within the stack and above (but not equal) current frame's _FP + + bool sender_fp_safe = (sender_fp <= thread->stack_base()) && + (sender_fp > _FP); + + if (!sender_fp_safe) { + return false; + } + + + // If the potential sender is the interpreter then we can do some more checking + if (Interpreter::contains(sender_pc)) { + return sender.is_interpreted_frame_valid(thread); + } + + // Could just be some random pointer within the codeBlob + if (!sender.cb()->instructions_contains(sender_pc)) return false; + + // We should never be able to see an adapter if the current frame is something from code cache + + if ( sender_blob->is_adapter_blob()) { + return false; + } + + if( sender.is_entry_frame()) { + // Validate the JavaCallWrapper an entry frame must have + + address jcw = (address)sender.entry_frame_call_wrapper(); + + bool jcw_safe = (jcw <= thread->stack_base()) && ( jcw > sender_fp); + + return jcw_safe; + } + + // If the frame size is 0 something is bad because every nmethod has a non-zero frame size + // because you must allocate window space + + if (sender_blob->frame_size() == 0) { + assert(!sender_blob->is_nmethod(), "should count return address at least"); + return false; + } + + // The sender should positively be an nmethod or call_stub. On sparc we might in fact see something else. + // The cause of this is because at a save instruction the O7 we get is a leftover from an earlier + // window use. So if a runtime stub creates two frames (common in fastdebug/jvmg) then we see the + // stale pc. So if the sender blob is not something we'd expect we have little choice but to declare + // the stack unwalkable. pd_get_top_frame_for_signal_handler tries to recover from this by unwinding + // that initial frame and retrying. + + if (!sender_blob->is_nmethod()) { + return false; + } + + // Could put some more validation for the potential non-interpreted sender + // frame we'd create by calling sender if I could think of any. Wait for next crash in forte... + + // One idea is seeing if the sender_pc we have is one that we'd expect to call to current cb + + // We've validated the potential sender that would be created + + return true; + } - return false; + + // Must be native-compiled frame. Since sender will try and use fp to find + // linkages it must be safe + + if (!fp_safe) return false; + + // could try and do some more potential verification of native frame if we could think of some... + + return true; } // constructors @@ -450,7 +586,7 @@ } -bool frame::is_interpreted_frame_valid() const { +bool frame::is_interpreted_frame_valid(JavaThread* thread) const { #ifdef CC_INTERP // Is there anything to do? #else @@ -462,6 +598,7 @@ if (sp() == 0 || (intptr_t(sp()) & (2*wordSize-1)) != 0) { return false; } + const intptr_t interpreter_frame_initial_sp_offset = interpreter_frame_vm_local_words; if (fp() + interpreter_frame_initial_sp_offset < sp()) { return false; @@ -471,9 +608,43 @@ if (fp() <= sp()) { // this attempts to deal with unsigned comparison above return false; } - if (fp() - sp() > 4096) { // stack frames shouldn't be large. + // do some validation of frame elements + + // first the method + + methodOop m = *interpreter_frame_method_addr(); + + // validate the method we'd find in this potential sender + if (!Universe::heap()->is_valid_method(m)) return false; + + // stack frames shouldn't be much larger than max_stack elements + + if (fp() - sp() > 1024 + m->max_stack()*Interpreter::stackElementSize()) { return false; } + + // validate bci/bcx + + intptr_t bcx = interpreter_frame_bcx(); + if (m->validate_bci_from_bcx(bcx) < 0) { + return false; + } + + // validate constantPoolCacheOop + + constantPoolCacheOop cp = *interpreter_frame_cache_addr(); + + if (cp == NULL || + !Space::is_aligned(cp) || + !Universe::heap()->is_permanent((void*)cp)) return false; + + // validate locals + + address locals = (address) *interpreter_frame_locals_addr(); + + if (locals > thread->stack_base() || locals < (address) fp()) return false; + + // We'd have to be pretty unlucky to be mislead at this point #endif /* CC_INTERP */ return true; } diff -r c6ff24ceec1c -r a49a647afe9a src/cpu/x86/vm/disassembler_x86.cpp --- a/src/cpu/x86/vm/disassembler_x86.cpp Thu Apr 10 15:49:16 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,201 +0,0 @@ -/* - * Copyright 1997-2007 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - * - */ - -# include "incls/_precompiled.incl" -# include "incls/_disassembler_x86.cpp.incl" - -#ifndef PRODUCT - -void* Disassembler::_library = NULL; -Disassembler::decode_func Disassembler::_decode_instruction = NULL; - -bool Disassembler::load_library() { - if (_library == NULL) { - char buf[1024]; - char ebuf[1024]; - sprintf(buf, "disassembler%s", os::dll_file_extension()); - _library = hpi::dll_load(buf, ebuf, sizeof ebuf); - if (_library != NULL) { - tty->print_cr("Loaded disassembler"); - _decode_instruction = CAST_TO_FN_PTR(Disassembler::decode_func, hpi::dll_lookup(_library, "decode_instruction")); - } - } - return (_library != NULL) && (_decode_instruction != NULL); -} - -class x86_env : public DisassemblerEnv { - private: - nmethod* code; - outputStream* output; - public: - x86_env(nmethod* rcode, outputStream* routput) { - code = rcode; - output = routput; - } - void print_label(intptr_t value); - void print_raw(char* str) { output->print_raw(str); } - void print(char* format, ...); - char* string_for_offset(intptr_t value); - char* string_for_constant(unsigned char* pc, intptr_t value, int is_decimal); -}; - - -void x86_env::print_label(intptr_t value) { - if (!Universe::is_fully_initialized()) { - output->print(INTPTR_FORMAT, value); - return; - } - address adr = (address) value; - if (StubRoutines::contains(adr)) { - StubCodeDesc* desc = StubCodeDesc::desc_for(adr); - const char * desc_name = "unknown stub"; - if (desc != NULL) { - desc_name = desc->name(); - } - output->print("Stub::%s", desc_name); - if (WizardMode) output->print(" " INTPTR_FORMAT, value); - } else { - output->print(INTPTR_FORMAT, value); - } -} - -void x86_env::print(char* format, ...) { - va_list ap; - va_start(ap, format); - output->vprint(format, ap); - va_end(ap); -} - -char* x86_env::string_for_offset(intptr_t value) { - stringStream st; - if (!Universe::is_fully_initialized()) { - st.print(INTX_FORMAT, value); - return st.as_string(); - } - BarrierSet* bs = Universe::heap()->barrier_set(); - BarrierSet::Name bsn = bs->kind(); - if (bs->kind() == BarrierSet::CardTableModRef && - (jbyte*) value == ((CardTableModRefBS*)(bs))->byte_map_base) { - st.print("word_map_base"); - } else { - st.print(INTX_FORMAT, value); - } - return st.as_string(); -} - -char* x86_env::string_for_constant(unsigned char* pc, intptr_t value, int is_decimal) { - stringStream st; - oop obj = NULL; - if (code && ((obj = code->embeddedOop_at(pc)) != NULL)) { - obj->print_value_on(&st); - } else { - if (is_decimal == 1) { - st.print(INTX_FORMAT, value); - } else { - st.print(INTPTR_FORMAT, value); - } - } - return st.as_string(); -} - - - -address Disassembler::decode_instruction(address start, DisassemblerEnv* env) { - return ((decode_func) _decode_instruction)(start, env); -} - - -void Disassembler::decode(CodeBlob* cb, outputStream* st) { - st = st ? st : tty; - st->print_cr("Decoding CodeBlob " INTPTR_FORMAT, cb); - decode(cb->instructions_begin(), cb->instructions_end(), st); -} - - -void Disassembler::decode(u_char* begin, u_char* end, outputStream* st) { - st = st ? st : tty; - - const int show_bytes = false; // for disassembler debugging - - if (!load_library()) { - st->print_cr("Could not load disassembler"); - return; - } - - x86_env env(NULL, st); - unsigned char* p = (unsigned char*) begin; - CodeBlob* cb = CodeCache::find_blob_unsafe(begin); - while (p < (unsigned char*) end) { - if (cb != NULL) { - cb->print_block_comment(st, (intptr_t)(p - cb->instructions_begin())); - } - - unsigned char* p0 = p; - st->print(" " INTPTR_FORMAT ": ", p); - p = decode_instruction(p, &env); - if (show_bytes) { - st->print("\t\t\t"); - while (p0 < p) st->print("%x ", *p0++); - } - st->cr(); - } -} - - -void Disassembler::decode(nmethod* nm, outputStream* st) { - st = st ? st : tty; - - st->print_cr("Decoding compiled method " INTPTR_FORMAT ":", nm); - st->print("Code:"); - st->cr(); - - if (!load_library()) { - st->print_cr("Could not load disassembler"); - return; - } - x86_env env(nm, st); - unsigned char* p = nm->instructions_begin(); - unsigned char* end = nm->instructions_end(); - while (p < end) { - if (p == nm->entry_point()) st->print_cr("[Entry Point]"); - if (p == nm->verified_entry_point()) st->print_cr("[Verified Entry Point]"); - if (p == nm->exception_begin()) st->print_cr("[Exception Handler]"); - if (p == nm->stub_begin()) st->print_cr("[Stub Code]"); - if (p == nm->consts_begin()) st->print_cr("[Constants]"); - nm->print_block_comment(st, (intptr_t)(p - nm->instructions_begin())); - unsigned char* p0 = p; - st->print(" " INTPTR_FORMAT ": ", p); - p = decode_instruction(p, &env); - nm->print_code_comment_on(st, 40, p0, p); - st->cr(); - // Output pc bucket ticks if we have any - address bucket_pc = FlatProfiler::bucket_start_for(p); - if (bucket_pc != NULL && bucket_pc > p0 && bucket_pc <= p) { - int bucket_count = FlatProfiler::bucket_count_for(bucket_pc); - tty->print_cr("[%d]", bucket_count); - } - } -} - -#endif // PRODUCT diff -r c6ff24ceec1c -r a49a647afe9a src/cpu/x86/vm/disassembler_x86.hpp --- a/src/cpu/x86/vm/disassembler_x86.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/cpu/x86/vm/disassembler_x86.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -1,5 +1,5 @@ /* - * Copyright 1997-1999 Sun Microsystems, Inc. All Rights Reserved. + * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -22,24 +22,10 @@ * */ -// The disassembler prints out intel 386 code annotated -// with Java specific information. + static int pd_instruction_alignment() { + return 1; + } -class Disassembler { -#ifndef PRODUCT - private: - typedef address (*decode_func)(address start, DisassemblerEnv* env); - // points the library. - static void* _library; - // points to the decode function. - static decode_func _decode_instruction; - // tries to load library and return whether it succedded. - static bool load_library(); - // decodes one instruction and return the start of the next instruction. - static address decode_instruction(address start, DisassemblerEnv* env); -#endif - public: - static void decode(CodeBlob *cb, outputStream* st = NULL) PRODUCT_RETURN; - static void decode(nmethod* nm, outputStream* st = NULL) PRODUCT_RETURN; - static void decode(u_char* begin, u_char* end, outputStream* st = NULL) PRODUCT_RETURN; -}; + static const char* pd_cpu_opts() { + return ""; + } diff -r c6ff24ceec1c -r a49a647afe9a src/cpu/x86/vm/frame_x86.cpp --- a/src/cpu/x86/vm/frame_x86.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/cpu/x86/vm/frame_x86.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -37,39 +37,181 @@ address sp = (address)_sp; address fp = (address)_fp; address unextended_sp = (address)_unextended_sp; - bool sp_safe = (sp != NULL && - (sp <= thread->stack_base()) && - (sp >= thread->stack_base() - thread->stack_size())); - bool unextended_sp_safe = (unextended_sp != NULL && - (unextended_sp <= thread->stack_base()) && - (unextended_sp >= thread->stack_base() - thread->stack_size())); - bool fp_safe = (fp != NULL && - (fp <= thread->stack_base()) && - (fp >= thread->stack_base() - thread->stack_size())); - if (sp_safe && unextended_sp_safe && fp_safe) { + // sp must be within the stack + bool sp_safe = (sp <= thread->stack_base()) && + (sp >= thread->stack_base() - thread->stack_size()); + + if (!sp_safe) { + return false; + } + + // unextended sp must be within the stack and above or equal sp + bool unextended_sp_safe = (unextended_sp <= thread->stack_base()) && + (unextended_sp >= sp); + + if (!unextended_sp_safe) { + return false; + } + + // an fp must be within the stack and above (but not equal) sp + bool fp_safe = (fp <= thread->stack_base()) && (fp > sp); + + // We know sp/unextended_sp are safe only fp is questionable here + + // If the current frame is known to the code cache then we can attempt to + // to construct the sender and do some validation of it. This goes a long way + // toward eliminating issues when we get in frame construction code + + if (_cb != NULL ) { + + // First check if frame is complete and tester is reliable // Unfortunately we can only check frame complete for runtime stubs and nmethod // other generic buffer blobs are more problematic so we just assume they are // ok. adapter blobs never have a frame complete and are never ok. - if (_cb != NULL && !_cb->is_frame_complete_at(_pc)) { + + if (!_cb->is_frame_complete_at(_pc)) { if (_cb->is_nmethod() || _cb->is_adapter_blob() || _cb->is_runtime_stub()) { return false; } } + // Entry frame checks + if (is_entry_frame()) { + // an entry frame must have a valid fp. + + if (!fp_safe) return false; + + // Validate the JavaCallWrapper an entry frame must have + + address jcw = (address)entry_frame_call_wrapper(); + + bool jcw_safe = (jcw <= thread->stack_base()) && ( jcw > fp); + + return jcw_safe; + + } + + intptr_t* sender_sp = NULL; + address sender_pc = NULL; + + if (is_interpreted_frame()) { + // fp must be safe + if (!fp_safe) { + return false; + } + + sender_pc = (address) this->fp()[return_addr_offset]; + sender_sp = (intptr_t*) addr_at(sender_sp_offset); + + } else { + // must be some sort of compiled/runtime frame + // fp does not have to be safe (although it could be check for c1?) + + sender_sp = _unextended_sp + _cb->frame_size(); + // On Intel the return_address is always the word on the stack + sender_pc = (address) *(sender_sp-1); + } + + // We must always be able to find a recognizable pc + CodeBlob* sender_blob = CodeCache::find_blob_unsafe(sender_pc); + if (sender_pc == NULL || sender_blob == NULL) { + return false; + } + + + // If the potential sender is the interpreter then we can do some more checking + if (Interpreter::contains(sender_pc)) { + + // ebp is always saved in a recognizable place in any code we generate. However + // only if the sender is interpreted/call_stub (c1 too?) are we certain that the saved ebp + // is really a frame pointer. + + intptr_t *saved_fp = (intptr_t*)*(sender_sp - frame::sender_sp_offset); + bool saved_fp_safe = ((address)saved_fp <= thread->stack_base()) && (saved_fp > sender_sp); + + if (!saved_fp_safe) { + return false; + } + + // construct the potential sender + + frame sender(sender_sp, saved_fp, sender_pc); + + return sender.is_interpreted_frame_valid(thread); + + } + + // Could just be some random pointer within the codeBlob + + if (!sender_blob->instructions_contains(sender_pc)) return false; + + // We should never be able to see an adapter if the current frame is something from code cache + + if ( sender_blob->is_adapter_blob()) { + return false; + } + + // Could be the call_stub + + if (StubRoutines::returns_to_call_stub(sender_pc)) { + intptr_t *saved_fp = (intptr_t*)*(sender_sp - frame::sender_sp_offset); + bool saved_fp_safe = ((address)saved_fp <= thread->stack_base()) && (saved_fp > sender_sp); + + if (!saved_fp_safe) { + return false; + } + + // construct the potential sender + + frame sender(sender_sp, saved_fp, sender_pc); + + // Validate the JavaCallWrapper an entry frame must have + address jcw = (address)sender.entry_frame_call_wrapper(); + + bool jcw_safe = (jcw <= thread->stack_base()) && ( jcw > (address)sender.fp()); + + return jcw_safe; + } + + // If the frame size is 0 something is bad because every nmethod has a non-zero frame size + // because the return address counts against the callee's frame. + + if (sender_blob->frame_size() == 0) { + assert(!sender_blob->is_nmethod(), "should count return address at least"); + return false; + } + + // We should never be able to see anything here except an nmethod. If something in the + // code cache (current frame) is called by an entity within the code cache that entity + // should not be anything but the call stub (already covered), the interpreter (already covered) + // or an nmethod. + + assert(sender_blob->is_nmethod(), "Impossible call chain"); + + // Could put some more validation for the potential non-interpreted sender + // frame we'd create by calling sender if I could think of any. Wait for next crash in forte... + + // One idea is seeing if the sender_pc we have is one that we'd expect to call to current cb + + // We've validated the potential sender that would be created return true; } - // Note: fp == NULL is not really a prerequisite for this to be safe to - // walk for c2. However we've modified the code such that if we get - // a failure with fp != NULL that we then try with FP == NULL. - // This is basically to mimic what a last_frame would look like if - // c2 had generated it. - if (sp_safe && unextended_sp_safe && fp == NULL) { - // frame must be complete if fp == NULL as fp == NULL is only sensible - // if we are looking at a nmethod and frame complete assures us of that. - if (_cb != NULL && _cb->is_frame_complete_at(_pc) && _cb->is_compiled_by_c2()) { - return true; - } + + // Must be native-compiled frame. Since sender will try and use fp to find + // linkages it must be safe + + if (!fp_safe) { + return false; } - return false; + + // Will the pc we fetch be non-zero (which we'll find at the oldest frame) + + if ( (address) this->fp()[return_addr_offset] == NULL) return false; + + + // could try and do some more potential verification of native frame if we could think of some... + + return true; + } @@ -292,7 +434,7 @@ // nothing done here now } -bool frame::is_interpreted_frame_valid() const { +bool frame::is_interpreted_frame_valid(JavaThread* thread) const { // QQQ #ifdef CC_INTERP #else @@ -312,9 +454,45 @@ if (fp() <= sp()) { // this attempts to deal with unsigned comparison above return false; } - if (fp() - sp() > 4096) { // stack frames shouldn't be large. + + // do some validation of frame elements + + // first the method + + methodOop m = *interpreter_frame_method_addr(); + + // validate the method we'd find in this potential sender + if (!Universe::heap()->is_valid_method(m)) return false; + + // stack frames shouldn't be much larger than max_stack elements + + if (fp() - sp() > 1024 + m->max_stack()*Interpreter::stackElementSize()) { return false; } + + // validate bci/bcx + + intptr_t bcx = interpreter_frame_bcx(); + if (m->validate_bci_from_bcx(bcx) < 0) { + return false; + } + + // validate constantPoolCacheOop + + constantPoolCacheOop cp = *interpreter_frame_cache_addr(); + + if (cp == NULL || + !Space::is_aligned(cp) || + !Universe::heap()->is_permanent((void*)cp)) return false; + + // validate locals + + address locals = (address) *interpreter_frame_locals_addr(); + + if (locals > thread->stack_base() || locals < (address) fp()) return false; + + // We'd have to be pretty unlucky to be mislead at this point + #endif // CC_INTERP return true; } diff -r c6ff24ceec1c -r a49a647afe9a src/cpu/x86/vm/frame_x86.inline.hpp --- a/src/cpu/x86/vm/frame_x86.inline.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/cpu/x86/vm/frame_x86.inline.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -72,15 +72,20 @@ _unextended_sp = sp; _fp = fp; _pc = (address)(sp[-1]); - assert(_pc != NULL, "no pc?"); + + // Here's a sticky one. This constructor can be called via AsyncGetCallTrace + // when last_Java_sp is non-null but the pc fetched is junk. If we are truly + // unlucky the junk value could be to a zombied method and we'll die on the + // find_blob call. This is also why we can have no asserts on the validity + // of the pc we find here. AsyncGetCallTrace -> pd_get_top_frame_for_signal_handler + // -> pd_last_frame should use a specialized version of pd_last_frame which could + // call a specilaized frame constructor instead of this one. + // Then we could use the assert below. However this assert is of somewhat dubious + // value. + // assert(_pc != NULL, "no pc?"); + _cb = CodeCache::find_blob(_pc); - // In case of native stubs, the pc retreived here might be - // wrong. (the _last_native_pc will have the right value) - // So do not put add any asserts on the _pc here. - // QQQ The above comment is wrong and has been wrong for years. This constructor - // should (and MUST) not be called in that situation. In the native situation - // the pc should be supplied to the constructor. _deopt_state = not_deoptimized; if (_cb != NULL && _cb->is_nmethod() && ((nmethod*)_cb)->is_deopt_pc(_pc)) { _pc = (((nmethod*)_cb)->get_original_pc(this)); diff -r c6ff24ceec1c -r a49a647afe9a src/cpu/x86/vm/templateTable_x86_32.cpp --- a/src/cpu/x86/vm/templateTable_x86_32.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/cpu/x86/vm/templateTable_x86_32.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -1632,7 +1632,7 @@ // We need to prepare to execute the OSR method. First we must // migrate the locals and monitors off of the stack. - __ movl(rsi, rax); // save the nmethod + __ movl(rbx, rax); // save the nmethod const Register thread = rcx; __ get_thread(thread); @@ -1688,7 +1688,7 @@ __ pushl(rdi); // and begin the OSR nmethod - __ jmp(Address(rsi, nmethod::osr_entry_point_offset())); + __ jmp(Address(rbx, nmethod::osr_entry_point_offset())); } } } diff -r c6ff24ceec1c -r a49a647afe9a src/os_cpu/solaris_sparc/vm/thread_solaris_sparc.cpp --- a/src/os_cpu/solaris_sparc/vm/thread_solaris_sparc.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/os_cpu/solaris_sparc/vm/thread_solaris_sparc.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -50,17 +50,6 @@ // even if isInJava == true. It should be more reliable than // ucontext info. if (jt->has_last_Java_frame() && jt->frame_anchor()->walkable()) { -#if 0 - // This sanity check may not be needed with the new frame - // walking code. Remove it for now. - if (!jt->frame_anchor()->post_Java_state_is_pc() - && frame::next_younger_sp_or_null(last_Java_sp(), - jt->frame_anchor()->post_Java_sp()) == NULL) { - // the anchor contains an SP, but the frame is not walkable - // because post_Java_sp isn't valid relative to last_Java_sp - return false; - } -#endif *fr_addr = jt->pd_last_frame(); return true; } @@ -77,23 +66,59 @@ return false; } + frame ret_frame(ret_sp, frame::unpatchable, addr.pc()); + // we were running Java code when SIGPROF came in if (isInJava) { + + + // If the frame we got is safe then it is most certainly valid + if (ret_frame.safe_for_sender(jt)) { + *fr_addr = ret_frame; + return true; + } + + // If it isn't safe then we can try several things to try and get + // a good starting point. + // + // On sparc the frames are almost certainly walkable in the sense + // of sp/fp linkages. However because of recycling of windows if + // a piece of code does multiple save's where the initial save creates + // a real frame with a return pc and the succeeding save's are used to + // simply get free registers and have no real pc then the pc linkage on these + // "inner" temporary frames will be bogus. + // Since there is in general only a nesting level like + // this one deep in general we'll try and unwind such an "inner" frame + // here ourselves and see if it makes sense + + frame unwind_frame(ret_frame.fp(), frame::unpatchable, addr.pc()); + + if (unwind_frame.safe_for_sender(jt)) { + *fr_addr = unwind_frame; + return true; + } + + // Well that didn't work. Most likely we're toast on this tick + // The previous code would try this. I think it is dubious in light + // of changes to safe_for_sender and the unwind trick above but + // if it gets us a safe frame who wants to argue. + // If we have a last_Java_sp, then the SIGPROF signal caught us // right when we were transitioning from _thread_in_Java to a new // JavaThreadState. We use last_Java_sp instead of the sp from // the ucontext since it should be more reliable. + if (jt->has_last_Java_frame()) { ret_sp = jt->last_Java_sp(); + frame ret_frame2(ret_sp, frame::unpatchable, addr.pc()); + if (ret_frame2.safe_for_sender(jt)) { + *fr_addr = ret_frame2; + return true; + } } - // Implied else: we don't have a last_Java_sp so we use what we - // got from the ucontext. - frame ret_frame(ret_sp, frame::unpatchable, addr.pc()); - if (!ret_frame.safe_for_sender(jt)) { - // nothing else to try if the frame isn't good - return false; - } + // This is the best we can do. We will only be able to decode the top frame + *fr_addr = ret_frame; return true; } @@ -105,17 +130,13 @@ if (jt->has_last_Java_frame()) { assert(!jt->frame_anchor()->walkable(), "case covered above"); - if (jt->thread_state() == _thread_in_native) { - frame ret_frame(jt->last_Java_sp(), frame::unpatchable, addr.pc()); - if (!ret_frame.safe_for_sender(jt)) { - // nothing else to try if the frame isn't good - return false; - } - *fr_addr = ret_frame; - return true; - } + frame ret_frame(jt->last_Java_sp(), frame::unpatchable, addr.pc()); + *fr_addr = ret_frame; + return true; } - // nothing else to try - return false; + // nothing else to try but what we found initially + + *fr_addr = ret_frame; + return true; } diff -r c6ff24ceec1c -r a49a647afe9a src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp --- a/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/os_cpu/solaris_x86/vm/os_solaris_x86.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -212,7 +212,8 @@ CAST_FROM_FN_PTR(address, os::current_frame)); if (os::is_first_C_frame(&myframe)) { // stack is not walkable - return frame(NULL, NULL, NULL); + frame ret; // This will be a null useless frame + return ret; } else { return os::get_sender_for_C_frame(&myframe); } diff -r c6ff24ceec1c -r a49a647afe9a src/os_cpu/solaris_x86/vm/thread_solaris_x86.cpp --- a/src/os_cpu/solaris_x86/vm/thread_solaris_x86.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/os_cpu/solaris_x86/vm/thread_solaris_x86.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -32,49 +32,53 @@ assert(Thread::current() == this, "caller must be current thread"); assert(this->is_Java_thread(), "must be JavaThread"); - JavaThread* jt = (JavaThread *)this; - // If we have a last_Java_frame, then we should use it even if - // isInJava == true. It should be more reliable than ucontext info. + // last_Java_frame is always walkable and safe use it if we have it + if (jt->has_last_Java_frame()) { *fr_addr = jt->pd_last_frame(); return true; } - // At this point, we don't have a last_Java_frame, so - // we try to glean some information out of the ucontext - // if we were running Java code when SIGPROF came in. - if (isInJava) { - ucontext_t* uc = (ucontext_t*) ucontext; + ucontext_t* uc = (ucontext_t*) ucontext; - intptr_t* ret_fp; - intptr_t* ret_sp; - ExtendedPC addr = os::Solaris::fetch_frame_from_ucontext(this, uc, - &ret_sp, &ret_fp); - if (addr.pc() == NULL || ret_sp == NULL ) { - // ucontext wasn't useful - return false; - } + // We always want to use the initial frame we create from the ucontext as + // it certainly signals where we currently are. However that frame may not + // be safe for calling sender. In that case if we have a last_Java_frame + // then the forte walker will switch to that frame as the virtual sender + // for the frame we create here which is not sender safe. - frame ret_frame(ret_sp, ret_fp, addr.pc()); - if (!ret_frame.safe_for_sender(jt)) { -#ifdef COMPILER2 - frame ret_frame2(ret_sp, NULL, addr.pc()); - if (!ret_frame2.safe_for_sender(jt)) { - // nothing else to try if the frame isn't good - return false; - } - ret_frame = ret_frame2; -#else - // nothing else to try if the frame isn't good - return false; -#endif /* COMPILER2 */ - } - *fr_addr = ret_frame; - return true; + intptr_t* ret_fp; + intptr_t* ret_sp; + ExtendedPC addr = os::Solaris::fetch_frame_from_ucontext(this, uc, &ret_sp, &ret_fp); + + // Something would really have to be screwed up to get a NULL pc + + if (addr.pc() == NULL ) { + assert(false, "NULL pc from signal handler!"); + return false; + } - // nothing else to try - return false; + // If sp and fp are nonsense just leave them out + + if ((address)ret_sp >= jt->stack_base() || + (address)ret_sp < jt->stack_base() - jt->stack_size() ) { + + ret_sp = NULL; + ret_fp = NULL; + } else { + + // sp is reasonable is fp reasonable? + if ( (address)ret_fp >= jt->stack_base() || ret_fp < ret_sp) { + ret_fp = NULL; + } + } + + frame ret_frame(ret_sp, ret_fp, addr.pc()); + + *fr_addr = ret_frame; + return true; + } diff -r c6ff24ceec1c -r a49a647afe9a src/share/tools/hsdis/Makefile --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/tools/hsdis/Makefile Fri Apr 11 09:56:35 2008 -0400 @@ -0,0 +1,135 @@ +# +# Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. +# DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. +# +# This code is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License version 2 only, as +# published by the Free Software Foundation. +# +# This code is distributed in the hope that it will be useful, but WITHOUT +# ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +# FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +# version 2 for more details (a copy is included in the LICENSE file that +# accompanied this code). +# +# You should have received a copy of the GNU General Public License version +# 2 along with this work; if not, write to the Free Software Foundation, +# Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. +# +# Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +# CA 95054 USA or visit www.sun.com if you need additional information or +# have any questions. +# +# + +# Single gnu makefile for solaris, linux and windows (windows requires mks or +# cygwin). + +ifeq ($(BINUTILS),) +# Pop all the way out of the workspace to look for binutils. +# ...You probably want to override this setting. +BINUTILS = $(shell cd ../../../../..;pwd)/binutils-2.17-$(LIBARCH) +endif + +# Default arch; it is changed below as needed. +ARCH = i386 +OS = $(shell uname) + +CPPFLAGS += -I$(BINUTILS)/include -I$(BINUTILS)/bfd +CPPFLAGS += -DHOTSPOT_LIB_ARCH=\"$(LIBARCH)\" -DLIBARCH_$(LIBARCH) +CPPFLAGS += -DHOTSPOT_OS=\"$(OS)\" -DOS_$(OS) + +## OS = SunOS ## +ifeq ($(OS),SunOS) +ARCH = $(shell uname -p) +OS = solaris +CC = cc +CCFLAGS += -Kpic -g +CCFLAGS/amd64 += -xarch=amd64 +CCFLAGS/sparcv9 += -xarch=v9 +CCFLAGS += $(CCFLAGS/$(LIBARCH)) +DLDFLAGS += -G +OUTFLAGS += -o $@ +LIB_EXT = .so +else +## OS = Linux ## +ifeq ($(OS),Linux) +CPU = $(shell uname -m) +ifeq ($(CPU),ia64) +ARCH = ia64 +else +ifeq ($(CPU),x86_64) +CCFLAGS += -fPIC +endif # x86_64 +endif # ia64 +OS = linux +CC = gcc +CCFLAGS += -O +DLDFLAGS += -shared +OUTFLAGS += -o $@ +LIB_EXT = .so +CPPFLAGS += -Iinclude -Iinclude/$(OS)_$(ARCH)/ +## OS = Windows ## +else # !SunOS, !Linux => Windows +OS = win +CC = cl +#CPPFLAGS += /D"WIN32" /D"_WINDOWS" /D"DEBUG" /D"NDEBUG" +CCFLAGS += /nologo /MD /W3 /WX /O2 /Fo$(@:.dll=.obj) /Gi- +CCFLAGS += -Iinclude -Iinclude/gnu -Iinclude/$(OS)_$(ARCH) +CCFLAGS += /D"HOTSPOT_LIB_ARCH=\"$(LIBARCH)\"" +DLDFLAGS += /dll /subsystem:windows /incremental:no \ + /export:decode_instruction +OUTFLAGS += /link /out:$@ +LIB_EXT = .dll +endif # Linux +endif # SunOS + +LIBARCH = $(ARCH) +ifdef LP64 +LIBARCH64/sparc = sparcv9 +LIBARCH64/i386 = amd64 +LIBARCH64 = $(LIBARCH64/$(ARCH)) +ifneq ($(LIBARCH64),) +LIBARCH = $(LIBARCH64) +endif # LIBARCH64/$(ARCH) +endif # LP64 + +TARGET_DIR = bin/$(OS) +TARGET = $(TARGET_DIR)/hsdis-$(LIBARCH)$(LIB_EXT) + +SOURCE = hsdis.c + +LIBRARIES = $(BINUTILS)/bfd/libbfd.a \ + $(BINUTILS)/opcodes/libopcodes.a \ + $(BINUTILS)/libiberty/libiberty.a + +DEMO_TARGET = $(TARGET_DIR)/hsdis-demo-$(LIBARCH) +DEMO_SOURCE = hsdis-demo.c + +.PHONY: all clean demo both + +all: $(TARGET) demo + +both: all all64 + +%64: + $(MAKE) LP64=1 ${@:%64=%} + +demo: $(TARGET) $(DEMO_TARGET) + +$(LIBRARIES): + @echo "*** Please build binutils first; see ./README: ***" + @sed < ./README '1,/__________/d' | head -20 + @echo "..."; exit 1 + +$(TARGET): $(SOURCE) $(LIBS) $(LIBRARIES) $(TARGET_DIR) + $(CC) $(OUTFLAGS) $(CPPFLAGS) $(CCFLAGS) $(SOURCE) $(DLDFLAGS) $(LIBRARIES) + +$(DEMO_TARGET): $(DEMO_SOURCE) $(TARGET) $(TARGET_DIR) + $(CC) $(OUTFLAGS) $(CPPFLAGS) $(CCFLAGS) $(DEMO_SOURCE) $(LDFLAGS) + +$(TARGET_DIR): + [ -d $@ ] || mkdir -p $@ + +clean: + rm -rf $(TARGET_DIR) diff -r c6ff24ceec1c -r a49a647afe9a src/share/tools/hsdis/README --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/tools/hsdis/README Fri Apr 11 09:56:35 2008 -0400 @@ -0,0 +1,95 @@ +Copyright (c) 2008 Sun Microsystems, Inc. All Rights Reserved. +DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + +This code is free software; you can redistribute it and/or modify it +under the terms of the GNU General Public License version 2 only, as +published by the Free Software Foundation. + +This code is distributed in the hope that it will be useful, but WITHOUT +ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or +FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License +version 2 for more details (a copy is included in the LICENSE file that +accompanied this code). + +You should have received a copy of the GNU General Public License version +2 along with this work; if not, write to the Free Software Foundation, +Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + +Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, +CA 95054 USA or visit www.sun.com if you need additional information or +have any questions. + +________________________________________________________________________ + +'hsdis': A HotSpot plugin for disassembling dynamically generated code. + +The files in this directory (Makefile, hsdis.[ch], hsdis-demo.c) +are built independently of the HotSpot JVM. + +To use the plugin with a JVM, you need a new version that can load it. +If the product mode of your JVM does not accept -XX:+PrintAssembly, +you do not have a version that is new enough. + +* Building + +To build this project you need a build of Gnu binutils to link against. +It is known to work with binutils 2.17. + +The makefile looks for this build in $BINUTILS, or (if that is not set), +in .../binutils-2.17-$LIBARCH, where LIBARCH (as in HotSpot) is one of +the jre subdirectory keywords i386, amd64, sparc, sparcv9, etc. + +To build Gnu binutils, first download a copy of the software: + http://directory.fsf.org/project/binutils/ + +Unpack the binutils tarball into an empty directory: + chdir ../../../../.. + tar -xzf - < ../binutils-2.17.tar.gz + mv binutils-2.17 binutils-2.17-i386 #or binutils-2.17-sparc + cd binutils-2.17-i386 + +From inside that directory, run configure and make: + ( export CFLAGS='-fPIC' + ./configure i386-pc-elf ) + gnumake + +(Leave out or change the argument to configure if not on an i386 system.) + +Next, untar again into another empty directory for the LP64 version: + chdir .. + tar -xzf - < ../binutils-2.17.tar.gz + mv binutils-2.17 binutils-2.17-amd64 #or binutils-2.17-sparcv9 + cd binutils-2.17-amd64 + +From inside that directory, run configure for LP64 and make: + ( export ac_cv_c_bigendian=no CFLAGS='-m64 -fPIC' LDFLAGS=-m64 + ./configure amd64-pc-elf ) + gnumake + +The -fPIC option is needed because the generated code will be +linked into the hsdid-$LIBARCH.so binary. If you miss the +option, the JVM will fail to load the disassembler. + +You probably want two builds, one for 32 and one for 64 bits. +To build the 64-bit variation of a platforn, add LP64=1 to +the make command line for hsdis. + +So, go back to the hsdis project and build: + chdir .../hsdis + gnumake + gnumake LP64=1 + +* Installing + +Products are named like bin/$OS/hsdis-$LIBARCH.so. +You can install them on your LD_LIBRARY_PATH, +or inside of your JRE next to $LIBARCH/libjvm.so. + +Now test: + export LD_LIBRARY_PATH .../hsdis/bin/solaris:$LD_LIBRARY_PATH + dargs='-XX:+UnlockDiagnosticVMOptions -XX:+PrintAssembly' + dargs=$dargs' -XX:PrintAssemblyOptions=hsdis-print-bytes' + java $dargs -Xbatch CompileCommand=print,*String.hashCode HelloWorld + +If the product mode of the JVM does not accept -XX:+PrintAssembly, +you do not have a version new enough to use the hsdis plugin. diff -r c6ff24ceec1c -r a49a647afe9a src/share/tools/hsdis/hsdis-demo.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/tools/hsdis/hsdis-demo.c Fri Apr 11 09:56:35 2008 -0400 @@ -0,0 +1,223 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* hsdis-demo.c -- dump a range of addresses as native instructions + This demonstrates the protocol required by the HotSpot PrintAssembly option. +*/ + +#include "hsdis.h" + +#include "stdio.h" +#include "stdlib.h" +#include "string.h" + +void greet(const char*); +void disassemble(void*, void*); +void end_of_file(); + +const char* options = NULL; +int raw = 0; +int xml = 0; + +int main(int ac, char** av) { + int greeted = 0; + int i; + for (i = 1; i < ac; i++) { + const char* arg = av[i]; + if (arg[0] == '-') { + if (!strcmp(arg, "-xml")) + xml ^= 1; + else if (!strcmp(arg, "-raw")) + raw ^= 1; + else if (!strncmp(arg, "-options=", 9)) + options = arg+9; + else + { printf("Usage: %s [-xml] [name...]\n"); exit(2); } + continue; + } + greet(arg); + greeted = 1; + } + if (!greeted) + greet("world"); + printf("...And now for something completely different:\n"); + disassemble((void*) &main, (void*) &end_of_file); + printf("Cheers!\n"); +} + +void greet(const char* whom) { + printf("Hello, %s!\n", whom); +} + +void end_of_file() { } + +/* don't disassemble after this point... */ + +#include "dlfcn.h" + +#ifdef HOTSPOT_LIB_ARCH +#define LIBARCH HOTSPOT_LIB_ARCH +#endif +#ifdef HOTSPOT_OS +#define OS HOTSPOT_OS +#endif + +#define DECODE_INSTRUCTIONS_NAME "decode_instructions" +#define HSDIS_NAME "hsdis" +static void* decode_instructions_pv = 0; +static const char* hsdis_path[] = { + HSDIS_NAME".so", +#ifdef OS + "bin/"OS"/"HSDIS_NAME".so", +#endif +#ifdef LIBARCH + HSDIS_NAME"-"LIBARCH".so", +#ifdef OS + "bin/"OS"/"HSDIS_NAME"-"LIBARCH".so", +#endif +#endif + NULL +}; + +static const char* load_decode_instructions() { + void* dllib = NULL; + const char* *next_in_path = hsdis_path; + while (1) { + decode_instructions_pv = dlsym(dllib, DECODE_INSTRUCTIONS_NAME); + if (decode_instructions_pv != NULL) + return NULL; + if (dllib != NULL) + return "plugin does not defined "DECODE_INSTRUCTIONS_NAME; + for (dllib = NULL; dllib == NULL; ) { + const char* next_lib = (*next_in_path++); + if (next_lib == NULL) + return "cannot find plugin "HSDIS_NAME".so"; + dllib = dlopen(next_lib, RTLD_LAZY); + } + } +} + + +static const char* lookup(void* addr) { +#define CHECK_NAME(fn) \ + if (addr == (void*) &fn) return #fn; + + CHECK_NAME(main); + CHECK_NAME(greet); + return NULL; +} + +/* does the event match the tag, followed by a null, space, or slash? */ +#define MATCH(event, tag) \ + (!strncmp(event, tag, sizeof(tag)-1) && \ + (!event[sizeof(tag)-1] || strchr(" /", event[sizeof(tag)-1]))) + + +static const char event_cookie[] = "event_cookie"; /* demo placeholder */ +static void* handle_event(void* cookie, const char* event, void* arg) { +#define NS_DEMO "demo:" + if (cookie != event_cookie) + printf("*** bad event cookie %p != %p\n", cookie, event_cookie); + + if (xml) { + /* We could almost do a printf(event, arg), + but for the sake of a better demo, + we dress the result up as valid XML. + */ + const char* fmt = strchr(event, ' '); + int evlen = (fmt ? fmt - event : strlen(event)); + if (!fmt) { + if (event[0] != '/') { + printf("<"NS_DEMO"%.*s>", evlen, event); + } else { + printf("", evlen-1, event+1); + } + } else { + if (event[0] != '/') { + printf("<"NS_DEMO"%.*s", evlen, event); + printf(fmt, arg); + printf(">"); + } else { + printf("<"NS_DEMO"%.*s_done", evlen-1, event+1); + printf(fmt, arg); + printf("/>", evlen-1, event+1); + } + } + } + + if (MATCH(event, "insn")) { + const char* name = lookup(arg); + if (name) printf("%s:\n", name); + + /* basic action for : */ + printf(" %p\t", arg); + + } else if (MATCH(event, "/insn")) { + /* basic action for : + (none, plugin puts the newline for us + */ + + } else if (MATCH(event, "mach")) { + printf("Decoding for CPU '%s'\n", (char*) arg); + + } else if (MATCH(event, "addr")) { + /* basic action for : */ + const char* name = lookup(arg); + if (name) { + printf("&%s (%p)", name, arg); + /* return non-null to notify hsdis not to print the addr */ + return arg; + } + } + + /* null return is always safe; can mean "I ignored it" */ + return NULL; +} + +#define fprintf_callback \ + (decode_instructions_printf_callback_ftype)&fprintf + +void disassemble(void* from, void* to) { + const char* err = load_decode_instructions(); + if (err != NULL) { + printf("%s: %s\n", err, dlerror()); + exit(1); + } + printf("Decoding from %p to %p...\n", from, to); + decode_instructions_ftype decode_instructions + = (decode_instructions_ftype) decode_instructions_pv; + void* res; + if (raw && xml) { + res = (*decode_instructions)(from, to, NULL, stdout, NULL, stdout, options); + } else if (raw) { + res = (*decode_instructions)(from, to, NULL, NULL, NULL, stdout, options); + } else { + res = (*decode_instructions)(from, to, + handle_event, (void*) event_cookie, + fprintf_callback, stdout, + options); + } + if (res != to) + printf("*** Result was %p!\n", res); +} diff -r c6ff24ceec1c -r a49a647afe9a src/share/tools/hsdis/hsdis.c --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/tools/hsdis/hsdis.c Fri Apr 11 09:56:35 2008 -0400 @@ -0,0 +1,499 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* hsdis.c -- dump a range of addresses as native instructions + This implements the plugin protocol required by the + HotSpot PrintAssembly option. +*/ + +#include "hsdis.h" + +#include +#include +#include +#include + +#ifndef bool +#define bool int +#define true 1 +#define false 0 +#endif /*bool*/ + +/* short names for stuff in hsdis.h */ +typedef decode_instructions_event_callback_ftype event_callback_t; +typedef decode_instructions_printf_callback_ftype printf_callback_t; + +/* disassemble_info.application_data object */ +struct hsdis_app_data { + /* the arguments to decode_instructions */ + uintptr_t start; uintptr_t end; + event_callback_t event_callback; void* event_stream; + printf_callback_t printf_callback; void* printf_stream; + bool losing; + + /* the architecture being disassembled */ + const char* arch_name; + const bfd_arch_info_type* arch_info; + + /* the disassembler we are going to use: */ + disassembler_ftype dfn; + struct disassemble_info dinfo; /* the actual struct! */ + + char mach_option[64]; + char insn_options[256]; +}; + +#define DECL_APP_DATA(dinfo) \ + struct hsdis_app_data* app_data = (struct hsdis_app_data*) (dinfo)->application_data + +#define DECL_EVENT_CALLBACK(app_data) \ + event_callback_t event_callback = (app_data)->event_callback; \ + void* event_stream = (app_data)->event_stream + +#define DECL_PRINTF_CALLBACK(app_data) \ + printf_callback_t printf_callback = (app_data)->printf_callback; \ + void* printf_stream = (app_data)->printf_stream + + +static void print_help(struct hsdis_app_data* app_data, + const char* msg, const char* arg); +static void setup_app_data(struct hsdis_app_data* app_data, + const char* options); +static const char* format_insn_close(const char* close, + disassemble_info* dinfo, + char* buf, size_t bufsize); + +void* +#ifdef DLL_ENTRY + DLL_ENTRY +#endif +decode_instructions(void* start_pv, void* end_pv, + event_callback_t event_callback_arg, void* event_stream_arg, + printf_callback_t printf_callback_arg, void* printf_stream_arg, + const char* options) { + struct hsdis_app_data app_data; + memset(&app_data, 0, sizeof(app_data)); + app_data.start = (uintptr_t) start_pv; + app_data.end = (uintptr_t) end_pv; + app_data.event_callback = event_callback_arg; + app_data.event_stream = event_stream_arg; + app_data.printf_callback = printf_callback_arg; + app_data.printf_stream = printf_stream_arg; + + setup_app_data(&app_data, options); + char buf[128]; + + { + /* now reload everything from app_data: */ + DECL_EVENT_CALLBACK(&app_data); + DECL_PRINTF_CALLBACK(&app_data); + uintptr_t start = app_data.start; + uintptr_t end = app_data.end; + uintptr_t p = start; + + (*event_callback)(event_stream, "insns", (void*)start); + + (*event_callback)(event_stream, "mach name='%s'", + (void*) app_data.arch_info->printable_name); + if (app_data.dinfo.bytes_per_line != 0) { + (*event_callback)(event_stream, "format bytes-per-line='%p'/", + (void*)(intptr_t) app_data.dinfo.bytes_per_line); + } + + while (p < end && !app_data.losing) { + (*event_callback)(event_stream, "insn", (void*) p); + + /* reset certain state, so we can read it with confidence */ + app_data.dinfo.insn_info_valid = 0; + app_data.dinfo.branch_delay_insns = 0; + app_data.dinfo.data_size = 0; + app_data.dinfo.insn_type = 0; + + int size = (*app_data.dfn)((bfd_vma) p, &app_data.dinfo); + + if (size > 0) p += size; + else app_data.losing = true; + + const char* insn_close = format_insn_close("/insn", &app_data.dinfo, + buf, sizeof(buf)); + (*event_callback)(event_stream, insn_close, (void*) p); + + /* follow each complete insn by a nice newline */ + (*printf_callback)(printf_stream, "\n"); + } + + (*event_callback)(event_stream, "/insns", (void*) p); + return (void*) p; + } +} + +/* take the address of the function, for luck, and also test the typedef: */ +const decode_instructions_ftype decode_instructions_address = &decode_instructions; + +static const char* format_insn_close(const char* close, + disassemble_info* dinfo, + char* buf, size_t bufsize) { + if (!dinfo->insn_info_valid) + return close; + enum dis_insn_type itype = dinfo->insn_type; + int dsize = dinfo->data_size, delays = dinfo->branch_delay_insns; + if ((itype == dis_nonbranch && (dsize | delays) == 0) + || (strlen(close) + 3*20 > bufsize)) + return close; + + const char* type = "unknown"; + switch (itype) { + case dis_nonbranch: type = NULL; break; + case dis_branch: type = "branch"; break; + case dis_condbranch: type = "condbranch"; break; + case dis_jsr: type = "jsr"; break; + case dis_condjsr: type = "condjsr"; break; + case dis_dref: type = "dref"; break; + case dis_dref2: type = "dref2"; break; + } + + strcpy(buf, close); + char* p = buf; + if (type) sprintf(p += strlen(p), " type='%s'", type); + if (dsize) sprintf(p += strlen(p), " dsize='%d'", dsize); + if (delays) sprintf(p += strlen(p), " delay='%d'", delays); + return buf; +} + +/* handler functions */ + +static int +hsdis_read_memory_func(bfd_vma memaddr, + bfd_byte* myaddr, + unsigned int length, + struct disassemble_info* dinfo) { + uintptr_t memaddr_p = (uintptr_t) memaddr; + DECL_APP_DATA(dinfo); + if (memaddr_p + length > app_data->end) { + /* read is out of bounds */ + return EIO; + } else { + memcpy(myaddr, (bfd_byte*) memaddr_p, length); + return 0; + } +} + +static void +hsdis_print_address_func(bfd_vma vma, struct disassemble_info* dinfo) { + /* the actual value to print: */ + void* addr_value = (void*) (uintptr_t) vma; + DECL_APP_DATA(dinfo); + DECL_EVENT_CALLBACK(app_data); + + /* issue the event: */ + void* result = + (*event_callback)(event_stream, "addr/", addr_value); + if (result == NULL) { + /* event declined */ + generic_print_address(vma, dinfo); + } +} + + +/* configuration */ + +static void set_optional_callbacks(struct hsdis_app_data* app_data); +static void parse_caller_options(struct hsdis_app_data* app_data, + const char* caller_options); +static const char* native_arch_name(); +static enum bfd_endian native_endian(); +static const bfd_arch_info_type* find_arch_info(const char* arch_nane); +static bfd* get_native_bfd(const bfd_arch_info_type* arch_info, + /* to avoid malloc: */ + bfd* empty_bfd, bfd_target* empty_xvec); +static void init_disassemble_info_from_bfd(struct disassemble_info* dinfo, + void *stream, + fprintf_ftype fprintf_func, + bfd* bfd, + char* disassembler_options); +static void parse_fake_insn(disassembler_ftype dfn, + struct disassemble_info* dinfo); + +static void setup_app_data(struct hsdis_app_data* app_data, + const char* caller_options) { + /* Make reasonable defaults for null callbacks. + A non-null stream for a null callback is assumed to be a FILE* for output. + Events are rendered as XML. + */ + set_optional_callbacks(app_data); + + /* Look into caller_options for anything interesting. */ + if (caller_options != NULL) + parse_caller_options(app_data, caller_options); + + /* Discover which architecture we are going to disassemble. */ + app_data->arch_name = &app_data->mach_option[0]; + if (app_data->arch_name[0] == '\0') + app_data->arch_name = native_arch_name(); + app_data->arch_info = find_arch_info(app_data->arch_name); + + /* Make a fake bfd to hold the arch. and byteorder info. */ + struct { + bfd_target empty_xvec; + bfd empty_bfd; + } buf; + bfd* native_bfd = get_native_bfd(app_data->arch_info, + /* to avoid malloc: */ + &buf.empty_bfd, &buf.empty_xvec); + init_disassemble_info_from_bfd(&app_data->dinfo, + app_data->printf_stream, + app_data->printf_callback, + native_bfd, + app_data->insn_options); + + /* Finish linking together the various callback blocks. */ + app_data->dinfo.application_data = (void*) app_data; + app_data->dfn = disassembler(native_bfd); + app_data->dinfo.print_address_func = hsdis_print_address_func; + app_data->dinfo.read_memory_func = hsdis_read_memory_func; + + if (app_data->dfn == NULL) { + const char* bad = app_data->arch_name; + static bool complained; + if (bad == &app_data->mach_option[0]) + print_help(app_data, "bad mach=%s", bad); + else if (!complained) + print_help(app_data, "bad native mach=%s; please port hsdis to this platform", bad); + complained = true; + /* must bail out */ + app_data->losing = true; + return; + } + + parse_fake_insn(app_data->dfn, &app_data->dinfo); +} + + +/* ignore all events, return a null */ +static void* null_event_callback(void* ignore_stream, const char* ignore_event, void* arg) { + return NULL; +} + +/* print all events as XML markup */ +static void* xml_event_callback(void* stream, const char* event, void* arg) { + FILE* fp = (FILE*) stream; +#define NS_PFX "dis:" + if (event[0] != '/') { + /* issue the tag, with or without a formatted argument */ + fprintf(fp, "<"NS_PFX); + fprintf(fp, event, arg); + fprintf(fp, ">"); + } else { + ++event; /* skip slash */ + const char* argp = strchr(event, ' '); + if (argp == NULL) { + /* no arguments; just issue the closing tag */ + fprintf(fp, "", event); + } else { + /* split out the closing attributes as */ + int event_prefix = (argp - event); + fprintf(fp, "<"NS_PFX"%.*s_done", event_prefix, event); + fprintf(fp, argp, arg); + fprintf(fp, "/>", event_prefix, event); + } + } + return NULL; +} + +static void set_optional_callbacks(struct hsdis_app_data* app_data) { + if (app_data->printf_callback == NULL) { + int (*fprintf_callback)(FILE*, const char*, ...) = &fprintf; + FILE* fprintf_stream = stdout; + app_data->printf_callback = (printf_callback_t) fprintf_callback; + if (app_data->printf_stream == NULL) + app_data->printf_stream = (void*) fprintf_stream; + } + if (app_data->event_callback == NULL) { + if (app_data->event_stream == NULL) + app_data->event_callback = &null_event_callback; + else + app_data->event_callback = &xml_event_callback; + } + +} + +static void parse_caller_options(struct hsdis_app_data* app_data, const char* caller_options) { + char* iop_base = app_data->insn_options; + char* iop_limit = iop_base + sizeof(app_data->insn_options) - 1; + char* iop = iop_base; + const char* p; + for (p = caller_options; p != NULL; ) { + const char* q = strchr(p, ','); + size_t plen = (q == NULL) ? strlen(p) : ((q++) - p); + if (plen == 4 && strncmp(p, "help", plen) == 0) { + print_help(app_data, NULL, NULL); + } else if (plen >= 5 && strncmp(p, "mach=", 5) == 0) { + char* mach_option = app_data->mach_option; + size_t mach_size = sizeof(app_data->mach_option); + mach_size -= 1; /*leave room for the null*/ + if (plen > mach_size) plen = mach_size; + strncpy(mach_option, p, plen); + mach_option[plen] = '\0'; + } else if (plen > 6 && strncmp(p, "hsdis-", 6)) { + // do not pass these to the next level + } else { + /* just copy it; {i386,sparc}-dis.c might like to see it */ + if (iop > iop_base && iop < iop_limit) (*iop++) = ','; + if (iop + plen > iop_limit) + plen = iop_limit - iop; + strncpy(iop, p, plen); + iop += plen; + } + p = q; + } +} + +static void print_help(struct hsdis_app_data* app_data, + const char* msg, const char* arg) { + DECL_PRINTF_CALLBACK(app_data); + if (msg != NULL) { + (*printf_callback)(printf_stream, "hsdis: "); + (*printf_callback)(printf_stream, msg, arg); + (*printf_callback)(printf_stream, "\n"); + } + (*printf_callback)(printf_stream, "hsdis output options:\n"); + if (printf_callback == (printf_callback_t) &fprintf) + disassembler_usage((FILE*) printf_stream); + else + disassembler_usage(stderr); /* better than nothing */ + (*printf_callback)(printf_stream, " mach= select disassembly mode\n"); +#if defined(LIBARCH_i386) || defined(LIBARCH_amd64) + (*printf_callback)(printf_stream, " mach=i386 select 32-bit mode\n"); + (*printf_callback)(printf_stream, " mach=x86-64 select 64-bit mode\n"); + (*printf_callback)(printf_stream, " suffix always print instruction suffix\n"); +#endif + (*printf_callback)(printf_stream, " help print this message\n"); +} + + +/* low-level bfd and arch stuff that binutils doesn't do for us */ + +static const bfd_arch_info_type* find_arch_info(const char* arch_name) { + const bfd_arch_info_type* arch_info = bfd_scan_arch(arch_name); + if (arch_info == NULL) { + extern const bfd_arch_info_type bfd_default_arch_struct; + arch_info = &bfd_default_arch_struct; + } + return arch_info; +} + +static const char* native_arch_name() { + const char* res = HOTSPOT_LIB_ARCH; +#ifdef LIBARCH_amd64 + res = "i386:x86-64"; +#endif +#ifdef LIBARCH_sparc + res = "sparc:v8plusb"; +#endif +#ifdef LIBARCH_sparc + res = "sparc:v8plusb"; +#endif +#ifdef LIBARCH_sparcv9 + res = "sparc:v9b"; +#endif + if (res == NULL) + res = "HOTSPOT_LIB_ARCH is not set in Makefile!"; + return res; +} + +static enum bfd_endian native_endian() { + int32_t endian_test = 'x'; + if (*(const char*) &endian_test == 'x') + return BFD_ENDIAN_LITTLE; + else + return BFD_ENDIAN_BIG; +} + +static bfd* get_native_bfd(const bfd_arch_info_type* arch_info, + bfd* empty_bfd, bfd_target* empty_xvec) { + memset(empty_bfd, 0, sizeof(*empty_bfd)); + memset(empty_xvec, 0, sizeof(*empty_xvec)); + empty_xvec->flavour = bfd_target_unknown_flavour; + empty_xvec->byteorder = native_endian(); + empty_bfd->xvec = empty_xvec; + empty_bfd->arch_info = arch_info; + return empty_bfd; +} + +static int read_zero_data_only(bfd_vma ignore_p, + bfd_byte* myaddr, unsigned int length, + struct disassemble_info *ignore_info) { + memset(myaddr, 0, length); + return 0; +} +static int print_to_dev_null(void* ignore_stream, const char* ignore_format, ...) { + return 0; +} + +/* Prime the pump by running the selected disassembler on a null input. + This forces the machine-specific disassembler to divulge invariant + information like bytes_per_line. + */ +static void parse_fake_insn(disassembler_ftype dfn, + struct disassemble_info* dinfo) { + typedef int (*read_memory_ftype) + (bfd_vma memaddr, bfd_byte *myaddr, unsigned int length, + struct disassemble_info *info); + read_memory_ftype read_memory_func = dinfo->read_memory_func; + fprintf_ftype fprintf_func = dinfo->fprintf_func; + + dinfo->read_memory_func = &read_zero_data_only; + dinfo->fprintf_func = &print_to_dev_null; + (*dfn)(0, dinfo); + + // put it back: + dinfo->read_memory_func = read_memory_func; + dinfo->fprintf_func = fprintf_func; +} + +static void init_disassemble_info_from_bfd(struct disassemble_info* dinfo, + void *stream, + fprintf_ftype fprintf_func, + bfd* abfd, + char* disassembler_options) { + init_disassemble_info(dinfo, stream, fprintf_func); + + dinfo->flavour = bfd_get_flavour(abfd); + dinfo->arch = bfd_get_arch(abfd); + dinfo->mach = bfd_get_mach(abfd); + dinfo->disassembler_options = disassembler_options; + dinfo->octets_per_byte = bfd_octets_per_byte (abfd); + dinfo->skip_zeroes = sizeof(void*) * 2; + dinfo->skip_zeroes_at_end = sizeof(void*)-1; + dinfo->disassembler_needs_relocs = FALSE; + + if (bfd_big_endian(abfd)) + dinfo->display_endian = dinfo->endian = BFD_ENDIAN_BIG; + else if (bfd_little_endian(abfd)) + dinfo->display_endian = dinfo->endian = BFD_ENDIAN_LITTLE; + else + dinfo->endian = native_endian(); + + disassemble_init_for_target(dinfo); +} diff -r c6ff24ceec1c -r a49a647afe9a src/share/tools/hsdis/hsdis.h --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/tools/hsdis/hsdis.h Fri Apr 11 09:56:35 2008 -0400 @@ -0,0 +1,67 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +/* decode_instructions -- dump a range of addresses as native instructions + This implements the protocol required by the HotSpot PrintAssembly option. + + The starting and ending addresses are within the current process's address space. + + The option string, if not empty, is interpreted by the disassembler implementation. + + The printf callback is 'fprintf' or any other workalike. + It is called as (*printf_callback)(printf_stream, "some format...", some, format, args). + + The event callback receives an event tag (a string) and an argument (a void*). + It is called as (*event_callback)(event_stream, "tag", arg). + + Events: + begin an instruction, at a given location + end an instruction, at a given location + emit the symbolic value of an address + + A tag format is one of three basic forms: "tag", "/tag", "tag/", + where tag is a simple identifier, signifying (as in XML) a element start, + element end, and standalone element. (To render as XML, add angle brackets.) +*/ +extern +#ifdef DLL_EXPORT + DLL_EXPORT +#endif +void* decode_instructions(void* start, void* end, + void* (*event_callback)(void*, const char*, void*), + void* event_stream, + int (*printf_callback)(void*, const char*, ...), + void* printf_stream, + const char* options); + +/* convenience typedefs */ + +typedef void* (*decode_instructions_event_callback_ftype) (void*, const char*, void*); +typedef int (*decode_instructions_printf_callback_ftype) (void*, const char*, ...); +typedef void* (*decode_instructions_ftype) (void* start, void* end, + decode_instructions_event_callback_ftype event_callback, + void* event_stream, + decode_instructions_printf_callback_ftype printf_callback, + void* printf_stream, + const char* options); diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/asm/codeBuffer.cpp --- a/src/share/vm/asm/codeBuffer.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/asm/codeBuffer.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -947,6 +947,7 @@ if (_comments != NULL) { CodeComment* c = _comments->find(offset); while (c && c->offset() == offset) { + stream->bol(); stream->print(" ;; "); stream->print_cr(c->comment()); c = c->next(); diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/classfile/javaClasses.cpp --- a/src/share/vm/classfile/javaClasses.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/classfile/javaClasses.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -1015,7 +1015,6 @@ typeArrayOop _bcis; int _index; bool _dirty; - bool _done; No_Safepoint_Verifier _nsv; public: @@ -1029,12 +1028,10 @@ }; // constructor for new backtrace - BacktraceBuilder(TRAPS): _methods(NULL), _bcis(NULL), _head(NULL) { + BacktraceBuilder(TRAPS): _methods(NULL), _bcis(NULL), _head(NULL), _dirty(false) { expand(CHECK); _backtrace = _head; _index = 0; - _dirty = false; - _done = false; } void flush() { diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/code/codeCache.hpp --- a/src/share/vm/code/codeCache.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/code/codeCache.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -71,7 +71,22 @@ // what you are doing) static CodeBlob* find_blob_unsafe(void* start) { CodeBlob* result = (CodeBlob*)_heap->find_start(start); - assert(result == NULL || result->blob_contains((address)start), "found wrong CodeBlob"); + // this assert is too strong because the heap code will return the + // heapblock containing start. That block can often be larger than + // the codeBlob itself. If you look up an address that is within + // the heapblock but not in the codeBlob you will assert. + // + // Most things will not lookup such bad addresses. However + // AsyncGetCallTrace can see intermediate frames and get that kind + // of invalid address and so can a developer using hsfind. + // + // The more correct answer is to return NULL if blob_contains() returns + // false. + // assert(result == NULL || result->blob_contains((address)start), "found wrong CodeBlob"); + + if (result != NULL && !result->blob_contains((address)start)) { + result = NULL; + } return result; } diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/code/nmethod.cpp --- a/src/share/vm/code/nmethod.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/code/nmethod.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -707,7 +707,9 @@ " entry points must be same for static methods and vice versa"); } - bool printnmethods = PrintNMethods || CompilerOracle::has_option_string(_method, "PrintNMethods"); + bool printnmethods = PrintNMethods + || CompilerOracle::should_print(_method) + || CompilerOracle::has_option_string(_method, "PrintNMethods"); if (printnmethods || PrintDebugInfo || PrintRelocations || PrintDependencies || PrintExceptionHandlers) { print_nmethod(printnmethods); } @@ -798,7 +800,6 @@ } -#ifndef PRODUCT void nmethod::print_nmethod(bool printmethod) { ttyLocker ttyl; // keep the following output all in one block if (xtty != NULL) { @@ -831,7 +832,6 @@ xtty->tail("print_nmethod"); } } -#endif void nmethod::set_version(int v) { @@ -1870,6 +1870,7 @@ } } +#endif // PRODUCT // Printing operations @@ -1948,6 +1949,14 @@ oops_size()); } +void nmethod::print_code() { + HandleMark hm; + ResourceMark m; + Disassembler::decode(this); +} + + +#ifndef PRODUCT void nmethod::print_scopes() { // Find the first pc desc for all scopes in the code and print it. @@ -1979,13 +1988,6 @@ } -void nmethod::print_code() { - HandleMark hm; - ResourceMark m; - Disassembler().decode(this); -} - - void nmethod::print_relocations() { ResourceMark m; // in case methods get printed via the debugger tty->print_cr("relocations:"); @@ -2021,6 +2023,7 @@ } } +#endif // PRODUCT const char* nmethod::reloc_string_for(u_char* begin, u_char* end) { RelocIterator iter(this, begin, end); @@ -2055,7 +2058,6 @@ return have_one ? "other" : NULL; } - // Return a the last scope in (begin..end] ScopeDesc* nmethod::scope_desc_in(address begin, address end) { PcDesc* p = pc_desc_near(begin+1); @@ -2078,29 +2080,26 @@ address pc = base + om->offset(); if (pc > begin) { if (pc <= end) { - st->fill_to(column); - if (st == tty) { - st->print("; OopMap "); - om->print(); - tty->cr(); - } else { - st->print_cr("; OopMap #%d offset:%d", i, om->offset()); - } + st->move_to(column); + st->print("; "); + om->print_on(st); } break; } } } + + // Print any debug info present at this pc. ScopeDesc* sd = scope_desc_in(begin, end); if (sd != NULL) { - st->fill_to(column); + st->move_to(column); if (sd->bci() == SynchronizationEntryBCI) { st->print(";*synchronization entry"); } else { if (sd->method().is_null()) { - tty->print("method is NULL"); + st->print("method is NULL"); } else if (sd->method()->is_native()) { - tty->print("method is native"); + st->print("method is native"); } else { address bcp = sd->method()->bcp_from(sd->bci()); Bytecodes::Code bc = Bytecodes::java_code_at(bcp); @@ -2137,13 +2136,13 @@ } } } - st->cr(); + // Print all scopes for (;sd != NULL; sd = sd->sender()) { - st->fill_to(column); + st->move_to(column); st->print("; -"); if (sd->method().is_null()) { - tty->print("method is NULL"); + st->print("method is NULL"); } else { sd->method()->print_short_name(st); } @@ -2161,17 +2160,19 @@ const char* str = reloc_string_for(begin, end); if (str != NULL) { if (sd != NULL) st->cr(); - st->fill_to(column); + st->move_to(column); st->print("; {%s}", str); } int cont_offset = ImplicitExceptionTable(this).at(begin - instructions_begin()); if (cont_offset != 0) { - st->fill_to(column); + st->move_to(column); st->print("; implicit exception: dispatches to " INTPTR_FORMAT, instructions_begin() + cont_offset); } } +#ifndef PRODUCT + void nmethod::print_value_on(outputStream* st) const { print_on(st, "nmethod"); } diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/code/nmethod.hpp --- a/src/share/vm/code/nmethod.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/code/nmethod.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -485,8 +485,8 @@ void verify_interrupt_point(address interrupt_point); // printing support - void print() const PRODUCT_RETURN; - void print_code() PRODUCT_RETURN; + void print() const; + void print_code(); void print_relocations() PRODUCT_RETURN; void print_pcs() PRODUCT_RETURN; void print_scopes() PRODUCT_RETURN; @@ -495,7 +495,7 @@ void print_calls(outputStream* st) PRODUCT_RETURN; void print_handler_table() PRODUCT_RETURN; void print_nul_chk_table() PRODUCT_RETURN; - void print_nmethod(bool print_code) PRODUCT_RETURN; + void print_nmethod(bool print_code); void print_on(outputStream* st, const char* title) const; @@ -505,7 +505,7 @@ void log_state_change(int state) const; // Prints a comment for one native instruction (reloc info, pc desc) - void print_code_comment_on(outputStream* st, int column, address begin, address end) PRODUCT_RETURN; + void print_code_comment_on(outputStream* st, int column, address begin, address end); static void print_statistics() PRODUCT_RETURN; // Compiler task identification. Note that all OSR methods diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/code/vmreg.cpp --- a/src/share/vm/code/vmreg.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/code/vmreg.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -36,7 +36,6 @@ // Register names const char *VMRegImpl::regName[ConcreteRegisterImpl::number_of_registers]; -#ifndef PRODUCT void VMRegImpl::print_on(outputStream* st) const { if( is_reg() ) { assert( VMRegImpl::regName[value()], "" ); @@ -48,4 +47,3 @@ st->print("BAD!"); } } -#endif // PRODUCT diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/code/vmreg.hpp --- a/src/share/vm/code/vmreg.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/code/vmreg.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -96,7 +96,7 @@ intptr_t value() const {return (intptr_t) this; } - void print_on(outputStream* st) const PRODUCT_RETURN; + void print_on(outputStream* st) const; void print() const { print_on(tty); } // bias a stack slot. @@ -156,22 +156,22 @@ _first = ptr; } // Return true if single register, even if the pair is really just adjacent stack slots - bool is_single_reg() { + bool is_single_reg() const { return (_first->is_valid()) && (_first->value() + 1 == _second->value()); } // Return true if single stack based "register" where the slot alignment matches input alignment - bool is_adjacent_on_stack(int alignment) { + bool is_adjacent_on_stack(int alignment) const { return (_first->is_stack() && (_first->value() + 1 == _second->value()) && ((_first->value() & (alignment-1)) == 0)); } // Return true if single stack based "register" where the slot alignment matches input alignment - bool is_adjacent_aligned_on_stack(int alignment) { + bool is_adjacent_aligned_on_stack(int alignment) const { return (_first->is_stack() && (_first->value() + 1 == _second->value()) && ((_first->value() & (alignment-1)) == 0)); } // Return true if single register but adjacent stack slots do not count - bool is_single_phys_reg() { + bool is_single_phys_reg() const { return (_first->is_reg() && (_first->value() + 1 == _second->value())); } diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/compiler/disassembler.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/compiler/disassembler.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -0,0 +1,443 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +# include "incls/_precompiled.incl" +# include "incls/_disassembler.cpp.incl" + +void* Disassembler::_library = NULL; +bool Disassembler::_tried_to_load_library = false; + +// This routine is in the shared library: +Disassembler::decode_func Disassembler::_decode_instructions = NULL; + +static const char hsdis_library_name[] = "hsdis-"HOTSPOT_LIB_ARCH; +static const char decode_instructions_name[] = "decode_instructions"; + +#define COMMENT_COLUMN 40 LP64_ONLY(+8) /*could be an option*/ +#define BYTES_COMMENT ";..." /* funky byte display comment */ + +bool Disassembler::load_library() { + if (_decode_instructions != NULL) { + // Already succeeded. + return true; + } + if (_tried_to_load_library) { + // Do not try twice. + // To force retry in debugger: assign _tried_to_load_library=0 + return false; + } + // Try to load it. + char ebuf[1024]; + char buf[JVM_MAXPATHLEN]; + os::jvm_path(buf, sizeof(buf)); + int jvm_offset = -1; + { + // Match "jvm[^/]*" in jvm_path. + const char* base = buf; + const char* p = strrchr(buf, '/'); + p = strstr(p ? p : base, "jvm"); + if (p != NULL) jvm_offset = p - base; + } + if (jvm_offset >= 0) { + // Find the disassembler next to libjvm.so. + strcpy(&buf[jvm_offset], hsdis_library_name); + strcat(&buf[jvm_offset], os::dll_file_extension()); + _library = hpi::dll_load(buf, ebuf, sizeof ebuf); + } + if (_library == NULL) { + // Try a free-floating lookup. + strcpy(&buf[0], hsdis_library_name); + strcat(&buf[0], os::dll_file_extension()); + _library = hpi::dll_load(buf, ebuf, sizeof ebuf); + } + if (_library != NULL) { + _decode_instructions = CAST_TO_FN_PTR(Disassembler::decode_func, + hpi::dll_lookup(_library, decode_instructions_name)); + } + _tried_to_load_library = true; + if (_decode_instructions == NULL) { + tty->print_cr("Could not load %s; %s; %s", buf, + ((_library != NULL) + ? "entry point is missing" + : (WizardMode || PrintMiscellaneous) + ? (const char*)ebuf + : "library not loadable"), + "PrintAssembly is disabled"); + return false; + } + + // Success. + tty->print_cr("Loaded disassembler from %s", buf); + return true; +} + + +class decode_env { + private: + nmethod* _nm; + CodeBlob* _code; + outputStream* _output; + address _start, _end; + + char _option_buf[512]; + char _print_raw; + bool _print_pc; + bool _print_bytes; + address _cur_insn; + int _total_ticks; + int _bytes_per_line; // arch-specific formatting option + + static bool match(const char* event, const char* tag) { + size_t taglen = strlen(tag); + if (strncmp(event, tag, taglen) != 0) + return false; + char delim = event[taglen]; + return delim == '\0' || delim == ' ' || delim == '/' || delim == '='; + } + + void collect_options(const char* p) { + if (p == NULL || p[0] == '\0') return; + size_t opt_so_far = strlen(_option_buf); + if (opt_so_far + 1 + strlen(p) + 1 > sizeof(_option_buf)) return; + char* fillp = &_option_buf[opt_so_far]; + if (opt_so_far > 0) *fillp++ = ','; + strcat(fillp, p); + // replace white space by commas: + char* q = fillp; + while ((q = strpbrk(q, " \t\n")) != NULL) + *q++ = ','; + // Note that multiple PrintAssemblyOptions flags accumulate with \n, + // which we want to be changed to a comma... + } + + void print_insn_labels(); + void print_insn_bytes(address pc0, address pc); + void print_address(address value); + + public: + decode_env(CodeBlob* code, outputStream* output); + + address decode_instructions(address start, address end); + + void start_insn(address pc) { + _cur_insn = pc; + output()->bol(); + print_insn_labels(); + } + + void end_insn(address pc) { + address pc0 = cur_insn(); + outputStream* st = output(); + if (_print_bytes && pc > pc0) + print_insn_bytes(pc0, pc); + if (_nm != NULL) + _nm->print_code_comment_on(st, COMMENT_COLUMN, pc0, pc); + + // Output pc bucket ticks if we have any + if (total_ticks() != 0) { + address bucket_pc = FlatProfiler::bucket_start_for(pc); + if (bucket_pc != NULL && bucket_pc > pc0 && bucket_pc <= pc) { + int bucket_count = FlatProfiler::bucket_count_for(pc0); + if (bucket_count != 0) { + st->bol(); + st->print_cr("%3.1f%% [%d]", bucket_count*100.0/total_ticks(), bucket_count); + } + } + } + } + + address handle_event(const char* event, address arg); + + outputStream* output() { return _output; } + address cur_insn() { return _cur_insn; } + int total_ticks() { return _total_ticks; } + void set_total_ticks(int n) { _total_ticks = n; } + const char* options() { return _option_buf; } +}; + +decode_env::decode_env(CodeBlob* code, outputStream* output) { + memset(this, 0, sizeof(*this)); + _output = output ? output : tty; + _code = code; + if (code != NULL && code->is_nmethod()) + _nm = (nmethod*) code; + + // by default, output pc but not bytes: + _print_pc = true; + _print_bytes = false; + _bytes_per_line = Disassembler::pd_instruction_alignment(); + + // parse the global option string: + collect_options(Disassembler::pd_cpu_opts()); + collect_options(PrintAssemblyOptions); + + if (strstr(options(), "hsdis-")) { + if (strstr(options(), "hsdis-print-raw")) + _print_raw = (strstr(options(), "xml") ? 2 : 1); + if (strstr(options(), "hsdis-print-pc")) + _print_pc = !_print_pc; + if (strstr(options(), "hsdis-print-bytes")) + _print_bytes = !_print_bytes; + } + if (strstr(options(), "help")) { + tty->print_cr("PrintAssemblyOptions help:"); + tty->print_cr(" hsdis-print-raw test plugin by requesting raw output"); + tty->print_cr(" hsdis-print-raw-xml test plugin by requesting raw xml"); + tty->print_cr(" hsdis-print-pc turn off PC printing (on by default)"); + tty->print_cr(" hsdis-print-bytes turn on instruction byte output"); + tty->print_cr("combined options: %s", options()); + } +} + +address decode_env::handle_event(const char* event, address arg) { + if (match(event, "insn")) { + start_insn(arg); + } else if (match(event, "/insn")) { + end_insn(arg); + } else if (match(event, "addr")) { + if (arg != NULL) { + print_address(arg); + return arg; + } + } else if (match(event, "mach")) { + output()->print_cr("[Disassembling for mach='%s']", arg); + } else if (match(event, "format bytes-per-line")) { + _bytes_per_line = (int) (intptr_t) arg; + } else { + // ignore unrecognized markup + } + return NULL; +} + +// called by the disassembler to print out jump targets and data addresses +void decode_env::print_address(address adr) { + outputStream* st = _output; + + if (adr == NULL) { + st->print("NULL"); + return; + } + + int small_num = (int)(intptr_t)adr; + if ((intptr_t)adr == (intptr_t)small_num + && -1 <= small_num && small_num <= 9) { + st->print("%d", small_num); + return; + } + + if (Universe::is_fully_initialized()) { + if (StubRoutines::contains(adr)) { + StubCodeDesc* desc = StubCodeDesc::desc_for(adr); + if (desc == NULL) + desc = StubCodeDesc::desc_for(adr + frame::pc_return_offset); + if (desc != NULL) { + st->print("Stub::%s", desc->name()); + if (desc->begin() != adr) + st->print("%+d 0x%p",adr - desc->begin(), adr); + else if (WizardMode) st->print(" " INTPTR_FORMAT, adr); + return; + } + st->print("Stub:: " INTPTR_FORMAT, adr); + return; + } + + BarrierSet* bs = Universe::heap()->barrier_set(); + if (bs->kind() == BarrierSet::CardTableModRef && + adr == (address)((CardTableModRefBS*)(bs))->byte_map_base) { + st->print("word_map_base"); + if (WizardMode) st->print(" " INTPTR_FORMAT, (intptr_t)adr); + return; + } + + oop obj; + if (_nm != NULL + && (obj = _nm->embeddedOop_at(cur_insn())) != NULL + && (address) obj == adr) { + obj->print_value_on(st); + return; + } + } + + // Fall through to a simple numeral. + st->print(INTPTR_FORMAT, (intptr_t)adr); +} + +void decode_env::print_insn_labels() { + address p = cur_insn(); + outputStream* st = output(); + nmethod* nm = _nm; + if (nm != NULL) { + if (p == nm->entry_point()) st->print_cr("[Entry Point]"); + if (p == nm->verified_entry_point()) st->print_cr("[Verified Entry Point]"); + if (p == nm->exception_begin()) st->print_cr("[Exception Handler]"); + if (p == nm->stub_begin()) st->print_cr("[Stub Code]"); + if (p == nm->consts_begin()) st->print_cr("[Constants]"); + } + CodeBlob* cb = _code; + if (cb != NULL) { + cb->print_block_comment(st, (intptr_t)(p - cb->instructions_begin())); + } + if (_print_pc) { + st->print(" " INTPTR_FORMAT ": ", (intptr_t) p); + } +} + +void decode_env::print_insn_bytes(address pc, address pc_limit) { + outputStream* st = output(); + size_t incr = 1; + size_t perline = _bytes_per_line; + if ((size_t) Disassembler::pd_instruction_alignment() >= sizeof(int) + && !((uintptr_t)pc % sizeof(int)) + && !((uintptr_t)pc_limit % sizeof(int))) { + incr = sizeof(int); + if (perline % incr) perline += incr - (perline % incr); + } + while (pc < pc_limit) { + // tab to the desired column: + st->move_to(COMMENT_COLUMN); + address pc0 = pc; + address pc1 = pc + perline; + if (pc1 > pc_limit) pc1 = pc_limit; + for (; pc < pc1; pc += incr) { + if (pc == pc0) + st->print(BYTES_COMMENT); + else if ((uint)(pc - pc0) % sizeof(int) == 0) + st->print(" "); // put out a space on word boundaries + if (incr == sizeof(int)) + st->print("%08lx", *(int*)pc); + else st->print("%02x", (*pc)&0xFF); + } + st->cr(); + } +} + + +static void* event_to_env(void* env_pv, const char* event, void* arg) { + decode_env* env = (decode_env*) env_pv; + return env->handle_event(event, (address) arg); +} + +static int printf_to_env(void* env_pv, const char* format, ...) { + decode_env* env = (decode_env*) env_pv; + outputStream* st = env->output(); + size_t flen = strlen(format); + const char* raw = NULL; + if (flen == 0) return 0; + if (flen == 1 && format[0] == '\n') { st->bol(); return 1; } + if (flen < 2 || + strchr(format, '%') == NULL) { + raw = format; + } else if (format[0] == '%' && format[1] == '%' && + strchr(format+2, '%') == NULL) { + // happens a lot on machines with names like %foo + flen--; + raw = format+1; + } + if (raw != NULL) { + st->print_raw(raw, (int) flen); + return (int) flen; + } + va_list ap; + va_start(ap, format); + julong cnt0 = st->count(); + st->vprint(format, ap); + julong cnt1 = st->count(); + va_end(ap); + return (int)(cnt1 - cnt0); +} + +address decode_env::decode_instructions(address start, address end) { + _start = start; _end = end; + + assert((((intptr_t)start | (intptr_t)end) % Disassembler::pd_instruction_alignment() == 0), "misaligned insn addr"); + + const int show_bytes = false; // for disassembler debugging + + //_version = Disassembler::pd_cpu_version(); + + if (!Disassembler::can_decode()) { + return NULL; + } + + // decode a series of instructions and return the end of the last instruction + + if (_print_raw) { + // Print whatever the library wants to print, w/o fancy callbacks. + // This is mainly for debugging the library itself. + FILE* out = stdout; + FILE* xmlout = (_print_raw > 1 ? out : NULL); + return (address) + (*Disassembler::_decode_instructions)(start, end, + NULL, (void*) xmlout, + NULL, (void*) out, + options()); + } + + return (address) + (*Disassembler::_decode_instructions)(start, end, + &event_to_env, (void*) this, + &printf_to_env, (void*) this, + options()); +} + + +void Disassembler::decode(CodeBlob* cb, outputStream* st) { + if (!load_library()) return; + decode_env env(cb, st); + env.output()->print_cr("Decoding CodeBlob " INTPTR_FORMAT, cb); + env.decode_instructions(cb->instructions_begin(), cb->instructions_end()); +} + + +void Disassembler::decode(address start, address end, outputStream* st) { + if (!load_library()) return; + decode_env env(CodeCache::find_blob_unsafe(start), st); + env.decode_instructions(start, end); +} + +void Disassembler::decode(nmethod* nm, outputStream* st) { + if (!load_library()) return; + decode_env env(nm, st); + env.output()->print_cr("Decoding compiled method " INTPTR_FORMAT ":", nm); + env.output()->print_cr("Code:"); + + unsigned char* p = nm->instructions_begin(); + unsigned char* end = nm->instructions_end(); + + // If there has been profiling, print the buckets. + if (FlatProfiler::bucket_start_for(p) != NULL) { + unsigned char* p1 = p; + int total_bucket_count = 0; + while (p1 < end) { + unsigned char* p0 = p1; + p1 += pd_instruction_alignment(); + address bucket_pc = FlatProfiler::bucket_start_for(p1); + if (bucket_pc != NULL && bucket_pc > p0 && bucket_pc <= p1) + total_bucket_count += FlatProfiler::bucket_count_for(p0); + } + env.set_total_ticks(total_bucket_count); + } + + env.decode_instructions(p, end); +} diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/compiler/disassembler.hpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/share/vm/compiler/disassembler.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -0,0 +1,59 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + * + */ + +class decode_env; + +// The disassembler prints out assembly code annotated +// with Java specific information. + +class Disassembler { + friend class decode_env; + private: + // this is the type of the dll entry point: + typedef void* (*decode_func)(void* start, void* end, + void* (*event_callback)(void*, const char*, void*), + void* event_stream, + int (*printf_callback)(void*, const char*, ...), + void* printf_stream, + const char* options); + // points to the library. + static void* _library; + // bailout + static bool _tried_to_load_library; + // points to the decode function. + static decode_func _decode_instructions; + // tries to load library and return whether it succedded. + static bool load_library(); + + // Machine dependent stuff + #include "incls/_disassembler_pd.hpp.incl" + + public: + static bool can_decode() { + return (_decode_instructions != NULL) || load_library(); + } + static void decode(CodeBlob *cb, outputStream* st = NULL); + static void decode(nmethod* nm, outputStream* st = NULL); + static void decode(address begin, address end, outputStream* st = NULL); +}; diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/compiler/disassemblerEnv.hpp --- a/src/share/vm/compiler/disassemblerEnv.hpp Thu Apr 10 15:49:16 2008 -0400 +++ /dev/null Thu Jan 01 00:00:00 1970 +0000 @@ -1,35 +0,0 @@ -/* - * Copyright 1997-2002 Sun Microsystems, Inc. All Rights Reserved. - * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. - * - * This code is free software; you can redistribute it and/or modify it - * under the terms of the GNU General Public License version 2 only, as - * published by the Free Software Foundation. - * - * This code is distributed in the hope that it will be useful, but WITHOUT - * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or - * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License - * version 2 for more details (a copy is included in the LICENSE file that - * accompanied this code). - * - * You should have received a copy of the GNU General Public License version - * 2 along with this work; if not, write to the Free Software Foundation, - * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. - * - * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, - * CA 95054 USA or visit www.sun.com if you need additional information or - * have any questions. - * - */ - -// Call-back interface for external disassembler -class DisassemblerEnv { - public: - // printing - virtual void print_label(intptr_t value) = 0; - virtual void print_raw(char* str) = 0; - virtual void print(char* format, ...) = 0; - // helpers - virtual char* string_for_offset(intptr_t value) = 0; - virtual char* string_for_constant(unsigned char* pc, intptr_t value, int is_decimal) = 0; -}; diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/compiler/oopMap.cpp --- a/src/share/vm/compiler/oopMap.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/compiler/oopMap.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -505,8 +505,13 @@ #endif // COMPILER2 } +#endif //PRODUCT -static void print_register_type(OopMapValue::oop_types x, VMReg optional, outputStream* st) { +// Printing code is present in product build for -XX:+PrintAssembly. + +static +void print_register_type(OopMapValue::oop_types x, VMReg optional, + outputStream* st) { switch( x ) { case OopMapValue::oop_value: st->print("Oop"); @@ -544,10 +549,12 @@ void OopMap::print_on(outputStream* st) const { OopMapValue omv; + st->print("OopMap{"); for(OopMapStream oms((OopMap*)this); !oms.is_done(); oms.next()) { omv = oms.current(); omv.print_on(st); } + st->print("off=%d}", (int) offset()); } @@ -558,12 +565,12 @@ for( i = 0; i < len; i++) { OopMap* m = at(i); - st->print_cr("OopMap #%d offset:%p",i,m->offset()); + st->print_cr("#%d ",i); m->print_on(st); - st->print_cr("\n"); + st->cr(); } } -#endif // !PRODUCT + //------------------------------DerivedPointerTable--------------------------- diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/compiler/oopMap.hpp --- a/src/share/vm/compiler/oopMap.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/compiler/oopMap.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -129,7 +129,7 @@ return reg()->reg2stack(); } - void print_on(outputStream* st) const PRODUCT_RETURN; + void print_on(outputStream* st) const; void print() const { print_on(tty); } }; @@ -193,7 +193,7 @@ } // Printing - void print_on(outputStream* st) const PRODUCT_RETURN; + void print_on(outputStream* st) const; void print() const { print_on(tty); } }; @@ -248,7 +248,7 @@ OopClosure* value_fn, OopClosure* dead_fn); // Printing - void print_on(outputStream* st) const PRODUCT_RETURN; + void print_on(outputStream* st) const; void print() const { print_on(tty); } }; diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -225,6 +225,34 @@ assert(_dilatation_factor >= 1.0, "from previous assert"); } + +// The field "_initiating_occupancy" represents the occupancy percentage +// at which we trigger a new collection cycle. Unless explicitly specified +// via CMSInitiating[Perm]OccupancyFraction (argument "io" below), it +// is calculated by: +// +// Let "f" be MinHeapFreeRatio in +// +// _intiating_occupancy = 100-f + +// f * (CMSTrigger[Perm]Ratio/100) +// where CMSTrigger[Perm]Ratio is the argument "tr" below. +// +// That is, if we assume the heap is at its desired maximum occupancy at the +// end of a collection, we let CMSTrigger[Perm]Ratio of the (purported) free +// space be allocated before initiating a new collection cycle. +// +void ConcurrentMarkSweepGeneration::init_initiating_occupancy(intx io, intx tr) { + assert(io <= 100 && tr >= 0 && tr <= 100, "Check the arguments"); + if (io >= 0) { + _initiating_occupancy = (double)io / 100.0; + } else { + _initiating_occupancy = ((100 - MinHeapFreeRatio) + + (double)(tr * MinHeapFreeRatio) / 100.0) + / 100.0; + } +} + + void ConcurrentMarkSweepGeneration::ref_processor_init() { assert(collector() != NULL, "no collector"); collector()->ref_processor_init(); @@ -520,8 +548,8 @@ _verification_mark_bm(0, Mutex::leaf + 1, "CMS_verification_mark_bm_lock"), _completed_initialization(false), _collector_policy(cp), - _unload_classes(false), - _unloaded_classes_last_cycle(false), + _should_unload_classes(false), + _concurrent_cycles_since_last_unload(0), _sweep_estimate(CMS_SweepWeight, CMS_SweepPadding) { if (ExplicitGCInvokesConcurrentAndUnloadsClasses) { @@ -642,26 +670,11 @@ } } - // "initiatingOccupancy" is the occupancy ratio at which we trigger - // a new collection cycle. Unless explicitly specified via - // CMSTriggerRatio, it is calculated by: - // Let "f" be MinHeapFreeRatio in - // - // intiatingOccupancy = 100-f + - // f * (CMSTriggerRatio/100) - // That is, if we assume the heap is at its desired maximum occupancy at the - // end of a collection, we let CMSTriggerRatio of the (purported) free - // space be allocated before initiating a new collection cycle. - if (CMSInitiatingOccupancyFraction > 0) { - _initiatingOccupancy = (double)CMSInitiatingOccupancyFraction / 100.0; - } else { - _initiatingOccupancy = ((100 - MinHeapFreeRatio) + - (double)(CMSTriggerRatio * - MinHeapFreeRatio) / 100.0) - / 100.0; - } + _cmsGen ->init_initiating_occupancy(CMSInitiatingOccupancyFraction, CMSTriggerRatio); + _permGen->init_initiating_occupancy(CMSInitiatingPermOccupancyFraction, CMSTriggerPermRatio); + // Clip CMSBootstrapOccupancy between 0 and 100. - _bootstrap_occupancy = ((double)MIN2((intx)100, MAX2((intx)0, CMSBootstrapOccupancy))) + _bootstrap_occupancy = ((double)MIN2((uintx)100, MAX2((uintx)0, CMSBootstrapOccupancy))) /(double)100; _full_gcs_since_conc_gc = 0; @@ -1413,7 +1426,8 @@ gclog_or_tty->print_cr("promotion_rate=%g", stats().promotion_rate()); gclog_or_tty->print_cr("cms_allocation_rate=%g", stats().cms_allocation_rate()); gclog_or_tty->print_cr("occupancy=%3.7f", _cmsGen->occupancy()); - gclog_or_tty->print_cr("initiatingOccupancy=%3.7f", initiatingOccupancy()); + gclog_or_tty->print_cr("initiatingOccupancy=%3.7f", _cmsGen->initiating_occupancy()); + gclog_or_tty->print_cr("initiatingPermOccupancy=%3.7f", _permGen->initiating_occupancy()); } // ------------------------------------------------------------------ @@ -1446,22 +1460,36 @@ // old gen want a collection cycle started. Each may use // an appropriate criterion for making this decision. // XXX We need to make sure that the gen expansion - // criterion dovetails well with this. - if (_cmsGen->shouldConcurrentCollect(initiatingOccupancy())) { + // criterion dovetails well with this. XXX NEED TO FIX THIS + if (_cmsGen->should_concurrent_collect()) { if (Verbose && PrintGCDetails) { gclog_or_tty->print_cr("CMS old gen initiated"); } return true; } - if (cms_should_unload_classes() && - _permGen->shouldConcurrentCollect(initiatingOccupancy())) { - if (Verbose && PrintGCDetails) { - gclog_or_tty->print_cr("CMS perm gen initiated"); + // We start a collection if we believe an incremental collection may fail; + // this is not likely to be productive in practice because it's probably too + // late anyway. + GenCollectedHeap* gch = GenCollectedHeap::heap(); + assert(gch->collector_policy()->is_two_generation_policy(), + "You may want to check the correctness of the following"); + if (gch->incremental_collection_will_fail()) { + if (PrintGCDetails && Verbose) { + gclog_or_tty->print("CMSCollector: collect because incremental collection will fail "); } return true; } + if (CMSClassUnloadingEnabled && _permGen->should_concurrent_collect()) { + bool res = update_should_unload_classes(); + if (res) { + if (Verbose && PrintGCDetails) { + gclog_or_tty->print_cr("CMS perm gen initiated"); + } + return true; + } + } return false; } @@ -1471,32 +1499,36 @@ _permGen->clear_expansion_cause(); } -bool ConcurrentMarkSweepGeneration::shouldConcurrentCollect( - double initiatingOccupancy) { - // We should be conservative in starting a collection cycle. To - // start too eagerly runs the risk of collecting too often in the - // extreme. To collect too rarely falls back on full collections, - // which works, even if not optimum in terms of concurrent work. - // As a work around for too eagerly collecting, use the flag - // UseCMSInitiatingOccupancyOnly. This also has the advantage of - // giving the user an easily understandable way of controlling the - // collections. - // We want to start a new collection cycle if any of the following - // conditions hold: - // . our current occupancy exceeds the initiating occupancy, or - // . we recently needed to expand and have not since that expansion, - // collected, or - // . we are not using adaptive free lists and linear allocation is - // going to fail, or - // . (for old gen) incremental collection has already failed or - // may soon fail in the near future as we may not be able to absorb - // promotions. +// We should be conservative in starting a collection cycle. To +// start too eagerly runs the risk of collecting too often in the +// extreme. To collect too rarely falls back on full collections, +// which works, even if not optimum in terms of concurrent work. +// As a work around for too eagerly collecting, use the flag +// UseCMSInitiatingOccupancyOnly. This also has the advantage of +// giving the user an easily understandable way of controlling the +// collections. +// We want to start a new collection cycle if any of the following +// conditions hold: +// . our current occupancy exceeds the configured initiating occupancy +// for this generation, or +// . we recently needed to expand this space and have not, since that +// expansion, done a collection of this generation, or +// . the underlying space believes that it may be a good idea to initiate +// a concurrent collection (this may be based on criteria such as the +// following: the space uses linear allocation and linear allocation is +// going to fail, or there is believed to be excessive fragmentation in +// the generation, etc... or ... +// [.(currently done by CMSCollector::shouldConcurrentCollect() only for +// the case of the old generation, not the perm generation; see CR 6543076): +// we may be approaching a point at which allocation requests may fail because +// we will be out of sufficient free space given allocation rate estimates.] +bool ConcurrentMarkSweepGeneration::should_concurrent_collect() const { + assert_lock_strong(freelistLock()); - - if (occupancy() > initiatingOccupancy) { + if (occupancy() > initiating_occupancy()) { if (PrintGCDetails && Verbose) { gclog_or_tty->print(" %s: collect because of occupancy %f / %f ", - short_name(), occupancy(), initiatingOccupancy); + short_name(), occupancy(), initiating_occupancy()); } return true; } @@ -1510,20 +1542,9 @@ } return true; } - GenCollectedHeap* gch = GenCollectedHeap::heap(); - assert(gch->collector_policy()->is_two_generation_policy(), - "You may want to check the correctness of the following"); - if (gch->incremental_collection_will_fail()) { + if (_cmsSpace->should_concurrent_collect()) { if (PrintGCDetails && Verbose) { - gclog_or_tty->print(" %s: collect because incremental collection will fail ", - short_name()); - } - return true; - } - if (!_cmsSpace->adaptive_freelists() && - _cmsSpace->linearAllocationWouldFail()) { - if (PrintGCDetails && Verbose) { - gclog_or_tty->print(" %s: collect because of linAB ", + gclog_or_tty->print(" %s: collect because cmsSpace says so ", short_name()); } return true; @@ -1970,8 +1991,9 @@ "Should have been NULL'd before baton was passed"); reset(false /* == !asynch */); _cmsGen->reset_after_compaction(); - - if (verifying() && !cms_should_unload_classes()) { + _concurrent_cycles_since_last_unload = 0; + + if (verifying() && !should_unload_classes()) { perm_gen_verify_bit_map()->clear_all(); } @@ -2098,6 +2120,7 @@ { bool safepoint_check = Mutex::_no_safepoint_check_flag; MutexLockerEx hl(Heap_lock, safepoint_check); + FreelistLocker fll(this); MutexLockerEx x(CGC_lock, safepoint_check); if (_foregroundGCIsActive || !UseAsyncConcMarkSweepGC) { // The foreground collector is active or we're @@ -2112,13 +2135,9 @@ // a new cycle. clear_expansion_cause(); } - _unloaded_classes_last_cycle = cms_should_unload_classes(); // ... from last cycle - // This controls class unloading in response to an explicit gc request. - // If ExplicitGCInvokesConcurrentAndUnloadsClasses is set, then - // we will unload classes even if CMSClassUnloadingEnabled is not set. - // See CR 6541037 and related CRs. - _unload_classes = _full_gc_requested // ... for this cycle - && ExplicitGCInvokesConcurrentAndUnloadsClasses; + // Decide if we want to enable class unloading as part of the + // ensuing concurrent GC cycle. + update_should_unload_classes(); _full_gc_requested = false; // acks all outstanding full gc requests // Signal that we are about to start a collection gch->increment_total_full_collections(); // ... starting a collection cycle @@ -3047,21 +3066,62 @@ } #endif // PRODUCT +// Decide if we want to enable class unloading as part of the +// ensuing concurrent GC cycle. We will collect the perm gen and +// unload classes if it's the case that: +// (1) an explicit gc request has been made and the flag +// ExplicitGCInvokesConcurrentAndUnloadsClasses is set, OR +// (2) (a) class unloading is enabled at the command line, and +// (b) (i) perm gen threshold has been crossed, or +// (ii) old gen is getting really full, or +// (iii) the previous N CMS collections did not collect the +// perm gen +// NOTE: Provided there is no change in the state of the heap between +// calls to this method, it should have idempotent results. Moreover, +// its results should be monotonically increasing (i.e. going from 0 to 1, +// but not 1 to 0) between successive calls between which the heap was +// not collected. For the implementation below, it must thus rely on +// the property that concurrent_cycles_since_last_unload() +// will not decrease unless a collection cycle happened and that +// _permGen->should_concurrent_collect() and _cmsGen->is_too_full() are +// themselves also monotonic in that sense. See check_monotonicity() +// below. +bool CMSCollector::update_should_unload_classes() { + _should_unload_classes = false; + // Condition 1 above + if (_full_gc_requested && ExplicitGCInvokesConcurrentAndUnloadsClasses) { + _should_unload_classes = true; + } else if (CMSClassUnloadingEnabled) { // Condition 2.a above + // Disjuncts 2.b.(i,ii,iii) above + _should_unload_classes = (concurrent_cycles_since_last_unload() >= + CMSClassUnloadingMaxInterval) + || _permGen->should_concurrent_collect() + || _cmsGen->is_too_full(); + } + return _should_unload_classes; +} + +bool ConcurrentMarkSweepGeneration::is_too_full() const { + bool res = should_concurrent_collect(); + res = res && (occupancy() > (double)CMSIsTooFullPercentage/100.0); + return res; +} + void CMSCollector::setup_cms_unloading_and_verification_state() { const bool should_verify = VerifyBeforeGC || VerifyAfterGC || VerifyDuringGC || VerifyBeforeExit; const int rso = SharedHeap::SO_Symbols | SharedHeap::SO_Strings | SharedHeap::SO_CodeCache; - if (cms_should_unload_classes()) { // Should unload classes this cycle + if (should_unload_classes()) { // Should unload classes this cycle remove_root_scanning_option(rso); // Shrink the root set appropriately set_verifying(should_verify); // Set verification state for this cycle return; // Nothing else needs to be done at this time } // Not unloading classes this cycle - assert(!cms_should_unload_classes(), "Inconsitency!"); - if ((!verifying() || cms_unloaded_classes_last_cycle()) && should_verify) { + assert(!should_unload_classes(), "Inconsitency!"); + if ((!verifying() || unloaded_classes_last_cycle()) && should_verify) { // We were not verifying, or we _were_ unloading classes in the last cycle, // AND some verification options are enabled this cycle; in this case, // we must make sure that the deadness map is allocated if not already so, @@ -4693,7 +4753,7 @@ GenCollectedHeap* gch = GenCollectedHeap::heap(); - if (cms_should_unload_classes()) { + if (should_unload_classes()) { CodeCache::gc_prologue(); } assert(haveFreelistLocks(), "must have free list locks"); @@ -4753,7 +4813,7 @@ verify_work_stacks_empty(); verify_overflow_empty(); - if (cms_should_unload_classes()) { + if (should_unload_classes()) { CodeCache::gc_epilogue(); } @@ -5623,7 +5683,7 @@ verify_work_stacks_empty(); } - if (cms_should_unload_classes()) { + if (should_unload_classes()) { { TraceTime t("class unloading", PrintGCDetails, false, gclog_or_tty); @@ -5726,7 +5786,7 @@ // this cycle, we preserve the perm gen object "deadness" information // in the perm_gen_verify_bit_map. In order to do that we traverse // all blocks in perm gen and mark all dead objects. - if (verifying() && !cms_should_unload_classes()) { + if (verifying() && !should_unload_classes()) { assert(perm_gen_verify_bit_map()->sizeInBits() != 0, "Should have already been allocated"); MarkDeadObjectsClosure mdo(this, _permGen->cmsSpace(), @@ -5753,7 +5813,7 @@ } // Now repeat for perm gen - if (cms_should_unload_classes()) { + if (should_unload_classes()) { CMSTokenSyncWithLocks ts(true, _permGen->freelistLock(), bitMapLock()); sweepWork(_permGen, asynch); @@ -5775,7 +5835,7 @@ // already have needed locks sweepWork(_cmsGen, asynch); - if (cms_should_unload_classes()) { + if (should_unload_classes()) { sweepWork(_permGen, asynch); } // Update heap occupancy information which is used as @@ -5937,6 +5997,11 @@ } gen->cmsSpace()->sweep_completed(); gen->cmsSpace()->endSweepFLCensus(sweepCount()); + if (should_unload_classes()) { // unloaded classes this cycle, + _concurrent_cycles_since_last_unload = 0; // ... reset count + } else { // did not unload classes, + _concurrent_cycles_since_last_unload++; // ... increment count + } } // Reset CMS data structures (for now just the marking bit map) @@ -7194,7 +7259,7 @@ _revisitStack(revisitStack), _finger(finger), _parent(parent), - _should_remember_klasses(collector->cms_should_unload_classes()) + _should_remember_klasses(collector->should_unload_classes()) { } Par_PushOrMarkClosure::Par_PushOrMarkClosure(CMSCollector* collector, @@ -7217,7 +7282,7 @@ _finger(finger), _global_finger_addr(global_finger_addr), _parent(parent), - _should_remember_klasses(collector->cms_should_unload_classes()) + _should_remember_klasses(collector->should_unload_classes()) { } @@ -7360,7 +7425,7 @@ _mark_stack(mark_stack), _revisit_stack(revisit_stack), _concurrent_precleaning(concurrent_precleaning), - _should_remember_klasses(collector->cms_should_unload_classes()) + _should_remember_klasses(collector->should_unload_classes()) { assert(_ref_processor != NULL, "_ref_processor shouldn't be NULL"); } @@ -7422,7 +7487,7 @@ _bit_map(bit_map), _work_queue(work_queue), _revisit_stack(revisit_stack), - _should_remember_klasses(collector->cms_should_unload_classes()) + _should_remember_klasses(collector->should_unload_classes()) { assert(_ref_processor != NULL, "_ref_processor shouldn't be NULL"); } @@ -7944,7 +8009,7 @@ #ifdef DEBUG if (oop(addr)->klass() != NULL && - ( !_collector->cms_should_unload_classes() + ( !_collector->should_unload_classes() || oop(addr)->is_parsable())) { // Ignore mark word because we are running concurrent with mutators assert(oop(addr)->is_oop(true), "live block should be an oop"); @@ -7957,7 +8022,7 @@ } else { // This should be an initialized object that's alive. assert(oop(addr)->klass() != NULL && - (!_collector->cms_should_unload_classes() + (!_collector->should_unload_classes() || oop(addr)->is_parsable()), "Should be an initialized object"); // Ignore mark word because we are running concurrent with mutators diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -535,13 +535,16 @@ // In support of ExplicitGCInvokesConcurrent static bool _full_gc_requested; unsigned int _collection_count_start; + // Should we unload classes this concurrent cycle? - // Set in response to a concurrent full gc request. - bool _unload_classes; - bool _unloaded_classes_last_cycle; + bool _should_unload_classes; + unsigned int _concurrent_cycles_since_last_unload; + unsigned int concurrent_cycles_since_last_unload() const { + return _concurrent_cycles_since_last_unload; + } // Did we (allow) unload classes in the previous concurrent cycle? - bool cms_unloaded_classes_last_cycle() const { - return _unloaded_classes_last_cycle || CMSClassUnloadingEnabled; + bool unloaded_classes_last_cycle() const { + return concurrent_cycles_since_last_unload() == 0; } // Verification support @@ -651,8 +654,6 @@ // number of full gc's since the last concurrent gc. uint _full_gcs_since_conc_gc; - // if occupancy exceeds this, start a new gc cycle - double _initiatingOccupancy; // occupancy used for bootstrapping stats double _bootstrap_occupancy; @@ -825,7 +826,6 @@ Mutex* bitMapLock() const { return _markBitMap.lock(); } static CollectorState abstract_state() { return _collectorState; } - double initiatingOccupancy() const { return _initiatingOccupancy; } bool should_abort_preclean() const; // Whether preclean should be aborted. size_t get_eden_used() const; @@ -849,11 +849,10 @@ // In support of ExplicitGCInvokesConcurrent static void request_full_gc(unsigned int full_gc_count); // Should we unload classes in a particular concurrent cycle? - bool cms_should_unload_classes() const { - assert(!_unload_classes || ExplicitGCInvokesConcurrentAndUnloadsClasses, - "Inconsistency; see CR 6541037"); - return _unload_classes || CMSClassUnloadingEnabled; + bool should_unload_classes() const { + return _should_unload_classes; } + bool update_should_unload_classes(); void direct_allocated(HeapWord* start, size_t size); @@ -1022,6 +1021,10 @@ _incremental_collection_failed = false; } + // accessors + void set_expansion_cause(CMSExpansionCause::Cause v) { _expansion_cause = v;} + CMSExpansionCause::Cause expansion_cause() const { return _expansion_cause; } + private: // For parallel young-gen GC support. CMSParGCThreadState** _par_gc_thread_states; @@ -1029,10 +1032,6 @@ // Reason generation was expanded CMSExpansionCause::Cause _expansion_cause; - // accessors - void set_expansion_cause(CMSExpansionCause::Cause v) { _expansion_cause = v;} - CMSExpansionCause::Cause expansion_cause() { return _expansion_cause; } - // In support of MinChunkSize being larger than min object size const double _dilatation_factor; @@ -1045,6 +1044,10 @@ CollectionTypes _debug_collection_type; + // Fraction of current occupancy at which to start a CMS collection which + // will collect this generation (at least). + double _initiating_occupancy; + protected: // Grow generation by specified size (returns false if unable to grow) bool grow_by(size_t bytes); @@ -1060,6 +1063,10 @@ // space. size_t max_available() const; + // getter and initializer for _initiating_occupancy field. + double initiating_occupancy() const { return _initiating_occupancy; } + void init_initiating_occupancy(intx io, intx tr); + public: ConcurrentMarkSweepGeneration(ReservedSpace rs, size_t initial_byte_size, int level, CardTableRS* ct, @@ -1103,7 +1110,7 @@ size_t capacity() const; size_t used() const; size_t free() const; - double occupancy() { return ((double)used())/((double)capacity()); } + double occupancy() const { return ((double)used())/((double)capacity()); } size_t contiguous_available() const; size_t unsafe_max_alloc_nogc() const; @@ -1158,8 +1165,8 @@ bool younger_handles_promotion_failure) const; bool should_collect(bool full, size_t size, bool tlab); - // XXXPERM - bool shouldConcurrentCollect(double initiatingOccupancy); // XXXPERM + virtual bool should_concurrent_collect() const; + virtual bool is_too_full() const; void collect(bool full, bool clear_all_soft_refs, size_t size, diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.inline.hpp --- a/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.inline.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/gc_implementation/concurrentMarkSweep/concurrentMarkSweepGeneration.inline.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -267,7 +267,7 @@ (_permGen->cmsSpace()->is_in_reserved(addr) && _permGen->cmsSpace()->block_is_obj(addr)), "must be object"); - return cms_should_unload_classes() && + return should_unload_classes() && _collectorState == Sweeping && !_markBitMap.isMarked(addr); } diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/includeDB_compiler1 --- a/src/share/vm/includeDB_compiler1 Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/includeDB_compiler1 Fri Apr 11 09:56:35 2008 -0400 @@ -323,7 +323,7 @@ c1_Runtime1.cpp compilationPolicy.hpp c1_Runtime1.cpp compiledIC.hpp c1_Runtime1.cpp copy.hpp -c1_Runtime1.cpp disassembler_.hpp +c1_Runtime1.cpp disassembler.hpp c1_Runtime1.cpp events.hpp c1_Runtime1.cpp interfaceSupport.hpp c1_Runtime1.cpp interpreter.hpp diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/includeDB_core --- a/src/share/vm/includeDB_core Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/includeDB_core Fri Apr 11 09:56:35 2008 -0400 @@ -244,7 +244,7 @@ assembler.inline.hpp assembler.hpp assembler.inline.hpp codeBuffer.hpp -assembler.inline.hpp disassembler_.hpp +assembler.inline.hpp disassembler.hpp assembler.inline.hpp threadLocalStorage.hpp assembler_.cpp assembler_.inline.hpp @@ -946,7 +946,7 @@ codeBlob.cpp bytecode.hpp codeBlob.cpp codeBlob.hpp codeBlob.cpp codeCache.hpp -codeBlob.cpp disassembler_.hpp +codeBlob.cpp disassembler.hpp codeBlob.cpp forte.hpp codeBlob.cpp handles.inline.hpp codeBlob.cpp heap.hpp @@ -968,7 +968,7 @@ codeBuffer.cpp codeBuffer.hpp codeBuffer.cpp copy.hpp -codeBuffer.cpp disassembler_.hpp +codeBuffer.cpp disassembler.hpp codeBuffer.hpp assembler.hpp codeBuffer.hpp oopRecorder.hpp @@ -1323,7 +1323,7 @@ debug.cpp collectedHeap.hpp debug.cpp compileBroker.hpp debug.cpp defaultStream.hpp -debug.cpp disassembler_.hpp +debug.cpp disassembler.hpp debug.cpp events.hpp debug.cpp frame.hpp debug.cpp heapDumper.hpp @@ -1442,7 +1442,7 @@ deoptimization.hpp frame.inline.hpp depChecker_.cpp depChecker_.hpp -depChecker_.cpp disassembler_.hpp +depChecker_.cpp disassembler.hpp depChecker_.cpp hpi.hpp dependencies.cpp ciArrayKlass.hpp @@ -1472,21 +1472,21 @@ dictionary.hpp oop.hpp dictionary.hpp systemDictionary.hpp -disassemblerEnv.hpp globals.hpp - -disassembler_.cpp cardTableModRefBS.hpp -disassembler_.cpp codeCache.hpp -disassembler_.cpp collectedHeap.hpp -disassembler_.cpp depChecker_.hpp -disassembler_.cpp disassembler_.hpp -disassembler_.cpp fprofiler.hpp -disassembler_.cpp handles.inline.hpp -disassembler_.cpp hpi.hpp -disassembler_.cpp stubCodeGenerator.hpp -disassembler_.cpp stubRoutines.hpp - -disassembler_.hpp disassemblerEnv.hpp -disassembler_.hpp os_.inline.hpp +disassembler_.hpp generate_platform_dependent_include + +disassembler.cpp cardTableModRefBS.hpp +disassembler.cpp codeCache.hpp +disassembler.cpp collectedHeap.hpp +disassembler.cpp depChecker_.hpp +disassembler.cpp disassembler.hpp +disassembler.cpp fprofiler.hpp +disassembler.cpp handles.inline.hpp +disassembler.cpp hpi.hpp +disassembler.cpp stubCodeGenerator.hpp +disassembler.cpp stubRoutines.hpp + +disassembler.hpp globals.hpp +disassembler.hpp os_.inline.hpp dtraceAttacher.cpp codeCache.hpp dtraceAttacher.cpp deoptimization.hpp @@ -2909,7 +2909,7 @@ nmethod.cpp compileLog.hpp nmethod.cpp compiledIC.hpp nmethod.cpp compilerOracle.hpp -nmethod.cpp disassembler_.hpp +nmethod.cpp disassembler.hpp nmethod.cpp dtrace.hpp nmethod.cpp events.hpp nmethod.cpp jvmtiRedefineClassesTrace.hpp @@ -3763,7 +3763,7 @@ statSampler.hpp task.hpp stubCodeGenerator.cpp assembler_.inline.hpp -stubCodeGenerator.cpp disassembler_.hpp +stubCodeGenerator.cpp disassembler.hpp stubCodeGenerator.cpp forte.hpp stubCodeGenerator.cpp oop.inline.hpp stubCodeGenerator.cpp stubCodeGenerator.hpp @@ -4530,7 +4530,7 @@ vmreg_.hpp generate_platform_dependent_include vtableStubs.cpp allocation.inline.hpp -vtableStubs.cpp disassembler_.hpp +vtableStubs.cpp disassembler.hpp vtableStubs.cpp forte.hpp vtableStubs.cpp handles.inline.hpp vtableStubs.cpp instanceKlass.hpp diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/memory/gcLocker.hpp --- a/src/share/vm/memory/gcLocker.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/memory/gcLocker.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -184,7 +184,9 @@ Thread *_thread; public: #ifdef ASSERT - No_Safepoint_Verifier(bool activated = true, bool verifygc = true ) : No_GC_Verifier(verifygc) { + No_Safepoint_Verifier(bool activated = true, bool verifygc = true ) : + No_GC_Verifier(verifygc), + _activated(activated) { _thread = Thread::current(); if (_activated) { _thread->_allow_allocation_count++; diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/oops/methodOop.cpp --- a/src/share/vm/oops/methodOop.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/oops/methodOop.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -888,10 +888,11 @@ symbolHandle name (THREAD, sym); klassOop klass = SystemDictionary::resolve_or_null(name, class_loader, protection_domain, THREAD); - // We are loading classes eagerly. If a ClassNotFoundException was generated, - // be sure to ignore it. + // We are loading classes eagerly. If a ClassNotFoundException or + // a LinkageError was generated, be sure to ignore it. if (HAS_PENDING_EXCEPTION) { - if (PENDING_EXCEPTION->is_a(SystemDictionary::classNotFoundException_klass())) { + if (PENDING_EXCEPTION->is_a(SystemDictionary::classNotFoundException_klass()) || + PENDING_EXCEPTION->is_a(SystemDictionary::linkageError_klass())) { CLEAR_PENDING_EXCEPTION; } else { return false; diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/opto/addnode.cpp --- a/src/share/vm/opto/addnode.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/opto/addnode.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -70,9 +70,14 @@ // Convert "Load+x" into "x+Load". // Now check for loads - if( in2->is_Load() ) return false; - // Left is a Load and Right is not; move it right. - if( in1->is_Load() ) { + if (in2->is_Load()) { + if (!in1->is_Load()) { + // already x+Load to return + return false; + } + // both are loads, so fall through to sort inputs by idx + } else if( in1->is_Load() ) { + // Left is a Load and Right is not; move it right. add->swap_edges(1, 2); return true; } diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/opto/compile.cpp --- a/src/share/vm/opto/compile.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/opto/compile.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -456,7 +456,15 @@ } TraceTime t1("Total compilation time", &_t_totalCompilation, TimeCompiler, TimeCompiler2); TraceTime t2(NULL, &_t_methodCompilation, TimeCompiler, false); - set_print_assembly(PrintOptoAssembly || _method->should_print_assembly()); + bool print_opto_assembly = PrintOptoAssembly || _method->has_option("PrintOptoAssembly"); + if (!print_opto_assembly) { + bool print_assembly = (PrintAssembly || _method->should_print_assembly()); + if (print_assembly && !Disassembler::can_decode()) { + tty->print_cr("PrintAssembly request changed to PrintOptoAssembly"); + print_opto_assembly = true; + } + } + set_print_assembly(print_opto_assembly); #endif if (ProfileTraps) { diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/opto/escape.cpp --- a/src/share/vm/opto/escape.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/opto/escape.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -256,39 +256,49 @@ } } -void ConnectionGraph::remove_deferred(uint ni) { - VectorSet visited(Thread::current()->resource_area()); +void ConnectionGraph::remove_deferred(uint ni, GrowableArray* deferred_edges, VectorSet* visited) { + // This method is most expensive during ConnectionGraph construction. + // Reuse vectorSet and an additional growable array for deferred edges. + deferred_edges->clear(); + visited->Clear(); uint i = 0; PointsToNode *ptn = ptnode_adr(ni); - while(i < ptn->edge_count()) { + // Mark current edges as visited and move deferred edges to separate array. + for (; i < ptn->edge_count(); i++) { uint t = ptn->edge_target(i); - PointsToNode *ptt = ptnode_adr(t); - if (ptn->edge_type(i) != PointsToNode::DeferredEdge) { - i++; - } else { +#ifdef ASSERT + assert(!visited->test_set(t), "expecting no duplications"); +#else + visited->set(t); +#endif + if (ptn->edge_type(i) == PointsToNode::DeferredEdge) { ptn->remove_edge(t, PointsToNode::DeferredEdge); - if(!visited.test_set(t)) { - for (uint j = 0; j < ptt->edge_count(); j++) { - uint n1 = ptt->edge_target(j); - PointsToNode *pt1 = ptnode_adr(n1); - switch(ptt->edge_type(j)) { - case PointsToNode::PointsToEdge: - add_pointsto_edge(ni, n1); - if(n1 == _phantom_object) { - // Special case - field set outside (globally escaping). - ptn->set_escape_state(PointsToNode::GlobalEscape); - } - break; - case PointsToNode::DeferredEdge: - add_deferred_edge(ni, n1); - break; - case PointsToNode::FieldEdge: - assert(false, "invalid connection graph"); - break; + deferred_edges->append(t); + } + } + for (int next = 0; next < deferred_edges->length(); ++next) { + uint t = deferred_edges->at(next); + PointsToNode *ptt = ptnode_adr(t); + for (uint j = 0; j < ptt->edge_count(); j++) { + uint n1 = ptt->edge_target(j); + if (visited->test_set(n1)) + continue; + switch(ptt->edge_type(j)) { + case PointsToNode::PointsToEdge: + add_pointsto_edge(ni, n1); + if(n1 == _phantom_object) { + // Special case - field set outside (globally escaping). + ptn->set_escape_state(PointsToNode::GlobalEscape); } - } + break; + case PointsToNode::DeferredEdge: + deferred_edges->append(n1); + break; + case PointsToNode::FieldEdge: + assert(false, "invalid connection graph"); + break; } } } @@ -1243,8 +1253,10 @@ } VectorSet ptset(Thread::current()->resource_area()); - GrowableArray alloc_worklist; - GrowableArray worklist; + GrowableArray alloc_worklist; + GrowableArray worklist; + GrowableArray deferred_edges; + VectorSet visited(Thread::current()->resource_area()); // remove deferred edges from the graph and collect // information we will need for type splitting @@ -1254,7 +1266,7 @@ PointsToNode::NodeType nt = ptn->node_type(); Node *n = ptn->_node; if (nt == PointsToNode::LocalVar || nt == PointsToNode::Field) { - remove_deferred(ni); + remove_deferred(ni, &deferred_edges, &visited); if (n->is_AddP()) { // If this AddP computes an address which may point to more that one // object, nothing the address points to can be scalar replaceable. diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/opto/escape.hpp --- a/src/share/vm/opto/escape.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/opto/escape.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -269,7 +269,7 @@ // Remove outgoing deferred edges from the node referenced by "ni". // Any outgoing edges from the target of the deferred edge are copied // to "ni". - void remove_deferred(uint ni); + void remove_deferred(uint ni, GrowableArray* deferred_edges, VectorSet* visited); Node_Array _node_map; // used for bookeeping during type splitting // Used for the following purposes: diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/opto/loopUnswitch.cpp --- a/src/share/vm/opto/loopUnswitch.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/opto/loopUnswitch.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -51,6 +51,9 @@ if( !LoopUnswitching ) { return false; } + if (!_head->is_Loop()) { + return false; + } uint nodes_left = MaxNodeLimit - phase->C->unique(); if (2 * _body.size() > nodes_left) { return false; // Too speculative if running low on nodes. diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/opto/loopopts.cpp --- a/src/share/vm/opto/loopopts.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/opto/loopopts.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -2257,6 +2257,9 @@ // bool PhaseIdealLoop::partial_peel( IdealLoopTree *loop, Node_List &old_new ) { + if (!loop->_head->is_Loop()) { + return false; } + LoopNode *head = loop->_head->as_Loop(); if (head->is_partial_peel_loop() || head->partial_peel_has_failed()) { diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/opto/parse3.cpp --- a/src/share/vm/opto/parse3.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/opto/parse3.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -408,7 +408,7 @@ jint dim_con = find_int_con(length[j], -1); expand_fanout *= dim_con; expand_count += expand_fanout; // count the level-J sub-arrays - if (dim_con < 0 + if (dim_con <= 0 || dim_con > expand_limit || expand_count > expand_limit) { expand_count = 0; diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/opto/superword.cpp --- a/src/share/vm/opto/superword.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/opto/superword.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -65,6 +65,11 @@ Node *cl_exit = cl->loopexit(); if (cl_exit->in(0) != lpt->_head) return; + // Make sure the are no extra control users of the loop backedge + if (cl->back_control()->outcnt() != 1) { + return; + } + // Check for pre-loop ending with CountedLoopEnd(Bool(Cmp(x,Opaque1(limit)))) CountedLoopEndNode* pre_end = get_pre_loop_end(cl); if (pre_end == NULL) return; diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/prims/forte.cpp --- a/src/share/vm/prims/forte.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/prims/forte.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -25,6 +25,20 @@ # include "incls/_precompiled.incl" # include "incls/_forte.cpp.incl" +// These name match the names reported by the forte quality kit +enum { + ticks_no_Java_frame = 0, + ticks_no_class_load = -1, + ticks_GC_active = -2, + ticks_unknown_not_Java = -3, + ticks_not_walkable_not_Java = -4, + ticks_unknown_Java = -5, + ticks_not_walkable_Java = -6, + ticks_unknown_state = -7, + ticks_thread_exit = -8, + ticks_deopt = -9, + ticks_safepoint = -10 +}; //------------------------------------------------------- @@ -41,297 +55,29 @@ }; -static void forte_is_walkable_compiled_frame(frame* fr, RegisterMap* map, +static void is_decipherable_compiled_frame(frame* fr, RegisterMap* map, bool* is_compiled_p, bool* is_walkable_p); -static bool forte_is_walkable_interpreted_frame(frame* fr, - methodOop* method_p, int* bci_p); +static bool is_decipherable_interpreted_frame(JavaThread* thread, + frame* fr, + methodOop* method_p, + int* bci_p); -// A Forte specific version of frame:safe_for_sender(). -static bool forte_safe_for_sender(frame* fr, JavaThread *thread) { - bool ret_value = false; // be pessimistic - -#ifdef COMPILER2 -#if defined(IA32) || defined(AMD64) - { - // This check is the same as the standard safe_for_sender() - // on IA32 or AMD64 except that NULL FP values are tolerated - // for C2. - address sp = (address)fr->sp(); - address fp = (address)fr->fp(); - ret_value = sp != NULL && sp <= thread->stack_base() && - sp >= thread->stack_base() - thread->stack_size() && - (fp == NULL || (fp <= thread->stack_base() && - fp >= thread->stack_base() - thread->stack_size())); - - // We used to use standard safe_for_sender() when we are supposed - // to be executing Java code. However, that prevents us from - // walking some intrinsic stacks so now we have to be more refined. - // If we passed the above check and we have a NULL frame pointer - // and we are supposed to be executing Java code, then we have a - // couple of more checks to make. - if (ret_value && fp == NULL && (thread->thread_state() == _thread_in_Java - || thread->thread_state() == _thread_in_Java_trans)) { - - if (fr->is_interpreted_frame()) { - // interpreted frames don't really have a NULL frame pointer - return false; - } else if (CodeCache::find_blob(fr->pc()) == NULL) { - // the NULL frame pointer should be associated with generated code - return false; - } - } - } - -#else // !(IA32 || AMD64) - ret_value = fr->safe_for_sender(thread); -#endif // IA32 || AMD64 - -#else // !COMPILER2 - ret_value = fr->safe_for_sender(thread); -#endif // COMPILER2 - - if (!ret_value) { - return ret_value; // not safe, nothing more to do - } - - address sp1; - -#ifdef SPARC - // On Solaris SPARC, when a compiler frame has an interpreted callee - // the _interpreter_sp_adjustment field contains the adjustment to - // this frame's SP made by that interpreted callee. - // For AsyncGetCallTrace(), we need to verify that the resulting SP - // is valid for the specified thread's stack. - sp1 = (address)fr->sp(); - address sp2 = (address)fr->unextended_sp(); - - // If the second SP is NULL, then the _interpreter_sp_adjustment - // field simply adjusts this frame's SP to NULL and the frame is - // not safe. This strange value can be set in the frame constructor - // when our peek into the interpreted callee's adjusted value for - // this frame's SP finds a NULL. This can happen when SIGPROF - // catches us while we are creating the interpreter frame. - // - if (sp2 == NULL || - - // If the two SPs are different, then _interpreter_sp_adjustment - // is non-zero and we need to validate the second SP. We invert - // the range check from frame::safe_for_sender() and bail out - // if the second SP is not safe. - (sp1 != sp2 && !(sp2 <= thread->stack_base() - && sp2 >= (thread->stack_base() - thread->stack_size())))) { - return false; - } -#endif // SPARC - - if (fr->is_entry_frame()) { - // This frame thinks it is an entry frame; we need to validate - // the JavaCallWrapper pointer. - // Note: frame::entry_frame_is_first() assumes that the - // JavaCallWrapper has a non-NULL _anchor field. We don't - // check that here (yet) since we've never seen a failure - // due to a NULL _anchor field. - // Update: Originally this check was done only for SPARC. However, - // this failure has now been seen on C2 C86. I have no reason to - // believe that this is not a general issue so I'm enabling the - // check for all compilers on all supported platforms. -#ifdef COMPILER2 -#if defined(IA32) || defined(AMD64) - if (fr->fp() == NULL) { - // C2 X86 allows NULL frame pointers, but if we have one then - // we cannot call entry_frame_call_wrapper(). - return false; - } -#endif // IA32 || AMD64 -#endif // COMPILER2 - - sp1 = (address)fr->entry_frame_call_wrapper(); - // We invert the range check from frame::safe_for_sender() and - // bail out if the JavaCallWrapper * is not safe. - if (!(sp1 <= thread->stack_base() - && sp1 >= (thread->stack_base() - thread->stack_size()))) { - return false; - } - } - - return ret_value; -} -// Unknown compiled frames have caused assertion failures on Solaris -// X86. This code also detects unknown compiled frames on Solaris -// SPARC, but no assertion failures have been observed. However, I'm -// paranoid so I'm enabling this code whenever we have a compiler. -// -// Returns true if the specified frame is an unknown compiled frame -// and false otherwise. -static bool is_unknown_compiled_frame(frame* fr, JavaThread *thread) { - bool ret_value = false; // be optimistic - - // This failure mode only occurs when the thread is in state - // _thread_in_Java so we are okay for this check for any other - // thread state. - // - // Note: _thread_in_Java does not always mean that the thread - // is executing Java code. AsyncGetCallTrace() has caught - // threads executing in JRT_LEAF() routines when the state - // will also be _thread_in_Java. - if (thread->thread_state() != _thread_in_Java) { - return ret_value; - } - - // This failure mode only occurs with compiled frames so we are - // okay for this check for both entry and interpreted frames. - if (fr->is_entry_frame() || fr->is_interpreted_frame()) { - return ret_value; - } - - // This failure mode only occurs when the compiled frame's PC - // is in the code cache so we are okay for this check if the - // PC is not in the code cache. - CodeBlob* cb = CodeCache::find_blob(fr->pc()); - if (cb == NULL) { - return ret_value; - } +vframeStreamForte::vframeStreamForte(JavaThread *jt, + frame fr, + bool stop_at_java_call_stub) : vframeStreamCommon(jt) { - // We have compiled code in the code cache so it is time for - // the final check: let's see if any frame type is set - ret_value = !( - // is_entry_frame() is checked above - // testers that are a subset of is_entry_frame(): - // is_first_frame() - fr->is_java_frame() - // testers that are a subset of is_java_frame(): - // is_interpreted_frame() - // is_compiled_frame() - || fr->is_native_frame() - || fr->is_runtime_frame() - || fr->is_safepoint_blob_frame() - ); - - // If there is no frame type set, then we have an unknown compiled - // frame and sender() should not be called on it. - - return ret_value; -} - -#define DebugNonSafepoints_IS_CLEARED \ - (!FLAG_IS_DEFAULT(DebugNonSafepoints) && !DebugNonSafepoints) - -// if -XX:-DebugNonSafepoints, then top-frame will be skipped -vframeStreamForte::vframeStreamForte(JavaThread *jt, frame fr, - bool stop_at_java_call_stub) : vframeStreamCommon(jt) { _stop_at_java_call_stub = stop_at_java_call_stub; - - if (!DebugNonSafepoints_IS_CLEARED) { - // decode the top frame fully - // (usual case, if JVMTI is enabled) - _frame = fr; - } else { - // skip top frame, as it may not be at safepoint - // For AsyncGetCallTrace(), we extracted as much info from the top - // frame as we could in forte_is_walkable_frame(). We also verified - // forte_safe_for_sender() so this sender() call is safe. - _frame = fr.sender(&_reg_map); - } - - if (jt->thread_state() == _thread_in_Java && !fr.is_first_frame()) { - bool sender_check = false; // assume sender is not safe - - if (forte_safe_for_sender(&_frame, jt)) { - // If the initial sender frame is safe, then continue on with other - // checks. The unsafe sender frame has been seen on Solaris X86 - // with both Compiler1 and Compiler2. It has not been seen on - // Solaris SPARC, but seems like a good sanity check to have - // anyway. + _frame = fr; - // SIGPROF caught us in Java code and the current frame is not the - // first frame so we should sanity check the sender frame. It is - // possible for SIGPROF to catch us in the middle of making a call. - // When that happens the current frame is actually a combination of - // the real sender and some of the new call's info. We can't find - // the real sender with such a current frame and things can get - // confused. - // - // This sanity check has caught problems with the sender frame on - // Solaris SPARC. So far Solaris X86 has not had a failure here. - sender_check = _frame.is_entry_frame() - // testers that are a subset of is_entry_frame(): - // is_first_frame() - || _frame.is_java_frame() - // testers that are a subset of is_java_frame(): - // is_interpreted_frame() - // is_compiled_frame() - || _frame.is_native_frame() - || _frame.is_runtime_frame() - || _frame.is_safepoint_blob_frame() - ; - - // We need an additional sanity check on an initial interpreted - // sender frame. This interpreted frame needs to be both walkable - // and have a valid BCI. This is yet another variant of SIGPROF - // catching us in the middle of making a call. - if (sender_check && _frame.is_interpreted_frame()) { - methodOop method = NULL; - int bci = -1; - - if (!forte_is_walkable_interpreted_frame(&_frame, &method, &bci) - || bci == -1) { - sender_check = false; - } - } - - // We need an additional sanity check on an initial compiled - // sender frame. This compiled frame also needs to be walkable. - // This is yet another variant of SIGPROF catching us in the - // middle of making a call. - if (sender_check && !_frame.is_interpreted_frame()) { - bool is_compiled, is_walkable; + // We must always have a valid frame to start filling - forte_is_walkable_compiled_frame(&_frame, &_reg_map, - &is_compiled, &is_walkable); - if (is_compiled && !is_walkable) { - sender_check = false; - } - } - } - - if (!sender_check) { - // nothing else to try if we can't recognize the sender - _mode = at_end_mode; - return; - } - } - - int loop_count = 0; - int loop_max = MaxJavaStackTraceDepth * 2; - - while (!fill_from_frame()) { - _frame = _frame.sender(&_reg_map); + bool filled_in = fill_from_frame(); -#ifdef COMPILER2 -#if defined(IA32) || defined(AMD64) - // Stress testing on C2 X86 has shown a periodic problem with - // the sender() call below. The initial _frame that we have on - // entry to the loop has already passed forte_safe_for_sender() - // so we only check frames after it. - if (!forte_safe_for_sender(&_frame, _thread)) { - _mode = at_end_mode; - return; - } -#endif // IA32 || AMD64 -#endif // COMPILER2 + assert(filled_in, "invariant"); - if (++loop_count >= loop_max) { - // We have looped more than twice the number of possible - // Java frames. This indicates that we are trying to walk - // a stack that is in the middle of being constructed and - // it is self referential. - _mode = at_end_mode; - return; - } - } } @@ -358,95 +104,57 @@ do { -#if defined(COMPILER1) && defined(SPARC) - bool prevIsInterpreted = _frame.is_interpreted_frame(); -#endif // COMPILER1 && SPARC + loop_count++; - _frame = _frame.sender(&_reg_map); + // By the time we get here we should never see unsafe but better + // safe then segv'd - if (!forte_safe_for_sender(&_frame, _thread)) { + if (loop_count > loop_max || !_frame.safe_for_sender(_thread)) { _mode = at_end_mode; return; } -#if defined(COMPILER1) && defined(SPARC) - if (prevIsInterpreted) { - // previous callee was interpreted and may require a special check - if (_frame.is_compiled_frame() && _frame.cb()->is_compiled_by_c1()) { - // compiled sender called interpreted callee so need one more check - bool is_compiled, is_walkable; + _frame = _frame.sender(&_reg_map); - // sanity check the compiled sender frame - forte_is_walkable_compiled_frame(&_frame, &_reg_map, - &is_compiled, &is_walkable); - assert(is_compiled, "sanity check"); - if (!is_walkable) { - // compiled sender frame is not walkable so bail out - _mode = at_end_mode; - return; - } - } - } -#endif // COMPILER1 && SPARC - - if (++loop_count >= loop_max) { - // We have looped more than twice the number of possible - // Java frames. This indicates that we are trying to walk - // a stack that is in the middle of being constructed and - // it is self referential. - _mode = at_end_mode; - return; - } } while (!fill_from_frame()); } -// Determine if 'fr' is a walkable, compiled frame. -// *is_compiled_p is set to true if the frame is compiled and if it -// is, then *is_walkable_p is set to true if it is also walkable. -static void forte_is_walkable_compiled_frame(frame* fr, RegisterMap* map, - bool* is_compiled_p, bool* is_walkable_p) { +// Determine if 'fr' is a decipherable compiled frame. We are already +// assured that fr is for a java nmethod. + +static bool is_decipherable_compiled_frame(frame* fr) { - *is_compiled_p = false; - *is_walkable_p = false; + assert(fr->cb() != NULL && fr->cb()->is_nmethod(), "invariant"); + nmethod* nm = (nmethod*) fr->cb(); + assert(nm->is_java_method(), "invariant"); + + // First try and find an exact PcDesc - CodeBlob* cb = CodeCache::find_blob(fr->pc()); - if (cb != NULL && - cb->is_nmethod() && - ((nmethod*)cb)->is_java_method()) { - // frame is compiled and executing a Java method - *is_compiled_p = true; + PcDesc* pc_desc = nm->pc_desc_at(fr->pc()); + + // Did we find a useful PcDesc? + if (pc_desc != NULL && + pc_desc->scope_decode_offset() == DebugInformationRecorder::serialized_null) { - // Increment PC because the PcDesc we want is associated with - // the *end* of the instruction, and pc_desc_near searches - // forward to the first matching PC after the probe PC. - PcDesc* pc_desc = NULL; - if (!DebugNonSafepoints_IS_CLEARED) { - // usual case: look for any safepoint near the sampled PC - address probe_pc = fr->pc() + 1; - pc_desc = ((nmethod*) cb)->pc_desc_near(probe_pc); - } else { - // reduced functionality: only recognize PCs immediately after calls - pc_desc = ((nmethod*) cb)->pc_desc_at(fr->pc()); - } - if (pc_desc != NULL && (pc_desc->scope_decode_offset() - == DebugInformationRecorder::serialized_null)) { - pc_desc = NULL; + address probe_pc = fr->pc() + 1; + pc_desc = nm->pc_desc_near(probe_pc); + + // Now do we have a useful PcDesc? + + if (pc_desc != NULL && + pc_desc->scope_decode_offset() == DebugInformationRecorder::serialized_null) { + // No debug information available for this pc + // vframeStream would explode if we try and walk the frames. + return false; } - if (pc_desc != NULL) { - // it has a PcDesc so the frame is also walkable - *is_walkable_p = true; - if (!DebugNonSafepoints_IS_CLEARED) { - // Normalize the PC to the one associated exactly with - // this PcDesc, so that subsequent stack-walking queries - // need not be approximate: - fr->set_pc(pc_desc->real_pc((nmethod*) cb)); - } - } - // Implied else: this compiled frame has no PcDesc, i.e., contains - // a frameless stub such as C1 method exit, so it is not walkable. + + // This PcDesc is useful however we must adjust the frame's pc + // so that the vframeStream lookups will use this same pc + + fr->set_pc(pc_desc->real_pc(nm)); } - // Implied else: this isn't a compiled frame so it isn't a - // walkable, compiled frame. + + return true; } // Determine if 'fr' is a walkable interpreted frame. Returns false @@ -457,159 +165,189 @@ // Note: this method returns true when a valid Java method is found // even if a valid BCI cannot be found. -static bool forte_is_walkable_interpreted_frame(frame* fr, - methodOop* method_p, int* bci_p) { +static bool is_decipherable_interpreted_frame(JavaThread* thread, + frame* fr, + methodOop* method_p, + int* bci_p) { assert(fr->is_interpreted_frame(), "just checking"); // top frame is an interpreted frame // check if it is walkable (i.e. valid methodOop and valid bci) - if (fr->is_interpreted_frame_valid()) { - if (fr->fp() != NULL) { - // access address in order not to trigger asserts that - // are built in interpreter_frame_method function - methodOop method = *fr->interpreter_frame_method_addr(); - if (Universe::heap()->is_valid_method(method)) { - intptr_t bcx = fr->interpreter_frame_bcx(); - int bci = method->validate_bci_from_bcx(bcx); - // note: bci is set to -1 if not a valid bci - *method_p = method; - *bci_p = bci; - return true; - } - } + + // Because we may be racing a gc thread the method and/or bci + // of a valid interpreter frame may look bad causing us to + // fail the is_interpreted_frame_valid test. If the thread + // is in any of the following states we are assured that the + // frame is in fact valid and we must have hit the race. + + JavaThreadState state = thread->thread_state(); + bool known_valid = (state == _thread_in_native || + state == _thread_in_vm || + state == _thread_blocked ); + + if (known_valid || fr->is_interpreted_frame_valid(thread)) { + + // The frame code should completely validate the frame so that + // references to methodOop and bci are completely safe to access + // If they aren't the frame code should be fixed not this + // code. However since gc isn't locked out the values could be + // stale. This is a race we can never completely win since we can't + // lock out gc so do one last check after retrieving their values + // from the frame for additional safety + + methodOop method = fr->interpreter_frame_method(); + + // We've at least found a method. + // NOTE: there is something to be said for the approach that + // if we don't find a valid bci then the method is not likely + // a valid method. Then again we may have caught an interpreter + // frame in the middle of construction and the bci field is + // not yet valid. + + *method_p = method; + + // See if gc may have invalidated method since we validated frame + + if (!Universe::heap()->is_valid_method(method)) return false; + + intptr_t bcx = fr->interpreter_frame_bcx(); + + int bci = method->validate_bci_from_bcx(bcx); + + // note: bci is set to -1 if not a valid bci + *bci_p = bci; + return true; } + return false; } -// Determine if 'fr' can be used to find a walkable frame. Returns -// false if a walkable frame cannot be found. *walkframe_p, *method_p, -// and *bci_p are not set when false is returned. Returns true if a -// walkable frame is returned via *walkframe_p. *method_p is non-NULL -// if the returned frame was executing a Java method. *bci_p is != -1 -// if a valid BCI in the Java method could be found. +// Determine if 'fr' can be used to find an initial Java frame. +// Return false if it can not find a fully decipherable Java frame +// (in other words a frame that isn't safe to use in a vframe stream). +// Obviously if it can't even find a Java frame false will also be returned. +// +// If we find a Java frame decipherable or not then by definition we have +// identified a method and that will be returned to the caller via method_p. +// If we can determine a bci that is returned also. (Hmm is it possible +// to return a method and bci and still return false? ) +// +// The initial Java frame we find (if any) is return via initial_frame_p. // -// *walkframe_p will be used by vframeStreamForte as the initial -// frame for walking the stack. Currently the initial frame is -// skipped by vframeStreamForte because we inherited the logic from -// the vframeStream class. This needs to be revisited in the future. -static bool forte_is_walkable_frame(JavaThread* thread, frame* fr, - frame* walkframe_p, methodOop* method_p, int* bci_p) { + +static bool find_initial_Java_frame(JavaThread* thread, + frame* fr, + frame* initial_frame_p, + methodOop* method_p, + int* bci_p) { + + // It is possible that for a frame containing an nmethod + // we can capture the method but no bci. If we get no + // bci the frame isn't walkable but the method is usable. + // Therefore we init the returned methodOop to NULL so the + // caller can make the distinction. + + *method_p = NULL; + + // On the initial call to this method the frame we get may not be + // recognizable to us. This should only happen if we are in a JRT_LEAF + // or something called by a JRT_LEAF method. + - if (!forte_safe_for_sender(fr, thread) - || is_unknown_compiled_frame(fr, thread) - ) { - // If the initial frame is not safe, then bail out. So far this - // has only been seen on Solaris X86 with Compiler2, but it seems - // like a great initial sanity check. - return false; + + frame candidate = *fr; + + // If the starting frame we were given has no codeBlob associated with + // it see if we can find such a frame because only frames with codeBlobs + // are possible Java frames. + + if (fr->cb() == NULL) { + + // See if we can find a useful frame + int loop_count; + int loop_max = MaxJavaStackTraceDepth * 2; + RegisterMap map(thread, false); + + for (loop_count = 0; loop_count < loop_max; loop_count++) { + if (!candidate.safe_for_sender(thread)) return false; + candidate = candidate.sender(&map); + if (candidate.cb() != NULL) break; + } + if (candidate.cb() == NULL) return false; } - if (fr->is_first_frame()) { - // If initial frame is frame from StubGenerator and there is no - // previous anchor, there are no java frames yet - return false; - } - - if (fr->is_interpreted_frame()) { - if (forte_is_walkable_interpreted_frame(fr, method_p, bci_p)) { - *walkframe_p = *fr; - return true; - } - return false; - } - - // At this point we have something other than a first frame or an - // interpreted frame. - - methodOop method = NULL; - frame candidate = *fr; - - // If we loop more than twice the number of possible Java - // frames, then this indicates that we are trying to walk - // a stack that is in the middle of being constructed and - // it is self referential. So far this problem has only - // been seen on Solaris X86 Compiler2, but it seems like - // a good robustness fix for all platforms. - + // We have a frame known to be in the codeCache + // We will hopefully be able to figure out something to do with it. int loop_count; int loop_max = MaxJavaStackTraceDepth * 2; + RegisterMap map(thread, false); for (loop_count = 0; loop_count < loop_max; loop_count++) { - // determine if the candidate frame is executing a Java method - if (CodeCache::contains(candidate.pc())) { - // candidate is a compiled frame or stub routine - CodeBlob* cb = CodeCache::find_blob(candidate.pc()); - if (cb->is_nmethod()) { - method = ((nmethod *)cb)->method(); - } - } // end if CodeCache has our PC - - RegisterMap map(thread, false); + if (candidate.is_first_frame()) { + // If initial frame is frame from StubGenerator and there is no + // previous anchor, there are no java frames associated with a method + return false; + } - // we have a Java frame that seems reasonable - if (method != NULL && candidate.is_java_frame() - && candidate.sp() != NULL && candidate.pc() != NULL) { - // we need to sanity check the candidate further - bool is_compiled, is_walkable; + if (candidate.is_interpreted_frame()) { + if (is_decipherable_interpreted_frame(thread, &candidate, method_p, bci_p)) { + *initial_frame_p = candidate; + return true; + } - forte_is_walkable_compiled_frame(&candidate, &map, &is_compiled, - &is_walkable); - if (is_compiled) { - // At this point, we know we have a compiled Java frame with - // method information that we want to return. We don't check - // the is_walkable flag here because that flag pertains to - // vframeStreamForte work that is done after we are done here. - break; - } + // Hopefully we got some data + return false; } - // At this point, the candidate doesn't work so try the sender. + if (candidate.cb()->is_nmethod()) { + + nmethod* nm = (nmethod*) candidate.cb(); + *method_p = nm->method(); - // For AsyncGetCallTrace() we cannot assume there is a sender - // for the initial frame. The initial forte_safe_for_sender() call - // and check for is_first_frame() is done on entry to this method. - candidate = candidate.sender(&map); - if (!forte_safe_for_sender(&candidate, thread)) { + // If the frame isn't fully decipherable then the default + // value for the bci is a signal that we don't have a bci. + // If we have a decipherable frame this bci value will + // not be used. + + *bci_p = -1; -#ifdef COMPILER2 -#if defined(IA32) || defined(AMD64) - // C2 on X86 can use the ebp register as a general purpose register - // which can cause the candidate to fail theforte_safe_for_sender() - // above. We try one more time using a NULL frame pointer (fp). + *initial_frame_p = candidate; + + // Native wrapper code is trivial to decode by vframeStream + + if (nm->is_native_method()) return true; - candidate = frame(candidate.sp(), NULL, candidate.pc()); - if (!forte_safe_for_sender(&candidate, thread)) { -#endif // IA32 || AMD64 -#endif // COMPILER2 + // If it isn't decipherable then we have found a pc that doesn't + // have a PCDesc that can get us a bci however we did find + // a method + if (!is_decipherable_compiled_frame(&candidate)) { return false; + } -#ifdef COMPILER2 -#if defined(IA32) || defined(AMD64) - } // end forte_safe_for_sender retry with NULL fp -#endif // IA32 || AMD64 -#endif // COMPILER2 + // is_decipherable_compiled_frame may modify candidate's pc + *initial_frame_p = candidate; - } // end first forte_safe_for_sender check + return true; + } + + // Must be some stub frame that we don't care about - if (candidate.is_first_frame() - || is_unknown_compiled_frame(&candidate, thread)) { - return false; - } - } // end for loop_count + if (!candidate.safe_for_sender(thread)) return false; + candidate = candidate.sender(&map); - if (method == NULL) { - // If we didn't get any method info from the candidate, then - // we have nothing to return so bail out. - return false; + // If it isn't in the code cache something is wrong + // since once we find a frame in the code cache they + // all should be there. + + if (candidate.cb() == NULL) return false; + } - *walkframe_p = candidate; - *method_p = method; - *bci_p = -1; - return true; + return false; + } @@ -627,10 +365,12 @@ } ASGCT_CallTrace; static void forte_fill_call_trace_given_top(JavaThread* thd, - ASGCT_CallTrace* trace, int depth, frame top_frame) { + ASGCT_CallTrace* trace, + int depth, + frame top_frame) { NoHandleMark nhm; - frame walkframe; + frame initial_Java_frame; methodOop method; int bci; int count; @@ -638,48 +378,51 @@ count = 0; assert(trace->frames != NULL, "trace->frames must be non-NULL"); - if (!forte_is_walkable_frame(thd, &top_frame, &walkframe, &method, &bci)) { - // return if no walkable frame is found - return; - } + bool fully_decipherable = find_initial_Java_frame(thd, &top_frame, &initial_Java_frame, &method, &bci); + + // The frame might not be walkable but still recovered a method + // (e.g. an nmethod with no scope info for the pc + + if (method == NULL) return; CollectedHeap* ch = Universe::heap(); - if (method != NULL) { - // The method is not stored GC safe so see if GC became active - // after we entered AsyncGetCallTrace() and before we try to - // use the methodOop. - // Yes, there is still a window after this check and before - // we use methodOop below, but we can't lock out GC so that - // has to be an acceptable risk. - if (!ch->is_valid_method(method)) { - trace->num_frames = -2; - return; - } - - if (DebugNonSafepoints_IS_CLEARED) { - // Take whatever method the top-frame decoder managed to scrape up. - // We look further at the top frame only if non-safepoint - // debugging information is available. - count++; - trace->num_frames = count; - trace->frames[0].method_id = method->find_jmethod_id_or_null(); - if (!method->is_native()) { - trace->frames[0].lineno = bci; - } else { - trace->frames[0].lineno = -3; - } - } - } - - // check has_last_Java_frame() after looking at the top frame - // which may be an interpreted Java frame. - if (!thd->has_last_Java_frame() && method == NULL) { - trace->num_frames = 0; + // The method is not stored GC safe so see if GC became active + // after we entered AsyncGetCallTrace() and before we try to + // use the methodOop. + // Yes, there is still a window after this check and before + // we use methodOop below, but we can't lock out GC so that + // has to be an acceptable risk. + if (!ch->is_valid_method(method)) { + trace->num_frames = ticks_GC_active; // -2 return; } - vframeStreamForte st(thd, walkframe, false); + // We got a Java frame however it isn't fully decipherable + // so it won't necessarily be safe to use it for the + // initial frame in the vframe stream. + + if (!fully_decipherable) { + // Take whatever method the top-frame decoder managed to scrape up. + // We look further at the top frame only if non-safepoint + // debugging information is available. + count++; + trace->num_frames = count; + trace->frames[0].method_id = method->find_jmethod_id_or_null(); + if (!method->is_native()) { + trace->frames[0].lineno = bci; + } else { + trace->frames[0].lineno = -3; + } + + if (!initial_Java_frame.safe_for_sender(thd)) return; + + RegisterMap map(thd, false); + initial_Java_frame = initial_Java_frame.sender(&map); + } + + vframeStreamForte st(thd, initial_Java_frame, false); + for (; !st.at_end() && count < depth; st.forte_next(), count++) { bci = st.bci(); method = st.method(); @@ -693,7 +436,7 @@ if (!ch->is_valid_method(method)) { // we throw away everything we've gathered in this sample since // none of it is safe - trace->num_frames = -2; + trace->num_frames = ticks_GC_active; // -2 return; } @@ -765,6 +508,11 @@ extern "C" { void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext) { + +// This is if'd out because we no longer use thread suspension. +// However if someone wanted to backport this to a 5.0 jvm then this +// code would be important. +#if 0 if (SafepointSynchronize::is_synchronizing()) { // The safepoint mechanism is trying to synchronize all the threads. // Since this can involve thread suspension, it is not safe for us @@ -774,9 +522,10 @@ // are suspended while holding a resource and another thread blocks // on that resource in the SIGPROF handler, then we will have a // three-thread deadlock (VMThread, this thread, the other thread). - trace->num_frames = -10; + trace->num_frames = ticks_safepoint; // -10 return; } +#endif JavaThread* thread; @@ -785,13 +534,13 @@ thread->is_exiting()) { // bad env_id, thread has exited or thread is exiting - trace->num_frames = -8; + trace->num_frames = ticks_thread_exit; // -8 return; } if (thread->in_deopt_handler()) { // thread is in the deoptimization handler so return no frames - trace->num_frames = -9; + trace->num_frames = ticks_deopt; // -9 return; } @@ -799,12 +548,12 @@ "AsyncGetCallTrace must be called by the current interrupted thread"); if (!JvmtiExport::should_post_class_load()) { - trace->num_frames = -1; + trace->num_frames = ticks_no_class_load; // -1 return; } if (Universe::heap()->is_gc_active()) { - trace->num_frames = -2; + trace->num_frames = ticks_GC_active; // -2 return; } @@ -827,14 +576,22 @@ // param isInJava == false - indicate we aren't in Java code if (!thread->pd_get_top_frame_for_signal_handler(&fr, ucontext, false)) { + trace->num_frames = ticks_unknown_not_Java; // -3 unknown frame + } else { if (!thread->has_last_Java_frame()) { - trace->num_frames = 0; // no Java frames + trace->num_frames = 0; // No Java frames } else { - trace->num_frames = -3; // unknown frame + trace->num_frames = ticks_not_walkable_not_Java; // -4 non walkable frame by default + forte_fill_call_trace_given_top(thread, trace, depth, fr); + + // This assert would seem to be valid but it is not. + // It would be valid if we weren't possibly racing a gc + // thread. A gc thread can make a valid interpreted frame + // look invalid. It's a small window but it does happen. + // The assert is left here commented out as a reminder. + // assert(trace->num_frames != ticks_not_walkable_not_Java, "should always be walkable"); + } - } else { - trace->num_frames = -4; // non walkable frame by default - forte_fill_call_trace_given_top(thread, trace, depth, fr); } } break; @@ -845,16 +602,16 @@ // param isInJava == true - indicate we are in Java code if (!thread->pd_get_top_frame_for_signal_handler(&fr, ucontext, true)) { - trace->num_frames = -5; // unknown frame + trace->num_frames = ticks_unknown_Java; // -5 unknown frame } else { - trace->num_frames = -6; // non walkable frame by default + trace->num_frames = ticks_not_walkable_Java; // -6, non walkable frame by default forte_fill_call_trace_given_top(thread, trace, depth, fr); } } break; default: // Unknown thread state - trace->num_frames = -7; + trace->num_frames = ticks_unknown_state; // -7 break; } } diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/runtime/fprofiler.cpp --- a/src/share/vm/runtime/fprofiler.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/runtime/fprofiler.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -924,29 +924,23 @@ FlatProfiler::record_thread_ticks(); } -void ThreadProfiler::record_interpreted_tick(frame fr, TickPosition where, int* ticks) { +void ThreadProfiler::record_interpreted_tick(JavaThread* thread, frame fr, TickPosition where, int* ticks) { FlatProfiler::all_int_ticks++; if (!FlatProfiler::full_profile()) { return; } - if (!fr.is_interpreted_frame_valid()) { + if (!fr.is_interpreted_frame_valid(thread)) { // tick came at a bad time interpreter_ticks += 1; FlatProfiler::interpreter_ticks += 1; return; } - methodOop method = NULL; - if (fr.fp() != NULL) { - method = *fr.interpreter_frame_method_addr(); - } - if (!Universe::heap()->is_valid_method(method)) { - // tick came at a bad time, stack frame not initialized correctly - interpreter_ticks += 1; - FlatProfiler::interpreter_ticks += 1; - return; - } + // The frame has been fully validated so we can trust the method and bci + + methodOop method = *fr.interpreter_frame_method_addr(); + interpreted_update(method, where); // update byte code table @@ -997,7 +991,7 @@ // The tick happend in real code -> non VM code if (fr.is_interpreted_frame()) { interval_data_ref()->inc_interpreted(); - record_interpreted_tick(fr, tp_code, FlatProfiler::bytecode_ticks); + record_interpreted_tick(thread, fr, tp_code, FlatProfiler::bytecode_ticks); return; } @@ -1028,7 +1022,7 @@ // The tick happend in VM code interval_data_ref()->inc_native(); if (fr.is_interpreted_frame()) { - record_interpreted_tick(fr, tp_native, FlatProfiler::bytecode_ticks_stub); + record_interpreted_tick(thread, fr, tp_native, FlatProfiler::bytecode_ticks_stub); return; } if (CodeCache::contains(fr.pc())) { diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/runtime/fprofiler.hpp --- a/src/share/vm/runtime/fprofiler.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/runtime/fprofiler.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -135,7 +135,7 @@ ProfilerNode** table; private: - void record_interpreted_tick(frame fr, TickPosition where, int* ticks); + void record_interpreted_tick(JavaThread* thread, frame fr, TickPosition where, int* ticks); void record_compiled_tick (JavaThread* thread, frame fr, TickPosition where); void interpreted_update(methodOop method, TickPosition where); void compiled_update (methodOop method, TickPosition where); diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/runtime/frame.hpp --- a/src/share/vm/runtime/frame.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/runtime/frame.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -108,7 +108,7 @@ bool is_first_frame() const; // oldest frame? (has no sender) bool is_first_java_frame() const; // same for Java frame - bool is_interpreted_frame_valid() const; // performs sanity checks on interpreted frames. + bool is_interpreted_frame_valid(JavaThread* thread) const; // performs sanity checks on interpreted frames. // tells whether this frame is marked for deoptimization bool should_be_deoptimized() const; diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/runtime/globals.cpp --- a/src/share/vm/runtime/globals.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/runtime/globals.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -68,18 +68,20 @@ if (is_uintx()) st->print("%-16lu", get_uintx()); if (is_ccstr()) { const char* cp = get_ccstr(); - const char* eol; - while ((eol = strchr(cp, '\n')) != NULL) { - char format_buffer[FORMAT_BUFFER_LEN]; - size_t llen = pointer_delta(eol, cp, sizeof(char)); - jio_snprintf(format_buffer, FORMAT_BUFFER_LEN, - "%%." SIZE_FORMAT "s", llen); - st->print(format_buffer, cp); - st->cr(); - cp = eol+1; - st->print("%5s %-35s += ", "", name); + if (cp != NULL) { + const char* eol; + while ((eol = strchr(cp, '\n')) != NULL) { + char format_buffer[FORMAT_BUFFER_LEN]; + size_t llen = pointer_delta(eol, cp, sizeof(char)); + jio_snprintf(format_buffer, FORMAT_BUFFER_LEN, + "%%." SIZE_FORMAT "s", llen); + st->print(format_buffer, cp); + st->cr(); + cp = eol+1; + st->print("%5s %-35s += ", "", name); + } + st->print("%-16s", cp); } - st->print("%-16s", cp); } st->print(" %s", kind); st->cr(); @@ -94,18 +96,21 @@ st->print("-XX:%s=" UINTX_FORMAT, name, get_uintx()); } else if (is_ccstr()) { st->print("-XX:%s=", name); - // Need to turn embedded '\n's back into separate arguments - // Not so efficient to print one character at a time, - // but the choice is to do the transformation to a buffer - // and print that. And this need not be efficient. - for (const char* cp = get_ccstr(); *cp != '\0'; cp += 1) { - switch (*cp) { - default: - st->print("%c", *cp); - break; - case '\n': - st->print(" -XX:%s=", name); - break; + const char* cp = get_ccstr(); + if (cp != NULL) { + // Need to turn embedded '\n's back into separate arguments + // Not so efficient to print one character at a time, + // but the choice is to do the transformation to a buffer + // and print that. And this need not be efficient. + for (; *cp != '\0'; cp += 1) { + switch (*cp) { + default: + st->print("%c", *cp); + break; + case '\n': + st->print(" -XX:%s=", name); + break; + } } } } else { diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/runtime/globals.hpp --- a/src/share/vm/runtime/globals.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/runtime/globals.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -668,16 +668,19 @@ notproduct(bool, PrintCompilation2, false, \ "Print additional statistics per compilation") \ \ - notproduct(bool, PrintAdapterHandlers, false, \ + diagnostic(bool, PrintAdapterHandlers, false, \ "Print code generated for i2c/c2i adapters") \ \ - develop(bool, PrintAssembly, false, \ - "Print assembly code") \ - \ - develop(bool, PrintNMethods, false, \ + diagnostic(bool, PrintAssembly, false, \ + "Print assembly code (using external disassembler.so)") \ + \ + diagnostic(ccstr, PrintAssemblyOptions, false, \ + "Options string passed to disassembler.so") \ + \ + diagnostic(bool, PrintNMethods, false, \ "Print assembly code for nmethods when generated") \ \ - develop(bool, PrintNativeNMethods, false, \ + diagnostic(bool, PrintNativeNMethods, false, \ "Print assembly code for native nmethods when generated") \ \ develop(bool, PrintDebugInfo, false, \ @@ -702,7 +705,7 @@ develop(bool, PrintCodeCache2, false, \ "Print detailed info on the compiled_code cache when exiting") \ \ - develop(bool, PrintStubCode, false, \ + diagnostic(bool, PrintStubCode, false, \ "Print generated stub code") \ \ product(bool, StackTraceInThrowable, true, \ @@ -1319,6 +1322,10 @@ product(bool, CMSClassUnloadingEnabled, false, \ "Whether class unloading enabled when using CMS GC") \ \ + product(uintx, CMSClassUnloadingMaxInterval, 0, \ + "When CMS class unloading is enabled, the maximum CMS cycle count"\ + " for which classes may not be unloaded") \ + \ product(bool, CMSCompactWhenClearAllSoftRefs, true, \ "Compact when asked to collect CMS gen with clear_all_soft_refs") \ \ @@ -1504,17 +1511,30 @@ "Percentage of MinHeapFreeRatio in CMS generation that is " \ " allocated before a CMS collection cycle commences") \ \ - product(intx, CMSBootstrapOccupancy, 50, \ + product(intx, CMSTriggerPermRatio, 80, \ + "Percentage of MinHeapFreeRatio in the CMS perm generation that" \ + " is allocated before a CMS collection cycle commences, that " \ + " also collects the perm generation") \ + \ + product(uintx, CMSBootstrapOccupancy, 50, \ "Percentage CMS generation occupancy at which to " \ " initiate CMS collection for bootstrapping collection stats") \ \ product(intx, CMSInitiatingOccupancyFraction, -1, \ "Percentage CMS generation occupancy to start a CMS collection " \ - " cycle (A negative value means that CMSTirggerRatio is used)") \ + " cycle (A negative value means that CMSTriggerRatio is used)") \ + \ + product(intx, CMSInitiatingPermOccupancyFraction, -1, \ + "Percentage CMS perm generation occupancy to start a CMScollection"\ + " cycle (A negative value means that CMSTriggerPermRatio is used)")\ \ product(bool, UseCMSInitiatingOccupancyOnly, false, \ "Only use occupancy as a crierion for starting a CMS collection") \ \ + product(intx, CMSIsTooFullPercentage, 98, \ + "An absolute ceiling above which CMS will always consider the" \ + " perm gen ripe for collection") \ + \ develop(bool, CMSTestInFreeList, false, \ "Check if the coalesced range is already in the " \ "free lists as claimed.") \ @@ -2250,7 +2270,7 @@ product_pd(bool, RewriteFrequentPairs, \ "Rewrite frequently used bytecode pairs into a single bytecode") \ \ - product(bool, PrintInterpreter, false, \ + diagnostic(bool, PrintInterpreter, false, \ "Prints the generated interpreter code") \ \ product(bool, UseInterpreter, true, \ @@ -2300,7 +2320,7 @@ develop(bool, PrintBytecodePairHistogram, false, \ "Print histogram of the executed bytecode pairs") \ \ - develop(bool, PrintSignatureHandlers, false, \ + diagnostic(bool, PrintSignatureHandlers, false, \ "Print code generated for native method signature handlers") \ \ develop(bool, VerifyOops, false, \ diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/runtime/stubCodeGenerator.cpp --- a/src/share/vm/runtime/stubCodeGenerator.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/runtime/stubCodeGenerator.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -69,7 +69,6 @@ _first_stub = _last_stub = NULL; } -#ifndef PRODUCT extern "C" { static int compare_cdesc(const void* void_a, const void* void_b) { int ai = (*((StubCodeDesc**) void_a))->index(); @@ -77,10 +76,8 @@ return ai - bi; } } -#endif StubCodeGenerator::~StubCodeGenerator() { -#ifndef PRODUCT if (PrintStubCode) { CodeBuffer* cbuf = _masm->code(); CodeBlob* blob = CodeCache::find_blob_unsafe(cbuf->insts()->start()); @@ -105,7 +102,6 @@ tty->cr(); } } -#endif //PRODUCT } diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/runtime/vframe.hpp --- a/src/share/vm/runtime/vframe.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/runtime/vframe.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -416,6 +416,48 @@ int decode_offset; if (pc_desc == NULL) { // Should not happen, but let fill_from_compiled_frame handle it. + + // If we are trying to walk the stack of a thread that is not + // at a safepoint (like AsyncGetCallTrace would do) then this is an + // acceptable result. [ This is assuming that safe_for_sender + // is so bullet proof that we can trust the frames it produced. ] + // + // So if we see that the thread is not safepoint safe + // then simply produce the method and a bci of zero + // and skip the possibility of decoding any inlining that + // may be present. That is far better than simply stopping (or + // asserting. If however the thread is safepoint safe this + // is the sign of a compiler bug and we'll let + // fill_from_compiled_frame handle it. + + + JavaThreadState state = _thread->thread_state(); + + // in_Java should be good enough to test safepoint safety + // if state were say in_Java_trans then we'd expect that + // the pc would have already been slightly adjusted to + // one that would produce a pcDesc since the trans state + // would be one that might in fact anticipate a safepoint + + if (state == _thread_in_Java ) { + // This will get a method a zero bci and no inlining. + // Might be nice to have a unique bci to signify this + // particular case but for now zero will do. + + fill_from_compiled_native_frame(); + + // There is something to be said for setting the mode to + // at_end_mode to prevent trying to walk further up the + // stack. There is evidence that if we walk any further + // that we could produce a bad stack chain. However until + // we see evidence that allowing this causes us to find + // frames bad enough to cause segv's or assertion failures + // we don't do it as while we may get a bad call chain the + // probability is much higher (several magnitudes) that we + // get good data. + + return true; + } decode_offset = DebugInformationRecorder::serialized_null; } else { decode_offset = pc_desc->scope_decode_offset(); diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/utilities/ostream.cpp --- a/src/share/vm/utilities/ostream.cpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/utilities/ostream.cpp Fri Apr 11 09:56:35 2008 -0400 @@ -52,8 +52,9 @@ _precount += _position + 1; _position = 0; } else if (ch == '\t') { - _position += 8; - _precount -= 7; // invariant: _precount + _position == total count + int tw = 8 - (_position & 7); + _position += tw; + _precount -= tw-1; // invariant: _precount + _position == total count } else { _position += 1; } @@ -133,7 +134,17 @@ } void outputStream::fill_to(int col) { - while (position() < col) sp(); + int need_fill = col - position(); + sp(need_fill); +} + +void outputStream::move_to(int col, int slop, int min_space) { + if (position() >= col + slop) + cr(); + int need_fill = col - position(); + if (need_fill < min_space) + need_fill = min_space; + sp(need_fill); } void outputStream::put(char ch) { @@ -142,8 +153,23 @@ write(buf, 1); } -void outputStream::sp() { - this->write(" ", 1); +#define SP_USE_TABS false + +void outputStream::sp(int count) { + if (count < 0) return; + if (SP_USE_TABS && count >= 8) { + int target = position() + count; + while (count >= 8) { + this->write("\t", 1); + count -= 8; + } + count = target - position(); + } + while (count > 0) { + int nw = (count > 8) ? 8 : count; + this->write(" ", nw); + count -= nw; + } } void outputStream::cr() { diff -r c6ff24ceec1c -r a49a647afe9a src/share/vm/utilities/ostream.hpp --- a/src/share/vm/utilities/ostream.hpp Thu Apr 10 15:49:16 2008 -0400 +++ b/src/share/vm/utilities/ostream.hpp Fri Apr 11 09:56:35 2008 -0400 @@ -59,6 +59,7 @@ int indentation() const { return _indentation; } void set_indentation(int i) { _indentation = i; } void fill_to(int col); + void move_to(int col, int slop = 6, int min_space = 2); // sizing int width() const { return _width; } @@ -78,7 +79,7 @@ void print_raw_cr(const char* str) { write(str, strlen(str)); cr(); } void print_raw_cr(const char* str, int len){ write(str, len); cr(); } void put(char ch); - void sp(); + void sp(int count = 1); void cr(); void bol() { if (_position > 0) cr(); } diff -r c6ff24ceec1c -r a49a647afe9a test/compiler/6646020/Tester.java --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/test/compiler/6646020/Tester.java Fri Apr 11 09:56:35 2008 -0400 @@ -0,0 +1,886 @@ +/* + * Copyright 2008 Sun Microsystems, Inc. All Rights Reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Sun Microsystems, Inc., 4150 Network Circle, Santa Clara, + * CA 95054 USA or visit www.sun.com if you need additional information or + * have any questions. + */ + +/* + * @test + * @bug 6646020 + * @summary assert(in_bb(n),"must be in block") in -Xcomp mode + */ + +/* Complexity upper bound: 3361 ops */ + +class Tester_Class_0 { + static byte var_1; + + + public Tester_Class_0() + { + "".length(); + { + var_1 = (var_1 = (new byte[(byte)'D'])[(byte)2.40457E38F]); + var_1 = (var_1 = (byte)1.738443503665377E307); + var_1 = (var_1 = (byte)1237144669662298112L); + } + var_1 = "baldh".equalsIgnoreCase("") ? (var_1 = (byte)7.2932087E37F) : (byte)3909726578709910528L; + var_1 = (var_1 = (var_1 = (var_1 = (byte)7.223761846153971E307))); + var_1 = (var_1 = (var_1 = (var_1 = (var_1 = (byte)((short)7860452029249754112L + (byte)1.7374232546809952E308))))); + var_1 = (!true ? (var_1 = (byte)4359229782598970368L) : (short)(byte)1.7509836746850026E308) >= 'P' ? (var_1 = (byte)3.275114793095594E307) : (byte)(- ((byte)1.5595572E38F) / 8.2971296E37F); + byte var_9 = (true ? true : (false ? true : false)) ? (var_1 = (var_1 = (byte)9.928434E37F)) : (var_1 = (byte)9.785060633966518E307); + final byte var_10 = 53; + var_9 <<= (true | true) & (((var_10 == "".substring(2001075014).compareToIgnoreCase("rhbytggv") ? !true : ! !true) ? !false : false) ? !true & true : !false) ? var_10 : var_10; + var_9 <<= - (var_9 -= - ~6397182310329038848L >> (char)955837891 << (short)- - -8.4452034E37F >> + ~5485157895941338112L); + --var_9; + var_9 >>= 'V'; + var_9 -= (new char[var_10])[var_9]; + double var_11; + var_11 = (var_11 = (new int[var_9 = (var_9 %= 684423748)])[var_9]); + var_9 /= 'q'; + var_9 *= ~var_9 | (short)1.7667766368850557E308 - "w".trim().charAt(- (var_9 /= + (var_11 = 'q'))); + if (var_10 <= 605036859609030656L | !false & false) + { + var_9 >>>= false ^ false ? (new short[var_10])[var_10] : (short)1013619326108001280L; + } + else + { + var_11 = var_9; + } + var_9 -= 'X'; + var_9 *= 'E'; + { + var_9 ^= (new short[var_9])[var_9 >>>= 'c']; + } + var_11 = 4315867074042433536L; + double var_12 = 1.2183900219527627E308; + var_9 <<= (false ? !false : false) ? '\\' : 'D'; + } + + + + + private final long func_0() + { + float var_2 = 0F; + var_1 = (var_1 = (var_1 = (byte)((short)1.4106931056021857E308 % var_2))); + for (new String(); true & (! !true ^ !false | false) && var_2 < 1; var_1 = (var_1 = (var_1 = (var_1 = (byte)1183673628639185920L)))) + { + var_1 = true | false ? (var_1 = (byte)1.6263855E37F) : (byte)'O'; + var_2++; + "fui".toUpperCase(); + final int var_3 = (var_1 = (var_1 = (byte)'i')) + (byte)2008561384 / (byte)1.4413369179905006E308; + } + var_1 = (var_1 = false ^ false ? (byte)2.3850814E38F : (byte)4.42887E37F); + final float var_4 = 3.052265E38F; + var_1 = (var_1 = (var_1 = (var_1 = (var_1 = (byte)'o')))); + long var_5; + var_1 = (var_1 = (byte)((var_1 = (byte)1913212786) * (var_1 = (byte)var_2))); + var_5 = (short)3.2024069E38F * (short)(var_5 = 'Q'); + var_5 = (false ? true : false) ? (short)1098137179 : (byte)~695765814858203136L; + var_1 = (var_1 = true & false ^ true ? (byte)1662737306 : (byte)'r'); + { + (true ? "a" : "lymivj".toString()).codePointCount((short)3.032349E38F + (var_1 = (var_1 = (var_1 = (var_1 = (byte)1.3159799E37F)))), (byte)2.0898819853138264E307 & (new short[(byte)(short)var_2])[var_1 = (byte)(short)4.859332921376913E307]); + } + double var_6; + var_6 = 1359078277; + final float var_7 = 3.5952457E37F; + var_5 = ('u' | 9005660398910009344L) << 'j'; + int var_8; + var_5 = (!false || true & !false) && false ? (byte)1836342254 : (byte)1.4836203E38F; + var_1 = (var_1 = (var_1 = (var_1 = (byte)1.5824984701060493E308))); + var_1 = (var_1 = (var_1 = (byte)~ (var_1 = (var_1 = (var_1 = (byte)var_7))))); + return +9.067416E37F <= (true | true ^ false ? (var_1 = (byte)(short)1.5243446E38F) : (var_1 = (byte)1.6893049E37F)) ? (byte)~4408841475280588800L - (var_5 = (var_1 = (byte)2.1542209E38F)) : (var_8 = (short)var_4); + } + + protected final static double func_1(final char arg_0, final long arg_1) + { + var_1 = (short)8779631802405542912L << 'x' <= arg_0 ? (byte)+9.96859509852443E307 : (var_1 = (var_1 = (byte)(short)5.218454879223281E307)); + return 5.57437404144192E307; + } + + double func_2(byte arg_0, final boolean arg_1, Object arg_2) + { + arg_2 = arg_1 != arg_1 ? "wq" : "w"; + arg_2 = arg_2; + if (arg_1) + { + arg_2 = false & arg_1 ? "hasmp" : (arg_2 = arg_2); + } + else + { + arg_2 = "lcquv"; + } + arg_0 -= arg_1 ^ false ? (arg_0 |= (short)arg_0) : (~3462197988186869760L | 7274210797196514304L) % - - + +130998764279904256L; + arg_0 &= (true ? - - ~7861994999369861120L << 'l' : 'c') * 1246069704; + return (arg_1 ? 9.311174E37F : 1.7085558737202237E308) * 1168887722; + } + + public String toString() + { + String result = "[\n"; + result += "Tester_Class_0.var_1 = "; result += Tester.Printer.print(var_1); + result += ""; + result += "\n]"; + return result; + } +} + + +final class Tester_Class_1 extends Tester_Class_0 { + static Object var_13; + final static boolean var_14 = false | (false ? false : true); + Object var_15; + static byte var_16; + final long var_17 = (long)(-9.40561658911133E307 - (short)2.2016736E38F) ^ (char)1099667310; + static boolean var_18; + static float var_19; + final static byte var_20 = 123; + static byte var_21 = var_1 = (var_1 = var_20); + final static float var_22 = 1.5415572E38F; + + + public Tester_Class_1() + { + char[][] var_39; + boolean var_40 = false | !var_14; + if (var_14) + { + final String[] var_41 = (new String[var_21][var_20])[var_21 *= var_21]; + var_15 = (new Tester_Class_0[var_20])[var_20]; + --var_21; + int var_42; + } + else + { + var_19 = (short)325110146; + } + var_40 &= true; + var_13 = (((new Tester_Class_1[var_21 |= (new char[var_20])[var_21]])[var_21]).var_15 = (new String[var_21][var_20][var_20])[var_21 >>= (byte)(int)var_22]); + var_15 = "m"; + } + + + + + + protected final static Tester_Class_0 func_0(final char arg_0, boolean arg_1) + { + final short var_23 = false ? (short)2.2956268E38F : var_20; + { + ((new Tester_Class_1[var_21])[var_20]).var_15 = ((new Tester_Class_0[var_20][var_21])[var_21])[var_20]; + } + var_19 = var_23; + { + var_21++; + --var_21; + var_13 = (false ? arg_1 : arg_1) ? "" : "aianteahl"; + arg_1 ^= ! (var_14 ? var_14 : !var_14); + } + (arg_1 ? "rq" : "certd").trim(); + arg_1 ^= 's' < var_22; + var_19 = 'T'; + var_19 = var_14 ? --var_21 : var_20; + var_19 = (var_21 >>>= ~ -1559436447128426496L >> 88912720393932800L) | (new char[var_20][var_21])[var_21][var_20]; + short var_24 = 7601; + if (arg_1) + { + var_13 = (new Tester_Class_0[var_20])[var_21]; + } + else + { + var_19 = var_23; + } + var_19 = var_24; + var_19 = 174274929356416000L; + return arg_1 ? (Tester_Class_0)(new Object[var_20])[var_21 >>>= - ((byte)6471979169965446144L)] : (new Tester_Class_0[var_21])[var_20]; + } + + private static int func_1(final Object arg_0, final boolean arg_1) + { + var_19 = 'N'; + var_13 = "ftspm".toUpperCase(); + var_18 = arg_1 ? !arg_1 : var_14; + var_19 = var_21 % 'j'; + { + var_13 = new short[var_21 >>= 8019540572802872320L]; + } + final Tester_Class_0 var_25 = arg_1 ? ((short)1.3614569631193786E308 >= (short)var_20 ? func_0('O', true) : (Tester_Class_0)arg_0) : func_0('e', false); + "cltpxrg".offsetByCodePoints((new short[var_20])[(byte)'F'] & var_20, 942627356); + final Object var_26 = ((new Tester_Class_1[var_21])[var_20]).var_15 = arg_0; + { + var_21 |= 'H'; + } + var_19 = 4705089801895780352L; + var_19 = (var_18 = arg_1 & false) ? var_20 : (! (~var_21 > var_22) ? (new short[var_20])[var_21] : (short)3904907750551380992L); + var_18 = false; + { + var_18 = "aoy".startsWith("ia", 18060804); + if (true) + { + final short var_27 = 4832; + } + else + { + var_18 = (var_18 = arg_1) ? !false : !var_14; + } + var_18 = (var_18 = var_14); + var_19 = 'L'; + } + func_0((false ? ! ((var_21 -= 4.670301365216022E307) > 1.1839209E37F) : (var_18 = false)) ? 's' : 'R', 'Z' > - ((long)var_21) << 2585724390819764224L & var_25.func_2(var_21, false, var_13 = var_25) != 4918861136400833536L); + double var_28 = 0; + var_21 %= -var_28; + for (byte var_29 = 91; arg_1 && (var_28 < 1 && false); var_19 = var_20) + { + var_19 = (var_18 = arg_1) & (var_18 = false) ? 'm' : '['; + var_28++; + var_18 = var_14; + var_21 += (short)1363703973; + } + var_19 = (var_19 = var_22); + var_18 = (var_18 = false | false ? 1743087391 <= (var_21 >>= 8790741242417599488L) : !arg_1); + var_18 = true | true; + --var_21; + var_18 = !var_14 & false; + "mt".indexOf(var_14 ? new String("fpu") : "awivb", (var_14 ? !true : (var_18 = var_14)) ? + ++var_21 : ~var_20); + return (short)(new float[var_21--])[var_21] & ((var_18 = false) ? (var_21 *= 'N') : var_20 + (short)1680927063794178048L) & 1839004800; + } + + protected static int func_2(Tester_Class_0[][] arg_0) + { + ((new Tester_Class_1[var_20][var_21])[var_20][var_20]).var_15 = ((new int[var_21][var_21][(byte)var_22])[var_21 <<= var_20])[var_20]; + ((new Tester_Class_1[var_20])[var_20]).var_15 = "d"; + int var_30 = 0; + "joxjgpywp".lastIndexOf(1834367264 >> var_21, (byte)7.572305E37F >>> (false ? (short)2.3909862E38F : + - +3939434849912855552L)); + while (var_14 | false ^ var_14 && (var_30 < 1 && true)) + { + var_1 = var_20; + var_30++; + var_13 = new float[var_21][--var_21]; + boolean var_31; + } + var_19 = ((new Tester_Class_1[var_21])[var_20]).var_17 <= (~2158227803735181312L & 6001748808824762368L) ? (short)var_20 : var_20; + var_18 = (var_18 = true); + return (byte)(new short[var_20])[var_20] >>> ((new char[var_21][var_21])[var_21 |= 6074708801143703552L])[var_20]; + } + + private final String func_3(boolean arg_0, short arg_1, short arg_2) + { + var_13 = (Tester_Class_0)((arg_0 ^= arg_0) ? (var_13 = (var_15 = (var_15 = "grfphyrs"))) : (var_13 = new Object[var_21 *= ']'])); + if (true & ! (arg_0 ^= !arg_0 | true)) + { + boolean var_32 = true; + var_19 = --arg_1; + arg_2 <<= var_21; + } + else + { + arg_0 |= false; + } + var_21 >>>= arg_1; + final float var_33 = 2.5500976E38F; + return ""; + } + + private static String func_4(final double arg_0, final Object arg_1, final short[] arg_2, final char arg_3) + { + float var_34; + var_21++; + ((new Tester_Class_1[var_20])[var_20]).var_15 = false ? arg_1 : arg_1; + var_13 = arg_1; + var_19 = var_22; + var_13 = new long[var_21 /= 1038797776 + var_21][--var_21]; + ++var_21; + var_18 = false && false; + var_21--; + "".lastIndexOf("kjro"); + final int var_35 = (var_21 <<= var_21--) * var_21--; + if ("kohilkx".startsWith("gy", var_35)) + { + var_34 = 2.0849673E37F; + } + else + { + double var_36 = arg_0; + } + var_34 = (var_21 /= var_20); + { + func_2(new Tester_Class_0[var_20][var_21]); + var_34 = var_20 * (- ~5805881602002385920L / arg_3) << (short)~8041668398152312832L; + var_13 = (var_13 = "qfwbfdf"); + } + ((new Tester_Class_1[var_20])[var_21 += var_20]).var_15 = false ? func_0(arg_3, var_14) : func_0('J', var_18 = var_14); + var_18 = (var_18 = var_14) & var_14; + if ((new boolean[var_21])[var_21 >>= 121380821]) + { + var_34 = 1382979413; + } + else + { + var_34 = (var_20 & var_20) + (true ? 'I' : arg_3); + } + byte var_37; + ((new Tester_Class_1[var_20][var_21])[var_14 ^ var_14 | !var_14 ? var_20 : var_20][var_21 ^= (short)1692053070 & + ~7232298887878750208L - 1512699919]).var_15 = arg_2; + byte var_38 = 1; + var_38 -= arg_0; + var_34 = arg_3; + return var_14 ? "" : "xgkr".toUpperCase(); + } + + public String toString() + { + String result = "[\n"; + result += "Tester_Class_1.var_1 = "; result += Tester.Printer.print(var_1); + result += "\n"; + result += "Tester_Class_1.var_16 = "; result += Tester.Printer.print(var_16); + result += "\n"; + result += "Tester_Class_1.var_20 = "; result += Tester.Printer.print(var_20); + result += "\n"; + result += "Tester_Class_1.var_21 = "; result += Tester.Printer.print(var_21); + result += "\n"; + result += "Tester_Class_1.var_14 = "; result += Tester.Printer.print(var_14); + result += "\n"; + result += "Tester_Class_1.var_18 = "; result += Tester.Printer.print(var_18); + result += "\n"; + result += "Tester_Class_1.var_17 = "; result += Tester.Printer.print(var_17); + result += "\n"; + result += "Tester_Class_1.var_19 = "; result += Tester.Printer.print(var_19); + result += "\n"; + result += "Tester_Class_1.var_22 = "; result += Tester.Printer.print(var_22); + result += "\n"; + result += "Tester_Class_1.var_13 = "; result += Tester.Printer.print(var_13); + result += "\n"; + result += "Tester_Class_1.var_15 = "; result += Tester.Printer.print(var_15); + result += ""; + result += "\n]"; + return result; + } +} + + +class Tester_Class_2 extends Tester_Class_0 { + final int var_43 = 1600723343; + static long var_44 = ~1297640037857117184L; + static String var_45 = "ejaglds"; + double var_46; + static float var_47 = 7.9423827E37F; + static Tester_Class_1[][] var_48; + + + public Tester_Class_2() + { + var_45 = (var_45 = "nkulkweqt"); + var_47 %= (new char[Tester_Class_1.var_21 >>= (short)Tester_Class_1.var_20])[Tester_Class_1.var_20]; + { + Tester_Class_1.var_18 = Tester_Class_1.var_14; + } + var_47 %= 1.559461406041646E308; + var_44 -= Tester_Class_1.var_21++ & ((new Tester_Class_1[Tester_Class_1.var_20])[Tester_Class_1.var_20]).var_17; + var_44 *= false ? (short)Tester_Class_1.var_20 : (short)var_47; + Tester_Class_1.var_13 = (new Tester_Class_1().var_15 = new char[Tester_Class_1.var_20]); + var_46 = 'i'; + double var_49 = var_46 = false ? (var_47 *= (var_46 = var_43)) : Tester_Class_1.var_20; + var_49 += 'k'; + } + + + + + public String toString() + { + String result = "[\n"; + result += "Tester_Class_2.var_43 = "; result += Tester.Printer.print(var_43); + result += "\n"; + result += "Tester_Class_2.var_48 = "; result += Tester.Printer.print(var_48); + result += "\n"; + result += "Tester_Class_2.var_44 = "; result += Tester.Printer.print(var_44); + result += "\n"; + result += "Tester_Class_2.var_46 = "; result += Tester.Printer.print(var_46); + result += "\n"; + result += "Tester_Class_2.var_47 = "; result += Tester.Printer.print(var_47); + result += "\n"; + result += "Tester_Class_2.var_1 = "; result += Tester.Printer.print(var_1); + result += "\n"; + result += "Tester_Class_2.var_45 = "; result += Tester.Printer.print(var_45); + result += ""; + result += "\n]"; + return result; + } +} + + +class Tester_Class_3 extends Tester_Class_0 { + byte var_50; + int var_51; + static double var_52; + static boolean var_53 = true; + long var_54; + static short var_55; + short var_56; + + + public Tester_Class_3() + { + var_53 |= false; + (Tester_Class_2.var_45 = "gpbcgq").replaceAll("m".concat(Tester_Class_2.var_45 = "q"), Tester_Class_2.var_45).indexOf(Tester_Class_2.var_45 = "d"); + Tester_Class_2.var_45 = Tester_Class_2.var_45; + double var_68 = 0; + Tester_Class_1.var_19 = (var_55 = Tester_Class_1.var_20); + do + { + var_53 ^= 'T' > Tester_Class_1.var_21-- & (var_53 |= Tester_Class_1.var_14); + Tester_Class_2.var_44 >>= (char)3.928497616986412E307; + var_68++; + new Tester_Class_2().func_2(Tester_Class_1.var_20, !var_53 & Tester_Class_1.var_14, Tester_Class_1.var_13 = (Tester_Class_2.var_45 = Tester_Class_2.var_45)); + } while ((((var_56 = (short)1161292485) != 'M' ? var_53 : Tester_Class_1.var_14) ? Tester_Class_1.var_14 ^ true : var_53) && var_68 < 1); + Tester_Class_2.var_45 = Tester_Class_2.var_45; + ((Tester_Class_1)(Tester_Class_1.var_13 = new Tester_Class_2())).var_15 = Tester_Class_2.var_45; + var_55 = func_1() | ((Tester_Class_1.var_18 = var_53) | (var_53 |= Tester_Class_1.var_14) | Tester_Class_1.var_14 | !Tester_Class_1.var_14) || false ? (short)Tester_Class_2.var_44 : (var_56 = (var_56 = (short)'[')); + var_52 = (var_51 = (var_55 = Tester_Class_1.var_20)); + double var_69 = 0; + Tester_Class_2.var_44 |= (Tester_Class_1.var_14 ? (Tester_Class_2)(Tester_Class_1.var_13 = (Tester_Class_2)(Tester_Class_1.var_13 = Tester_Class_2.var_45)) : (Tester_Class_2)(Tester_Class_0)(Tester_Class_1.var_13 = Tester_Class_2.var_45)).var_43; + do + { + var_51 = 495861255; + var_69++; + } while (var_69 < 3); + Tester_Class_2.var_47 -= Tester_Class_1.var_20; + Tester_Class_2.var_47 %= '['; + } + + + + + static Object func_0(final Tester_Class_0 arg_0, String arg_1, final float arg_2, final long arg_3) + { + (!var_53 | (var_53 &= var_53) ^ false ? new Tester_Class_1() : (Tester_Class_1)(new Tester_Class_0[Tester_Class_1.var_21])[Tester_Class_1.var_21]).var_15 = Tester_Class_1.var_14 ? new Tester_Class_1() : new Tester_Class_1(); + Tester_Class_2.var_47 /= !var_53 || var_53 ? (short)(((Tester_Class_2)arg_0).var_46 = (new char[Tester_Class_1.var_21][Tester_Class_1.var_21])[Tester_Class_1.var_20][Tester_Class_1.var_20]) : Tester_Class_1.var_21; + return (new Object[Tester_Class_1.var_21])[Tester_Class_1.var_21]; + } + + boolean func_1() + { + { + Tester_Class_1.var_21 >>= (var_56 = (Tester_Class_1.var_21 |= (Tester_Class_1.var_21 -= Tester_Class_1.var_20))); + Tester_Class_2.var_45 = "w"; + var_51 = Tester_Class_1.var_21; + Object var_57; + ((Tester_Class_2)(Tester_Class_0)((new Object[Tester_Class_1.var_21][Tester_Class_1.var_21])[Tester_Class_1.var_20])[Tester_Class_1.var_20]).var_46 = (var_52 = 1.3957085765622284E308); + } + Tester_Class_1.var_21 &= (var_55 = (byte)(Tester_Class_1.var_14 ? -Tester_Class_1.var_20 : 4290961666344782848L)); + Tester_Class_2.var_45 = Tester_Class_2.var_45; + var_51 = (var_53 ^= ((var_53 &= Tester_Class_1.var_14) ? 'J' : 'M') > (var_56 = Tester_Class_1.var_21)) && (var_53 = Tester_Class_1.var_14) ? (Tester_Class_1.var_21 &= ~Tester_Class_1.var_20) : Tester_Class_1.var_20; + { + final Tester_Class_1 var_58 = (Tester_Class_1)(Tester_Class_0)(Tester_Class_1.var_13 = (new Object[Tester_Class_1.var_21])[Tester_Class_1.var_20]); + Object var_59; + Tester_Class_1.var_21 |= 'X'; + var_53 ^= Tester_Class_1.var_14; + } + int var_60 = 0; + var_53 |= var_53; + for (char var_61 = 'i'; (Tester_Class_1.var_14 ? false : Tester_Class_1.var_14) | (true | Tester_Class_1.var_14) && var_60 < 1; var_53 &= !Tester_Class_1.var_14) + { + var_51 = var_61; + var_60++; + var_61 &= (new short[Tester_Class_1.var_20][Tester_Class_1.var_20])[Tester_Class_1.var_20][Tester_Class_1.var_21]; + Tester_Class_2.var_45 = "vsuy"; + } + Tester_Class_2 var_62 = ((var_53 &= Tester_Class_1.var_14 | Tester_Class_1.var_14 || Tester_Class_1.var_14) ? Tester_Class_1.var_14 : "hgwne".startsWith("etyhd", var_60)) ? (var_53 ? (Tester_Class_2)(Tester_Class_1.var_13 = "uyiaxtqc") : (Tester_Class_2)(Tester_Class_1.var_13 = Tester_Class_2.var_45)) : new Tester_Class_2(); + var_62 = var_62; + float var_63; + Object var_64; + Tester_Class_2.var_44 <<= 'v'; + String var_65; + { + var_51 = Tester_Class_1.var_21; + } + var_55 = true ? (var_56 = Tester_Class_1.var_20) : (var_55 = Tester_Class_1.var_20); + var_56 = Tester_Class_1.var_21; + Tester_Class_1.var_21 |= var_60; + Object var_66; + Tester_Class_2 var_67; + return true & Tester_Class_1.var_14 ^ (false ? var_53 : var_53); + } + + public String toString() + { + String result = "[\n"; + result += "Tester_Class_3.var_51 = "; result += Tester.Printer.print(var_51); + result += "\n"; + result += "Tester_Class_3.var_54 = "; result += Tester.Printer.print(var_54); + result += "\n"; + result += "Tester_Class_3.var_52 = "; result += Tester.Printer.print(var_52); + result += "\n"; + result += "Tester_Class_3.var_55 = "; result += Tester.Printer.print(var_55); + result += "\n"; + result += "Tester_Class_3.var_56 = "; result += Tester.Printer.print(var_56); + result += "\n"; + result += "Tester_Class_3.var_1 = "; result += Tester.Printer.print(var_1); + result += "\n"; + result += "Tester_Class_3.var_50 = "; result += Tester.Printer.print(var_50); + result += "\n"; + result += "Tester_Class_3.var_53 = "; result += Tester.Printer.print(var_53); + result += ""; + result += "\n]"; + return result; + } +} + +public class Tester { + final long var_70 = Tester_Class_2.var_44; + int var_71; + static double var_72; + static short var_73 = (Tester_Class_3.var_53 &= (Tester_Class_3.var_53 ^= Tester_Class_3.var_53)) ? (short)(byte)(Tester_Class_3.var_55 = Tester_Class_1.var_20) : (Tester_Class_3.var_55 = Tester_Class_1.var_20); + final static short var_74 = (Tester_Class_3.var_53 &= Tester_Class_3.var_53) ? (Tester_Class_3.var_53 ? var_73 : var_73++) : (var_73 *= (Tester_Class_1.var_21 |= var_73)); + float var_75; + + + protected final Tester_Class_2 func_0() + { + Tester_Class_1.var_21 ^= ~Tester_Class_1.var_21; + if (false) + { + ((Tester_Class_3)(new Object[Tester_Class_1.var_21])[Tester_Class_1.var_21 -= + + (Tester_Class_2.var_44 >>>= Tester_Class_1.var_21)]).var_50 = (Tester_Class_1.var_21 &= (var_71 = 554295231)); + } + else + { + Tester_Class_2.var_47 += 'H'; + } + final Tester_Class_0 var_76 = ((new Tester_Class_0[Tester_Class_1.var_20][Tester_Class_1.var_21])[Tester_Class_1.var_20])[Tester_Class_1.var_20]; + (Tester_Class_1.var_14 ? (Tester_Class_2)var_76 : (Tester_Class_2)var_76).var_46 = (var_73 %= var_74 / (((new Tester_Class_2[Tester_Class_1.var_20])[Tester_Class_1.var_21 |= Tester_Class_1.var_20]).var_46 = Tester_Class_1.var_22)); + var_73 |= ((Tester_Class_2)(Tester_Class_1.var_13 = var_76)).var_43 | Tester_Class_1.var_20; + return new Tester_Class_2(); + } + + private static Tester_Class_3 func_1(byte arg_0, Tester_Class_1 arg_1, Tester_Class_1 arg_2, final int arg_3) + { + arg_0 <<= '`'; + return false ? (Tester_Class_3)(Tester_Class_0)(arg_1.var_15 = (arg_1 = arg_2)) : (Tester_Class_3)((new Tester_Class_0[Tester_Class_1.var_20][arg_0])[Tester_Class_1.var_20])[Tester_Class_1.var_20]; + } + + public static String execute() + { + try { + Tester t = new Tester(); + try { t.test(); } + catch(Throwable e) { } + try { return t.toString(); } + catch (Throwable e) { return "Error during result conversion to String"; } + } catch (Throwable e) { return "Error during test execution"; } + } + + public static void main(String[] args) + { + for (int i = 0; i < 20000; i++) { + Tester t = new Tester(); + try { t.test(); } + catch(Throwable e) { } + if (t.var_71 != 0 || + t.var_70 != -1297640037857117185L || + t.var_72 != 0.0 || + t.var_75 != 0.0 || + t.var_73 != -1 || + t.var_74 != 15129) { + throw new InternalError("wrong answer"); + } + } + } + + private void test() + { + long var_77 = 0L; + var_73 /= (Tester_Class_2.var_47 = 'D' | 'Q'); + Tester_Class_2.var_47 *= 't'; + while (var_77 < 36) + { + var_73 += Tester_Class_1.var_22; + Tester_Class_2.var_47 += Tester_Class_1.var_20; + var_77++; + Tester_Class_2.var_45 = ""; + Tester_Class_2.var_45 = (Tester_Class_2.var_45 = Tester_Class_2.var_45); + } + if (Tester_Class_3.var_53 |= false) + { + int var_78 = 0; + (false ? "idipdjrln" : "l").startsWith(Tester_Class_2.var_45); + while ((Tester_Class_3.var_53 |= (Tester_Class_3.var_53 &= ! (Tester_Class_1.var_18 = true)) | Tester_Class_3.var_53) && (var_78 < 15 && (Tester_Class_3.var_53 &= Tester_Class_1.var_14))) + { + Tester_Class_2.var_44 <<= 'b'; + var_78++; + var_72 = var_74; + var_71 = (char)6792782617594333184L; + } + float var_79 = Tester_Class_2.var_47 /= 1.5148047552641134E308; + ((new boolean[Tester_Class_1.var_20])[Tester_Class_1.var_21 <= (Tester_Class_1.var_21 -= 9.675021723726166E307) / - + (var_72 = 4.3844763012510596E307) ? (byte)(Tester_Class_2.var_44 += ~Tester_Class_1.var_21) : (Tester_Class_1.var_21 += 1.7430965313164616E308)] ? (Tester_Class_2)(new Tester_Class_1().var_15 = func_0()) : new Tester_Class_2()).var_46 = (var_72 = (Tester_Class_1.var_21 *= 'j')); + Tester_Class_1.var_13 = (new Tester_Class_3[Tester_Class_1.var_21 >>>= var_78][Tester_Class_1.var_21])[Tester_Class_1.var_21][Tester_Class_1.var_20]; + } + else + { + long var_80 = 0L; + ((Tester_Class_2)(Tester_Class_1.var_13 = new long[Tester_Class_1.var_21])).var_46 = 'r'; + do + { + final float var_81 = 7.3633934E37F; + var_80++; + var_73 ^= Tester_Class_2.var_44; + } while (Tester_Class_3.var_53 && var_80 < 4); + Tester_Class_1.var_18 = Tester_Class_2.var_47 >= var_73; + Tester_Class_2.var_45 = "xvodcylp"; + Tester_Class_2.var_45.codePointCount("indreb".charAt(+(new byte[Tester_Class_1.var_20][Tester_Class_1.var_20])[Tester_Class_1.var_21][Tester_Class_1.var_21]) * ~ (Tester_Class_1.var_21 %= (var_71 = --var_73)), ((Tester_Class_3.var_53 ^= Tester_Class_2.var_45.equalsIgnoreCase("rkxwa")) || Tester_Class_2.var_47 <= (Tester_Class_2.var_47 %= -var_80) ? (Tester_Class_1.var_21 ^= var_70) : var_73) & (var_71 = 'k')); + Tester_Class_1.var_13 = ((new long[Tester_Class_1.var_21][Tester_Class_1.var_20][Tester_Class_1.var_21])[Tester_Class_1.var_21])[Tester_Class_1.var_21]; + } + var_73 <<= (Tester_Class_1.var_18 = false) ? 't' : (false ? 'E' : 'u'); + var_73++; + int var_82 = 0; + Tester_Class_1.var_13 = func_1(Tester_Class_1.var_20, new Tester_Class_1(), (new Tester_Class_1[Tester_Class_1.var_21])[Tester_Class_1.var_21], 'M' & var_74); + "gdrlrsubb".substring(12438522, var_82); + Tester_Class_2.var_44 |= (((new Tester_Class_3[Tester_Class_1.var_21][Tester_Class_1.var_21])[Tester_Class_1.var_21 >>= 7993744087962264576L][Tester_Class_1.var_21]).var_51 = Tester_Class_3.var_53 ? 'B' : '['); + final long var_83 = ~ (4544638910183665664L << (((Tester_Class_3)((new Tester_Class_0[Tester_Class_1.var_20][Tester_Class_1.var_21])[Tester_Class_1.var_21])[Tester_Class_1.var_21]).var_56 = (Tester_Class_3.var_53 &= Tester_Class_3.var_53) ? Tester_Class_1.var_21 : Tester_Class_1.var_20)); + Tester_Class_2.var_45 = Tester_Class_2.var_45; + while (var_82 < 2 && Tester_Class_3.var_53 & (Tester_Class_3.var_53 ^= !false)) + { + (Tester_Class_3.var_53 ? "xqeisnyf" : (Tester_Class_2.var_45 = (Tester_Class_2.var_45 = (Tester_Class_2.var_45 = Tester_Class_2.var_45)))).concat(Tester_Class_2.var_45 = "i"); + var_82++; + boolean var_84 = false; + Tester_Class_2.var_45 = Tester_Class_2.var_45; + } + var_71 = ~Tester_Class_2.var_44 != Tester_Class_2.var_44-- ? (var_73 = var_73) : (var_73 >>>= var_73); + char var_85; + Tester_Class_3.var_53 |= (Tester_Class_3.var_53 ^= true); + int var_86 = 0; + Tester_Class_1.var_21 %= (var_73 | (Tester_Class_1.var_21 *= 9.831691E37F)) * (Tester_Class_1.var_21 += 6784278051481715712L); + while (Tester_Class_3.var_53 && (var_86 < 24 && ((((Tester_Class_3.var_53 ^= true) ? Tester_Class_3.var_53 : Tester_Class_1.var_14) ? !Tester_Class_3.var_53 : Tester_Class_3.var_53) ? (Tester_Class_1.var_18 = Tester_Class_3.var_53) : Tester_Class_1.var_14 || true))) + { + final byte var_87 = (byte)((false & true ? Tester_Class_1.var_20 : 257407175) & 4242055901066916864L * (var_73 *= 1621204618) / ((((Tester_Class_1)(new Object[(byte)4.925362697409246E307])[Tester_Class_1.var_21]).var_17 ^ (var_71 = var_86)) & 1859382584)); + var_86++; + Tester_Class_2.var_45 = (Tester_Class_2.var_45 = (Tester_Class_2.var_45 = "arceo")); + float var_88; + } + "a".lastIndexOf(var_71 = Tester_Class_3.var_53 ^ false ? (var_71 = 1058420888) : Tester_Class_1.var_20); + int var_89 = 0; + { + var_71 = 661164411; + } + boolean var_90; + --var_73; + Tester_Class_2.var_45.concat(Tester_Class_2.var_45); + { + var_85 = (Tester_Class_3.var_53 ? Tester_Class_3.var_53 : Tester_Class_3.var_53) ? 'R' : '['; + } + ((new Tester_Class_2[Tester_Class_1.var_21][Tester_Class_1.var_21])[Tester_Class_1.var_20][Tester_Class_1.var_20]).var_46 = Tester_Class_1.var_20; + final float var_91 = ((new Tester_Class_0[Tester_Class_1.var_21][Tester_Class_1.var_21])[Tester_Class_1.var_20][Tester_Class_1.var_21 -= Tester_Class_1.var_21]).equals(((new Tester_Class_1[Tester_Class_1.var_20])[Tester_Class_1.var_21]).var_15 = (Tester_Class_2.var_45 = Tester_Class_2.var_45)) ? (var_71 = Tester_Class_1.var_20) : 2.2259766E38F + Tester_Class_2.var_44; + Tester_Class_2.var_47 *= ((Tester_Class_2)(Tester_Class_0)(Tester_Class_1.var_13 = Tester_Class_2.var_45)).var_43; + Tester_Class_2.var_45 = Tester_Class_2.var_45; + Tester_Class_3.var_53 &= Tester_Class_1.var_14; + while (Tester_Class_1.var_20 >= ++Tester_Class_1.var_21 && var_89 < 2) + { + Tester_Class_1.var_13 = (Tester_Class_3)(new Tester_Class_0[Tester_Class_1.var_21])[Tester_Class_1.var_21]; + var_89++; + if (true) + { + Tester_Class_3.var_53 |= true; + break; + } + else + { + Tester_Class_2 var_92; + } + ((Tester_Class_3)((Tester_Class_3.var_53 |= Tester_Class_3.var_53) ? (new Tester_Class_1().var_15 = (Tester_Class_0)(Tester_Class_1.var_13 = new boolean[Tester_Class_1.var_20][Tester_Class_1.var_21])) : new Tester_Class_0[Tester_Class_1.var_21][Tester_Class_1.var_21])).var_54 = (Tester_Class_1.var_21 = (Tester_Class_1.var_21 /= (Tester_Class_2.var_44 |= (int)(Tester_Class_1.var_21 >>>= var_82)))); + ((Tester_Class_3)(Tester_Class_1.var_13 = (new Tester_Class_1().var_15 = new Tester_Class_1()))).var_51 = Tester_Class_1.var_20; + final char var_93 = 'u'; + ((Tester_Class_2)(new Tester_Class_1().var_15 = (Tester_Class_2.var_45 = Tester_Class_2.var_45))).var_46 = var_93; + Tester_Class_2.var_45.toUpperCase(); + Tester_Class_2.var_45 = "mhk"; + (true | false ? new Tester_Class_1() : (new Tester_Class_1[Tester_Class_1.var_20])[Tester_Class_1.var_20]).var_15 = (Tester_Class_1)(((new Tester_Class_1[Tester_Class_1.var_21 |= Tester_Class_1.var_20][Tester_Class_1.var_21])[Tester_Class_1.var_21][Tester_Class_1.var_21]).var_15 = (Tester_Class_1.var_13 = (Tester_Class_1)(Tester_Class_1.var_13 = (Tester_Class_2.var_45 = "ofkbg")))); + } + float var_94 = 0F; + Tester_Class_2.var_44 |= (var_73 >>>= (var_85 = (var_85 = 'j'))); + Tester_Class_3.var_52 = 1835242863964218368L; + do + { + int var_95 = 1361237611; + var_94++; + Tester_Class_3.var_53 ^= (Tester_Class_3.var_53 |= Tester_Class_1.var_14); + } while (var_94 < 16); + { + var_73 = var_73--; + Tester_Class_2.var_45 = (Tester_Class_1.var_14 ? Tester_Class_1.var_14 : !false) ? "oaxg" : "igdnja"; + } + ((new Tester_Class_1[Tester_Class_1.var_21])[Tester_Class_1.var_21]).equals(new Tester_Class_1().var_15 = (Tester_Class_2.var_45 = "agdnue").charAt(1416972150) != Tester_Class_2.var_47 ? new Tester_Class_1() : new Tester_Class_1()); + byte var_96 = Tester_Class_1.var_21 >>>= (var_85 = (var_85 = '`')); + Tester_Class_2.var_45 = ""; + Tester_Class_2.var_47 += Tester_Class_2.var_47; + Tester_Class_2.var_45 = Tester_Class_2.var_45; + } + public String toString() + { + String result = "[\n"; + result += "Tester.var_71 = "; result += Printer.print(var_71); + result += "\n"; + result += "Tester.var_70 = "; result += Printer.print(var_70); + result += "\n"; + result += "Tester.var_72 = "; result += Printer.print(var_72); + result += "\n"; + result += "Tester.var_75 = "; result += Printer.print(var_75); + result += "\n"; + result += "Tester.var_73 = "; result += Printer.print(var_73); + result += "\n"; + result += "Tester.var_74 = "; result += Printer.print(var_74); + result += ""; + result += "\n]"; + return result; + } + static class Printer + { + public static String print(boolean arg) { return String.valueOf(arg); } + public static String print(byte arg) { return String.valueOf(arg); } + public static String print(short arg) { return String.valueOf(arg); } + public static String print(char arg) { return String.valueOf((int)arg); } + public static String print(int arg) { return String.valueOf(arg); } + public static String print(long arg) { return String.valueOf(arg); } + public static String print(float arg) { return String.valueOf(arg); } + public static String print(double arg) { return String.valueOf(arg); } + + + public static String print(Object arg) + { + return print_r(new java.util.Stack(), arg); + } + + private static String print_r(java.util.Stack visitedObjects, Object arg) + { + String result = ""; + if (arg == null) + result += "null"; + else + if (arg.getClass().isArray()) + { + for (int i = 0; i < visitedObjects.size(); i++) + if (visitedObjects.elementAt(i) == arg) return ""; + + visitedObjects.push(arg); + + final String delimiter = ", "; + result += "["; + + if (arg instanceof Object[]) + { + Object[] array = (Object[]) arg; + for (int i = 0; i < array.length; i++) + { + result += print_r(visitedObjects, array[i]); + if (i < array.length - 1) result += delimiter; + } + } + else + if (arg instanceof boolean[]) + { + boolean[] array = (boolean[]) arg; + for (int i = 0; i < array.length; i++) + { + result += print(array[i]); + if (i < array.length - 1) result += delimiter; + } + } + else + if (arg instanceof byte[]) + { + byte[] array = (byte[]) arg; + for (int i = 0; i < array.length; i++) + { + result += print(array[i]); + if (i < array.length - 1) result += delimiter; + } + } + else + if (arg instanceof short[]) + { + short[] array = (short[]) arg; + for (int i = 0; i < array.length; i++) + { + result += print(array[i]); + if (i < array.length - 1) result += delimiter; + } + } + else + if (arg instanceof char[]) + { + char[] array = (char[]) arg; + for (int i = 0; i < array.length; i++) + { + result += print(array[i]); + if (i < array.length - 1) result += delimiter; + } + } + else + if (arg instanceof int[]) + { + int[] array = (int[]) arg; + for (int i = 0; i < array.length; i++) + { + result += print(array[i]); + if (i < array.length - 1) result += delimiter; + } + } + else + if (arg instanceof long[]) + { + long[] array = (long[]) arg; + for (int i = 0; i < array.length; i++) + { + result += print(array[i]); + if (i < array.length - 1) result += delimiter; + } + } + else + if (arg instanceof float[]) + { + float[] array = (float[]) arg; + for (int i = 0; i < array.length; i++) + { + result += print(array[i]); + if (i < array.length - 1) result += delimiter; + } + } + else + if (arg instanceof double[]) + { + double[] array = (double[]) arg; + for (int i = 0; i < array.length; i++) + { + result += print(array[i]); + if (i < array.length - 1) result += delimiter; + } + } + + result += "]"; + visitedObjects.pop(); + + } else + { + result += arg.toString(); + } + + return result; + } + } +} + +