Merge
This commit is contained in:
commit
f784be24d1
hotspot
.hgignore
build
linux
solaris
makefiles
platform_amd64platform_amd64.gccplatform_i486platform_i486.gccplatform_sparcplatform_sparc.gccplatform_sparcv9platform_sparcv9.gccwindows
src
cpu
sparc/vm
x86/vm
os
os_cpu
share
tools/hsdis
vm
asm
ci
classfile
code
compiler
gc_implementation/parallelScavenge
includeDB_compiler1includeDB_corememory
oops
opto
addnode.cppcompile.cppescape.cppescape.hpploopUnswitch.cpploopopts.cppmemnode.cppparse3.cppsuperword.cpp
prims
runtime
deoptimization.cppdeoptimization.hppfprofiler.cppfprofiler.hppframe.hppglobals.cppglobals.hppos.hppstubCodeGenerator.cppvframe.cppvframe.hppvframe_hp.cpp
utilities
test/compiler/6646020
@ -168,3 +168,4 @@
|
||||
^build/linux/export-linux-x64/
|
||||
^dist/
|
||||
^nbproject/private/
|
||||
^src/share/tools/hsdis/bin/
|
||||
|
@ -38,6 +38,7 @@ include $(GAMMADIR)/make/scm.make
|
||||
|
||||
NAWK = awk
|
||||
RM = rm -f
|
||||
HG = hg
|
||||
CS_TOP = ../..
|
||||
|
||||
CSDIRS = $(CS_TOP)/src $(CS_TOP)/build
|
||||
@ -140,13 +141,17 @@ TAGS.clean: nametable.clean
|
||||
|
||||
# .nametable.files and .nametable.files.tmp are used to determine if any files
|
||||
# were added to/deleted from/renamed in the workspace. If not, then there's
|
||||
# normally no need to run find. To force a 'find': gmake nametable.clean.
|
||||
# normally no need to rebuild the cscope database. To force a rebuild of
|
||||
# the cscope database: gmake nametable.clean.
|
||||
.nametable.files: .nametable.files.tmp
|
||||
cmp -s $@ $< || cp $< $@
|
||||
( cmp -s $@ $< ) || ( cp $< $@ )
|
||||
-$(RM) $<
|
||||
|
||||
.nametable.files.tmp: $(CS_TOP)/Codemgr_wsdata/nametable
|
||||
$(NAWK) \
|
||||
'{ if (sub("( [a-z0-9]{2,8}){4}$$", "")) print $$0; }' $< > $@
|
||||
# `hg status' is slightly faster than `hg fstatus'. Both are
|
||||
# quite a bit slower on an NFS mounted file system, so this is
|
||||
# really geared towards repos on local file systems.
|
||||
.nametable.files.tmp:
|
||||
-$(HG) fstatus -acmn > $@
|
||||
|
||||
nametable.clean:
|
||||
-$(RM) .nametable.files .nametable.files.tmp
|
||||
|
@ -71,6 +71,7 @@ endif
|
||||
# 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 @@ CPPFLAGS = \
|
||||
${BUILD_VERSION} \
|
||||
${BUILD_TARGET} \
|
||||
${BUILD_USER} \
|
||||
${HS_LIB_ARCH} \
|
||||
${JRE_VERSION} \
|
||||
${VM_DISTRO}
|
||||
|
||||
|
@ -12,6 +12,4 @@ lib_arch = amd64
|
||||
|
||||
compiler = gcc
|
||||
|
||||
gnu_dis_arch = amd64
|
||||
|
||||
sysdefs = -DLINUX -D_GNU_SOURCE -DAMD64
|
||||
|
@ -12,6 +12,4 @@ lib_arch = i386
|
||||
|
||||
compiler = gcc
|
||||
|
||||
gnu_dis_arch = i386
|
||||
|
||||
sysdefs = -DLINUX -D_GNU_SOURCE -DIA32
|
||||
|
@ -12,6 +12,4 @@ lib_arch = sparc
|
||||
|
||||
compiler = gcc
|
||||
|
||||
gnu_dis_arch = sparc
|
||||
|
||||
sysdefs = -DLINUX -D_GNU_SOURCE -DSPARC
|
||||
|
@ -38,6 +38,7 @@ include $(GAMMADIR)/make/scm.make
|
||||
|
||||
NAWK = /usr/xpg4/bin/awk
|
||||
RM = rm -f
|
||||
HG = hg
|
||||
CS_TOP = ../..
|
||||
|
||||
CSDIRS = $(CS_TOP)/src $(CS_TOP)/build
|
||||
@ -141,13 +142,17 @@ TAGS.clean: nametable.clean
|
||||
|
||||
# .nametable.files and .nametable.files.tmp are used to determine if any files
|
||||
# were added to/deleted from/renamed in the workspace. If not, then there's
|
||||
# normally no need to run find. To force a 'find': gmake nametable.clean.
|
||||
# normally no need to rebuild the cscope database. To force a rebuild of
|
||||
# the cscope database: gmake nametable.clean.
|
||||
.nametable.files: .nametable.files.tmp
|
||||
cmp -s $@ $< || cp $< $@
|
||||
( cmp -s $@ $< ) || ( cp $< $@ )
|
||||
-$(RM) $<
|
||||
|
||||
.nametable.files.tmp: $(CS_TOP)/Codemgr_wsdata/nametable
|
||||
$(NAWK) \
|
||||
'{ if (sub("( [a-z0-9]{2,8}){4}$$", "")) print $$0; }' $< > $@
|
||||
# `hg status' is slightly faster than `hg fstatus'. Both are
|
||||
# quite a bit slower on an NFS mounted file system, so this is
|
||||
# really geared towards repos on local file systems.
|
||||
.nametable.files.tmp:
|
||||
-$(HG) fstatus -acmn > $@
|
||||
|
||||
nametable.clean:
|
||||
-$(RM) .nametable.files .nametable.files.tmp
|
||||
|
@ -63,6 +63,7 @@ endif
|
||||
# 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 @@ CPPFLAGS = \
|
||||
${BUILD_VERSION} \
|
||||
${BUILD_TARGET} \
|
||||
${BUILD_USER} \
|
||||
${HS_LIB_ARCH} \
|
||||
${JRE_VERSION} \
|
||||
${VM_DISTRO}
|
||||
|
||||
|
@ -12,6 +12,4 @@ lib_arch = amd64
|
||||
|
||||
compiler = sparcWorks
|
||||
|
||||
gnu_dis_arch = amd64
|
||||
|
||||
sysdefs = -DSOLARIS -DSPARC_WORKS -DAMD64
|
||||
|
@ -12,6 +12,4 @@ lib_arch = amd64
|
||||
|
||||
compiler = gcc
|
||||
|
||||
gnu_dis_arch = amd64
|
||||
|
||||
sysdefs = -DSOLARIS -D_GNU_SOURCE -DAMD64
|
||||
|
@ -12,6 +12,4 @@ lib_arch = i386
|
||||
|
||||
compiler = sparcWorks
|
||||
|
||||
gnu_dis_arch = i386
|
||||
|
||||
sysdefs = -DSOLARIS -DSPARC_WORKS -DIA32
|
||||
|
@ -12,6 +12,4 @@ lib_arch = i386
|
||||
|
||||
compiler = gcc
|
||||
|
||||
gnu_dis_arch = i386
|
||||
|
||||
sysdefs = -DSOLARIS -D_GNU_SOURCE -DIA32
|
||||
|
@ -12,6 +12,4 @@ lib_arch = sparc
|
||||
|
||||
compiler = sparcWorks
|
||||
|
||||
gnu_dis_arch = sparc
|
||||
|
||||
sysdefs = -DSOLARIS -DSPARC_WORKS -DSPARC
|
||||
|
@ -12,6 +12,4 @@ lib_arch = sparc
|
||||
|
||||
compiler = gcc
|
||||
|
||||
gnu_dis_arch = sparc
|
||||
|
||||
sysdefs = -DSOLARIS -D_GNU_SOURCE -DSPARC
|
||||
|
@ -8,10 +8,8 @@ os_arch = solaris_sparc
|
||||
|
||||
os_arch_model = solaris_sparc
|
||||
|
||||
lib_arch = sparc
|
||||
lib_arch = sparcv9
|
||||
|
||||
compiler = sparcWorks
|
||||
|
||||
gnu_dis_arch = sparc
|
||||
|
||||
sysdefs = -DSOLARIS -DSPARC_WORKS -DSPARC
|
||||
|
@ -8,10 +8,8 @@ os_arch = solaris_sparc
|
||||
|
||||
os_arch_model = solaris_sparc
|
||||
|
||||
lib_arch = sparc
|
||||
lib_arch = sparcv9
|
||||
|
||||
compiler = gcc
|
||||
|
||||
gnu_dis_arch = sparc
|
||||
|
||||
sysdefs = -DSOLARIS -D_GNU_SOURCE -DSPARC
|
||||
|
@ -58,6 +58,7 @@ CPP_FLAGS=$(CPP_FLAGS) /D "COMPILER1" /D "COMPILER2"
|
||||
# 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)\""
|
||||
|
@ -10,6 +10,6 @@ os_arch = windows_x86
|
||||
|
||||
os_arch_model = windows_x86_64
|
||||
|
||||
compiler = visCPP
|
||||
lib_arch = amd64
|
||||
|
||||
gnu_dis_arch = amd64
|
||||
compiler = visCPP
|
||||
|
@ -10,7 +10,6 @@ os_arch = windows_x86
|
||||
|
||||
os_arch_model = windows_x86_32
|
||||
|
||||
lib_arch = i386
|
||||
|
||||
compiler = visCPP
|
||||
|
||||
gnu_dis_arch = i386
|
||||
|
||||
|
@ -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
|
@ -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");
|
||||
}
|
||||
|
@ -157,22 +157,158 @@ void RegisterMap::shift_individual_registers() {
|
||||
check_location_valid();
|
||||
}
|
||||
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
return true;
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
}
|
||||
|
||||
// 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 @@ void frame::pd_gc_epilog() {
|
||||
}
|
||||
|
||||
|
||||
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 @@ bool frame::is_interpreted_frame_valid() const {
|
||||
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 @@ bool frame::is_interpreted_frame_valid() const {
|
||||
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;
|
||||
}
|
||||
|
@ -6023,7 +6023,7 @@ instruct cmovII_imm(cmpOp cmp, flagsReg icc, iRegI dst, immI11 src) %{
|
||||
ins_pipe(ialu_imm);
|
||||
%}
|
||||
|
||||
instruct cmovII_U_reg(cmpOp cmp, flagsRegU icc, iRegI dst, iRegI src) %{
|
||||
instruct cmovII_U_reg(cmpOpU cmp, flagsRegU icc, iRegI dst, iRegI src) %{
|
||||
match(Set dst (CMoveI (Binary cmp icc) (Binary dst src)));
|
||||
ins_cost(150);
|
||||
size(4);
|
||||
@ -6032,7 +6032,7 @@ instruct cmovII_U_reg(cmpOp cmp, flagsRegU icc, iRegI dst, iRegI src) %{
|
||||
ins_pipe(ialu_reg);
|
||||
%}
|
||||
|
||||
instruct cmovII_U_imm(cmpOp cmp, flagsRegU icc, iRegI dst, immI11 src) %{
|
||||
instruct cmovII_U_imm(cmpOpU cmp, flagsRegU icc, iRegI dst, immI11 src) %{
|
||||
match(Set dst (CMoveI (Binary cmp icc) (Binary dst src)));
|
||||
ins_cost(140);
|
||||
size(4);
|
||||
|
@ -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
|
@ -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 "";
|
||||
}
|
||||
|
@ -37,39 +37,181 @@ bool frame::safe_for_sender(JavaThread *thread) {
|
||||
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 @@ void frame::pd_gc_epilog() {
|
||||
// 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 @@ bool frame::is_interpreted_frame_valid() const {
|
||||
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;
|
||||
}
|
||||
|
@ -72,15 +72,20 @@ inline frame::frame(intptr_t* sp, intptr_t* fp) {
|
||||
_unextended_sp = sp;
|
||||
_fp = fp;
|
||||
_pc = (address)(sp[-1]);
|
||||
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.
|
||||
// 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);
|
||||
|
||||
_deopt_state = not_deoptimized;
|
||||
if (_cb != NULL && _cb->is_nmethod() && ((nmethod*)_cb)->is_deopt_pc(_pc)) {
|
||||
_pc = (((nmethod*)_cb)->get_original_pc(this));
|
||||
|
@ -1632,7 +1632,7 @@ void TemplateTable::branch(bool is_jsr, bool is_wide) {
|
||||
// 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 @@ void TemplateTable::branch(bool is_jsr, bool is_wide) {
|
||||
__ pushl(rdi);
|
||||
|
||||
// and begin the OSR nmethod
|
||||
__ jmp(Address(rsi, nmethod::osr_entry_point_offset()));
|
||||
__ jmp(Address(rbx, nmethod::osr_entry_point_offset()));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -2480,6 +2480,10 @@ bool os::can_commit_large_page_memory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool os::can_execute_large_page_memory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Reserve memory at an arbitrary address, only if that area is
|
||||
// available (and not reserved for something else).
|
||||
|
||||
|
@ -3089,6 +3089,8 @@ bool os::large_page_init() {
|
||||
if (UseISM) {
|
||||
// ISM disables MPSS to be compatible with old JDK behavior
|
||||
UseMPSS = false;
|
||||
_page_sizes[0] = _large_page_size;
|
||||
_page_sizes[1] = vm_page_size();
|
||||
}
|
||||
|
||||
UseMPSS = UseMPSS &&
|
||||
@ -3178,6 +3180,10 @@ bool os::can_commit_large_page_memory() {
|
||||
return UseISM ? false : true;
|
||||
}
|
||||
|
||||
bool os::can_execute_large_page_memory() {
|
||||
return UseISM ? false : true;
|
||||
}
|
||||
|
||||
static int os_sleep(jlong millis, bool interruptible) {
|
||||
const jlong limit = INT_MAX;
|
||||
jlong prevtime;
|
||||
@ -4385,61 +4391,52 @@ static address resolve_symbol(const char *name) {
|
||||
// threads. Calling thr_setprio is meaningless in this case.
|
||||
//
|
||||
bool isT2_libthread() {
|
||||
int i, rslt;
|
||||
static prheader_t * lwpArray = NULL;
|
||||
static int lwpSize = 0;
|
||||
static int lwpFile = -1;
|
||||
lwpstatus_t * that;
|
||||
int aslwpcount;
|
||||
char lwpName [128];
|
||||
bool isT2 = false;
|
||||
|
||||
#define ADR(x) ((uintptr_t)(x))
|
||||
#define LWPINDEX(ary,ix) ((lwpstatus_t *)(((ary)->pr_entsize * (ix)) + (ADR((ary) + 1))))
|
||||
|
||||
aslwpcount = 0;
|
||||
lwpSize = 16*1024;
|
||||
lwpArray = ( prheader_t *)NEW_C_HEAP_ARRAY (char, lwpSize);
|
||||
lwpFile = open ("/proc/self/lstatus", O_RDONLY, 0);
|
||||
if (lwpArray == NULL) {
|
||||
if ( ThreadPriorityVerbose ) warning ("Couldn't allocate T2 Check array\n");
|
||||
return(isT2);
|
||||
}
|
||||
lwpFile = open("/proc/self/lstatus", O_RDONLY, 0);
|
||||
if (lwpFile < 0) {
|
||||
if ( ThreadPriorityVerbose ) warning ("Couldn't open /proc/self/lstatus\n");
|
||||
return(isT2);
|
||||
if (ThreadPriorityVerbose) warning ("Couldn't open /proc/self/lstatus\n");
|
||||
return false;
|
||||
}
|
||||
lwpSize = 16*1024;
|
||||
for (;;) {
|
||||
lseek (lwpFile, 0, SEEK_SET);
|
||||
rslt = read (lwpFile, lwpArray, lwpSize);
|
||||
if ((lwpArray->pr_nent * lwpArray->pr_entsize) <= lwpSize) {
|
||||
lwpArray = (prheader_t *)NEW_C_HEAP_ARRAY(char, lwpSize);
|
||||
if (read(lwpFile, lwpArray, lwpSize) < 0) {
|
||||
if (ThreadPriorityVerbose) warning("Error reading /proc/self/lstatus\n");
|
||||
break;
|
||||
}
|
||||
if ((lwpArray->pr_nent * lwpArray->pr_entsize) <= lwpSize) {
|
||||
// We got a good snapshot - now iterate over the list.
|
||||
int aslwpcount = 0;
|
||||
for (int i = 0; i < lwpArray->pr_nent; i++ ) {
|
||||
that = LWPINDEX(lwpArray,i);
|
||||
if (that->pr_flags & PR_ASLWP) {
|
||||
aslwpcount++;
|
||||
}
|
||||
}
|
||||
if (aslwpcount == 0) isT2 = true;
|
||||
break;
|
||||
}
|
||||
FREE_C_HEAP_ARRAY(char, lwpArray);
|
||||
lwpSize = lwpArray->pr_nent * lwpArray->pr_entsize;
|
||||
lwpArray = ( prheader_t *)NEW_C_HEAP_ARRAY (char, lwpSize);
|
||||
if (lwpArray == NULL) {
|
||||
if ( ThreadPriorityVerbose ) warning ("Couldn't allocate T2 Check array\n");
|
||||
return(isT2);
|
||||
}
|
||||
FREE_C_HEAP_ARRAY(char, lwpArray); // retry.
|
||||
}
|
||||
|
||||
// We got a good snapshot - now iterate over the list.
|
||||
for (i = 0; i < lwpArray->pr_nent; i++ ) {
|
||||
that = LWPINDEX(lwpArray,i);
|
||||
if (that->pr_flags & PR_ASLWP) {
|
||||
aslwpcount++;
|
||||
}
|
||||
}
|
||||
if ( aslwpcount == 0 ) isT2 = true;
|
||||
|
||||
FREE_C_HEAP_ARRAY(char, lwpArray);
|
||||
close (lwpFile);
|
||||
if ( ThreadPriorityVerbose ) {
|
||||
if ( isT2 ) tty->print_cr("We are running with a T2 libthread\n");
|
||||
if (ThreadPriorityVerbose) {
|
||||
if (isT2) tty->print_cr("We are running with a T2 libthread\n");
|
||||
else tty->print_cr("We are not running with a T2 libthread\n");
|
||||
}
|
||||
return (isT2);
|
||||
return isT2;
|
||||
}
|
||||
|
||||
|
||||
|
@ -2516,9 +2516,13 @@ bool os::can_commit_large_page_memory() {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool os::can_execute_large_page_memory() {
|
||||
return true;
|
||||
}
|
||||
|
||||
char* os::reserve_memory_special(size_t bytes) {
|
||||
DWORD flag = MEM_RESERVE | MEM_COMMIT | MEM_LARGE_PAGES;
|
||||
char * res = (char *)VirtualAlloc(NULL, bytes, flag, PAGE_READWRITE);
|
||||
char * res = (char *)VirtualAlloc(NULL, bytes, flag, PAGE_EXECUTE_READWRITE);
|
||||
return res;
|
||||
}
|
||||
|
||||
|
@ -50,17 +50,6 @@ bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr,
|
||||
// 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 @@ bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr,
|
||||
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 @@ bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr,
|
||||
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;
|
||||
}
|
||||
|
@ -212,7 +212,8 @@ frame os::current_frame() {
|
||||
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);
|
||||
}
|
||||
|
@ -32,49 +32,53 @@ bool JavaThread::pd_get_top_frame_for_signal_handler(frame* fr_addr,
|
||||
|
||||
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.
|
||||
|
||||
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;
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
|
||||
}
|
||||
|
135
hotspot/src/share/tools/hsdis/Makefile
Normal file
135
hotspot/src/share/tools/hsdis/Makefile
Normal file
@ -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)
|
95
hotspot/src/share/tools/hsdis/README
Normal file
95
hotspot/src/share/tools/hsdis/README
Normal file
@ -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.
|
223
hotspot/src/share/tools/hsdis/hsdis-demo.c
Normal file
223
hotspot/src/share/tools/hsdis/hsdis-demo.c
Normal file
@ -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("</"NS_DEMO"%.*s>", 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("/></"NS_DEMO"%.*s>", evlen-1, event+1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (MATCH(event, "insn")) {
|
||||
const char* name = lookup(arg);
|
||||
if (name) printf("%s:\n", name);
|
||||
|
||||
/* basic action for <insn>: */
|
||||
printf(" %p\t", arg);
|
||||
|
||||
} else if (MATCH(event, "/insn")) {
|
||||
/* basic action for </insn>:
|
||||
(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 <addr/>: */
|
||||
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);
|
||||
}
|
499
hotspot/src/share/tools/hsdis/hsdis.c
Normal file
499
hotspot/src/share/tools/hsdis/hsdis.c
Normal file
@ -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 <sysdep.h>
|
||||
#include <libiberty.h>
|
||||
#include <bfd.h>
|
||||
#include <dis-asm.h>
|
||||
|
||||
#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, "</"NS_PFX"%s>", event);
|
||||
} else {
|
||||
/* split out the closing attributes as <dis:foo_done attr='val'/> */
|
||||
int event_prefix = (argp - event);
|
||||
fprintf(fp, "<"NS_PFX"%.*s_done", event_prefix, event);
|
||||
fprintf(fp, argp, arg);
|
||||
fprintf(fp, "/></"NS_PFX"%.*s>", 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=<arch> 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);
|
||||
}
|
67
hotspot/src/share/tools/hsdis/hsdis.h
Normal file
67
hotspot/src/share/tools/hsdis/hsdis.h
Normal file
@ -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:
|
||||
<insn pc='%p'> begin an instruction, at a given location
|
||||
</insn pc='%d'> end an instruction, at a given location
|
||||
<addr value='%p'/> 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);
|
@ -947,6 +947,7 @@ void CodeComments::print_block_comment(outputStream* stream, intptr_t offset) {
|
||||
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();
|
||||
|
@ -1247,8 +1247,14 @@ void BCEscapeAnalyzer::compute_escape_info() {
|
||||
|
||||
initialize();
|
||||
|
||||
// do not scan method if it has no object parameters
|
||||
if (_arg_local.is_empty()) {
|
||||
// Do not scan method if it has no object parameters and
|
||||
// does not returns an object (_return_allocated is set in initialize()).
|
||||
if (_arg_local.is_empty() && !_return_allocated) {
|
||||
// Clear all info since method's bytecode was not analysed and
|
||||
// set pessimistic escape information.
|
||||
clear_escape_info();
|
||||
methodData()->set_eflag(methodDataOopDesc::allocated_escapes);
|
||||
methodData()->set_eflag(methodDataOopDesc::unknown_modified);
|
||||
methodData()->set_eflag(methodDataOopDesc::estimated);
|
||||
return;
|
||||
}
|
||||
@ -1259,45 +1265,8 @@ void BCEscapeAnalyzer::compute_escape_info() {
|
||||
success = do_analysis();
|
||||
}
|
||||
|
||||
// dump result of bytecode analysis
|
||||
#ifndef PRODUCT
|
||||
if (BCEATraceLevel >= 3) {
|
||||
tty->print("[EA] estimated escape information for");
|
||||
if (iid != vmIntrinsics::_none)
|
||||
tty->print(" intrinsic");
|
||||
method()->print_short_name();
|
||||
tty->print_cr(has_dependencies() ? " (not stored)" : "");
|
||||
tty->print(" non-escaping args: ");
|
||||
_arg_local.print_on(tty);
|
||||
tty->print(" stack-allocatable args: ");
|
||||
_arg_stack.print_on(tty);
|
||||
if (_return_local) {
|
||||
tty->print(" returned args: ");
|
||||
_arg_returned.print_on(tty);
|
||||
} else if (is_return_allocated()) {
|
||||
tty->print_cr(" allocated return values");
|
||||
} else {
|
||||
tty->print_cr(" non-local return values");
|
||||
}
|
||||
tty->print(" modified args: ");
|
||||
for (int i = 0; i < _arg_size; i++) {
|
||||
if (_arg_modified[i] == 0)
|
||||
tty->print(" 0");
|
||||
else
|
||||
tty->print(" 0x%x", _arg_modified[i]);
|
||||
}
|
||||
tty->cr();
|
||||
tty->print(" flags: ");
|
||||
if (_unknown_modified)
|
||||
tty->print(" unknown_modified");
|
||||
if (_return_allocated)
|
||||
tty->print(" return_allocated");
|
||||
tty->cr();
|
||||
}
|
||||
|
||||
#endif
|
||||
// don't store interprocedural escape information if it introduces dependencies
|
||||
// or if method data is empty
|
||||
// don't store interprocedural escape information if it introduces
|
||||
// dependencies or if method data is empty
|
||||
//
|
||||
if (!has_dependencies() && !methodData()->is_empty()) {
|
||||
for (i = 0; i < _arg_size; i++) {
|
||||
@ -1316,6 +1285,15 @@ void BCEscapeAnalyzer::compute_escape_info() {
|
||||
if (_return_local) {
|
||||
methodData()->set_eflag(methodDataOopDesc::return_local);
|
||||
}
|
||||
if (_return_allocated) {
|
||||
methodData()->set_eflag(methodDataOopDesc::return_allocated);
|
||||
}
|
||||
if (_allocated_escapes) {
|
||||
methodData()->set_eflag(methodDataOopDesc::allocated_escapes);
|
||||
}
|
||||
if (_unknown_modified) {
|
||||
methodData()->set_eflag(methodDataOopDesc::unknown_modified);
|
||||
}
|
||||
methodData()->set_eflag(methodDataOopDesc::estimated);
|
||||
}
|
||||
}
|
||||
@ -1331,33 +1309,47 @@ void BCEscapeAnalyzer::read_escape_info() {
|
||||
_arg_modified[i] = methodData()->arg_modified(i);
|
||||
}
|
||||
_return_local = methodData()->eflag_set(methodDataOopDesc::return_local);
|
||||
|
||||
// dump result of loaded escape information
|
||||
#ifndef PRODUCT
|
||||
if (BCEATraceLevel >= 4) {
|
||||
tty->print(" non-escaping args: ");
|
||||
_arg_local.print_on(tty);
|
||||
tty->print(" stack-allocatable args: ");
|
||||
_arg_stack.print_on(tty);
|
||||
if (_return_local) {
|
||||
tty->print(" returned args: ");
|
||||
_arg_returned.print_on(tty);
|
||||
} else {
|
||||
tty->print_cr(" non-local return values");
|
||||
}
|
||||
tty->print(" modified args: ");
|
||||
for (int i = 0; i < _arg_size; i++) {
|
||||
if (_arg_modified[i] == 0)
|
||||
tty->print(" 0");
|
||||
else
|
||||
tty->print(" 0x%x", _arg_modified[i]);
|
||||
}
|
||||
tty->cr();
|
||||
}
|
||||
#endif
|
||||
_return_allocated = methodData()->eflag_set(methodDataOopDesc::return_allocated);
|
||||
_allocated_escapes = methodData()->eflag_set(methodDataOopDesc::allocated_escapes);
|
||||
_unknown_modified = methodData()->eflag_set(methodDataOopDesc::unknown_modified);
|
||||
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
void BCEscapeAnalyzer::dump() {
|
||||
tty->print("[EA] estimated escape information for");
|
||||
method()->print_short_name();
|
||||
tty->print_cr(has_dependencies() ? " (not stored)" : "");
|
||||
tty->print(" non-escaping args: ");
|
||||
_arg_local.print_on(tty);
|
||||
tty->print(" stack-allocatable args: ");
|
||||
_arg_stack.print_on(tty);
|
||||
if (_return_local) {
|
||||
tty->print(" returned args: ");
|
||||
_arg_returned.print_on(tty);
|
||||
} else if (is_return_allocated()) {
|
||||
tty->print_cr(" return allocated value");
|
||||
} else {
|
||||
tty->print_cr(" return non-local value");
|
||||
}
|
||||
tty->print(" modified args: ");
|
||||
for (int i = 0; i < _arg_size; i++) {
|
||||
if (_arg_modified[i] == 0)
|
||||
tty->print(" 0");
|
||||
else
|
||||
tty->print(" 0x%x", _arg_modified[i]);
|
||||
}
|
||||
tty->cr();
|
||||
tty->print(" flags: ");
|
||||
if (_return_allocated)
|
||||
tty->print(" return_allocated");
|
||||
if (_allocated_escapes)
|
||||
tty->print(" allocated_escapes");
|
||||
if (_unknown_modified)
|
||||
tty->print(" unknown_modified");
|
||||
tty->cr();
|
||||
}
|
||||
#endif
|
||||
|
||||
BCEscapeAnalyzer::BCEscapeAnalyzer(ciMethod* method, BCEscapeAnalyzer* parent)
|
||||
: _conservative(method == NULL || !EstimateArgEscape)
|
||||
@ -1401,6 +1393,12 @@ BCEscapeAnalyzer::BCEscapeAnalyzer(ciMethod* method, BCEscapeAnalyzer* parent)
|
||||
compute_escape_info();
|
||||
methodData()->update_escape_info();
|
||||
}
|
||||
#ifndef PRODUCT
|
||||
if (BCEATraceLevel >= 3) {
|
||||
// dump escape information
|
||||
dump();
|
||||
}
|
||||
#endif
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -50,9 +50,9 @@ class BCEscapeAnalyzer : public ResourceObj {
|
||||
uint *_arg_modified;
|
||||
|
||||
bool _return_local;
|
||||
bool _return_allocated;
|
||||
bool _allocated_escapes;
|
||||
bool _unknown_modified;
|
||||
bool _return_allocated;
|
||||
|
||||
ciObjectList _dependencies;
|
||||
|
||||
@ -153,4 +153,9 @@ class BCEscapeAnalyzer : public ResourceObj {
|
||||
|
||||
// Copy dependencies from this analysis into "deps"
|
||||
void copy_dependencies(Dependencies *deps);
|
||||
|
||||
#ifndef PRODUCT
|
||||
// dump escape information
|
||||
void dump();
|
||||
#endif
|
||||
};
|
||||
|
@ -1015,7 +1015,6 @@ class BacktraceBuilder: public StackObj {
|
||||
typeArrayOop _bcis;
|
||||
int _index;
|
||||
bool _dirty;
|
||||
bool _done;
|
||||
No_Safepoint_Verifier _nsv;
|
||||
|
||||
public:
|
||||
@ -1029,12 +1028,10 @@ class BacktraceBuilder: public StackObj {
|
||||
};
|
||||
|
||||
// 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() {
|
||||
|
@ -71,7 +71,22 @@ class CodeCache : AllStatic {
|
||||
// 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;
|
||||
}
|
||||
|
||||
|
@ -707,7 +707,9 @@ nmethod::nmethod(
|
||||
" 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 @@ void nmethod::print_on(outputStream* st, const char* title) const {
|
||||
}
|
||||
|
||||
|
||||
#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 @@ void nmethod::print_nmethod(bool printmethod) {
|
||||
xtty->tail("print_nmethod");
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
void nmethod::set_version(int v) {
|
||||
@ -1870,6 +1870,7 @@ void nmethod::check_store() {
|
||||
}
|
||||
}
|
||||
|
||||
#endif // PRODUCT
|
||||
|
||||
// Printing operations
|
||||
|
||||
@ -1948,6 +1949,14 @@ void nmethod::print() const {
|
||||
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_dependencies() {
|
||||
}
|
||||
|
||||
|
||||
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 @@ void nmethod::print_pcs() {
|
||||
}
|
||||
}
|
||||
|
||||
#endif // PRODUCT
|
||||
|
||||
const char* nmethod::reloc_string_for(u_char* begin, u_char* end) {
|
||||
RelocIterator iter(this, begin, end);
|
||||
@ -2055,7 +2058,6 @@ const char* nmethod::reloc_string_for(u_char* begin, u_char* end) {
|
||||
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 @@ void nmethod::print_code_comment_on(outputStream* st, int column, u_char* begin,
|
||||
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 @@ void nmethod::print_code_comment_on(outputStream* st, int column, u_char* begin,
|
||||
}
|
||||
}
|
||||
}
|
||||
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 @@ void nmethod::print_code_comment_on(outputStream* st, int column, u_char* begin,
|
||||
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");
|
||||
}
|
||||
|
@ -485,8 +485,8 @@ class nmethod : public CodeBlob {
|
||||
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 @@ class nmethod : public CodeBlob {
|
||||
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 @@ class nmethod : public CodeBlob {
|
||||
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
|
||||
|
@ -36,7 +36,6 @@ const int VMRegImpl::register_count = ConcreteRegisterImpl::number_of_registers;
|
||||
// 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 @@ void VMRegImpl::print_on(outputStream* st) const {
|
||||
st->print("BAD!");
|
||||
}
|
||||
}
|
||||
#endif // PRODUCT
|
||||
|
@ -96,7 +96,7 @@ public:
|
||||
|
||||
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 @@ public:
|
||||
_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()));
|
||||
}
|
||||
|
||||
|
443
hotspot/src/share/vm/compiler/disassembler.cpp
Normal file
443
hotspot/src/share/vm/compiler/disassembler.cpp
Normal file
@ -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::<unknown> " 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);
|
||||
}
|
59
hotspot/src/share/vm/compiler/disassembler.hpp
Normal file
59
hotspot/src/share/vm/compiler/disassembler.hpp
Normal file
@ -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);
|
||||
};
|
@ -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;
|
||||
};
|
@ -505,8 +505,13 @@ bool OopMap::has_derived_pointer() const {
|
||||
#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 OopMapValue::print_on(outputStream* st) const {
|
||||
|
||||
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 @@ void OopMapSet::print_on(outputStream* st) const {
|
||||
|
||||
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---------------------------
|
||||
|
@ -129,7 +129,7 @@ public:
|
||||
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 @@ class OopMap: public ResourceObj {
|
||||
}
|
||||
|
||||
// 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 @@ class OopMapSet : public ResourceObj {
|
||||
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); }
|
||||
};
|
||||
|
||||
|
@ -41,7 +41,7 @@ ParMarkBitMap::initialize(MemRegion covered_region)
|
||||
|
||||
const size_t rs_align = page_sz == (size_t) os::vm_page_size() ? 0 :
|
||||
MAX2(page_sz, granularity);
|
||||
ReservedSpace rs(bytes, rs_align, false);
|
||||
ReservedSpace rs(bytes, rs_align, rs_align > 0);
|
||||
os::trace_page_sizes("par bitmap", raw_bytes, raw_bytes, page_sz,
|
||||
rs.base(), rs.size());
|
||||
_virtual_space = new PSVirtualSpace(rs, page_sz);
|
||||
|
@ -413,7 +413,7 @@ ParallelCompactData::create_vspace(size_t count, size_t element_size)
|
||||
|
||||
const size_t rs_align = page_sz == (size_t) os::vm_page_size() ? 0 :
|
||||
MAX2(page_sz, granularity);
|
||||
ReservedSpace rs(bytes, rs_align, false);
|
||||
ReservedSpace rs(bytes, rs_align, rs_align > 0);
|
||||
os::trace_page_sizes("par compact", raw_bytes, raw_bytes, page_sz, rs.base(),
|
||||
rs.size());
|
||||
PSVirtualSpace* vspace = new PSVirtualSpace(rs, page_sz);
|
||||
|
@ -323,7 +323,7 @@ c1_Runtime1.cpp collectedHeap.hpp
|
||||
c1_Runtime1.cpp compilationPolicy.hpp
|
||||
c1_Runtime1.cpp compiledIC.hpp
|
||||
c1_Runtime1.cpp copy.hpp
|
||||
c1_Runtime1.cpp disassembler_<arch>.hpp
|
||||
c1_Runtime1.cpp disassembler.hpp
|
||||
c1_Runtime1.cpp events.hpp
|
||||
c1_Runtime1.cpp interfaceSupport.hpp
|
||||
c1_Runtime1.cpp interpreter.hpp
|
||||
|
@ -244,7 +244,7 @@ assembler.hpp vm_version_<arch_model>.hpp
|
||||
|
||||
assembler.inline.hpp assembler.hpp
|
||||
assembler.inline.hpp codeBuffer.hpp
|
||||
assembler.inline.hpp disassembler_<arch>.hpp
|
||||
assembler.inline.hpp disassembler.hpp
|
||||
assembler.inline.hpp threadLocalStorage.hpp
|
||||
|
||||
assembler_<arch_model>.cpp assembler_<arch_model>.inline.hpp
|
||||
@ -951,7 +951,7 @@ codeBlob.cpp allocation.inline.hpp
|
||||
codeBlob.cpp bytecode.hpp
|
||||
codeBlob.cpp codeBlob.hpp
|
||||
codeBlob.cpp codeCache.hpp
|
||||
codeBlob.cpp disassembler_<arch>.hpp
|
||||
codeBlob.cpp disassembler.hpp
|
||||
codeBlob.cpp forte.hpp
|
||||
codeBlob.cpp handles.inline.hpp
|
||||
codeBlob.cpp heap.hpp
|
||||
@ -973,7 +973,7 @@ codeBlob.hpp oopMap.hpp
|
||||
|
||||
codeBuffer.cpp codeBuffer.hpp
|
||||
codeBuffer.cpp copy.hpp
|
||||
codeBuffer.cpp disassembler_<arch>.hpp
|
||||
codeBuffer.cpp disassembler.hpp
|
||||
|
||||
codeBuffer.hpp assembler.hpp
|
||||
codeBuffer.hpp oopRecorder.hpp
|
||||
@ -1328,7 +1328,7 @@ debug.cpp codeCache.hpp
|
||||
debug.cpp collectedHeap.hpp
|
||||
debug.cpp compileBroker.hpp
|
||||
debug.cpp defaultStream.hpp
|
||||
debug.cpp disassembler_<arch>.hpp
|
||||
debug.cpp disassembler.hpp
|
||||
debug.cpp events.hpp
|
||||
debug.cpp frame.hpp
|
||||
debug.cpp heapDumper.hpp
|
||||
@ -1447,7 +1447,7 @@ deoptimization.hpp allocation.hpp
|
||||
deoptimization.hpp frame.inline.hpp
|
||||
|
||||
depChecker_<arch>.cpp depChecker_<arch>.hpp
|
||||
depChecker_<arch>.cpp disassembler_<arch>.hpp
|
||||
depChecker_<arch>.cpp disassembler.hpp
|
||||
depChecker_<arch>.cpp hpi.hpp
|
||||
|
||||
dependencies.cpp ciArrayKlass.hpp
|
||||
@ -1477,21 +1477,21 @@ dictionary.hpp instanceKlass.hpp
|
||||
dictionary.hpp oop.hpp
|
||||
dictionary.hpp systemDictionary.hpp
|
||||
|
||||
disassemblerEnv.hpp globals.hpp
|
||||
disassembler_<arch>.hpp generate_platform_dependent_include
|
||||
|
||||
disassembler_<arch>.cpp cardTableModRefBS.hpp
|
||||
disassembler_<arch>.cpp codeCache.hpp
|
||||
disassembler_<arch>.cpp collectedHeap.hpp
|
||||
disassembler_<arch>.cpp depChecker_<arch>.hpp
|
||||
disassembler_<arch>.cpp disassembler_<arch>.hpp
|
||||
disassembler_<arch>.cpp fprofiler.hpp
|
||||
disassembler_<arch>.cpp handles.inline.hpp
|
||||
disassembler_<arch>.cpp hpi.hpp
|
||||
disassembler_<arch>.cpp stubCodeGenerator.hpp
|
||||
disassembler_<arch>.cpp stubRoutines.hpp
|
||||
disassembler.cpp cardTableModRefBS.hpp
|
||||
disassembler.cpp codeCache.hpp
|
||||
disassembler.cpp collectedHeap.hpp
|
||||
disassembler.cpp depChecker_<arch>.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_<arch>.hpp disassemblerEnv.hpp
|
||||
disassembler_<arch>.hpp os_<os_family>.inline.hpp
|
||||
disassembler.hpp globals.hpp
|
||||
disassembler.hpp os_<os_family>.inline.hpp
|
||||
|
||||
dtraceAttacher.cpp codeCache.hpp
|
||||
dtraceAttacher.cpp deoptimization.hpp
|
||||
@ -2915,7 +2915,7 @@ nmethod.cpp codeCache.hpp
|
||||
nmethod.cpp compileLog.hpp
|
||||
nmethod.cpp compiledIC.hpp
|
||||
nmethod.cpp compilerOracle.hpp
|
||||
nmethod.cpp disassembler_<arch>.hpp
|
||||
nmethod.cpp disassembler.hpp
|
||||
nmethod.cpp dtrace.hpp
|
||||
nmethod.cpp events.hpp
|
||||
nmethod.cpp jvmtiRedefineClassesTrace.hpp
|
||||
@ -3774,7 +3774,7 @@ statSampler.hpp perfData.hpp
|
||||
statSampler.hpp task.hpp
|
||||
|
||||
stubCodeGenerator.cpp assembler_<arch_model>.inline.hpp
|
||||
stubCodeGenerator.cpp disassembler_<arch>.hpp
|
||||
stubCodeGenerator.cpp disassembler.hpp
|
||||
stubCodeGenerator.cpp forte.hpp
|
||||
stubCodeGenerator.cpp oop.inline.hpp
|
||||
stubCodeGenerator.cpp stubCodeGenerator.hpp
|
||||
@ -4541,7 +4541,7 @@ vmreg_<arch>.cpp vmreg.hpp
|
||||
vmreg_<arch>.hpp generate_platform_dependent_include
|
||||
|
||||
vtableStubs.cpp allocation.inline.hpp
|
||||
vtableStubs.cpp disassembler_<arch>.hpp
|
||||
vtableStubs.cpp disassembler.hpp
|
||||
vtableStubs.cpp forte.hpp
|
||||
vtableStubs.cpp handles.inline.hpp
|
||||
vtableStubs.cpp instanceKlass.hpp
|
||||
|
@ -184,7 +184,9 @@ class No_Safepoint_Verifier : public No_GC_Verifier {
|
||||
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++;
|
||||
|
@ -102,8 +102,9 @@ bool CodeHeap::reserve(size_t reserved_size, size_t committed_size,
|
||||
_log2_segment_size = exact_log2(segment_size);
|
||||
|
||||
// Reserve and initialize space for _memory.
|
||||
const size_t page_size = os::page_size_for_region(committed_size,
|
||||
reserved_size, 8);
|
||||
const size_t page_size = os::can_execute_large_page_memory() ?
|
||||
os::page_size_for_region(committed_size, reserved_size, 8) :
|
||||
os::vm_page_size();
|
||||
const size_t granularity = os::vm_allocation_granularity();
|
||||
const size_t r_align = MAX2(page_size, granularity);
|
||||
const size_t r_size = align_size_up(reserved_size, r_align);
|
||||
@ -111,7 +112,7 @@ bool CodeHeap::reserve(size_t reserved_size, size_t committed_size,
|
||||
|
||||
const size_t rs_align = page_size == (size_t) os::vm_page_size() ? 0 :
|
||||
MAX2(page_size, granularity);
|
||||
ReservedSpace rs(r_size, rs_align, false);
|
||||
ReservedSpace rs(r_size, rs_align, rs_align > 0);
|
||||
os::trace_page_sizes("code heap", committed_size, reserved_size, page_size,
|
||||
rs.base(), rs.size());
|
||||
if (!_memory.initialize(rs, c_size)) {
|
||||
|
@ -1253,7 +1253,10 @@ public:
|
||||
// Support for interprocedural escape analysis, from Thomas Kotzmann.
|
||||
enum EscapeFlag {
|
||||
estimated = 1 << 0,
|
||||
return_local = 1 << 1
|
||||
return_local = 1 << 1,
|
||||
return_allocated = 1 << 2,
|
||||
allocated_escapes = 1 << 3,
|
||||
unknown_modified = 1 << 4
|
||||
};
|
||||
|
||||
intx eflags() { return _eflags; }
|
||||
|
@ -888,10 +888,11 @@ bool methodOopDesc::load_signature_classes(methodHandle m, TRAPS) {
|
||||
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;
|
||||
|
@ -70,9 +70,14 @@ static bool commute( Node *add, int con_left, int con_right ) {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
@ -456,7 +456,15 @@ Compile::Compile( ciEnv* ci_env, C2Compiler* compiler, ciMethod* target, int osr
|
||||
}
|
||||
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) {
|
||||
|
@ -51,21 +51,21 @@ void PointsToNode::remove_edge(uint targIdx, PointsToNode::EdgeType et) {
|
||||
}
|
||||
|
||||
#ifndef PRODUCT
|
||||
static char *node_type_names[] = {
|
||||
static const char *node_type_names[] = {
|
||||
"UnknownType",
|
||||
"JavaObject",
|
||||
"LocalVar",
|
||||
"Field"
|
||||
};
|
||||
|
||||
static char *esc_names[] = {
|
||||
static const char *esc_names[] = {
|
||||
"UnknownEscape",
|
||||
"NoEscape",
|
||||
"ArgEscape",
|
||||
"GlobalEscape"
|
||||
};
|
||||
|
||||
static char *edge_type_suffix[] = {
|
||||
static const char *edge_type_suffix[] = {
|
||||
"?", // UnknownEdge
|
||||
"P", // PointsToEdge
|
||||
"D", // DeferredEdge
|
||||
@ -256,39 +256,49 @@ void ConnectionGraph::PointsTo(VectorSet &ptset, Node * n, PhaseTransform *phase
|
||||
}
|
||||
}
|
||||
|
||||
void ConnectionGraph::remove_deferred(uint ni) {
|
||||
VectorSet visited(Thread::current()->resource_area());
|
||||
void ConnectionGraph::remove_deferred(uint ni, GrowableArray<uint>* 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -383,18 +393,25 @@ static Node* get_addp_base(Node *addp) {
|
||||
// | |
|
||||
// AddP ( base == address )
|
||||
//
|
||||
// case #6. Constant Pool or ThreadLocal or Raw object's field reference:
|
||||
// ConP # Object from Constant Pool.
|
||||
// case #6. Constant Pool, ThreadLocal, CastX2P or
|
||||
// Raw object's field reference:
|
||||
// {ConP, ThreadLocal, CastX2P, raw Load}
|
||||
// top |
|
||||
// \ |
|
||||
// AddP ( base == top )
|
||||
//
|
||||
// case #7. Klass's field reference.
|
||||
// LoadKlass
|
||||
// | |
|
||||
// AddP ( base == address )
|
||||
//
|
||||
Node *base = addp->in(AddPNode::Base)->uncast();
|
||||
if (base->is_top()) { // The AddP case #3 and #6.
|
||||
base = addp->in(AddPNode::Address)->uncast();
|
||||
assert(base->Opcode() == Op_ConP || base->Opcode() == Op_ThreadLocal ||
|
||||
base->is_Mem() && base->bottom_type() == TypeRawPtr::NOTNULL ||
|
||||
base->is_Proj() && base->in(0)->is_Allocate(), "sanity");
|
||||
base->Opcode() == Op_CastX2P ||
|
||||
(base->is_Mem() && base->bottom_type() == TypeRawPtr::NOTNULL) ||
|
||||
(base->is_Proj() && base->in(0)->is_Allocate()), "sanity");
|
||||
}
|
||||
return base;
|
||||
}
|
||||
@ -1236,8 +1253,10 @@ void ConnectionGraph::compute_escape() {
|
||||
}
|
||||
|
||||
VectorSet ptset(Thread::current()->resource_area());
|
||||
GrowableArray<Node*> alloc_worklist;
|
||||
GrowableArray<int> worklist;
|
||||
GrowableArray<Node*> alloc_worklist;
|
||||
GrowableArray<int> worklist;
|
||||
GrowableArray<uint> deferred_edges;
|
||||
VectorSet visited(Thread::current()->resource_area());
|
||||
|
||||
// remove deferred edges from the graph and collect
|
||||
// information we will need for type splitting
|
||||
@ -1247,7 +1266,7 @@ void ConnectionGraph::compute_escape() {
|
||||
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.
|
||||
|
@ -269,7 +269,7 @@ private:
|
||||
// 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<uint>* deferred_edges, VectorSet* visited);
|
||||
|
||||
Node_Array _node_map; // used for bookeeping during type splitting
|
||||
// Used for the following purposes:
|
||||
|
@ -51,6 +51,9 @@ bool IdealLoopTree::policy_unswitching( PhaseIdealLoop *phase ) const {
|
||||
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.
|
||||
|
@ -2257,6 +2257,9 @@ bool PhaseIdealLoop::is_valid_clone_loop_form( IdealLoopTree *loop, Node_List& p
|
||||
//
|
||||
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()) {
|
||||
|
@ -1122,6 +1122,12 @@ Node *LoadNode::Ideal(PhaseGVN *phase, bool can_reshape) {
|
||||
}
|
||||
// Split through Phi (see original code in loopopts.cpp).
|
||||
assert(phase->C->have_alias_type(addr_t), "instance should have alias type");
|
||||
|
||||
// Do nothing here if Identity will find a value
|
||||
// (to avoid infinite chain of value phis generation).
|
||||
if ( !phase->eqv(this, this->Identity(phase)) )
|
||||
return NULL;
|
||||
|
||||
const Type* this_type = this->bottom_type();
|
||||
int this_index = phase->C->get_alias_index(addr_t);
|
||||
int this_offset = addr_t->offset();
|
||||
|
@ -408,7 +408,7 @@ void Parse::do_multianewarray() {
|
||||
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;
|
||||
|
@ -65,6 +65,11 @@ void SuperWord::transform_loop(IdealLoopTree* lpt) {
|
||||
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;
|
||||
|
@ -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 @@ class vframeStreamForte : public vframeStreamCommon {
|
||||
};
|
||||
|
||||
|
||||
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
|
||||
vframeStreamForte::vframeStreamForte(JavaThread *jt,
|
||||
frame fr,
|
||||
bool stop_at_java_call_stub) : vframeStreamCommon(jt) {
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// 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;
|
||||
_frame = fr;
|
||||
|
||||
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);
|
||||
}
|
||||
// We must always have a valid frame to start filling
|
||||
|
||||
if (jt->thread_state() == _thread_in_Java && !fr.is_first_frame()) {
|
||||
bool sender_check = false; // assume sender is not safe
|
||||
bool filled_in = fill_from_frame();
|
||||
|
||||
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.
|
||||
assert(filled_in, "invariant");
|
||||
|
||||
// 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;
|
||||
|
||||
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);
|
||||
|
||||
#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
|
||||
|
||||
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 @@ void vframeStreamForte::forte_next() {
|
||||
|
||||
do {
|
||||
|
||||
#if defined(COMPILER1) && defined(SPARC)
|
||||
bool prevIsInterpreted = _frame.is_interpreted_frame();
|
||||
#endif // COMPILER1 && SPARC
|
||||
loop_count++;
|
||||
|
||||
// By the time we get here we should never see unsafe but better
|
||||
// safe then segv'd
|
||||
|
||||
if (loop_count > loop_max || !_frame.safe_for_sender(_thread)) {
|
||||
_mode = at_end_mode;
|
||||
return;
|
||||
}
|
||||
|
||||
_frame = _frame.sender(&_reg_map);
|
||||
|
||||
if (!forte_safe_for_sender(&_frame, _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;
|
||||
|
||||
// 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.
|
||||
|
||||
*is_compiled_p = false;
|
||||
*is_walkable_p = false;
|
||||
static bool is_decipherable_compiled_frame(frame* fr) {
|
||||
|
||||
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;
|
||||
assert(fr->cb() != NULL && fr->cb()->is_nmethod(), "invariant");
|
||||
nmethod* nm = (nmethod*) fr->cb();
|
||||
assert(nm->is_java_method(), "invariant");
|
||||
|
||||
// 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());
|
||||
// First try and find an exact PcDesc
|
||||
|
||||
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) {
|
||||
|
||||
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 && (pc_desc->scope_decode_offset()
|
||||
== DebugInformationRecorder::serialized_null)) {
|
||||
pc_desc = NULL;
|
||||
}
|
||||
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 @@ static void forte_is_walkable_compiled_frame(frame* fr, RegisterMap* map,
|
||||
// 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) {
|
||||
|
||||
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;
|
||||
}
|
||||
static bool find_initial_Java_frame(JavaThread* thread,
|
||||
frame* fr,
|
||||
frame* initial_frame_p,
|
||||
methodOop* method_p,
|
||||
int* bci_p) {
|
||||
|
||||
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;
|
||||
}
|
||||
// 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 (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.
|
||||
// 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.
|
||||
|
||||
int loop_count;
|
||||
int loop_max = MaxJavaStackTraceDepth * 2;
|
||||
|
||||
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
|
||||
if (fr->cb() == NULL) {
|
||||
|
||||
// See if we can find a useful frame
|
||||
int loop_count;
|
||||
int loop_max = MaxJavaStackTraceDepth * 2;
|
||||
RegisterMap map(thread, 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;
|
||||
|
||||
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;
|
||||
}
|
||||
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;
|
||||
}
|
||||
|
||||
// At this point, the candidate doesn't work so try the sender.
|
||||
|
||||
// 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)) {
|
||||
|
||||
#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).
|
||||
|
||||
candidate = frame(candidate.sp(), NULL, candidate.pc());
|
||||
if (!forte_safe_for_sender(&candidate, thread)) {
|
||||
#endif // IA32 || AMD64
|
||||
#endif // COMPILER2
|
||||
|
||||
return false;
|
||||
|
||||
#ifdef COMPILER2
|
||||
#if defined(IA32) || defined(AMD64)
|
||||
} // end forte_safe_for_sender retry with NULL fp
|
||||
#endif // IA32 || AMD64
|
||||
#endif // COMPILER2
|
||||
|
||||
} // end first forte_safe_for_sender check
|
||||
|
||||
if (candidate.is_first_frame()
|
||||
|| is_unknown_compiled_frame(&candidate, thread)) {
|
||||
return false;
|
||||
}
|
||||
} // end for loop_count
|
||||
|
||||
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 (candidate.cb() == NULL) return false;
|
||||
}
|
||||
|
||||
*walkframe_p = candidate;
|
||||
*method_p = method;
|
||||
*bci_p = -1;
|
||||
return true;
|
||||
// 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++) {
|
||||
|
||||
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;
|
||||
}
|
||||
|
||||
if (candidate.is_interpreted_frame()) {
|
||||
if (is_decipherable_interpreted_frame(thread, &candidate, method_p, bci_p)) {
|
||||
*initial_frame_p = candidate;
|
||||
return true;
|
||||
}
|
||||
|
||||
// Hopefully we got some data
|
||||
return false;
|
||||
}
|
||||
|
||||
if (candidate.cb()->is_nmethod()) {
|
||||
|
||||
nmethod* nm = (nmethod*) candidate.cb();
|
||||
*method_p = nm->method();
|
||||
|
||||
// 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;
|
||||
|
||||
*initial_frame_p = candidate;
|
||||
|
||||
// Native wrapper code is trivial to decode by vframeStream
|
||||
|
||||
if (nm->is_native_method()) return true;
|
||||
|
||||
// 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;
|
||||
}
|
||||
|
||||
// is_decipherable_compiled_frame may modify candidate's pc
|
||||
*initial_frame_p = candidate;
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
// Must be some stub frame that we don't care about
|
||||
|
||||
if (!candidate.safe_for_sender(thread)) return false;
|
||||
candidate = candidate.sender(&map);
|
||||
|
||||
// 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;
|
||||
|
||||
}
|
||||
|
||||
return false;
|
||||
|
||||
}
|
||||
|
||||
|
||||
@ -627,10 +365,12 @@ typedef struct {
|
||||
} 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 @@ static void forte_fill_call_trace_given_top(JavaThread* thd,
|
||||
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 @@ static void forte_fill_call_trace_given_top(JavaThread* thd,
|
||||
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 @@ static void forte_fill_call_trace_given_top(JavaThread* thd,
|
||||
|
||||
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 @@ void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext) {
|
||||
// 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 @@ void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext) {
|
||||
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 @@ void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext) {
|
||||
"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 @@ void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext) {
|
||||
|
||||
// param isInJava == false - indicate we aren't in Java code
|
||||
if (!thread->pd_get_top_frame_for_signal_handler(&fr, ucontext, false)) {
|
||||
if (!thread->has_last_Java_frame()) {
|
||||
trace->num_frames = 0; // no Java frames
|
||||
} else {
|
||||
trace->num_frames = -3; // unknown frame
|
||||
}
|
||||
trace->num_frames = ticks_unknown_not_Java; // -3 unknown frame
|
||||
} else {
|
||||
trace->num_frames = -4; // non walkable frame by default
|
||||
forte_fill_call_trace_given_top(thread, trace, depth, fr);
|
||||
if (!thread->has_last_Java_frame()) {
|
||||
trace->num_frames = 0; // No Java frames
|
||||
} else {
|
||||
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");
|
||||
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
@ -845,16 +602,16 @@ void AsyncGetCallTrace(ASGCT_CallTrace *trace, jint depth, void* ucontext) {
|
||||
|
||||
// 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;
|
||||
}
|
||||
}
|
||||
|
@ -143,6 +143,7 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
||||
// relock objects if synchronization on them was eliminated.
|
||||
if (DoEscapeAnalysis) {
|
||||
if (EliminateAllocations) {
|
||||
assert (chunk->at(0)->scope() != NULL,"expect only compiled java frames");
|
||||
GrowableArray<ScopeValue*>* objects = chunk->at(0)->scope()->objects();
|
||||
bool reallocated = false;
|
||||
if (objects != NULL) {
|
||||
@ -162,19 +163,26 @@ Deoptimization::UnrollBlock* Deoptimization::fetch_unroll_info_helper(JavaThread
|
||||
}
|
||||
}
|
||||
if (EliminateLocks) {
|
||||
#ifndef PRODUCT
|
||||
bool first = true;
|
||||
#endif
|
||||
for (int i = 0; i < chunk->length(); i++) {
|
||||
GrowableArray<MonitorValue*>* monitors = chunk->at(i)->scope()->monitors();
|
||||
if (monitors != NULL) {
|
||||
relock_objects(&deoptee, &map, monitors);
|
||||
compiledVFrame* cvf = chunk->at(i);
|
||||
assert (cvf->scope() != NULL,"expect only compiled java frames");
|
||||
GrowableArray<MonitorInfo*>* monitors = cvf->monitors();
|
||||
if (monitors->is_nonempty()) {
|
||||
relock_objects(monitors, thread);
|
||||
#ifndef PRODUCT
|
||||
if (TraceDeoptimization) {
|
||||
ttyLocker ttyl;
|
||||
tty->print_cr("RELOCK OBJECTS in thread " INTPTR_FORMAT, thread);
|
||||
for (int j = 0; j < monitors->length(); j++) {
|
||||
MonitorValue* mv = monitors->at(j);
|
||||
if (mv->eliminated()) {
|
||||
StackValue* owner = StackValue::create_stack_value(&deoptee, &map, mv->owner());
|
||||
tty->print_cr(" object <" INTPTR_FORMAT "> locked", owner->get_obj()());
|
||||
MonitorInfo* mi = monitors->at(j);
|
||||
if (mi->eliminated()) {
|
||||
if (first) {
|
||||
first = false;
|
||||
tty->print_cr("RELOCK OBJECTS in thread " INTPTR_FORMAT, thread);
|
||||
}
|
||||
tty->print_cr(" object <" INTPTR_FORMAT "> locked", mi->owner());
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -799,18 +807,27 @@ void Deoptimization::reassign_fields(frame* fr, RegisterMap* reg_map, GrowableAr
|
||||
|
||||
|
||||
// relock objects for which synchronization was eliminated
|
||||
void Deoptimization::relock_objects(frame* fr, RegisterMap* reg_map, GrowableArray<MonitorValue*>* monitors) {
|
||||
void Deoptimization::relock_objects(GrowableArray<MonitorInfo*>* monitors, JavaThread* thread) {
|
||||
for (int i = 0; i < monitors->length(); i++) {
|
||||
MonitorValue* mv = monitors->at(i);
|
||||
StackValue* owner = StackValue::create_stack_value(fr, reg_map, mv->owner());
|
||||
if (mv->eliminated()) {
|
||||
Handle obj = owner->get_obj();
|
||||
assert(obj.not_null(), "reallocation was missed");
|
||||
BasicLock* lock = StackValue::resolve_monitor_lock(fr, mv->basic_lock());
|
||||
lock->set_displaced_header(obj->mark());
|
||||
obj->set_mark((markOop) lock);
|
||||
MonitorInfo* mon_info = monitors->at(i);
|
||||
if (mon_info->eliminated()) {
|
||||
assert(mon_info->owner() != NULL, "reallocation was missed");
|
||||
Handle obj = Handle(mon_info->owner());
|
||||
markOop mark = obj->mark();
|
||||
if (UseBiasedLocking && mark->has_bias_pattern()) {
|
||||
// New allocated objects may have the mark set to anonymously biased.
|
||||
// Also the deoptimized method may called methods with synchronization
|
||||
// where the thread-local object is bias locked to the current thread.
|
||||
assert(mark->is_biased_anonymously() ||
|
||||
mark->biased_locker() == thread, "should be locked to current thread");
|
||||
// Reset mark word to unbiased prototype.
|
||||
markOop unbiased_prototype = markOopDesc::prototype()->set_age(mark->age());
|
||||
obj->set_mark(unbiased_prototype);
|
||||
}
|
||||
BasicLock* lock = mon_info->lock();
|
||||
ObjectSynchronizer::slow_enter(obj, lock, thread);
|
||||
}
|
||||
assert(owner->get_obj()->is_locked(), "object must be locked now");
|
||||
assert(mon_info->owner()->is_locked(), "object must be locked now");
|
||||
}
|
||||
}
|
||||
|
||||
@ -916,7 +933,7 @@ static void collect_monitors(compiledVFrame* cvf, GrowableArray<Handle>* objects
|
||||
GrowableArray<MonitorInfo*>* monitors = cvf->monitors();
|
||||
for (int i = 0; i < monitors->length(); i++) {
|
||||
MonitorInfo* mon_info = monitors->at(i);
|
||||
if (mon_info->owner() != NULL) {
|
||||
if (mon_info->owner() != NULL && !mon_info->eliminated()) {
|
||||
objects_to_revoke->append(Handle(mon_info->owner()));
|
||||
}
|
||||
}
|
||||
|
@ -105,7 +105,7 @@ class Deoptimization : AllStatic {
|
||||
static void reassign_type_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, typeArrayOop obj, BasicType type);
|
||||
static void reassign_object_array_elements(frame* fr, RegisterMap* reg_map, ObjectValue* sv, objArrayOop obj);
|
||||
static void reassign_fields(frame* fr, RegisterMap* reg_map, GrowableArray<ScopeValue*>* objects);
|
||||
static void relock_objects(frame* fr, RegisterMap* reg_map, GrowableArray<MonitorValue*>* monitors);
|
||||
static void relock_objects(GrowableArray<MonitorInfo*>* monitors, JavaThread* thread);
|
||||
NOT_PRODUCT(static void print_objects(GrowableArray<ScopeValue*>* objects);)
|
||||
#endif // COMPILER2
|
||||
|
||||
|
@ -924,29 +924,23 @@ void FlatProfilerTask::task() {
|
||||
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 @@ void ThreadProfiler::record_tick_for_running_frame(JavaThread* thread, frame fr)
|
||||
// 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 @@ void ThreadProfiler::record_tick_for_calling_frame(JavaThread* thread, frame fr)
|
||||
// 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())) {
|
||||
|
@ -135,7 +135,7 @@ private:
|
||||
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);
|
||||
|
@ -108,7 +108,7 @@ class frame VALUE_OBJ_CLASS_SPEC {
|
||||
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;
|
||||
|
@ -68,18 +68,20 @@ void Flag::print_on(outputStream* st) {
|
||||
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 @@ void Flag::print_as_flag(outputStream* st) {
|
||||
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 {
|
||||
|
@ -668,16 +668,19 @@ class CommandLineFlags {
|
||||
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") \
|
||||
diagnostic(bool, PrintAssembly, false, \
|
||||
"Print assembly code (using external disassembler.so)") \
|
||||
\
|
||||
develop(bool, PrintNMethods, false, \
|
||||
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 @@ class CommandLineFlags {
|
||||
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, \
|
||||
@ -2271,7 +2274,7 @@ class CommandLineFlags {
|
||||
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, \
|
||||
@ -2321,7 +2324,7 @@ class CommandLineFlags {
|
||||
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, \
|
||||
|
@ -228,6 +228,7 @@ class os: AllStatic {
|
||||
static bool large_page_init();
|
||||
static size_t large_page_size();
|
||||
static bool can_commit_large_page_memory();
|
||||
static bool can_execute_large_page_memory();
|
||||
|
||||
// OS interface to polling page
|
||||
static address get_polling_page() { return _polling_page; }
|
||||
|
@ -69,7 +69,6 @@ StubCodeGenerator::StubCodeGenerator(CodeBuffer* code) {
|
||||
_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 @@ extern "C" {
|
||||
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 @@ StubCodeGenerator::~StubCodeGenerator() {
|
||||
tty->cr();
|
||||
}
|
||||
}
|
||||
#endif //PRODUCT
|
||||
}
|
||||
|
||||
|
||||
|
@ -206,7 +206,7 @@ GrowableArray<MonitorInfo*>* interpretedVFrame::monitors() const {
|
||||
for (BasicObjectLock* current = (fr().previous_monitor_in_interpreter_frame(fr().interpreter_frame_monitor_begin()));
|
||||
current >= fr().interpreter_frame_monitor_end();
|
||||
current = fr().previous_monitor_in_interpreter_frame(current)) {
|
||||
result->push(new MonitorInfo(current->obj(), current->lock()));
|
||||
result->push(new MonitorInfo(current->obj(), current->lock(), false));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -230,15 +230,18 @@ class MonitorInfo : public ResourceObj {
|
||||
private:
|
||||
oop _owner; // the object owning the monitor
|
||||
BasicLock* _lock;
|
||||
bool _eliminated;
|
||||
public:
|
||||
// Constructor
|
||||
MonitorInfo(oop owner, BasicLock* lock) {
|
||||
MonitorInfo(oop owner, BasicLock* lock, bool eliminated) {
|
||||
_owner = owner;
|
||||
_lock = lock;
|
||||
_eliminated = eliminated;
|
||||
}
|
||||
// Accessors
|
||||
oop owner() const { return _owner; }
|
||||
BasicLock* lock() const { return _lock; }
|
||||
bool eliminated() const { return _eliminated; }
|
||||
};
|
||||
|
||||
class vframeStreamCommon : StackObj {
|
||||
@ -413,6 +416,48 @@ inline bool vframeStreamCommon::fill_from_frame() {
|
||||
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();
|
||||
|
@ -190,7 +190,7 @@ GrowableArray<MonitorInfo*>* compiledVFrame::monitors() const {
|
||||
// Casting away const
|
||||
frame& fr = (frame&) _fr;
|
||||
MonitorInfo* info = new MonitorInfo(fr.compiled_synchronized_native_monitor_owner(nm),
|
||||
fr.compiled_synchronized_native_monitor(nm));
|
||||
fr.compiled_synchronized_native_monitor(nm), false);
|
||||
monitors->push(info);
|
||||
return monitors;
|
||||
}
|
||||
@ -202,7 +202,7 @@ GrowableArray<MonitorInfo*>* compiledVFrame::monitors() const {
|
||||
for (int index = 0; index < monitors->length(); index++) {
|
||||
MonitorValue* mv = monitors->at(index);
|
||||
StackValue *owner_sv = create_stack_value(mv->owner()); // it is an oop
|
||||
result->push(new MonitorInfo(owner_sv->get_obj()(), resolve_monitor_lock(mv->basic_lock())));
|
||||
result->push(new MonitorInfo(owner_sv->get_obj()(), resolve_monitor_lock(mv->basic_lock()), mv->eliminated()));
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
@ -52,8 +52,9 @@ void outputStream::update_position(const char* s, size_t len) {
|
||||
_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::vprint_cr(const char* format, va_list argptr) {
|
||||
}
|
||||
|
||||
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 @@ void outputStream::put(char ch) {
|
||||
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() {
|
||||
@ -829,7 +855,7 @@ bool networkStream::connect(const char *ip, short port) {
|
||||
server.sin_port = htons(port);
|
||||
|
||||
server.sin_addr.s_addr = inet_addr(ip);
|
||||
if (server.sin_addr.s_addr == (unsigned long)-1) {
|
||||
if (server.sin_addr.s_addr == (uint32_t)-1) {
|
||||
#ifdef _WINDOWS
|
||||
struct hostent* host = hpi::get_host_by_name((char*)ip);
|
||||
#else
|
||||
|
@ -59,6 +59,7 @@ class outputStream : public ResourceObj {
|
||||
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 @@ class outputStream : public ResourceObj {
|
||||
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(); }
|
||||
|
||||
|
886
hotspot/test/compiler/6646020/Tester.java
Normal file
886
hotspot/test/compiler/6646020/Tester.java
Normal file
@ -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 "<recursive>";
|
||||
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
Loading…
x
Reference in New Issue
Block a user