diff options
author | duke <none@none> | 2007-12-01 00:00:00 +0000 |
---|---|---|
committer | duke <none@none> | 2007-12-01 00:00:00 +0000 |
commit | fa6b5a8027b86d2f8a200e72b4ef6a0d3f9189d3 (patch) | |
tree | 8376f6e5c41e70162b5867d9e1fea3f17f540473 /src/share/vm/utilities |
Initial loadjdk7-b24
Diffstat (limited to 'src/share/vm/utilities')
51 files changed, 13561 insertions, 0 deletions
diff --git a/src/share/vm/utilities/accessFlags.cpp b/src/share/vm/utilities/accessFlags.cpp new file mode 100644 index 000000000..3ad8f1f00 --- /dev/null +++ b/src/share/vm/utilities/accessFlags.cpp @@ -0,0 +1,73 @@ +/* + * Copyright 1997-2006 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/_accessFlags.cpp.incl" + + +void AccessFlags::atomic_set_bits(jint bits) { + // Atomically update the flags with the bits given + jint old_flags, new_flags, f; + do { + old_flags = _flags; + new_flags = old_flags | bits; + f = Atomic::cmpxchg(new_flags, &_flags, old_flags); + } while(f != old_flags); +} + +void AccessFlags::atomic_clear_bits(jint bits) { + // Atomically update the flags with the bits given + jint old_flags, new_flags, f; + do { + old_flags = _flags; + new_flags = old_flags & ~bits; + f = Atomic::cmpxchg(new_flags, &_flags, old_flags); + } while(f != old_flags); +} + +#ifndef PRODUCT + +void AccessFlags::print_on(outputStream* st) const { + if (is_public ()) st->print("public " ); + if (is_private ()) st->print("private " ); + if (is_protected ()) st->print("protected " ); + if (is_static ()) st->print("static " ); + if (is_final ()) st->print("final " ); + if (is_synchronized()) st->print("synchronized "); + if (is_volatile ()) st->print("volatile " ); + if (is_transient ()) st->print("transient " ); + if (is_native ()) st->print("native " ); + if (is_interface ()) st->print("interface " ); + if (is_abstract ()) st->print("abstract " ); + if (is_strict ()) st->print("strict " ); + if (is_synthetic ()) st->print("synthetic " ); + if (is_old ()) st->print("{old} " ); + if (is_obsolete ()) st->print("{obsolete} " ); +} + +#endif + +void accessFlags_init() { + assert(sizeof(AccessFlags) == sizeof(jint), "just checking size of flags"); +} diff --git a/src/share/vm/utilities/accessFlags.hpp b/src/share/vm/utilities/accessFlags.hpp new file mode 100644 index 000000000..4562be620 --- /dev/null +++ b/src/share/vm/utilities/accessFlags.hpp @@ -0,0 +1,204 @@ +/* + * 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. + * + */ + +// AccessFlags is an abstraction over Java access flags. + + +enum { + // See jvm.h for shared JVM_ACC_XXX access flags + + // HotSpot-specific access flags + + // flags actually put in .class file + JVM_ACC_WRITTEN_FLAGS = 0x00007FFF, + + // methodOop flags + JVM_ACC_MONITOR_MATCH = 0x10000000, // True if we know that monitorenter/monitorexit bytecodes match + JVM_ACC_HAS_MONITOR_BYTECODES = 0x20000000, // Method contains monitorenter/monitorexit bytecodes + JVM_ACC_HAS_LOOPS = 0x40000000, // Method has loops + JVM_ACC_LOOPS_FLAG_INIT = (int)0x80000000,// The loop flag has been initialized + JVM_ACC_QUEUED = 0x01000000, // Queued for compilation + JVM_ACC_NOT_TIER1_COMPILABLE = 0x04000000, + JVM_ACC_NOT_OSR_COMPILABLE = 0x08000000, + JVM_ACC_HAS_LINE_NUMBER_TABLE = 0x00100000, + JVM_ACC_HAS_CHECKED_EXCEPTIONS = 0x00400000, + JVM_ACC_HAS_JSRS = 0x00800000, + JVM_ACC_IS_OLD = 0x00010000, // RedefineClasses() has replaced this method + JVM_ACC_IS_OBSOLETE = 0x00020000, // RedefineClasses() has made method obsolete + JVM_ACC_IS_PREFIXED_NATIVE = 0x00040000, // JVMTI has prefixed this native method + + // klassOop flags + JVM_ACC_HAS_MIRANDA_METHODS = 0x10000000, // True if this class has miranda methods in it's vtable + JVM_ACC_HAS_VANILLA_CONSTRUCTOR = 0x20000000, // True if klass has a vanilla default constructor + JVM_ACC_HAS_FINALIZER = 0x40000000, // True if klass has a non-empty finalize() method + JVM_ACC_IS_CLONEABLE = (int)0x80000000,// True if klass supports the Clonable interface + JVM_ACC_HAS_FINAL_METHOD = 0x01000000, // True if klass has final method + + // klassOop and methodOop flags + JVM_ACC_HAS_LOCAL_VARIABLE_TABLE= 0x00200000, + + JVM_ACC_PROMOTED_FLAGS = 0x00200000, // flags promoted from methods to the holding klass + + // field flags + // Note: these flags must be defined in the low order 16 bits because + // instanceKlass only stores a ushort worth of information from the + // AccessFlags value. + // These bits must not conflict with any other field-related access flags + // (e.g., ACC_ENUM). + // Note that the class-related ACC_ANNOTATION bit conflicts with these flags. + JVM_ACC_FIELD_ACCESS_WATCHED = 0x00002000, // field access is watched by JVMTI + JVM_ACC_FIELD_MODIFICATION_WATCHED = 0x00008000, // field modification is watched by JVMTI + + // flags accepted by set_field_flags() + JVM_ACC_FIELD_FLAGS = 0x00008000 | JVM_ACC_WRITTEN_FLAGS +}; + + +class AccessFlags VALUE_OBJ_CLASS_SPEC { + friend class VMStructs; + private: + jint _flags; + + public: + // Java access flags + bool is_public () const { return (_flags & JVM_ACC_PUBLIC ) != 0; } + bool is_private () const { return (_flags & JVM_ACC_PRIVATE ) != 0; } + bool is_protected () const { return (_flags & JVM_ACC_PROTECTED ) != 0; } + bool is_static () const { return (_flags & JVM_ACC_STATIC ) != 0; } + bool is_final () const { return (_flags & JVM_ACC_FINAL ) != 0; } + bool is_synchronized() const { return (_flags & JVM_ACC_SYNCHRONIZED) != 0; } + bool is_super () const { return (_flags & JVM_ACC_SUPER ) != 0; } + bool is_volatile () const { return (_flags & JVM_ACC_VOLATILE ) != 0; } + bool is_transient () const { return (_flags & JVM_ACC_TRANSIENT ) != 0; } + bool is_native () const { return (_flags & JVM_ACC_NATIVE ) != 0; } + bool is_interface () const { return (_flags & JVM_ACC_INTERFACE ) != 0; } + bool is_abstract () const { return (_flags & JVM_ACC_ABSTRACT ) != 0; } + bool is_strict () const { return (_flags & JVM_ACC_STRICT ) != 0; } + + // Attribute flags + bool is_synthetic () const { return (_flags & JVM_ACC_SYNTHETIC ) != 0; } + + // methodOop flags + bool is_monitor_matching () const { return (_flags & JVM_ACC_MONITOR_MATCH ) != 0; } + bool has_monitor_bytecodes () const { return (_flags & JVM_ACC_HAS_MONITOR_BYTECODES ) != 0; } + bool has_loops () const { return (_flags & JVM_ACC_HAS_LOOPS ) != 0; } + bool loops_flag_init () const { return (_flags & JVM_ACC_LOOPS_FLAG_INIT ) != 0; } + bool queued_for_compilation () const { return (_flags & JVM_ACC_QUEUED ) != 0; } + bool is_not_tier1_compilable () const { return (_flags & JVM_ACC_NOT_TIER1_COMPILABLE ) != 0; } + bool is_not_osr_compilable () const { return (_flags & JVM_ACC_NOT_OSR_COMPILABLE ) != 0; } + bool has_linenumber_table () const { return (_flags & JVM_ACC_HAS_LINE_NUMBER_TABLE ) != 0; } + bool has_checked_exceptions () const { return (_flags & JVM_ACC_HAS_CHECKED_EXCEPTIONS ) != 0; } + bool has_jsrs () const { return (_flags & JVM_ACC_HAS_JSRS ) != 0; } + bool is_old () const { return (_flags & JVM_ACC_IS_OLD ) != 0; } + bool is_obsolete () const { return (_flags & JVM_ACC_IS_OBSOLETE ) != 0; } + bool is_prefixed_native () const { return (_flags & JVM_ACC_IS_PREFIXED_NATIVE ) != 0; } + + // klassOop flags + bool has_miranda_methods () const { return (_flags & JVM_ACC_HAS_MIRANDA_METHODS ) != 0; } + bool has_vanilla_constructor () const { return (_flags & JVM_ACC_HAS_VANILLA_CONSTRUCTOR) != 0; } + bool has_finalizer () const { return (_flags & JVM_ACC_HAS_FINALIZER ) != 0; } + bool has_final_method () const { return (_flags & JVM_ACC_HAS_FINAL_METHOD ) != 0; } + bool is_cloneable () const { return (_flags & JVM_ACC_IS_CLONEABLE ) != 0; } + // klassOop and methodOop flags + bool has_localvariable_table () const { return (_flags & JVM_ACC_HAS_LOCAL_VARIABLE_TABLE) != 0; } + void set_has_localvariable_table() { atomic_set_bits(JVM_ACC_HAS_LOCAL_VARIABLE_TABLE); } + void clear_has_localvariable_table() { atomic_clear_bits(JVM_ACC_HAS_LOCAL_VARIABLE_TABLE); } + + // field flags + bool is_field_access_watched() const { return (_flags & JVM_ACC_FIELD_ACCESS_WATCHED) != 0; } + bool is_field_modification_watched() const + { return (_flags & JVM_ACC_FIELD_MODIFICATION_WATCHED) != 0; } + + // get .class file flags + jint get_flags () const { return (_flags & JVM_ACC_WRITTEN_FLAGS); } + + // Initialization + void add_promoted_flags(jint flags) { _flags |= (flags & JVM_ACC_PROMOTED_FLAGS); } + void set_field_flags(jint flags) { _flags = (flags & JVM_ACC_FIELD_FLAGS); } + void set_flags(jint flags) { _flags = (flags & JVM_ACC_WRITTEN_FLAGS); } + + void set_queued_for_compilation() { atomic_set_bits(JVM_ACC_QUEUED); } + void clear_queued_for_compilation() { atomic_clear_bits(JVM_ACC_QUEUED); } + + // Atomic update of flags + void atomic_set_bits(jint bits); + void atomic_clear_bits(jint bits); + + private: + friend class methodOopDesc; + friend class Klass; + friend class ClassFileParser; + // the functions below should only be called on the _access_flags inst var directly, + // otherwise they are just changing a copy of the flags + + // attribute flags + void set_is_synthetic() { atomic_set_bits(JVM_ACC_SYNTHETIC); } + + // methodOop flags + void set_monitor_matching() { atomic_set_bits(JVM_ACC_MONITOR_MATCH); } + void set_has_monitor_bytecodes() { atomic_set_bits(JVM_ACC_HAS_MONITOR_BYTECODES); } + void set_has_loops() { atomic_set_bits(JVM_ACC_HAS_LOOPS); } + void set_loops_flag_init() { atomic_set_bits(JVM_ACC_LOOPS_FLAG_INIT); } + void set_not_tier1_compilable() { atomic_set_bits(JVM_ACC_NOT_TIER1_COMPILABLE); } + void set_not_osr_compilable() { atomic_set_bits(JVM_ACC_NOT_OSR_COMPILABLE); } + void set_has_linenumber_table() { atomic_set_bits(JVM_ACC_HAS_LINE_NUMBER_TABLE); } + void set_has_checked_exceptions() { atomic_set_bits(JVM_ACC_HAS_CHECKED_EXCEPTIONS); } + void set_has_jsrs() { atomic_set_bits(JVM_ACC_HAS_JSRS); } + void set_is_old() { atomic_set_bits(JVM_ACC_IS_OLD); } + void set_is_obsolete() { atomic_set_bits(JVM_ACC_IS_OBSOLETE); } + void set_is_prefixed_native() { atomic_set_bits(JVM_ACC_IS_PREFIXED_NATIVE); } + + // klassOop flags + void set_has_vanilla_constructor() { atomic_set_bits(JVM_ACC_HAS_VANILLA_CONSTRUCTOR); } + void set_has_finalizer() { atomic_set_bits(JVM_ACC_HAS_FINALIZER); } + void set_has_final_method() { atomic_set_bits(JVM_ACC_HAS_FINAL_METHOD); } + void set_is_cloneable() { atomic_set_bits(JVM_ACC_IS_CLONEABLE); } + void set_has_miranda_methods() { atomic_set_bits(JVM_ACC_HAS_MIRANDA_METHODS); } + + public: + // field flags + void set_is_field_access_watched(const bool value) + { + if (value) { + atomic_set_bits(JVM_ACC_FIELD_ACCESS_WATCHED); + } else { + atomic_clear_bits(JVM_ACC_FIELD_ACCESS_WATCHED); + } + } + void set_is_field_modification_watched(const bool value) + { + if (value) { + atomic_set_bits(JVM_ACC_FIELD_MODIFICATION_WATCHED); + } else { + atomic_clear_bits(JVM_ACC_FIELD_MODIFICATION_WATCHED); + } + } + + // Conversion + jshort as_short() { return (jshort)_flags; } + jint as_int() { return _flags; } + + // Printing/debugging + void print_on(outputStream* st) const PRODUCT_RETURN; +}; diff --git a/src/share/vm/utilities/array.cpp b/src/share/vm/utilities/array.cpp new file mode 100644 index 000000000..22b452330 --- /dev/null +++ b/src/share/vm/utilities/array.cpp @@ -0,0 +1,88 @@ +/* + * Copyright 2000-2004 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/_array.cpp.incl" + + +#ifdef ASSERT +void ResourceArray::init_nesting() { + _nesting = Thread::current()->resource_area()->nesting(); +} +#endif + + +void ResourceArray::sort(size_t esize, ftype f) { + if (!is_empty()) qsort(_data, length(), esize, f); +} +void CHeapArray::sort(size_t esize, ftype f) { + if (!is_empty()) qsort(_data, length(), esize, f); +} + + +void ResourceArray::expand(size_t esize, int i, int& size) { + // make sure we are expanding within the original resource mark + assert( + _nesting == Thread::current()->resource_area()->nesting(), + "allocating outside original resource mark" + ); + // determine new size + if (size == 0) size = 4; // prevent endless loop + while (i >= size) size *= 2; + // allocate and initialize new data section + void* data = resource_allocate_bytes(esize * size); + memcpy(data, _data, esize * length()); + _data = data; +} + + +void CHeapArray::expand(size_t esize, int i, int& size) { + // determine new size + if (size == 0) size = 4; // prevent endless loop + while (i >= size) size *= 2; + // allocate and initialize new data section + void* data = NEW_C_HEAP_ARRAY(char*, esize * size); + memcpy(data, _data, esize * length()); + FREE_C_HEAP_ARRAY(char*, _data); + _data = data; +} + + +void ResourceArray::remove_at(size_t esize, int i) { + assert(0 <= i && i < length(), "index out of bounds"); + _length--; + void* dst = (char*)_data + i*esize; + void* src = (char*)dst + esize; + size_t cnt = (length() - i)*esize; + memmove(dst, src, cnt); +} + +void CHeapArray::remove_at(size_t esize, int i) { + assert(0 <= i && i < length(), "index out of bounds"); + _length--; + void* dst = (char*)_data + i*esize; + void* src = (char*)dst + esize; + size_t cnt = (length() - i)*esize; + memmove(dst, src, cnt); +} diff --git a/src/share/vm/utilities/array.hpp b/src/share/vm/utilities/array.hpp new file mode 100644 index 000000000..1c229505f --- /dev/null +++ b/src/share/vm/utilities/array.hpp @@ -0,0 +1,264 @@ +/* + * Copyright 2000-2005 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. + * + */ + +// correct linkage required to compile w/o warnings +// (must be on file level - cannot be local) +extern "C" { typedef int (*ftype)(const void*, const void*); } + + +class ResourceArray: public ResourceObj { + protected: + int _length; // the number of array elements + void* _data; // the array memory +#ifdef ASSERT + int _nesting; // the resource area nesting level +#endif + + // creation + ResourceArray() { + _length = 0; + _data = NULL; + DEBUG_ONLY(init_nesting();) + } + + + ResourceArray(size_t esize, int length) { + assert(length >= 0, "illegal length"); + _length = length; + _data = resource_allocate_bytes(esize * length); + DEBUG_ONLY(init_nesting();) + } + +#ifdef ASSERT + void init_nesting(); +#endif + + // helper functions + void sort (size_t esize, ftype f); // sort the array + void expand (size_t esize, int i, int& size);// expand the array to include slot i + void remove_at(size_t esize, int i); // remove the element in slot i + + public: + // standard operations + int length() const { return _length; } + bool is_empty() const { return length() == 0; } +}; + + +class CHeapArray: public CHeapObj { + protected: + int _length; // the number of array elements + void* _data; // the array memory + + // creation + CHeapArray() { + _length = 0; + _data = NULL; + } + + + CHeapArray(size_t esize, int length) { + assert(length >= 0, "illegal length"); + _length = length; + _data = (void*) NEW_C_HEAP_ARRAY(char *, esize * length); + } + +#ifdef ASSERT + void init_nesting(); +#endif + + // helper functions + void sort (size_t esize, ftype f); // sort the array + void expand (size_t esize, int i, int& size);// expand the array to include slot i + void remove_at(size_t esize, int i); // remove the element in slot i + + public: + // standard operations + int length() const { return _length; } + bool is_empty() const { return length() == 0; } +}; + +#define define_generic_array(array_name,element_type, base_class) \ + class array_name: public base_class { \ + protected: \ + typedef element_type etype; \ + enum { esize = sizeof(etype) }; \ + \ + void base_remove_at(size_t size, int i) { base_class::remove_at(size, i); } \ + \ + public: \ + /* creation */ \ + array_name() : base_class() {} \ + array_name(const int length) : base_class(esize, length) {} \ + array_name(const int length, const etype fx) : base_class(esize, length) { \ + for (int i = 0; i < length; i++) ((etype*)_data)[i] = fx; \ + } \ + \ + /* standard operations */ \ + etype& operator [] (const int i) const { \ + assert(0 <= i && i < length(), "index out of bounds"); \ + return ((etype*)_data)[i]; \ + } \ + \ + int index_of(const etype x) const { \ + int i = length(); \ + while (i-- > 0 && ((etype*)_data)[i] != x) ; \ + /* i < 0 || ((etype*)_data)_data[i] == x */ \ + return i; \ + } \ + \ + void sort(int f(etype*, etype*)) { base_class::sort(esize, (ftype)f); } \ + bool contains(const etype x) const { return index_of(x) >= 0; } \ + \ + /* deprecated operations - for compatibility with GrowableArray only */ \ + etype at(const int i) const { return (*this)[i]; } \ + void at_put(const int i, const etype x) { (*this)[i] = x; } \ + etype* adr_at(const int i) { return &(*this)[i]; } \ + int find(const etype x) { return index_of(x); } \ + }; \ + + +#define define_array(array_name,element_type) \ + define_generic_array(array_name, element_type, ResourceArray) + + +#define define_stack(stack_name,array_name) \ + class stack_name: public array_name { \ + protected: \ + int _size; \ + \ + void grow(const int i, const etype fx) { \ + assert(i >= length(), "index too small"); \ + if (i >= size()) expand(esize, i, _size); \ + for (int j = length(); j <= i; j++) ((etype*)_data)[j] = fx; \ + _length = i+1; \ + } \ + \ + public: \ + /* creation */ \ + stack_name() : array_name() { _size = 0; } \ + stack_name(const int size) : array_name(size){ _length = 0; _size = size; } \ + stack_name(const int size, const etype fx) : array_name(size, fx) { _size = size; } \ + \ + /* standard operations */ \ + int size() const { return _size; } \ + \ + void push(const etype x) { \ + if (length() >= size()) expand(esize, length(), _size); \ + ((etype*)_data)[_length++] = x; \ + } \ + \ + etype pop() { \ + assert(!is_empty(), "stack is empty"); \ + return ((etype*)_data)[--_length]; \ + } \ + \ + etype top() const { \ + assert(!is_empty(), "stack is empty"); \ + return ((etype*)_data)[length() - 1]; \ + } \ + \ + void push_all(const stack_name* stack) { \ + const int l = stack->length(); \ + for (int i = 0; i < l; i++) push(((etype*)(stack->_data))[i]); \ + } \ + \ + etype at_grow(const int i, const etype fx) { \ + if (i >= length()) grow(i, fx); \ + return ((etype*)_data)[i]; \ + } \ + \ + void at_put_grow(const int i, const etype x, const etype fx) { \ + if (i >= length()) grow(i, fx); \ + ((etype*)_data)[i] = x; \ + } \ + \ + void truncate(const int length) { \ + assert(0 <= length && length <= this->length(), "illegal length"); \ + _length = length; \ + } \ + \ + void remove_at(int i) { base_remove_at(esize, i); } \ + void remove(etype x) { remove_at(index_of(x)); } \ + \ + /* inserts the given element before the element at index i */ \ + void insert_before(const int i, const etype el) { \ + int len = length(); \ + int new_length = len + 1; \ + if (new_length >= size()) expand(esize, new_length, _size); \ + for (int j = len - 1; j >= i; j--) { \ + ((etype*)_data)[j + 1] = ((etype*)_data)[j]; \ + } \ + _length = new_length; \ + at_put(i, el); \ + } \ + \ + /* inserts contents of the given stack before the element at index i */ \ + void insert_before(const int i, const stack_name *st) { \ + if (st->length() == 0) return; \ + int len = length(); \ + int st_len = st->length(); \ + int new_length = len + st_len; \ + if (new_length >= size()) expand(esize, new_length, _size); \ + int j; \ + for (j = len - 1; j >= i; j--) { \ + ((etype*)_data)[j + st_len] = ((etype*)_data)[j]; \ + } \ + for (j = 0; j < st_len; j++) { \ + ((etype*)_data)[i + j] = ((etype*)st->_data)[j]; \ + } \ + _length = new_length; \ + } \ + \ + /* deprecated operations - for compatibility with GrowableArray only */ \ + int capacity() const { return size(); } \ + void clear() { truncate(0); } \ + void trunc_to(const int length) { truncate(length); } \ + void append(const etype x) { push(x); } \ + void appendAll(const stack_name* stack) { push_all(stack); } \ + etype last() const { return top(); } \ + }; \ + + +#define define_resource_list(element_type) \ + define_generic_array(element_type##Array, element_type, ResourceArray) \ + define_stack(element_type##List, element_type##Array) + +#define define_resource_pointer_list(element_type) \ + define_generic_array(element_type##Array, element_type *, ResourceArray) \ + define_stack(element_type##List, element_type##Array) + +#define define_c_heap_list(element_type) \ + define_generic_array(element_type##Array, element_type, CHeapArray) \ + define_stack(element_type##List, element_type##Array) + +#define define_c_heap_pointer_list(element_type) \ + define_generic_array(element_type##Array, element_type *, CHeapArray) \ + define_stack(element_type##List, element_type##Array) + + +// Arrays for basic types + +define_array(boolArray, bool) define_stack(boolStack, boolArray) +define_array(intArray , int ) define_stack(intStack , intArray ) diff --git a/src/share/vm/utilities/bitMap.cpp b/src/share/vm/utilities/bitMap.cpp new file mode 100644 index 000000000..b1d466b3e --- /dev/null +++ b/src/share/vm/utilities/bitMap.cpp @@ -0,0 +1,572 @@ +/* + * Copyright 1997-2006 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/_bitMap.cpp.incl" + + +BitMap::BitMap(idx_t* map, idx_t size_in_bits) { + assert(size_in_bits >= 0, "just checking"); + _map = map; + _size = size_in_bits; +} + + +BitMap::BitMap(idx_t size_in_bits) { + assert(size_in_bits >= 0, "just checking"); + _size = size_in_bits; + _map = NEW_RESOURCE_ARRAY(idx_t, size_in_words()); +} + + +void BitMap::resize(idx_t size_in_bits) { + assert(size_in_bits >= 0, "just checking"); + size_t old_size_in_words = size_in_words(); + uintptr_t* old_map = map(); + _size = size_in_bits; + size_t new_size_in_words = size_in_words(); + _map = NEW_RESOURCE_ARRAY(idx_t, new_size_in_words); + Copy::disjoint_words((HeapWord*) old_map, (HeapWord*) _map, MIN2(old_size_in_words, new_size_in_words)); + if (new_size_in_words > old_size_in_words) { + clear_range_of_words(old_size_in_words, size_in_words()); + } +} + +// Returns a bit mask for a range of bits [beg, end) within a single word. Each +// bit in the mask is 0 if the bit is in the range, 1 if not in the range. The +// returned mask can be used directly to clear the range, or inverted to set the +// range. Note: end must not be 0. +inline BitMap::idx_t +BitMap::inverted_bit_mask_for_range(idx_t beg, idx_t end) const { + assert(end != 0, "does not work when end == 0"); + assert(beg == end || word_index(beg) == word_index(end - 1), + "must be a single-word range"); + idx_t mask = bit_mask(beg) - 1; // low (right) bits + if (bit_in_word(end) != 0) { + mask |= ~(bit_mask(end) - 1); // high (left) bits + } + return mask; +} + +void BitMap::set_range_within_word(idx_t beg, idx_t end) { + // With a valid range (beg <= end), this test ensures that end != 0, as + // required by inverted_bit_mask_for_range. Also avoids an unnecessary write. + if (beg != end) { + idx_t mask = inverted_bit_mask_for_range(beg, end); + *word_addr(beg) |= ~mask; + } +} + +void BitMap::clear_range_within_word(idx_t beg, idx_t end) { + // With a valid range (beg <= end), this test ensures that end != 0, as + // required by inverted_bit_mask_for_range. Also avoids an unnecessary write. + if (beg != end) { + idx_t mask = inverted_bit_mask_for_range(beg, end); + *word_addr(beg) &= mask; + } +} + +void BitMap::par_put_range_within_word(idx_t beg, idx_t end, bool value) { + assert(value == 0 || value == 1, "0 for clear, 1 for set"); + // With a valid range (beg <= end), this test ensures that end != 0, as + // required by inverted_bit_mask_for_range. Also avoids an unnecessary write. + if (beg != end) { + intptr_t* pw = (intptr_t*)word_addr(beg); + intptr_t w = *pw; + intptr_t mr = (intptr_t)inverted_bit_mask_for_range(beg, end); + intptr_t nw = value ? (w | ~mr) : (w & mr); + while (true) { + intptr_t res = Atomic::cmpxchg_ptr(nw, pw, w); + if (res == w) break; + w = *pw; + nw = value ? (w | ~mr) : (w & mr); + } + } +} + +inline void BitMap::set_large_range_of_words(idx_t beg, idx_t end) { + memset(_map + beg, ~(unsigned char)0, (end - beg) * sizeof(uintptr_t)); +} + +inline void BitMap::clear_large_range_of_words(idx_t beg, idx_t end) { + memset(_map + beg, 0, (end - beg) * sizeof(uintptr_t)); +} + +inline BitMap::idx_t BitMap::word_index_round_up(idx_t bit) const { + idx_t bit_rounded_up = bit + (BitsPerWord - 1); + // Check for integer arithmetic overflow. + return bit_rounded_up > bit ? word_index(bit_rounded_up) : size_in_words(); +} + +void BitMap::set_range(idx_t beg, idx_t end) { + verify_range(beg, end); + + idx_t beg_full_word = word_index_round_up(beg); + idx_t end_full_word = word_index(end); + + if (beg_full_word < end_full_word) { + // The range includes at least one full word. + set_range_within_word(beg, bit_index(beg_full_word)); + set_range_of_words(beg_full_word, end_full_word); + set_range_within_word(bit_index(end_full_word), end); + } else { + // The range spans at most 2 partial words. + idx_t boundary = MIN2(bit_index(beg_full_word), end); + set_range_within_word(beg, boundary); + set_range_within_word(boundary, end); + } +} + +void BitMap::clear_range(idx_t beg, idx_t end) { + verify_range(beg, end); + + idx_t beg_full_word = word_index_round_up(beg); + idx_t end_full_word = word_index(end); + + if (beg_full_word < end_full_word) { + // The range includes at least one full word. + clear_range_within_word(beg, bit_index(beg_full_word)); + clear_range_of_words(beg_full_word, end_full_word); + clear_range_within_word(bit_index(end_full_word), end); + } else { + // The range spans at most 2 partial words. + idx_t boundary = MIN2(bit_index(beg_full_word), end); + clear_range_within_word(beg, boundary); + clear_range_within_word(boundary, end); + } +} + +void BitMap::set_large_range(idx_t beg, idx_t end) { + verify_range(beg, end); + + idx_t beg_full_word = word_index_round_up(beg); + idx_t end_full_word = word_index(end); + + assert(end_full_word - beg_full_word >= 32, + "the range must include at least 32 bytes"); + + // The range includes at least one full word. + set_range_within_word(beg, bit_index(beg_full_word)); + set_large_range_of_words(beg_full_word, end_full_word); + set_range_within_word(bit_index(end_full_word), end); +} + +void BitMap::clear_large_range(idx_t beg, idx_t end) { + verify_range(beg, end); + + idx_t beg_full_word = word_index_round_up(beg); + idx_t end_full_word = word_index(end); + + assert(end_full_word - beg_full_word >= 32, + "the range must include at least 32 bytes"); + + // The range includes at least one full word. + clear_range_within_word(beg, bit_index(beg_full_word)); + clear_large_range_of_words(beg_full_word, end_full_word); + clear_range_within_word(bit_index(end_full_word), end); +} + +void BitMap::at_put(idx_t offset, bool value) { + if (value) { + set_bit(offset); + } else { + clear_bit(offset); + } +} + +// Return true to indicate that this thread changed +// the bit, false to indicate that someone else did. +// In either case, the requested bit is in the +// requested state some time during the period that +// this thread is executing this call. More importantly, +// if no other thread is executing an action to +// change the requested bit to a state other than +// the one that this thread is trying to set it to, +// then the the bit is in the expected state +// at exit from this method. However, rather than +// make such a strong assertion here, based on +// assuming such constrained use (which though true +// today, could change in the future to service some +// funky parallel algorithm), we encourage callers +// to do such verification, as and when appropriate. +bool BitMap::par_at_put(idx_t bit, bool value) { + return value ? par_set_bit(bit) : par_clear_bit(bit); +} + +void BitMap::at_put_grow(idx_t offset, bool value) { + if (offset >= size()) { + resize(2 * MAX2(size(), offset)); + } + at_put(offset, value); +} + +void BitMap::at_put_range(idx_t start_offset, idx_t end_offset, bool value) { + if (value) { + set_range(start_offset, end_offset); + } else { + clear_range(start_offset, end_offset); + } +} + +void BitMap::par_at_put_range(idx_t beg, idx_t end, bool value) { + verify_range(beg, end); + + idx_t beg_full_word = word_index_round_up(beg); + idx_t end_full_word = word_index(end); + + if (beg_full_word < end_full_word) { + // The range includes at least one full word. + par_put_range_within_word(beg, bit_index(beg_full_word), value); + if (value) { + set_range_of_words(beg_full_word, end_full_word); + } else { + clear_range_of_words(beg_full_word, end_full_word); + } + par_put_range_within_word(bit_index(end_full_word), end, value); + } else { + // The range spans at most 2 partial words. + idx_t boundary = MIN2(bit_index(beg_full_word), end); + par_put_range_within_word(beg, boundary, value); + par_put_range_within_word(boundary, end, value); + } + +} + +void BitMap::at_put_large_range(idx_t beg, idx_t end, bool value) { + if (value) { + set_large_range(beg, end); + } else { + clear_large_range(beg, end); + } +} + +void BitMap::par_at_put_large_range(idx_t beg, idx_t end, bool value) { + verify_range(beg, end); + + idx_t beg_full_word = word_index_round_up(beg); + idx_t end_full_word = word_index(end); + + assert(end_full_word - beg_full_word >= 32, + "the range must include at least 32 bytes"); + + // The range includes at least one full word. + par_put_range_within_word(beg, bit_index(beg_full_word), value); + if (value) { + set_large_range_of_words(beg_full_word, end_full_word); + } else { + clear_large_range_of_words(beg_full_word, end_full_word); + } + par_put_range_within_word(bit_index(end_full_word), end, value); +} + +bool BitMap::contains(const BitMap other) const { + assert(size() == other.size(), "must have same size"); + uintptr_t* dest_map = map(); + uintptr_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size_in_words(); index++) { + uintptr_t word_union = dest_map[index] | other_map[index]; + // If this has more bits set than dest_map[index], then other is not a + // subset. + if (word_union != dest_map[index]) return false; + } + return true; +} + +bool BitMap::intersects(const BitMap other) const { + assert(size() == other.size(), "must have same size"); + uintptr_t* dest_map = map(); + uintptr_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size_in_words(); index++) { + if ((dest_map[index] & other_map[index]) != 0) return true; + } + // Otherwise, no intersection. + return false; +} + +void BitMap::set_union(BitMap other) { + assert(size() == other.size(), "must have same size"); + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size_in_words(); index++) { + dest_map[index] = dest_map[index] | other_map[index]; + } +} + + +void BitMap::set_difference(BitMap other) { + assert(size() == other.size(), "must have same size"); + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size_in_words(); index++) { + dest_map[index] = dest_map[index] & ~(other_map[index]); + } +} + + +void BitMap::set_intersection(BitMap other) { + assert(size() == other.size(), "must have same size"); + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size; index++) { + dest_map[index] = dest_map[index] & other_map[index]; + } +} + + +bool BitMap::set_union_with_result(BitMap other) { + assert(size() == other.size(), "must have same size"); + bool changed = false; + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size; index++) { + idx_t temp = map(index) | other_map[index]; + changed = changed || (temp != map(index)); + map()[index] = temp; + } + return changed; +} + + +bool BitMap::set_difference_with_result(BitMap other) { + assert(size() == other.size(), "must have same size"); + bool changed = false; + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size; index++) { + idx_t temp = dest_map[index] & ~(other_map[index]); + changed = changed || (temp != dest_map[index]); + dest_map[index] = temp; + } + return changed; +} + + +bool BitMap::set_intersection_with_result(BitMap other) { + assert(size() == other.size(), "must have same size"); + bool changed = false; + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size; index++) { + idx_t orig = dest_map[index]; + idx_t temp = orig & other_map[index]; + changed = changed || (temp != orig); + dest_map[index] = temp; + } + return changed; +} + + +void BitMap::set_from(BitMap other) { + assert(size() == other.size(), "must have same size"); + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size; index++) { + dest_map[index] = other_map[index]; + } +} + + +bool BitMap::is_same(BitMap other) { + assert(size() == other.size(), "must have same size"); + idx_t* dest_map = map(); + idx_t* other_map = other.map(); + idx_t size = size_in_words(); + for (idx_t index = 0; index < size; index++) { + if (dest_map[index] != other_map[index]) return false; + } + return true; +} + +bool BitMap::is_full() const { + uintptr_t* word = map(); + idx_t rest = size(); + for (; rest >= (idx_t) BitsPerWord; rest -= BitsPerWord) { + if (*word != (uintptr_t) AllBits) return false; + word++; + } + return rest == 0 || (*word | ~right_n_bits((int)rest)) == (uintptr_t) AllBits; +} + + +bool BitMap::is_empty() const { + uintptr_t* word = map(); + idx_t rest = size(); + for (; rest >= (idx_t) BitsPerWord; rest -= BitsPerWord) { + if (*word != (uintptr_t) NoBits) return false; + word++; + } + return rest == 0 || (*word & right_n_bits((int)rest)) == (uintptr_t) NoBits; +} + +void BitMap::clear_large() { + clear_large_range_of_words(0, size_in_words()); +} + +// Note that if the closure itself modifies the bitmap +// then modifications in and to the left of the _bit_ being +// currently sampled will not be seen. Note also that the +// interval [leftOffset, rightOffset) is right open. +void BitMap::iterate(BitMapClosure* blk, idx_t leftOffset, idx_t rightOffset) { + verify_range(leftOffset, rightOffset); + + idx_t startIndex = word_index(leftOffset); + idx_t endIndex = MIN2(word_index(rightOffset) + 1, size_in_words()); + for (idx_t index = startIndex, offset = leftOffset; + offset < rightOffset && index < endIndex; + offset = (++index) << LogBitsPerWord) { + idx_t rest = map(index) >> (offset & (BitsPerWord - 1)); + for (; offset < rightOffset && rest != (uintptr_t)NoBits; offset++) { + if (rest & 1) { + blk->do_bit(offset); + // resample at each closure application + // (see, for instance, CMS bug 4525989) + rest = map(index) >> (offset & (BitsPerWord -1)); + // XXX debugging: remove + // The following assertion assumes that closure application + // doesn't clear bits (may not be true in general, e.g. G1). + assert(rest & 1, + "incorrect shift or closure application can clear bits?"); + } + rest = rest >> 1; + } + } +} + +BitMap::idx_t BitMap::get_next_one_offset(idx_t l_offset, + idx_t r_offset) const { + assert(l_offset <= size(), "BitMap index out of bounds"); + assert(r_offset <= size(), "BitMap index out of bounds"); + assert(l_offset <= r_offset, "l_offset > r_offset ?"); + + if (l_offset == r_offset) { + return l_offset; + } + idx_t index = word_index(l_offset); + idx_t r_index = word_index(r_offset-1) + 1; + idx_t res_offset = l_offset; + + // check bits including and to the _left_ of offset's position + idx_t pos = bit_in_word(res_offset); + idx_t res = map(index) >> pos; + if (res != (uintptr_t)NoBits) { + // find the position of the 1-bit + for (; !(res & 1); res_offset++) { + res = res >> 1; + } + assert(res_offset >= l_offset, "just checking"); + return MIN2(res_offset, r_offset); + } + // skip over all word length 0-bit runs + for (index++; index < r_index; index++) { + res = map(index); + if (res != (uintptr_t)NoBits) { + // found a 1, return the offset + for (res_offset = index << LogBitsPerWord; !(res & 1); + res_offset++) { + res = res >> 1; + } + assert(res & 1, "tautology; see loop condition"); + assert(res_offset >= l_offset, "just checking"); + return MIN2(res_offset, r_offset); + } + } + return r_offset; +} + +BitMap::idx_t BitMap::get_next_zero_offset(idx_t l_offset, + idx_t r_offset) const { + assert(l_offset <= size(), "BitMap index out of bounds"); + assert(r_offset <= size(), "BitMap index out of bounds"); + assert(l_offset <= r_offset, "l_offset > r_offset ?"); + + if (l_offset == r_offset) { + return l_offset; + } + idx_t index = word_index(l_offset); + idx_t r_index = word_index(r_offset-1) + 1; + idx_t res_offset = l_offset; + + // check bits including and to the _left_ of offset's position + idx_t pos = res_offset & (BitsPerWord - 1); + idx_t res = (map(index) >> pos) | left_n_bits((int)pos); + + if (res != (uintptr_t)AllBits) { + // find the position of the 0-bit + for (; res & 1; res_offset++) { + res = res >> 1; + } + assert(res_offset >= l_offset, "just checking"); + return MIN2(res_offset, r_offset); + } + // skip over all word length 1-bit runs + for (index++; index < r_index; index++) { + res = map(index); + if (res != (uintptr_t)AllBits) { + // found a 0, return the offset + for (res_offset = index << LogBitsPerWord; res & 1; + res_offset++) { + res = res >> 1; + } + assert(!(res & 1), "tautology; see loop condition"); + assert(res_offset >= l_offset, "just checking"); + return MIN2(res_offset, r_offset); + } + } + return r_offset; +} + +#ifndef PRODUCT + +void BitMap::print_on(outputStream* st) const { + tty->print("Bitmap(%d):", size()); + for (idx_t index = 0; index < size(); index++) { + tty->print("%c", at(index) ? '1' : '0'); + } + tty->cr(); +} + +#endif + + +BitMap2D::BitMap2D(uintptr_t* map, idx_t size_in_slots, idx_t bits_per_slot) + : _bits_per_slot(bits_per_slot) + , _map(map, size_in_slots * bits_per_slot) +{ +} + + +BitMap2D::BitMap2D(idx_t size_in_slots, idx_t bits_per_slot) + : _bits_per_slot(bits_per_slot) + , _map(size_in_slots * bits_per_slot) +{ +} diff --git a/src/share/vm/utilities/bitMap.hpp b/src/share/vm/utilities/bitMap.hpp new file mode 100644 index 000000000..961a2f1b3 --- /dev/null +++ b/src/share/vm/utilities/bitMap.hpp @@ -0,0 +1,396 @@ +/* + * Copyright 1997-2006 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. + * + */ + +// Closure for iterating over BitMaps + +class BitMapClosure VALUE_OBJ_CLASS_SPEC { + public: + // Callback when bit in map is set + virtual void do_bit(size_t offset) = 0; +}; + + +// Operations for bitmaps represented as arrays of unsigned 32- or 64-bit +// integers (uintptr_t). +// +// Bit offsets are numbered from 0 to size-1 + +class BitMap VALUE_OBJ_CLASS_SPEC { + friend class BitMap2D; + + public: + typedef size_t idx_t; // Type used for bit and word indices. + + // Hints for range sizes. + typedef enum { + unknown_range, small_range, large_range + } RangeSizeHint; + + private: + idx_t* _map; // First word in bitmap + idx_t _size; // Size of bitmap (in bits) + + // Puts the given value at the given offset, using resize() to size + // the bitmap appropriately if needed using factor-of-two expansion. + void at_put_grow(idx_t index, bool value); + + protected: + // Return the position of bit within the word that contains it (e.g., if + // bitmap words are 32 bits, return a number 0 <= n <= 31). + static idx_t bit_in_word(idx_t bit) { return bit & (BitsPerWord - 1); } + + // Return a mask that will select the specified bit, when applied to the word + // containing the bit. + static idx_t bit_mask(idx_t bit) { return (idx_t)1 << bit_in_word(bit); } + + // Return the index of the word containing the specified bit. + static idx_t word_index(idx_t bit) { return bit >> LogBitsPerWord; } + + // Return the bit number of the first bit in the specified word. + static idx_t bit_index(idx_t word) { return word << LogBitsPerWord; } + + // Return the array of bitmap words, or a specific word from it. + idx_t* map() const { return _map; } + idx_t map(idx_t word) const { return _map[word]; } + + // Return a pointer to the word containing the specified bit. + idx_t* word_addr(idx_t bit) const { return map() + word_index(bit); } + + // Set a word to a specified value or to all ones; clear a word. + void set_word (idx_t word, idx_t val) { _map[word] = val; } + void set_word (idx_t word) { set_word(word, ~(uintptr_t)0); } + void clear_word(idx_t word) { _map[word] = 0; } + + // Utilities for ranges of bits. Ranges are half-open [beg, end). + + // Ranges within a single word. + inline idx_t inverted_bit_mask_for_range(idx_t beg, idx_t end) const; + inline void set_range_within_word (idx_t beg, idx_t end); + inline void clear_range_within_word (idx_t beg, idx_t end); + inline void par_put_range_within_word (idx_t beg, idx_t end, bool value); + + // Ranges spanning entire words. + inline void set_range_of_words (idx_t beg, idx_t end); + inline void clear_range_of_words (idx_t beg, idx_t end); + inline void set_large_range_of_words (idx_t beg, idx_t end); + inline void clear_large_range_of_words (idx_t beg, idx_t end); + + // The index of the first full word in a range. + inline idx_t word_index_round_up(idx_t bit) const; + + // Verification, statistics. + void verify_index(idx_t index) const { + assert(index < _size, "BitMap index out of bounds"); + } + + void verify_range(idx_t beg_index, idx_t end_index) const { +#ifdef ASSERT + assert(beg_index <= end_index, "BitMap range error"); + // Note that [0,0) and [size,size) are both valid ranges. + if (end_index != _size) verify_index(end_index); +#endif + } + + public: + + // Constructs a bitmap with no map, and size 0. + BitMap() : _map(NULL), _size(0) {} + + // Construction + BitMap(idx_t* map, idx_t size_in_bits); + + // Allocates necessary data structure in resource area + BitMap(idx_t size_in_bits); + + void set_map(idx_t* map) { _map = map; } + void set_size(idx_t size_in_bits) { _size = size_in_bits; } + + // Allocates necessary data structure in resource area. + // Preserves state currently in bit map by copying data. + // Zeros any newly-addressable bits. + // Does not perform any frees (i.e., of current _map). + void resize(idx_t size_in_bits); + + // Accessing + idx_t size() const { return _size; } + idx_t size_in_words() const { + return word_index(size() + BitsPerWord - 1); + } + + bool at(idx_t index) const { + verify_index(index); + return (*word_addr(index) & bit_mask(index)) != 0; + } + + // Align bit index up or down to the next bitmap word boundary, or check + // alignment. + static idx_t word_align_up(idx_t bit) { + return align_size_up(bit, BitsPerWord); + } + static idx_t word_align_down(idx_t bit) { + return align_size_down(bit, BitsPerWord); + } + static bool is_word_aligned(idx_t bit) { + return word_align_up(bit) == bit; + } + + // Set or clear the specified bit. + inline void set_bit(idx_t bit); + inline void clear_bit(idx_t bit); + + // Atomically set or clear the specified bit. + inline bool par_set_bit(idx_t bit); + inline bool par_clear_bit(idx_t bit); + + // Put the given value at the given offset. The parallel version + // will CAS the value into the bitmap and is quite a bit slower. + // The parallel version also returns a value indicating if the + // calling thread was the one that changed the value of the bit. + void at_put(idx_t index, bool value); + bool par_at_put(idx_t index, bool value); + + // Update a range of bits. Ranges are half-open [beg, end). + void set_range (idx_t beg, idx_t end); + void clear_range (idx_t beg, idx_t end); + void set_large_range (idx_t beg, idx_t end); + void clear_large_range (idx_t beg, idx_t end); + void at_put_range(idx_t beg, idx_t end, bool value); + void par_at_put_range(idx_t beg, idx_t end, bool value); + void at_put_large_range(idx_t beg, idx_t end, bool value); + void par_at_put_large_range(idx_t beg, idx_t end, bool value); + + // Update a range of bits, using a hint about the size. Currently only + // inlines the predominant case of a 1-bit range. Works best when hint is a + // compile-time constant. + inline void set_range(idx_t beg, idx_t end, RangeSizeHint hint); + inline void clear_range(idx_t beg, idx_t end, RangeSizeHint hint); + inline void par_set_range(idx_t beg, idx_t end, RangeSizeHint hint); + inline void par_clear_range (idx_t beg, idx_t end, RangeSizeHint hint); + + // Clearing + void clear(); + void clear_large(); + + // Iteration support + void iterate(BitMapClosure* blk, idx_t leftIndex, idx_t rightIndex); + inline void iterate(BitMapClosure* blk) { + // call the version that takes an interval + iterate(blk, 0, size()); + } + + // Looking for 1's and 0's to the "right" + idx_t get_next_one_offset (idx_t l_index, idx_t r_index) const; + idx_t get_next_zero_offset(idx_t l_index, idx_t r_index) const; + + idx_t get_next_one_offset(idx_t offset) const { + return get_next_one_offset(offset, size()); + } + idx_t get_next_zero_offset(idx_t offset) const { + return get_next_zero_offset(offset, size()); + } + + + + // Find the next one bit in the range [beg_bit, end_bit), or return end_bit if + // no one bit is found. Equivalent to get_next_one_offset(), but inline for + // use in performance-critical code. + inline idx_t find_next_one_bit(idx_t beg_bit, idx_t end_bit) const; + + // Set operations. + void set_union(BitMap bits); + void set_difference(BitMap bits); + void set_intersection(BitMap bits); + // Returns true iff "this" is a superset of "bits". + bool contains(const BitMap bits) const; + // Returns true iff "this and "bits" have a non-empty intersection. + bool intersects(const BitMap bits) const; + + // Returns result of whether this map changed + // during the operation + bool set_union_with_result(BitMap bits); + bool set_difference_with_result(BitMap bits); + bool set_intersection_with_result(BitMap bits); + + void set_from(BitMap bits); + + bool is_same(BitMap bits); + + // Test if all bits are set or cleared + bool is_full() const; + bool is_empty() const; + + +#ifndef PRODUCT + public: + // Printing + void print_on(outputStream* st) const; +#endif +}; + +inline void BitMap::set_bit(idx_t bit) { + verify_index(bit); + *word_addr(bit) |= bit_mask(bit); +} + +inline void BitMap::clear_bit(idx_t bit) { + verify_index(bit); + *word_addr(bit) &= ~bit_mask(bit); +} + +inline void BitMap::set_range(idx_t beg, idx_t end, RangeSizeHint hint) { + if (hint == small_range && end - beg == 1) { + set_bit(beg); + } else { + if (hint == large_range) { + set_large_range(beg, end); + } else { + set_range(beg, end); + } + } +} + +inline void BitMap::clear_range(idx_t beg, idx_t end, RangeSizeHint hint) { + if (hint == small_range && end - beg == 1) { + clear_bit(beg); + } else { + if (hint == large_range) { + clear_large_range(beg, end); + } else { + clear_range(beg, end); + } + } +} + +inline void BitMap::par_set_range(idx_t beg, idx_t end, RangeSizeHint hint) { + if (hint == small_range && end - beg == 1) { + par_at_put(beg, true); + } else { + if (hint == large_range) { + par_at_put_large_range(beg, end, true); + } else { + par_at_put_range(beg, end, true); + } + } +} + + +// Convenience class wrapping BitMap which provides multiple bits per slot. +class BitMap2D VALUE_OBJ_CLASS_SPEC { + public: + typedef size_t idx_t; // Type used for bit and word indices. + + private: + BitMap _map; + idx_t _bits_per_slot; + + idx_t bit_index(idx_t slot_index, idx_t bit_within_slot_index) const { + return slot_index * _bits_per_slot + bit_within_slot_index; + } + + void verify_bit_within_slot_index(idx_t index) const { + assert(index < _bits_per_slot, "bit_within_slot index out of bounds"); + } + + public: + // Construction. bits_per_slot must be greater than 0. + BitMap2D(uintptr_t* map, idx_t size_in_slots, idx_t bits_per_slot); + + // Allocates necessary data structure in resource area. bits_per_slot must be greater than 0. + BitMap2D(idx_t size_in_slots, idx_t bits_per_slot); + + idx_t size_in_bits() { + return _map.size(); + } + + // Returns number of full slots that have been allocated + idx_t size_in_slots() { + // Round down + return _map.size() / _bits_per_slot; + } + + bool is_valid_index(idx_t slot_index, idx_t bit_within_slot_index) { + verify_bit_within_slot_index(bit_within_slot_index); + return (bit_index(slot_index, bit_within_slot_index) < size_in_bits()); + } + + bool at(idx_t slot_index, idx_t bit_within_slot_index) const { + verify_bit_within_slot_index(bit_within_slot_index); + return _map.at(bit_index(slot_index, bit_within_slot_index)); + } + + void set_bit(idx_t slot_index, idx_t bit_within_slot_index) { + verify_bit_within_slot_index(bit_within_slot_index); + _map.set_bit(bit_index(slot_index, bit_within_slot_index)); + } + + void clear_bit(idx_t slot_index, idx_t bit_within_slot_index) { + verify_bit_within_slot_index(bit_within_slot_index); + _map.clear_bit(bit_index(slot_index, bit_within_slot_index)); + } + + void at_put(idx_t slot_index, idx_t bit_within_slot_index, bool value) { + verify_bit_within_slot_index(bit_within_slot_index); + _map.at_put(bit_index(slot_index, bit_within_slot_index), value); + } + + void at_put_grow(idx_t slot_index, idx_t bit_within_slot_index, bool value) { + verify_bit_within_slot_index(bit_within_slot_index); + _map.at_put_grow(bit_index(slot_index, bit_within_slot_index), value); + } + + void clear() { + _map.clear(); + } +}; + + + +inline void BitMap::set_range_of_words(idx_t beg, idx_t end) { + uintptr_t* map = _map; + for (idx_t i = beg; i < end; ++i) map[i] = ~(uintptr_t)0; +} + + +inline void BitMap::clear_range_of_words(idx_t beg, idx_t end) { + uintptr_t* map = _map; + for (idx_t i = beg; i < end; ++i) map[i] = 0; +} + + +inline void BitMap::clear() { + clear_range_of_words(0, size_in_words()); +} + + +inline void BitMap::par_clear_range(idx_t beg, idx_t end, RangeSizeHint hint) { + if (hint == small_range && end - beg == 1) { + par_at_put(beg, false); + } else { + if (hint == large_range) { + par_at_put_large_range(beg, end, false); + } else { + par_at_put_range(beg, end, false); + } + } +} diff --git a/src/share/vm/utilities/bitMap.inline.hpp b/src/share/vm/utilities/bitMap.inline.hpp new file mode 100644 index 000000000..5e656d99e --- /dev/null +++ b/src/share/vm/utilities/bitMap.inline.hpp @@ -0,0 +1,105 @@ +/* + * Copyright 2005-2006 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. + * + */ + +inline bool BitMap::par_set_bit(idx_t bit) { + verify_index(bit); + volatile idx_t* const addr = word_addr(bit); + const idx_t mask = bit_mask(bit); + idx_t old_val = *addr; + + do { + const idx_t new_val = old_val | mask; + if (new_val == old_val) { + return false; // Someone else beat us to it. + } + const idx_t cur_val = (idx_t) Atomic::cmpxchg_ptr((void*) new_val, + (volatile void*) addr, + (void*) old_val); + if (cur_val == old_val) { + return true; // Success. + } + old_val = cur_val; // The value changed, try again. + } while (true); +} + +inline bool BitMap::par_clear_bit(idx_t bit) { + verify_index(bit); + volatile idx_t* const addr = word_addr(bit); + const idx_t mask = ~bit_mask(bit); + idx_t old_val = *addr; + + do { + const idx_t new_val = old_val & mask; + if (new_val == old_val) { + return false; // Someone else beat us to it. + } + const idx_t cur_val = (idx_t) Atomic::cmpxchg_ptr((void*) new_val, + (volatile void*) addr, + (void*) old_val); + if (cur_val == old_val) { + return true; // Success. + } + old_val = cur_val; // The value changed, try again. + } while (true); +} + +inline BitMap::idx_t +BitMap::find_next_one_bit(idx_t beg_bit, idx_t end_bit) const +{ + verify_range(beg_bit, end_bit); + assert(bit_in_word(end_bit) == 0, "end_bit not word-aligned"); + + if (beg_bit == end_bit) { + return beg_bit; + } + + idx_t index = word_index(beg_bit); + idx_t r_index = word_index(end_bit); + idx_t res_bit = beg_bit; + + // check bits including and to the _left_ of offset's position + idx_t res = map(index) >> bit_in_word(res_bit); + if (res != (uintptr_t) NoBits) { + // find the position of the 1-bit + for (; !(res & 1); res_bit++) { + res = res >> 1; + } + assert(res_bit >= beg_bit && res_bit < end_bit, "just checking"); + return res_bit; + } + // skip over all word length 0-bit runs + for (index++; index < r_index; index++) { + res = map(index); + if (res != (uintptr_t) NoBits) { + // found a 1, return the offset + for (res_bit = bit_index(index); !(res & 1); res_bit++) { + res = res >> 1; + } + assert(res & 1, "tautology; see loop condition"); + assert(res_bit >= beg_bit && res_bit < end_bit, "just checking"); + return res_bit; + } + } + return end_bit; +} diff --git a/src/share/vm/utilities/constantTag.cpp b/src/share/vm/utilities/constantTag.cpp new file mode 100644 index 000000000..e9cbace4f --- /dev/null +++ b/src/share/vm/utilities/constantTag.cpp @@ -0,0 +1,83 @@ +/* + * Copyright 1997-1999 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/_constantTag.cpp.incl" + +#ifndef PRODUCT + +void constantTag::print_on(outputStream* st) const { + switch (_tag) { + case JVM_CONSTANT_Class : + st->print("Class"); + break; + case JVM_CONSTANT_Fieldref : + st->print("Field"); + break; + case JVM_CONSTANT_Methodref : + st->print("Method"); + break; + case JVM_CONSTANT_InterfaceMethodref : + st->print("InterfaceMethod"); + break; + case JVM_CONSTANT_String : + st->print("String"); + break; + case JVM_CONSTANT_Integer : + st->print("Integer"); + break; + case JVM_CONSTANT_Float : + st->print("Float"); + break; + case JVM_CONSTANT_Long : + st->print("Long"); + break; + case JVM_CONSTANT_Double : + st->print("Double"); + break; + case JVM_CONSTANT_NameAndType : + st->print("NameAndType"); + break; + case JVM_CONSTANT_Utf8 : + st->print("Utf8"); + break; + case JVM_CONSTANT_UnresolvedClass : + st->print("Unresolved class"); + break; + case JVM_CONSTANT_ClassIndex : + st->print("Unresolved class index"); + break; + case JVM_CONSTANT_UnresolvedString : + st->print("Unresolved string"); + break; + case JVM_CONSTANT_StringIndex : + st->print("Unresolved string index"); + break; + default: + ShouldNotReachHere(); + break; + } +} + +#endif // PRODUCT diff --git a/src/share/vm/utilities/constantTag.hpp b/src/share/vm/utilities/constantTag.hpp new file mode 100644 index 000000000..b8a213f8b --- /dev/null +++ b/src/share/vm/utilities/constantTag.hpp @@ -0,0 +1,86 @@ +/* + * Copyright 1997-2005 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. + * + */ + +// constant tags in Java .class files + + +enum { + // See jvm.h for shared JVM_CONSTANT_XXX tags + // NOTE: replicated in SA in vm/agent/sun/jvm/hotspot/utilities/ConstantTag.java + // Hotspot specific tags + JVM_CONSTANT_Invalid = 0, // For bad value initialization + JVM_CONSTANT_InternalMin = 100, // First implementation tag (aside from bad value of course) + JVM_CONSTANT_UnresolvedClass = 100, // Temporary tag until actual use + JVM_CONSTANT_ClassIndex = 101, // Temporary tag while constructing constant pool + JVM_CONSTANT_UnresolvedString = 102, // Temporary tag until actual use + JVM_CONSTANT_StringIndex = 103, // Temporary tag while constructing constant pool + JVM_CONSTANT_UnresolvedClassInError = 104, // Error tag due to resolution error + JVM_CONSTANT_InternalMax = 104 // Last implementation tag +}; + + +class constantTag VALUE_OBJ_CLASS_SPEC { + private: + jbyte _tag; + public: + bool is_klass() const { return _tag == JVM_CONSTANT_Class; } + bool is_field () const { return _tag == JVM_CONSTANT_Fieldref; } + bool is_method() const { return _tag == JVM_CONSTANT_Methodref; } + bool is_interface_method() const { return _tag == JVM_CONSTANT_InterfaceMethodref; } + bool is_string() const { return _tag == JVM_CONSTANT_String; } + bool is_int() const { return _tag == JVM_CONSTANT_Integer; } + bool is_float() const { return _tag == JVM_CONSTANT_Float; } + bool is_long() const { return _tag == JVM_CONSTANT_Long; } + bool is_double() const { return _tag == JVM_CONSTANT_Double; } + bool is_name_and_type() const { return _tag == JVM_CONSTANT_NameAndType; } + bool is_utf8() const { return _tag == JVM_CONSTANT_Utf8; } + + bool is_invalid() const { return _tag == JVM_CONSTANT_Invalid; } + + bool is_unresolved_klass() const { + return _tag == JVM_CONSTANT_UnresolvedClass || _tag == JVM_CONSTANT_UnresolvedClassInError; + } + + bool is_unresolved_klass_in_error() const { + return _tag == JVM_CONSTANT_UnresolvedClassInError; + } + + bool is_klass_index() const { return _tag == JVM_CONSTANT_ClassIndex; } + bool is_unresolved_string() const { return _tag == JVM_CONSTANT_UnresolvedString; } + bool is_string_index() const { return _tag == JVM_CONSTANT_StringIndex; } + + bool is_klass_reference() const { return is_klass_index() || is_unresolved_klass(); } + bool is_field_or_method() const { return is_field() || is_method() || is_interface_method(); } + bool is_symbol() const { return is_utf8(); } + + constantTag(jbyte tag) { + assert((tag >= 0 && tag <= JVM_CONSTANT_NameAndType) || + (tag >= JVM_CONSTANT_InternalMin && tag <= JVM_CONSTANT_InternalMax), "Invalid constant tag"); + _tag = tag; + } + + jbyte value() { return _tag; } + + void print_on(outputStream* st) const PRODUCT_RETURN; +}; diff --git a/src/share/vm/utilities/copy.cpp b/src/share/vm/utilities/copy.cpp new file mode 100644 index 000000000..1bfef0afe --- /dev/null +++ b/src/share/vm/utilities/copy.cpp @@ -0,0 +1,92 @@ +/* + * Copyright 2006-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/_copy.cpp.incl" + + +// Copy bytes; larger units are filled atomically if everything is aligned. +void Copy::conjoint_memory_atomic(void* from, void* to, size_t size) { + address src = (address) from; + address dst = (address) to; + uintptr_t bits = (uintptr_t) src | (uintptr_t) dst | (uintptr_t) size; + + // (Note: We could improve performance by ignoring the low bits of size, + // and putting a short cleanup loop after each bulk copy loop. + // There are plenty of other ways to make this faster also, + // and it's a slippery slope. For now, let's keep this code simple + // since the simplicity helps clarify the atomicity semantics of + // this operation. There are also CPU-specific assembly versions + // which may or may not want to include such optimizations.) + + if (bits % sizeof(jlong) == 0) { + Copy::conjoint_jlongs_atomic((jlong*) src, (jlong*) dst, size / sizeof(jlong)); + } else if (bits % sizeof(jint) == 0) { + Copy::conjoint_jints_atomic((jint*) src, (jint*) dst, size / sizeof(jint)); + } else if (bits % sizeof(jshort) == 0) { + Copy::conjoint_jshorts_atomic((jshort*) src, (jshort*) dst, size / sizeof(jshort)); + } else { + // Not aligned, so no need to be atomic. + Copy::conjoint_bytes((void*) src, (void*) dst, size); + } +} + + +// Fill bytes; larger units are filled atomically if everything is aligned. +void Copy::fill_to_memory_atomic(void* to, size_t size, jubyte value) { + address dst = (address) to; + uintptr_t bits = (uintptr_t) to | (uintptr_t) size; + if (bits % sizeof(jlong) == 0) { + jlong fill = (julong)( (jubyte)value ); // zero-extend + if (fill != 0) { + fill += fill << 8; + fill += fill << 16; + fill += fill << 32; + } + //Copy::fill_to_jlongs_atomic((jlong*) dst, size / sizeof(jlong)); + for (uintptr_t off = 0; off < size; off += sizeof(jlong)) { + *(jlong*)(dst + off) = fill; + } + } else if (bits % sizeof(jint) == 0) { + jint fill = (juint)( (jubyte)value ); // zero-extend + if (fill != 0) { + fill += fill << 8; + fill += fill << 16; + } + //Copy::fill_to_jints_atomic((jint*) dst, size / sizeof(jint)); + for (uintptr_t off = 0; off < size; off += sizeof(jint)) { + *(jint*)(dst + off) = fill; + } + } else if (bits % sizeof(jshort) == 0) { + jshort fill = (jushort)( (jubyte)value ); // zero-extend + fill += fill << 8; + //Copy::fill_to_jshorts_atomic((jshort*) dst, size / sizeof(jshort)); + for (uintptr_t off = 0; off < size; off += sizeof(jshort)) { + *(jshort*)(dst + off) = fill; + } + } else { + // Not aligned, so no need to be atomic. + Copy::fill_to_bytes(dst, size, value); + } +} diff --git a/src/share/vm/utilities/copy.hpp b/src/share/vm/utilities/copy.hpp new file mode 100644 index 000000000..1bbea38ad --- /dev/null +++ b/src/share/vm/utilities/copy.hpp @@ -0,0 +1,332 @@ +/* + * Copyright 2003-2006 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. + * + */ + +// Assembly code for platforms that need it. +extern "C" { + void _Copy_conjoint_words(HeapWord* from, HeapWord* to, size_t count); + void _Copy_disjoint_words(HeapWord* from, HeapWord* to, size_t count); + + void _Copy_conjoint_words_atomic(HeapWord* from, HeapWord* to, size_t count); + void _Copy_disjoint_words_atomic(HeapWord* from, HeapWord* to, size_t count); + + void _Copy_aligned_conjoint_words(HeapWord* from, HeapWord* to, size_t count); + void _Copy_aligned_disjoint_words(HeapWord* from, HeapWord* to, size_t count); + + void _Copy_conjoint_bytes(void* from, void* to, size_t count); + + void _Copy_conjoint_bytes_atomic (void* from, void* to, size_t count); + void _Copy_conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count); + void _Copy_conjoint_jints_atomic (jint* from, jint* to, size_t count); + void _Copy_conjoint_jlongs_atomic (jlong* from, jlong* to, size_t count); + void _Copy_conjoint_oops_atomic (oop* from, oop* to, size_t count); + + void _Copy_arrayof_conjoint_bytes (HeapWord* from, HeapWord* to, size_t count); + void _Copy_arrayof_conjoint_jshorts(HeapWord* from, HeapWord* to, size_t count); + void _Copy_arrayof_conjoint_jints (HeapWord* from, HeapWord* to, size_t count); + void _Copy_arrayof_conjoint_jlongs (HeapWord* from, HeapWord* to, size_t count); + void _Copy_arrayof_conjoint_oops (HeapWord* from, HeapWord* to, size_t count); +} + +class Copy : AllStatic { + public: + // Block copy methods have four attributes. We don't define all possibilities. + // alignment: aligned according to minimum Java object alignment (MinObjAlignment) + // arrayof: arraycopy operation with both operands aligned on the same + // boundary as the first element of an array of the copy unit. + // This is currently a HeapWord boundary on all platforms, except + // for long and double arrays, which are aligned on an 8-byte + // boundary on all platforms. + // arraycopy operations are implicitly atomic on each array element. + // overlap: disjoint or conjoint. + // copy unit: bytes or words (i.e., HeapWords) or oops (i.e., pointers). + // atomicity: atomic or non-atomic on the copy unit. + // + // Names are constructed thusly: + // + // [ 'aligned_' | 'arrayof_' ] + // ('conjoint_' | 'disjoint_') + // ('words' | 'bytes' | 'jshorts' | 'jints' | 'jlongs' | 'oops') + // [ '_atomic' ] + // + // Except in the arrayof case, whatever the alignment is, we assume we can copy + // whole alignment units. E.g., if MinObjAlignment is 2x word alignment, an odd + // count may copy an extra word. In the arrayof case, we are allowed to copy + // only the number of copy units specified. + + // HeapWords + + // Word-aligned words, conjoint, not atomic on each word + static void conjoint_words(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogHeapWordSize); + pd_conjoint_words(from, to, count); + } + + // Word-aligned words, disjoint, not atomic on each word + static void disjoint_words(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogHeapWordSize); + assert_disjoint(from, to, count); + pd_disjoint_words(from, to, count); + } + + // Word-aligned words, disjoint, atomic on each word + static void disjoint_words_atomic(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogHeapWordSize); + assert_disjoint(from, to, count); + pd_disjoint_words_atomic(from, to, count); + } + + // Object-aligned words, conjoint, not atomic on each word + static void aligned_conjoint_words(HeapWord* from, HeapWord* to, size_t count) { + assert_params_aligned(from, to); + assert_non_zero(count); + pd_aligned_conjoint_words(from, to, count); + } + + // Object-aligned words, disjoint, not atomic on each word + static void aligned_disjoint_words(HeapWord* from, HeapWord* to, size_t count) { + assert_params_aligned(from, to); + assert_disjoint(from, to, count); + assert_non_zero(count); + pd_aligned_disjoint_words(from, to, count); + } + + // bytes, jshorts, jints, jlongs, oops + + // bytes, conjoint, not atomic on each byte (not that it matters) + static void conjoint_bytes(void* from, void* to, size_t count) { + assert_non_zero(count); + pd_conjoint_bytes(from, to, count); + } + + // bytes, conjoint, atomic on each byte (not that it matters) + static void conjoint_bytes_atomic(void* from, void* to, size_t count) { + assert_non_zero(count); + pd_conjoint_bytes(from, to, count); + } + + // jshorts, conjoint, atomic on each jshort + static void conjoint_jshorts_atomic(jshort* from, jshort* to, size_t count) { + assert_params_ok(from, to, LogBytesPerShort); + assert_non_zero(count); + pd_conjoint_jshorts_atomic(from, to, count); + } + + // jints, conjoint, atomic on each jint + static void conjoint_jints_atomic(jint* from, jint* to, size_t count) { + assert_params_ok(from, to, LogBytesPerInt); + assert_non_zero(count); + pd_conjoint_jints_atomic(from, to, count); + } + + // jlongs, conjoint, atomic on each jlong + static void conjoint_jlongs_atomic(jlong* from, jlong* to, size_t count) { + assert_params_ok(from, to, LogBytesPerLong); + assert_non_zero(count); + pd_conjoint_jlongs_atomic(from, to, count); + } + + // oops, conjoint, atomic on each oop + static void conjoint_oops_atomic(oop* from, oop* to, size_t count) { + assert_params_ok(from, to, LogBytesPerOop); + assert_non_zero(count); + pd_conjoint_oops_atomic(from, to, count); + } + + // Copy a span of memory. If the span is an integral number of aligned + // longs, words, or ints, copy those units atomically. + // The largest atomic transfer unit is 8 bytes, or the largest power + // of two which divides all of from, to, and size, whichever is smaller. + static void conjoint_memory_atomic(void* from, void* to, size_t size); + + // bytes, conjoint array, atomic on each byte (not that it matters) + static void arrayof_conjoint_bytes(HeapWord* from, HeapWord* to, size_t count) { + assert_non_zero(count); + pd_arrayof_conjoint_bytes(from, to, count); + } + + // jshorts, conjoint array, atomic on each jshort + static void arrayof_conjoint_jshorts(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogBytesPerShort); + assert_non_zero(count); + pd_arrayof_conjoint_jshorts(from, to, count); + } + + // jints, conjoint array, atomic on each jint + static void arrayof_conjoint_jints(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogBytesPerInt); + assert_non_zero(count); + pd_arrayof_conjoint_jints(from, to, count); + } + + // jlongs, conjoint array, atomic on each jlong + static void arrayof_conjoint_jlongs(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogBytesPerLong); + assert_non_zero(count); + pd_arrayof_conjoint_jlongs(from, to, count); + } + + // oops, conjoint array, atomic on each oop + static void arrayof_conjoint_oops(HeapWord* from, HeapWord* to, size_t count) { + assert_params_ok(from, to, LogBytesPerOop); + assert_non_zero(count); + pd_arrayof_conjoint_oops(from, to, count); + } + + // Known overlap methods + + // Copy word-aligned words from higher to lower addresses, not atomic on each word + inline static void conjoint_words_to_lower(HeapWord* from, HeapWord* to, size_t byte_count) { + // byte_count is in bytes to check its alignment + assert_params_ok(from, to, LogHeapWordSize); + assert_byte_count_ok(byte_count, HeapWordSize); + + size_t count = (size_t)round_to(byte_count, HeapWordSize) >> LogHeapWordSize; + assert(to <= from || from + count <= to, "do not overwrite source data"); + + while (count-- > 0) { + *to++ = *from++; + } + } + + // Copy word-aligned words from lower to higher addresses, not atomic on each word + inline static void conjoint_words_to_higher(HeapWord* from, HeapWord* to, size_t byte_count) { + // byte_count is in bytes to check its alignment + assert_params_ok(from, to, LogHeapWordSize); + assert_byte_count_ok(byte_count, HeapWordSize); + + size_t count = (size_t)round_to(byte_count, HeapWordSize) >> LogHeapWordSize; + assert(from <= to || to + count <= from, "do not overwrite source data"); + + from += count - 1; + to += count - 1; + while (count-- > 0) { + *to-- = *from--; + } + } + + // Fill methods + + // Fill word-aligned words, not atomic on each word + // set_words + static void fill_to_words(HeapWord* to, size_t count, juint value = 0) { + assert_params_ok(to, LogHeapWordSize); + pd_fill_to_words(to, count, value); + } + + static void fill_to_aligned_words(HeapWord* to, size_t count, juint value = 0) { + assert_params_aligned(to); + pd_fill_to_aligned_words(to, count, value); + } + + // Fill bytes + static void fill_to_bytes(void* to, size_t count, jubyte value = 0) { + pd_fill_to_bytes(to, count, value); + } + + // Fill a span of memory. If the span is an integral number of aligned + // longs, words, or ints, store to those units atomically. + // The largest atomic transfer unit is 8 bytes, or the largest power + // of two which divides both to and size, whichever is smaller. + static void fill_to_memory_atomic(void* to, size_t size, jubyte value = 0); + + // Zero-fill methods + + // Zero word-aligned words, not atomic on each word + static void zero_to_words(HeapWord* to, size_t count) { + assert_params_ok(to, LogHeapWordSize); + pd_zero_to_words(to, count); + } + + // Zero bytes + static void zero_to_bytes(void* to, size_t count) { + pd_zero_to_bytes(to, count); + } + + private: + static bool params_disjoint(HeapWord* from, HeapWord* to, size_t count) { + if (from < to) { + return pointer_delta(to, from) >= count; + } + return pointer_delta(from, to) >= count; + } + + // These methods raise a fatal if they detect a problem. + + static void assert_disjoint(HeapWord* from, HeapWord* to, size_t count) { +#ifdef ASSERT + if (!params_disjoint(from, to, count)) + basic_fatal("source and dest overlap"); +#endif + } + + static void assert_params_ok(void* from, void* to, intptr_t log_align) { +#ifdef ASSERT + if (mask_bits((uintptr_t)from, right_n_bits(log_align)) != 0) + basic_fatal("not aligned"); + if (mask_bits((uintptr_t)to, right_n_bits(log_align)) != 0) + basic_fatal("not aligned"); +#endif + } + + static void assert_params_ok(HeapWord* to, intptr_t log_align) { +#ifdef ASSERT + if (mask_bits((uintptr_t)to, right_n_bits(log_align)) != 0) + basic_fatal("not word aligned"); +#endif + } + static void assert_params_aligned(HeapWord* from, HeapWord* to) { +#ifdef ASSERT + if (mask_bits((uintptr_t)from, MinObjAlignmentInBytes-1) != 0) + basic_fatal("not object aligned"); + if (mask_bits((uintptr_t)to, MinObjAlignmentInBytes-1) != 0) + basic_fatal("not object aligned"); +#endif + } + + static void assert_params_aligned(HeapWord* to) { +#ifdef ASSERT + if (mask_bits((uintptr_t)to, MinObjAlignmentInBytes-1) != 0) + basic_fatal("not object aligned"); +#endif + } + + static void assert_non_zero(size_t count) { +#ifdef ASSERT + if (count == 0) { + basic_fatal("count must be non-zero"); + } +#endif + } + + static void assert_byte_count_ok(size_t byte_count, size_t unit_size) { +#ifdef ASSERT + if ((size_t)round_to(byte_count, unit_size) != byte_count) { + basic_fatal("byte count must be aligned"); + } +#endif + } + + // Platform dependent implementations of the above methods. + #include "incls/_copy_pd.hpp.incl" +}; diff --git a/src/share/vm/utilities/debug.cpp b/src/share/vm/utilities/debug.cpp new file mode 100644 index 000000000..8eb84cddc --- /dev/null +++ b/src/share/vm/utilities/debug.cpp @@ -0,0 +1,936 @@ +/* + * 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/_debug.cpp.incl" + +#ifndef ASSERT +# ifdef _DEBUG + // NOTE: don't turn the lines below into a comment -- if you're getting + // a compile error here, change the settings to define ASSERT + ASSERT should be defined when _DEBUG is defined. It is not intended to be used for debugging + functions that do not slow down the system too much and thus can be left in optimized code. + On the other hand, the code should not be included in a production version. +# endif // _DEBUG +#endif // ASSERT + + +#ifdef _DEBUG +# ifndef ASSERT + configuration error: ASSERT must be defined in debug version +# endif // ASSERT +#endif // _DEBUG + + +#ifdef PRODUCT +# if -defined _DEBUG || -defined ASSERT + configuration error: ASSERT et al. must not be defined in PRODUCT version +# endif +#endif // PRODUCT + + +void warning(const char* format, ...) { + // In case error happens before init or during shutdown + if (tty == NULL) ostream_init(); + + tty->print("%s warning: ", VM_Version::vm_name()); + va_list ap; + va_start(ap, format); + tty->vprint_cr(format, ap); + va_end(ap); + if (BreakAtWarning) BREAKPOINT; +} + +#ifndef PRODUCT + +#define is_token_break(ch) (isspace(ch) || (ch) == ',') + +static const char* last_file_name = NULL; +static int last_line_no = -1; + +// assert/guarantee/... may happen very early during VM initialization. +// Don't rely on anything that is initialized by Threads::create_vm(). For +// example, don't use tty. +bool assert_is_suppressed(const char* file_name, int line_no) { + // The following 1-element cache requires that passed-in + // file names are always only constant literals. + if (file_name == last_file_name && line_no == last_line_no) return true; + + int file_name_len = (int)strlen(file_name); + char separator = os::file_separator()[0]; + const char* base_name = strrchr(file_name, separator); + if (base_name == NULL) + base_name = file_name; + + // scan the SuppressErrorAt option + const char* cp = SuppressErrorAt; + for (;;) { + const char* sfile; + int sfile_len; + int sline; + bool noisy; + while ((*cp) != '\0' && is_token_break(*cp)) cp++; + if ((*cp) == '\0') break; + sfile = cp; + while ((*cp) != '\0' && !is_token_break(*cp) && (*cp) != ':') cp++; + sfile_len = cp - sfile; + if ((*cp) == ':') cp++; + sline = 0; + while ((*cp) != '\0' && isdigit(*cp)) { + sline *= 10; + sline += (*cp) - '0'; + cp++; + } + // "file:line!" means the assert suppression is not silent + noisy = ((*cp) == '!'); + while ((*cp) != '\0' && !is_token_break(*cp)) cp++; + // match the line + if (sline != 0) { + if (sline != line_no) continue; + } + // match the file + if (sfile_len > 0) { + const char* look = file_name; + const char* look_max = file_name + file_name_len - sfile_len; + const char* foundp; + bool match = false; + while (!match + && (foundp = strchr(look, sfile[0])) != NULL + && foundp <= look_max) { + match = true; + for (int i = 1; i < sfile_len; i++) { + if (sfile[i] != foundp[i]) { + match = false; + break; + } + } + look = foundp + 1; + } + if (!match) continue; + } + // got a match! + if (noisy) { + fdStream out(defaultStream::output_fd()); + out.print_raw("[error suppressed at "); + out.print_raw(base_name); + char buf[16]; + jio_snprintf(buf, sizeof(buf), ":%d]", line_no); + out.print_raw_cr(buf); + } else { + // update 1-element cache for fast silent matches + last_file_name = file_name; + last_line_no = line_no; + } + return true; + } + + if (!is_error_reported()) { + // print a friendly hint: + fdStream out(defaultStream::output_fd()); + out.print_raw_cr("# To suppress the following error report, specify this argument"); + out.print_raw ("# after -XX: or in .hotspotrc: SuppressErrorAt="); + out.print_raw (base_name); + char buf[16]; + jio_snprintf(buf, sizeof(buf), ":%d", line_no); + out.print_raw_cr(buf); + } + return false; +} + +#undef is_token_break + +#else + +// Place-holder for non-existent suppression check: +#define assert_is_suppressed(file_name, line_no) (false) + +#endif //PRODUCT + +void report_assertion_failure(const char* file_name, int line_no, const char* message) { + if (Debugging || assert_is_suppressed(file_name, line_no)) return; + VMError err(ThreadLocalStorage::get_thread_slow(), message, file_name, line_no); + err.report_and_die(); +} + +void report_fatal(const char* file_name, int line_no, const char* message) { + if (Debugging || assert_is_suppressed(file_name, line_no)) return; + VMError err(ThreadLocalStorage::get_thread_slow(), message, file_name, line_no); + err.report_and_die(); +} + +void report_fatal_vararg(const char* file_name, int line_no, const char* format, ...) { + char buffer[256]; + va_list ap; + va_start(ap, format); + jio_vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + report_fatal(file_name, line_no, buffer); +} + + +// Used by report_vm_out_of_memory to detect recursion. +static jint _exiting_out_of_mem = 0; + +// Just passing the flow to VMError to handle error +void report_vm_out_of_memory(const char* file_name, int line_no, size_t size, const char* message) { + if (Debugging || assert_is_suppressed(file_name, line_no)) return; + + // We try to gather additional information for the first out of memory + // error only; gathering additional data might cause an allocation and a + // recursive out_of_memory condition. + + const jint exiting = 1; + // If we succeed in changing the value, we're the first one in. + bool first_time_here = Atomic::xchg(exiting, &_exiting_out_of_mem) != exiting; + + if (first_time_here) { + Thread* thread = ThreadLocalStorage::get_thread_slow(); + VMError(thread, size, message, file_name, line_no).report_and_die(); + } + vm_abort(); +} + +void report_vm_out_of_memory_vararg(const char* file_name, int line_no, size_t size, const char* format, ...) { + char buffer[256]; + va_list ap; + va_start(ap, format); + jio_vsnprintf(buffer, sizeof(buffer), format, ap); + va_end(ap); + report_vm_out_of_memory(file_name, line_no, size, buffer); +} + +void report_should_not_call(const char* file_name, int line_no) { + if (Debugging || assert_is_suppressed(file_name, line_no)) return; + VMError err(ThreadLocalStorage::get_thread_slow(), "ShouldNotCall()", file_name, line_no); + err.report_and_die(); +} + + +void report_should_not_reach_here(const char* file_name, int line_no) { + if (Debugging || assert_is_suppressed(file_name, line_no)) return; + VMError err(ThreadLocalStorage::get_thread_slow(), "ShouldNotReachHere()", file_name, line_no); + err.report_and_die(); +} + + +void report_unimplemented(const char* file_name, int line_no) { + if (Debugging || assert_is_suppressed(file_name, line_no)) return; + VMError err(ThreadLocalStorage::get_thread_slow(), "Unimplemented()", file_name, line_no); + err.report_and_die(); +} + + +void report_untested(const char* file_name, int line_no, const char* msg) { +#ifndef PRODUCT + warning("Untested: %s in %s: %d\n", msg, file_name, line_no); +#endif // PRODUCT +} + +void report_java_out_of_memory(const char* message) { + static jint out_of_memory_reported = 0; + + // A number of threads may attempt to report OutOfMemoryError at around the + // same time. To avoid dumping the heap or executing the data collection + // commands multiple times we just do it once when the first threads reports + // the error. + if (Atomic::cmpxchg(1, &out_of_memory_reported, 0) == 0) { + // create heap dump before OnOutOfMemoryError commands are executed + if (HeapDumpOnOutOfMemoryError) { + tty->print_cr("java.lang.OutOfMemoryError: %s", message); + HeapDumper::dump_heap(); + } + + if (OnOutOfMemoryError && OnOutOfMemoryError[0]) { + VMError err(message); + err.report_java_out_of_memory(); + } + } +} + + +extern "C" void ps(); + +static bool error_reported = false; + +// call this when the VM is dying--it might loosen some asserts +void set_error_reported() { + error_reported = true; +} + +bool is_error_reported() { + return error_reported; +} + +// ------ helper functions for debugging go here ------------ + +#ifndef PRODUCT +// All debug entries should be wrapped with a stack allocated +// Command object. It makes sure a resource mark is set and +// flushes the logfile to prevent file sharing problems. + +class Command : public StackObj { + private: + ResourceMark rm; + ResetNoHandleMark rnhm; + HandleMark hm; + bool debug_save; + public: + static int level; + Command(const char* str) { + debug_save = Debugging; + Debugging = true; + if (level++ > 0) return; + tty->cr(); + tty->print_cr("\"Executing %s\"", str); + } + + ~Command() { tty->flush(); Debugging = debug_save; level--; } +}; + +int Command::level = 0; + +extern "C" void blob(CodeBlob* cb) { + Command c("blob"); + cb->print(); +} + + +extern "C" void dump_vtable(address p) { + Command c("dump_vtable"); + klassOop k = (klassOop)p; + instanceKlass::cast(k)->vtable()->print(); +} + + +extern "C" void nm(intptr_t p) { + // Actually we look through all CodeBlobs (the nm name has been kept for backwards compatability) + Command c("nm"); + CodeBlob* cb = CodeCache::find_blob((address)p); + if (cb == NULL) { + tty->print_cr("NULL"); + } else { + cb->print(); + } +} + + +extern "C" void disnm(intptr_t p) { + Command c("disnm"); + CodeBlob* cb = CodeCache::find_blob((address) p); + cb->print(); + Disassembler::decode(cb); +} + + +extern "C" void printnm(intptr_t p) { + char buffer[256]; + sprintf(buffer, "printnm: " INTPTR_FORMAT, p); + Command c(buffer); + CodeBlob* cb = CodeCache::find_blob((address) p); + if (cb->is_nmethod()) { + nmethod* nm = (nmethod*)cb; + nm->print_nmethod(true); + } +} + + +extern "C" void universe() { + Command c("universe"); + Universe::print(); +} + + +extern "C" void verify() { + // try to run a verify on the entire system + // note: this may not be safe if we're not at a safepoint; for debugging, + // this manipulates the safepoint settings to avoid assertion failures + Command c("universe verify"); + bool safe = SafepointSynchronize::is_at_safepoint(); + if (!safe) { + tty->print_cr("warning: not at safepoint -- verify may fail"); + SafepointSynchronize::set_is_at_safepoint(); + } + // Ensure Eden top is correct before verification + Universe::heap()->prepare_for_verify(); + Universe::verify(true); + if (!safe) SafepointSynchronize::set_is_not_at_safepoint(); +} + + +extern "C" void pp(void* p) { + Command c("pp"); + FlagSetting fl(PrintVMMessages, true); + if (Universe::heap()->is_in(p)) { + oop obj = oop(p); + obj->print(); + } else { + tty->print("%#p", p); + } +} + + +// pv: print vm-printable object +extern "C" void pa(intptr_t p) { ((AllocatedObj*) p)->print(); } +extern "C" void findpc(intptr_t x); + +extern "C" void ps() { // print stack + Command c("ps"); + + + // Prints the stack of the current Java thread + JavaThread* p = JavaThread::active(); + tty->print(" for thread: "); + p->print(); + tty->cr(); + + if (p->has_last_Java_frame()) { + // If the last_Java_fp is set we are in C land and + // can call the standard stack_trace function. + p->trace_stack(); + } else { + frame f = os::current_frame(); + RegisterMap reg_map(p); + f = f.sender(®_map); + tty->print("(guessing starting frame id=%#p based on current fp)\n", f.id()); + p->trace_stack_from(vframe::new_vframe(&f, ®_map, p)); + pd_ps(f); + } + +} + + +extern "C" void psf() { // print stack frames + { + Command c("psf"); + JavaThread* p = JavaThread::active(); + tty->print(" for thread: "); + p->print(); + tty->cr(); + if (p->has_last_Java_frame()) { + p->trace_frames(); + } + } +} + + +extern "C" void threads() { + Command c("threads"); + Threads::print(false, true); +} + + +extern "C" void psd() { + Command c("psd"); + SystemDictionary::print(); +} + + +extern "C" void safepoints() { + Command c("safepoints"); + SafepointSynchronize::print_state(); +} + + +extern "C" void pss() { // print all stacks + Command c("pss"); + Threads::print(true, true); +} + + +extern "C" void debug() { // to set things up for compiler debugging + Command c("debug"); + WizardMode = true; + PrintVMMessages = PrintCompilation = true; + PrintInlining = PrintAssembly = true; + tty->flush(); +} + + +extern "C" void ndebug() { // undo debug() + Command c("ndebug"); + PrintCompilation = false; + PrintInlining = PrintAssembly = false; + tty->flush(); +} + + +extern "C" void flush() { + Command c("flush"); + tty->flush(); +} + + +extern "C" void events() { + Command c("events"); + Events::print_last(tty, 50); +} + + +extern "C" void nevents(int n) { + Command c("events"); + Events::print_last(tty, n); +} + + +// Given a heap address that was valid before the most recent GC, if +// the oop that used to contain it is still live, prints the new +// location of the oop and the address. Useful for tracking down +// certain kinds of naked oop and oop map bugs. +extern "C" void pnl(intptr_t old_heap_addr) { + // Print New Location of old heap address + Command c("pnl"); +#ifndef VALIDATE_MARK_SWEEP + tty->print_cr("Requires build with VALIDATE_MARK_SWEEP defined (debug build) and RecordMarkSweepCompaction enabled"); +#else + MarkSweep::print_new_location_of_heap_address((HeapWord*) old_heap_addr); +#endif +} + + +extern "C" methodOop findm(intptr_t pc) { + Command c("findm"); + nmethod* nm = CodeCache::find_nmethod((address)pc); + return (nm == NULL) ? (methodOop)NULL : nm->method(); +} + + +extern "C" nmethod* findnm(intptr_t addr) { + Command c("findnm"); + return CodeCache::find_nmethod((address)addr); +} + +static address same_page(address x, address y) { + intptr_t page_bits = -os::vm_page_size(); + if ((intptr_t(x) & page_bits) == (intptr_t(y) & page_bits)) { + return x; + } else if (x > y) { + return (address)(intptr_t(y) | ~page_bits) + 1; + } else { + return (address)(intptr_t(y) & page_bits); + } +} + + +static void find(intptr_t x, bool print_pc) { + address addr = (address)x; + + CodeBlob* b = CodeCache::find_blob_unsafe(addr); + if (b != NULL) { + if (b->is_buffer_blob()) { + // the interpreter is generated into a buffer blob + InterpreterCodelet* i = Interpreter::codelet_containing(addr); + if (i != NULL) { + i->print(); + return; + } + if (Interpreter::contains(addr)) { + tty->print_cr(INTPTR_FORMAT " is pointing into interpreter code (not bytecode specific)", addr); + return; + } + // + if (AdapterHandlerLibrary::contains(b)) { + AdapterHandlerLibrary::print_handler(b); + } + // the stubroutines are generated into a buffer blob + StubCodeDesc* d = StubCodeDesc::desc_for(addr); + if (d != NULL) { + d->print(); + if (print_pc) tty->cr(); + return; + } + if (StubRoutines::contains(addr)) { + tty->print_cr(INTPTR_FORMAT " is pointing to an (unnamed) stub routine", addr); + return; + } + // the InlineCacheBuffer is using stubs generated into a buffer blob + if (InlineCacheBuffer::contains(addr)) { + tty->print_cr(INTPTR_FORMAT "is pointing into InlineCacheBuffer", addr); + return; + } + VtableStub* v = VtableStubs::stub_containing(addr); + if (v != NULL) { + v->print(); + return; + } + } + if (print_pc && b->is_nmethod()) { + ResourceMark rm; + tty->print("%#p: Compiled ", addr); + ((nmethod*)b)->method()->print_value_on(tty); + tty->print(" = (CodeBlob*)" INTPTR_FORMAT, b); + tty->cr(); + return; + } + if ( b->is_nmethod()) { + if (b->is_zombie()) { + tty->print_cr(INTPTR_FORMAT " is zombie nmethod", b); + } else if (b->is_not_entrant()) { + tty->print_cr(INTPTR_FORMAT " is non-entrant nmethod", b); + } + } + b->print(); + return; + } + + if (Universe::heap()->is_in_reserved(addr)) { + HeapWord* p = Universe::heap()->block_start(addr); + bool print = false; + // If we couldn't find it it just may mean that heap wasn't parseable + // See if we were just given an oop directly + if (p != NULL && Universe::heap()->block_is_obj(p)) { + print = true; + } else if (p == NULL && ((oopDesc*)addr)->is_oop()) { + p = (HeapWord*) addr; + print = true; + } + if (print) { + oop(p)->print(); + if (p != (HeapWord*)x && oop(p)->is_constMethod() && + constMethodOop(p)->contains(addr)) { + Thread *thread = Thread::current(); + HandleMark hm(thread); + methodHandle mh (thread, constMethodOop(p)->method()); + if (!mh->is_native()) { + tty->print_cr("bci_from(%p) = %d; print_codes():", + addr, mh->bci_from(address(x))); + mh->print_codes(); + } + } + return; + } + } + if (JNIHandles::is_global_handle((jobject) addr)) { + tty->print_cr(INTPTR_FORMAT "is a global jni handle", addr); + return; + } + if (JNIHandles::is_weak_global_handle((jobject) addr)) { + tty->print_cr(INTPTR_FORMAT "is a weak global jni handle", addr); + return; + } + if (JNIHandleBlock::any_contains((jobject) addr)) { + tty->print_cr(INTPTR_FORMAT "is a local jni handle", addr); + return; + } + + for(JavaThread *thread = Threads::first(); thread; thread = thread->next()) { + // Check for priviledge stack + if (thread->privileged_stack_top() != NULL && thread->privileged_stack_top()->contains(addr)) { + tty->print_cr(INTPTR_FORMAT "is pointing into the priviledge stack for thread: " INTPTR_FORMAT, addr, thread); + return; + } + // If the addr is a java thread print information about that. + if (addr == (address)thread) { + thread->print(); + return; + } + } + + // Try an OS specific find + if (os::find(addr)) { + return; + } + + if (print_pc) { + tty->print_cr(INTPTR_FORMAT ": probably in C++ code; check debugger", addr); + Disassembler::decode(same_page(addr-40,addr),same_page(addr+40,addr)); + return; + } + + tty->print_cr(INTPTR_FORMAT "is pointing to unknown location", addr); +} + + +class LookForRefInGenClosure : public OopsInGenClosure { +public: + oop target; + void do_oop(oop* o) { + if (o != NULL && *o == target) { + tty->print_cr("0x%08x", o); + } + } +}; + + +class LookForRefInObjectClosure : public ObjectClosure { +private: + LookForRefInGenClosure look_in_object; +public: + LookForRefInObjectClosure(oop target) { look_in_object.target = target; } + void do_object(oop obj) { + obj->oop_iterate(&look_in_object); + } +}; + + +static void findref(intptr_t x) { + GenCollectedHeap *gch = GenCollectedHeap::heap(); + LookForRefInGenClosure lookFor; + lookFor.target = (oop) x; + LookForRefInObjectClosure look_in_object((oop) x); + + tty->print_cr("Searching heap:"); + gch->object_iterate(&look_in_object); + + tty->print_cr("Searching strong roots:"); + Universe::oops_do(&lookFor, false); + JNIHandles::oops_do(&lookFor); // Global (strong) JNI handles + Threads::oops_do(&lookFor); + ObjectSynchronizer::oops_do(&lookFor); + //FlatProfiler::oops_do(&lookFor); + SystemDictionary::oops_do(&lookFor); + + tty->print_cr("Done."); +} + +class FindClassObjectClosure: public ObjectClosure { + private: + const char* _target; + public: + FindClassObjectClosure(const char name[]) { _target = name; } + + virtual void do_object(oop obj) { + if (obj->is_klass()) { + Klass* k = klassOop(obj)->klass_part(); + if (k->name() != NULL) { + ResourceMark rm; + const char* ext = k->external_name(); + if ( strcmp(_target, ext) == 0 ) { + tty->print_cr("Found " INTPTR_FORMAT, obj); + obj->print(); + } + } + } + } +}; + +// +extern "C" void findclass(const char name[]) { + Command c("findclass"); + if (name != NULL) { + tty->print_cr("Finding class %s -> ", name); + FindClassObjectClosure srch(name); + Universe::heap()->permanent_object_iterate(&srch); + } +} + +// Another interface that isn't ambiguous in dbx. +// Can we someday rename the other find to hsfind? +extern "C" void hsfind(intptr_t x) { + Command c("hsfind"); + find(x, false); +} + + +extern "C" void hsfindref(intptr_t x) { + Command c("hsfindref"); + findref(x); +} + +extern "C" void find(intptr_t x) { + Command c("find"); + find(x, false); +} + + +extern "C" void findpc(intptr_t x) { + Command c("findpc"); + find(x, true); +} + + +// int versions of all methods to avoid having to type type casts in the debugger + +void pp(intptr_t p) { pp((void*)p); } +void pp(oop p) { pp((void*)p); } + +void help() { + Command c("help"); + tty->print_cr("basic"); + tty->print_cr(" pp(void* p) - try to make sense of p"); + tty->print_cr(" pv(intptr_t p)- ((PrintableResourceObj*) p)->print()"); + tty->print_cr(" ps() - print current thread stack"); + tty->print_cr(" pss() - print all thread stacks"); + tty->print_cr(" pm(int pc) - print methodOop given compiled PC"); + tty->print_cr(" findm(intptr_t pc) - finds methodOop"); + tty->print_cr(" find(intptr_t x) - finds & prints nmethod/stub/bytecode/oop based on pointer into it"); + + tty->print_cr("misc."); + tty->print_cr(" flush() - flushes the log file"); + tty->print_cr(" events() - dump last 50 events"); + + + tty->print_cr("compiler debugging"); + tty->print_cr(" debug() - to set things up for compiler debugging"); + tty->print_cr(" ndebug() - undo debug"); +} + +#if 0 + +// BobV's command parser for debugging on windows when nothing else works. + +enum CommandID { + CMDID_HELP, + CMDID_QUIT, + CMDID_HSFIND, + CMDID_PSS, + CMDID_PS, + CMDID_PSF, + CMDID_FINDM, + CMDID_FINDNM, + CMDID_PP, + CMDID_BPT, + CMDID_EXIT, + CMDID_VERIFY, + CMDID_THREADS, + CMDID_ILLEGAL = 99 +}; + +struct CommandParser { + char *name; + CommandID code; + char *description; +}; + +struct CommandParser CommandList[] = { + (char *)"help", CMDID_HELP, " Dump this list", + (char *)"quit", CMDID_QUIT, " Return from this routine", + (char *)"hsfind", CMDID_HSFIND, "Perform an hsfind on an address", + (char *)"ps", CMDID_PS, " Print Current Thread Stack Trace", + (char *)"pss", CMDID_PSS, " Print All Thread Stack Trace", + (char *)"psf", CMDID_PSF, " Print All Stack Frames", + (char *)"findm", CMDID_FINDM, " Find a methodOop from a PC", + (char *)"findnm", CMDID_FINDNM, "Find an nmethod from a PC", + (char *)"pp", CMDID_PP, " Find out something about a pointer", + (char *)"break", CMDID_BPT, " Execute a breakpoint", + (char *)"exitvm", CMDID_EXIT, "Exit the VM", + (char *)"verify", CMDID_VERIFY, "Perform a Heap Verify", + (char *)"thread", CMDID_THREADS, "Dump Info on all Threads", + (char *)0, CMDID_ILLEGAL +}; + + +// get_debug_command() +// +// Read a command from standard input. +// This is useful when you have a debugger +// which doesn't support calling into functions. +// +void get_debug_command() +{ + ssize_t count; + int i,j; + bool gotcommand; + intptr_t addr; + char buffer[256]; + nmethod *nm; + methodOop m; + + tty->print_cr("You have entered the diagnostic command interpreter"); + tty->print("The supported commands are:\n"); + for ( i=0; ; i++ ) { + if ( CommandList[i].code == CMDID_ILLEGAL ) + break; + tty->print_cr(" %s \n", CommandList[i].name ); + } + + while ( 1 ) { + gotcommand = false; + tty->print("Please enter a command: "); + count = scanf("%s", buffer) ; + if ( count >=0 ) { + for ( i=0; ; i++ ) { + if ( CommandList[i].code == CMDID_ILLEGAL ) { + if (!gotcommand) tty->print("Invalid command, please try again\n"); + break; + } + if ( strcmp(buffer, CommandList[i].name) == 0 ) { + gotcommand = true; + switch ( CommandList[i].code ) { + case CMDID_PS: + ps(); + break; + case CMDID_PSS: + pss(); + break; + case CMDID_PSF: + psf(); + break; + case CMDID_FINDM: + tty->print("Please enter the hex addr to pass to findm: "); + scanf("%I64X", &addr); + m = (methodOop)findm(addr); + tty->print("findm(0x%I64X) returned 0x%I64X\n", addr, m); + break; + case CMDID_FINDNM: + tty->print("Please enter the hex addr to pass to findnm: "); + scanf("%I64X", &addr); + nm = (nmethod*)findnm(addr); + tty->print("findnm(0x%I64X) returned 0x%I64X\n", addr, nm); + break; + case CMDID_PP: + tty->print("Please enter the hex addr to pass to pp: "); + scanf("%I64X", &addr); + pp((void*)addr); + break; + case CMDID_EXIT: + exit(0); + case CMDID_HELP: + tty->print("Here are the supported commands: "); + for ( j=0; ; j++ ) { + if ( CommandList[j].code == CMDID_ILLEGAL ) + break; + tty->print_cr(" %s -- %s\n", CommandList[j].name, + CommandList[j].description ); + } + break; + case CMDID_QUIT: + return; + break; + case CMDID_BPT: + BREAKPOINT; + break; + case CMDID_VERIFY: + verify();; + break; + case CMDID_THREADS: + threads();; + break; + case CMDID_HSFIND: + tty->print("Please enter the hex addr to pass to hsfind: "); + scanf("%I64X", &addr); + tty->print("Calling hsfind(0x%I64X)\n", addr); + hsfind(addr); + break; + default: + case CMDID_ILLEGAL: + break; + } + } + } + } + } +} +#endif + +#endif // PRODUCT diff --git a/src/share/vm/utilities/debug.hpp b/src/share/vm/utilities/debug.hpp new file mode 100644 index 000000000..98d3e6226 --- /dev/null +++ b/src/share/vm/utilities/debug.hpp @@ -0,0 +1,129 @@ +/* + * 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. + * + */ + +// assertions +#ifdef ASSERT +// Turn this off by default: +//#define USE_REPEATED_ASSERTS +#ifdef USE_REPEATED_ASSERTS + #define assert(p,msg) \ + { for (int __i = 0; __i < AssertRepeat; __i++) { \ + if (!(p)) { \ + report_assertion_failure(__FILE__, __LINE__, \ + "assert(" XSTR(p) ",\"" msg "\")");\ + BREAKPOINT; \ + } \ + } \ + } +#else + #define assert(p,msg) \ + if (!(p)) { \ + report_assertion_failure(__FILE__, __LINE__, \ + "assert(" XSTR(p) ",\"" msg "\")");\ + BREAKPOINT; \ + } +#endif + +// This version of assert is for use with checking return status from +// library calls that return actual error values eg. EINVAL, +// ENOMEM etc, rather than returning -1 and setting errno. +// When the status is not what is expected it is very useful to know +// what status was actually returned, so we pass the status variable as +// an extra arg and use strerror to convert it to a meaningful string +// like "Invalid argument", "out of memory" etc +#define assert_status(p, status, msg) \ + do { \ + if (!(p)) { \ + char buf[128]; \ + snprintf(buf, 127, \ + "assert_status(" XSTR(p) ", error: %s(%d), \"" msg "\")" , \ + strerror((status)), (status)); \ + report_assertion_failure(__FILE__, __LINE__, buf); \ + BREAKPOINT; \ + } \ + } while (0) + +// Another version of assert where the message is not a string literal +// The boolean condition is not printed out because cpp doesn't like it. +#define assert_msg(p, msg) \ + if (!(p)) { \ + report_assertion_failure(__FILE__, __LINE__, msg); \ + BREAKPOINT; \ + } + +// Do not assert this condition if there's already another error reported. +#define assert_if_no_error(cond,msg) assert((cond) || is_error_reported(), msg) +#else + #define assert(p,msg) + #define assert_status(p,status,msg) + #define assert_if_no_error(cond,msg) + #define assert_msg(cond,msg) +#endif + + +// fatals +#define fatal(m) { report_fatal(__FILE__, __LINE__, m ); BREAKPOINT; } +#define fatal1(m,x1) { report_fatal_vararg(__FILE__, __LINE__, m, x1 ); BREAKPOINT; } +#define fatal2(m,x1,x2) { report_fatal_vararg(__FILE__, __LINE__, m, x1, x2 ); BREAKPOINT; } +#define fatal3(m,x1,x2,x3) { report_fatal_vararg(__FILE__, __LINE__, m, x1, x2, x3 ); BREAKPOINT; } +#define fatal4(m,x1,x2,x3,x4) { report_fatal_vararg(__FILE__, __LINE__, m, x1, x2, x3, x4 ); BREAKPOINT; } + +// out of memory +#define vm_exit_out_of_memory(s,m) { report_vm_out_of_memory(__FILE__, __LINE__, s, m ); BREAKPOINT; } +#define vm_exit_out_of_memory1(s,m,x1) { report_vm_out_of_memory_vararg(__FILE__, __LINE__, s, m, x1 ); BREAKPOINT; } +#define vm_exit_out_of_memory2(s,m,x1,x2) { report_vm_out_of_memory_vararg(__FILE__, __LINE__, s, m, x1, x2 ); BREAKPOINT; } +#define vm_exit_out_of_memory3(s,m,x1,x2,x3) { report_vm_out_of_memory_vararg(__FILE__, __LINE__, s, m, x1, x2, x3 ); BREAKPOINT; } +#define vm_exit_out_of_memory4(s,m,x1,x2,x3,x4) { report_vm_out_of_memory_vararg(__FILE__, __LINE__, s, m, x1, x2, x3, x4); BREAKPOINT; } + +// guarantee is like assert except it's always executed -- use it for +// cheap tests that catch errors that would otherwise be hard to find +// guarantee is also used for Verify options. +#define guarantee(b,msg) { if (!(b)) fatal("guarantee(" XSTR(b) ",\"" msg "\")"); } + +#define ShouldNotCallThis() { report_should_not_call (__FILE__, __LINE__); BREAKPOINT; } +#define ShouldNotReachHere() { report_should_not_reach_here (__FILE__, __LINE__); BREAKPOINT; } +#define Unimplemented() { report_unimplemented (__FILE__, __LINE__); BREAKPOINT; } +#define Untested(msg) { report_untested (__FILE__, __LINE__, msg); BREAKPOINT; } + +// error reporting helper functions +void report_assertion_failure(const char* file_name, int line_no, const char* message); +void report_fatal_vararg(const char* file_name, int line_no, const char* format, ...); +void report_fatal(const char* file_name, int line_no, const char* message); +void report_vm_out_of_memory_vararg(const char* file_name, int line_no, size_t size, const char* format, ...); +void report_vm_out_of_memory(const char* file_name, int line_no, size_t size, const char* message); +void report_should_not_call(const char* file_name, int line_no); +void report_should_not_reach_here(const char* file_name, int line_no); +void report_unimplemented(const char* file_name, int line_no); +void report_untested(const char* file_name, int line_no, const char* msg); +void warning(const char* format, ...); + +// out of memory reporting +void report_java_out_of_memory(const char* message); + +// Support for self-destruct +bool is_error_reported(); +void set_error_reported(); + +void pd_ps(frame f); +void pd_obfuscate_location(char *buf, size_t buflen); diff --git a/src/share/vm/utilities/defaultStream.hpp b/src/share/vm/utilities/defaultStream.hpp new file mode 100644 index 000000000..979f5cd74 --- /dev/null +++ b/src/share/vm/utilities/defaultStream.hpp @@ -0,0 +1,90 @@ +/* + * Copyright 2003-2004 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 defaultStream : public xmlTextStream { + friend void ostream_abort(); + public: + enum { NO_WRITER = -1 }; + private: + bool _inited; + fileStream* _log_file; // XML-formatted file shared by all threads + static int _output_fd; + static int _error_fd; + static FILE* _output_stream; + static FILE* _error_stream; + + void init(); + void init_log(); + void finish_log(); + void finish_log_on_error(char *buf, int buflen); + public: + // must defer time stamp due to the fact that os::init() hasn't + // yet been called and os::elapsed_counter() may not be valid + defaultStream() { + _log_file = NULL; + _inited = false; + _writer = -1; + _last_writer = -1; + } + + ~defaultStream() { + if (has_log_file()) finish_log(); + } + + static inline FILE* output_stream() { + return DisplayVMOutputToStderr ? _error_stream : _output_stream; + } + static inline FILE* error_stream() { + return DisplayVMOutputToStdout ? _output_stream : _error_stream; + } + static inline int output_fd() { + return DisplayVMOutputToStderr ? _error_fd : _output_fd; + } + static inline int error_fd() { + return DisplayVMOutputToStdout ? _output_fd : _error_fd; + } + + virtual void write(const char* s, size_t len); + + void flush() { + // once we can determine whether we are in a signal handler, we + // should add the following assert here: + // assert(xxxxxx, "can not flush buffer inside signal handler"); + xmlTextStream::flush(); + fflush(output_stream()); + if (has_log_file()) _log_file->flush(); + } + + // advisory lock/unlock of _writer field: + private: + intx _writer; // thread_id with current rights to output + intx _last_writer; + public: + intx hold(intx writer_id); + void release(intx holder); + intx writer() { return _writer; } + bool has_log_file(); + + static defaultStream* instance; // sole instance +}; diff --git a/src/share/vm/utilities/dtrace.hpp b/src/share/vm/utilities/dtrace.hpp new file mode 100644 index 000000000..e4e9f03a4 --- /dev/null +++ b/src/share/vm/utilities/dtrace.hpp @@ -0,0 +1,125 @@ +/* + * Copyright 2005-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. + * + */ + +#if defined(SOLARIS) && defined(DTRACE_ENABLED) + +#include <sys/sdt.h> + +#define DTRACE_ONLY(x) x +#define NOT_DTRACE(x) + +#else // ndef SOLARIS || ndef DTRACE_ENABLED + +#define DTRACE_ONLY(x) +#define NOT_DTRACE(x) x + +#define DTRACE_PROBE(a,b) {;} +#define DTRACE_PROBE1(a,b,c) {;} +#define DTRACE_PROBE2(a,b,c,d) {;} +#define DTRACE_PROBE3(a,b,c,d,e) {;} +#define DTRACE_PROBE4(a,b,c,d,e,f) {;} +#define DTRACE_PROBE5(a,b,c,d,e,f,g) {;} + +#endif + +#define HS_DTRACE_PROBE_FN(provider,name)\ + __dtrace_##provider##___##name + +#define HS_DTRACE_PROBE_DECL_N(provider,name,args) \ + DTRACE_ONLY(extern "C" void HS_DTRACE_PROBE_FN(provider,name) args) +#define HS_DTRACE_PROBE_CDECL_N(provider,name,args) \ + DTRACE_ONLY(extern void HS_DTRACE_PROBE_FN(provider,name) args) + +/* Dtrace probe declarations */ +#define HS_DTRACE_PROBE_DECL(provider,name) \ + HS_DTRACE_PROBE_DECL0(provider,name) +#define HS_DTRACE_PROBE_DECL0(provider,name)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(void)) +#define HS_DTRACE_PROBE_DECL1(provider,name,t0)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(uintptr_t)) +#define HS_DTRACE_PROBE_DECL2(provider,name,t0,t1)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(uintptr_t,uintptr_t)) +#define HS_DTRACE_PROBE_DECL3(provider,name,t0,t1,t2)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(uintptr_t,uintptr_t,uintptr_t)) +#define HS_DTRACE_PROBE_DECL4(provider,name,t0,t1,t2,t3)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(uintptr_t,uintptr_t,uintptr_t,\ + uintptr_t)) +#define HS_DTRACE_PROBE_DECL5(provider,name,t0,t1,t2,t3,t4)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(\ + uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t)) +#define HS_DTRACE_PROBE_DECL6(provider,name,t0,t1,t2,t3,t4,t5)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(\ + uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t)) +#define HS_DTRACE_PROBE_DECL7(provider,name,t0,t1,t2,t3,t4,t5,t6)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(\ + uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t)) +#define HS_DTRACE_PROBE_DECL8(provider,name,t0,t1,t2,t3,t4,t5,t6,t7)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(\ + uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,\ + uintptr_t)) +#define HS_DTRACE_PROBE_DECL9(provider,name,t0,t1,t2,t3,t4,t5,t6,t7,t8)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(\ + uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,\ + uintptr_t,uintptr_t)) +#define HS_DTRACE_PROBE_DECL10(provider,name,t0,t1,t2,t3,t4,t5,t6,t7,t8,t9)\ + HS_DTRACE_PROBE_DECL_N(provider,name,(\ + uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,uintptr_t,\ + uintptr_t,uintptr_t,uintptr_t)) + +/* Dtrace probe definitions */ +#define HS_DTRACE_PROBE_N(provider,name, args) \ + DTRACE_ONLY(HS_DTRACE_PROBE_FN(provider,name) args) + +#define HS_DTRACE_PROBE(provider,name) HS_DTRACE_PROBE0(provider,name) +#define HS_DTRACE_PROBE0(provider,name)\ + HS_DTRACE_PROBE_N(provider,name,()) +#define HS_DTRACE_PROBE1(provider,name,a0)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0)) +#define HS_DTRACE_PROBE2(provider,name,a0,a1)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1)) +#define HS_DTRACE_PROBE3(provider,name,a0,a1,a2)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2)) +#define HS_DTRACE_PROBE4(provider,name,a0,a1,a2,a3)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3)) +#define HS_DTRACE_PROBE5(provider,name,a0,a1,a2,a3,a4)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3,(uintptr_t)a4)) +#define HS_DTRACE_PROBE6(provider,name,a0,a1,a2,a3,a4,a5)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3,(uintptr_t)a4,(uintptr_t)a5)) +#define HS_DTRACE_PROBE7(provider,name,a0,a1,a2,a3,a4,a5,a6)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3,(uintptr_t)a4,(uintptr_t)a5,(uintptr_t)a6)) +#define HS_DTRACE_PROBE8(provider,name,a0,a1,a2,a3,a4,a5,a6,a7)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3,(uintptr_t)a4,(uintptr_t)a5,(uintptr_t)a6,(uintptr_t)a7)) +#define HS_DTRACE_PROBE9(provider,name,a0,a1,a2,a3,a4,a5,a6,a7,a8)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3,(uintptr_t)a4,(uintptr_t)a5,(uintptr_t)a6,(uintptr_t)a7,\ + (uintptr_t)a8)) +#define HS_DTRACE_PROBE10(provider,name,a0,a1,a2,a3,a4,a5,a6,a7,a8,a9)\ + HS_DTRACE_PROBE_N(provider,name,((uintptr_t)a0,(uintptr_t)a1,(uintptr_t)a2,\ + (uintptr_t)a3,(uintptr_t)a4,(uintptr_t)a5,(uintptr_t)a6,(uintptr_t)a7,\ + (uintptr_t)a8,(uintptr_t)a9)) diff --git a/src/share/vm/utilities/events.cpp b/src/share/vm/utilities/events.cpp new file mode 100644 index 000000000..b24452e83 --- /dev/null +++ b/src/share/vm/utilities/events.cpp @@ -0,0 +1,249 @@ +/* + * Copyright 1997-2006 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/_events.cpp.incl" + + +#ifndef PRODUCT + +//////////////////////////////////////////////////////////////////////////// +// Event + +typedef u4 EventID; + +class Event VALUE_OBJ_CLASS_SPEC { + private: + jlong _time_tick; + intx _thread_id; + const char* _format; + int _indent; + intptr_t _arg_1; + intptr_t _arg_2; + intptr_t _arg_3; + + // only EventBuffer::add_event() can assign event id + friend class EventBuffer; + EventID _id; + + public: + + void clear() { _format = NULL; } + + EventID id() const { return _id; } + + void fill(int indent, const char* format, intptr_t arg_1, intptr_t arg_2, intptr_t arg_3) { + _format = format; + _arg_1 = arg_1; + _arg_2 = arg_2; + _arg_3 = arg_3; + + _indent = indent; + + _thread_id = os::current_thread_id(); + _time_tick = os::elapsed_counter(); + } + + void print_on(outputStream *st) { + if (_format == NULL) return; + st->print(" %d", _thread_id); + st->print(" %3.2g ", (double)_time_tick / os::elapsed_frequency()); + st->fill_to(20); + for (int index = 0; index < _indent; index++) { + st->print("| "); + } + st->print_cr(_format, _arg_1, _arg_2, _arg_3); + } +}; + +//////////////////////////////////////////////////////////////////////////// +// EventBuffer +// +// Simple lock-free event queue. Every event has a unique 32-bit id. +// It's fine if two threads add events at the same time, because they +// will get different event id, and then write to different buffer location. +// However, it is assumed that add_event() is quick enough (or buffer size +// is big enough), so when one thread is adding event, there can't be more +// than "size" events created by other threads; otherwise we'll end up having +// two threads writing to the same location. + +class EventBuffer : AllStatic { + private: + static Event* buffer; + static int size; + static jint indent; + static volatile EventID _current_event_id; + + static EventID get_next_event_id() { + return (EventID)Atomic::add(1, (jint*)&_current_event_id); + } + + public: + static void inc_indent() { Atomic::inc(&indent); } + static void dec_indent() { Atomic::dec(&indent); } + + static bool get_event(EventID id, Event* event) { + int index = (int)(id % size); + if (buffer[index].id() == id) { + memcpy(event, &buffer[index], sizeof(Event)); + // check id again; if buffer[index] is being updated by another thread, + // event->id() will contain different value. + return (event->id() == id); + } else { + // id does not match - id is invalid, or event is overwritten + return false; + } + } + + // add a new event to the queue; if EventBuffer is full, this call will + // overwrite the oldest event in the queue + static EventID add_event(const char* format, + intptr_t arg_1, intptr_t arg_2, intptr_t arg_3) { + // assign a unique id + EventID id = get_next_event_id(); + + // event will be copied to buffer[index] + int index = (int)(id % size); + + // first, invalidate id, buffer[index] can't have event with id = index + 2 + buffer[index]._id = index + 2; + + // make sure everyone has seen that buffer[index] is invalid + OrderAccess::fence(); + + // ... before updating its value + buffer[index].fill(indent, format, arg_1, arg_2, arg_3); + + // finally, set up real event id, now buffer[index] contains valid event + OrderAccess::release_store(&(buffer[index]._id), id); + + return id; + } + + static void print_last(outputStream *st, int number) { + st->print_cr("[Last %d events in the event buffer]", number); + st->print_cr("-<thd>-<elapsed sec>-<description>---------------------"); + + int count = 0; + EventID id = _current_event_id; + while (count < number) { + Event event; + if (get_event(id, &event)) { + event.print_on(st); + } + id--; + count++; + } + } + + static void print_all(outputStream* st) { + print_last(st, size); + } + + static void init() { + // Allocate the event buffer + size = EventLogLength; + buffer = NEW_C_HEAP_ARRAY(Event, size); + + _current_event_id = 0; + + // Clear the event buffer + for (int index = 0; index < size; index++) { + buffer[index]._id = index + 1; // index + 1 is invalid id + buffer[index].clear(); + } + } +}; + +Event* EventBuffer::buffer; +int EventBuffer::size; +volatile EventID EventBuffer::_current_event_id; +int EventBuffer::indent; + +//////////////////////////////////////////////////////////////////////////// +// Events + +// Events::log() is safe for signal handlers +void Events::log(const char* format, ...) { + if (LogEvents) { + va_list ap; + va_start(ap, format); + intptr_t arg_1 = va_arg(ap, intptr_t); + intptr_t arg_2 = va_arg(ap, intptr_t); + intptr_t arg_3 = va_arg(ap, intptr_t); + va_end(ap); + + EventBuffer::add_event(format, arg_1, arg_2, arg_3); + } +} + +void Events::print_all(outputStream *st) { + EventBuffer::print_all(st); +} + +void Events::print_last(outputStream *st, int number) { + EventBuffer::print_last(st, number); +} + +/////////////////////////////////////////////////////////////////////////// +// EventMark + +EventMark::EventMark(const char* format, ...) { + if (LogEvents) { + va_list ap; + va_start(ap, format); + intptr_t arg_1 = va_arg(ap, intptr_t); + intptr_t arg_2 = va_arg(ap, intptr_t); + intptr_t arg_3 = va_arg(ap, intptr_t); + va_end(ap); + + EventBuffer::add_event(format, arg_1, arg_2, arg_3); + EventBuffer::inc_indent(); + } +} + +EventMark::~EventMark() { + if (LogEvents) { + EventBuffer::dec_indent(); + EventBuffer::add_event("done", 0, 0, 0); + } +} + +/////////////////////////////////////////////////////////////////////////// + +void eventlog_init() { + EventBuffer::init(); +} + +int print_all_events(outputStream *st) { + EventBuffer::print_all(st); + return 1; +} + +#else + +void eventlog_init() {} +int print_all_events(outputStream *st) { return 0; } + +#endif // PRODUCT diff --git a/src/share/vm/utilities/events.hpp b/src/share/vm/utilities/events.hpp new file mode 100644 index 000000000..fa76807aa --- /dev/null +++ b/src/share/vm/utilities/events.hpp @@ -0,0 +1,64 @@ +/* + * Copyright 1997-2003 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. + * + */ + +// Events and EventMark provide interfaces to log events taking place in the vm. +// This facility is extremly useful for post-mortem debugging. The eventlog +// often provides crucial information about events leading up to the crash. +// +// All arguments past the format string must be passed as an intptr_t. +// +// To log a single event use: +// Events::log("New nmethod has been created " INTPTR_FORMAT, nm); +// +// To log a block of events use: +// EventMark m("GarbageCollecting %d", (intptr_t)gc_number); +// +// The constructor to eventlog indents the eventlog until the +// destructor has been executed. +// +// IMPLEMENTATION RESTRICTION: +// Max 3 arguments are saved for each logged event. +// + +class Events : AllStatic { + public: + // Logs an event, format as printf + static void log(const char* format, ...) PRODUCT_RETURN; + + // Prints all events in the buffer + static void print_all(outputStream* st) PRODUCT_RETURN; + + // Prints last number events from the event buffer + static void print_last(outputStream *st, int number) PRODUCT_RETURN; +}; + +class EventMark : public StackObj { + public: + // log a begin event, format as printf + EventMark(const char* format, ...) PRODUCT_RETURN; + // log an end event + ~EventMark() PRODUCT_RETURN; +}; + +int print_all_events(outputStream *st); diff --git a/src/share/vm/utilities/exceptions.cpp b/src/share/vm/utilities/exceptions.cpp new file mode 100644 index 000000000..91c3cd733 --- /dev/null +++ b/src/share/vm/utilities/exceptions.cpp @@ -0,0 +1,388 @@ +/* + * Copyright 1998-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/_exceptions.cpp.incl" + + +// Implementation of ThreadShadow +void check_ThreadShadow() { + const ByteSize offset1 = byte_offset_of(ThreadShadow, _pending_exception); + const ByteSize offset2 = Thread::pending_exception_offset(); + if (offset1 != offset2) fatal("ThreadShadow::_pending_exception is not positioned correctly"); +} + + +void ThreadShadow::set_pending_exception(oop exception, const char* file, int line) { + assert(exception != NULL && exception->is_oop(), "invalid exception oop"); + _pending_exception = exception; + _exception_file = file; + _exception_line = line; +} + +void ThreadShadow::clear_pending_exception() { + if (TraceClearedExceptions) { + if (_pending_exception != NULL) { + tty->print_cr("Thread::clear_pending_exception: cleared exception:"); + _pending_exception->print(); + } + } + _pending_exception = NULL; + _exception_file = NULL; + _exception_line = 0; +} +// Implementation of Exceptions + +bool Exceptions::special_exception(Thread* thread, const char* file, int line, Handle h_exception) { + // bootstrapping check + if (!Universe::is_fully_initialized()) { + vm_exit_during_initialization(h_exception); + ShouldNotReachHere(); + } + + if (thread->is_VM_thread() + || thread->is_Compiler_thread() ) { + // We do not care what kind of exception we get for the vm-thread or a thread which + // is compiling. We just install a dummy exception object + thread->set_pending_exception(Universe::vm_exception(), file, line); + return true; + } + + return false; +} + +bool Exceptions::special_exception(Thread* thread, const char* file, int line, symbolHandle h_name, const char* message) { + // bootstrapping check + if (!Universe::is_fully_initialized()) { + if (h_name.is_null()) { + // atleast an informative message. + vm_exit_during_initialization("Exception", message); + } else { + vm_exit_during_initialization(h_name, message); + } + ShouldNotReachHere(); + } + + if (thread->is_VM_thread() + || thread->is_Compiler_thread() ) { + // We do not care what kind of exception we get for the vm-thread or a thread which + // is compiling. We just install a dummy exception object + thread->set_pending_exception(Universe::vm_exception(), file, line); + return true; + } + + return false; +} + +// This method should only be called from generated code, +// therefore the exception oop should be in the oopmap. +void Exceptions::_throw_oop(Thread* thread, const char* file, int line, oop exception) { + assert(exception != NULL, "exception should not be NULL"); + Handle h_exception = Handle(thread, exception); + _throw(thread, file, line, h_exception); +} + +void Exceptions::_throw(Thread* thread, const char* file, int line, Handle h_exception) { + assert(h_exception() != NULL, "exception should not be NULL"); + + // tracing (do this up front - so it works during boot strapping) + if (TraceExceptions) { + ttyLocker ttyl; + ResourceMark rm; + tty->print_cr("Exception <%s> (" INTPTR_FORMAT " ) \nthrown [%s, line %d]\nfor thread " INTPTR_FORMAT, + h_exception->print_value_string(), (address)h_exception(), file, line, thread); + } + // for AbortVMOnException flag + NOT_PRODUCT(Exceptions::debug_check_abort(h_exception)); + + // Check for special boot-strapping/vm-thread handling + if (special_exception(thread, file, line, h_exception)) return; + + assert(h_exception->is_a(SystemDictionary::throwable_klass()), "exception is not a subclass of java/lang/Throwable"); + + // set the pending exception + thread->set_pending_exception(h_exception(), file, line); + + // vm log + Events::log("throw_exception " INTPTR_FORMAT, (address)h_exception()); +} + + +void Exceptions::_throw_msg(Thread* thread, const char* file, int line, symbolHandle h_name, const char* message, Handle h_loader, Handle h_protection_domain) { + // Check for special boot-strapping/vm-thread handling + if (special_exception(thread, file, line, h_name, message)) return; + // Create and throw exception + Handle h_cause(thread, NULL); + Handle h_exception = new_exception(thread, h_name, message, h_cause, h_loader, h_protection_domain); + _throw(thread, file, line, h_exception); +} + +// Throw an exception with a message and a cause +void Exceptions::_throw_msg_cause(Thread* thread, const char* file, int line, symbolHandle h_name, const char* message, Handle h_cause, Handle h_loader, Handle h_protection_domain) { + // Check for special boot-strapping/vm-thread handling + if (special_exception(thread, file, line, h_name, message)) return; + // Create and throw exception and init cause + Handle h_exception = new_exception(thread, h_name, message, h_cause, h_loader, h_protection_domain); + _throw(thread, file, line, h_exception); +} + +// This version creates handles and calls the other version +void Exceptions::_throw_msg(Thread* thread, const char* file, int line, + symbolOop name, const char* message) { + symbolHandle h_name(thread, name); + Handle h_loader(thread, NULL); + Handle h_protection_domain(thread, NULL); + Exceptions::_throw_msg(thread, file, line, h_name, message, h_loader, h_protection_domain); +} + +// This version already has a handle for name +void Exceptions::_throw_msg(Thread* thread, const char* file, int line, + symbolHandle name, const char* message) { + Handle h_loader(thread, NULL); + Handle h_protection_domain(thread, NULL); + Exceptions::_throw_msg(thread, file, line, name, message, h_loader, h_protection_domain); +} + +// This version already has a handle for name +void Exceptions::_throw_msg_cause(Thread* thread, const char* file, int line, + symbolHandle name, const char* message, Handle cause) { + Handle h_loader(thread, NULL); + Handle h_protection_domain(thread, NULL); + Exceptions::_throw_msg_cause(thread, file, line, name, message, cause, h_loader, h_protection_domain); +} + +void Exceptions::_throw_args(Thread* thread, const char* file, int line, symbolHandle h_name, symbolHandle h_signature, JavaCallArguments *args) { + // Check for special boot-strapping/vm-thread handling + if (special_exception(thread, file, line, h_name, NULL)) return; + // Create and throw exception + Handle h_loader(thread, NULL); + Handle h_prot(thread, NULL); + Handle h_cause(thread, NULL); + Handle exception = new_exception(thread, h_name, h_signature, args, h_cause, h_loader, h_prot); + _throw(thread, file, line, exception); +} + + +void Exceptions::throw_stack_overflow_exception(Thread* THREAD, const char* file, int line) { + Handle exception; + if (!THREAD->has_pending_exception()) { + klassOop k = SystemDictionary::StackOverflowError_klass(); + oop e = instanceKlass::cast(k)->allocate_instance(CHECK); + exception = Handle(THREAD, e); // fill_in_stack trace does gc + if (StackTraceInThrowable) { + java_lang_Throwable::fill_in_stack_trace(exception); + } + } else { + // if prior exception, throw that one instead + exception = Handle(THREAD, THREAD->pending_exception()); + } + _throw_oop(THREAD, file, line, exception()); +} + +void Exceptions::fthrow(Thread* thread, const char* file, int line, symbolHandle h_name, const char* format, ...) { + const int max_msg_size = 1024; + va_list ap; + va_start(ap, format); + char msg[max_msg_size]; + vsnprintf(msg, max_msg_size, format, ap); + msg[max_msg_size-1] = '\0'; + va_end(ap); + _throw_msg(thread, file, line, h_name, msg); +} + +// Creates an exception oop, calls the <init> method with the given signature. +// and returns a Handle +// Initializes the cause if cause non-null +Handle Exceptions::new_exception(Thread *thread, symbolHandle h_name, + symbolHandle signature, + JavaCallArguments *args, + Handle h_cause, Handle h_loader, + Handle h_protection_domain) { + assert(Universe::is_fully_initialized(), + "cannot be called during initialization"); + assert(thread->is_Java_thread(), "can only be called by a Java thread"); + assert(!thread->has_pending_exception(), "already has exception"); + + Handle h_exception; + + // Resolve exception klass + klassOop ik = SystemDictionary::resolve_or_fail(h_name, h_loader, h_protection_domain, true, thread); + instanceKlassHandle klass (thread, ik); + + if (!thread->has_pending_exception()) { + assert(klass.not_null(), "klass must exist"); + // We are about to create an instance - so make sure that klass is initialized + klass->initialize(thread); + if (!thread->has_pending_exception()) { + // Allocate new exception + h_exception = klass->allocate_instance_handle(thread); + if (!thread->has_pending_exception()) { + JavaValue result(T_VOID); + args->set_receiver(h_exception); + // Call constructor + JavaCalls::call_special(&result, klass, + vmSymbolHandles::object_initializer_name(), + signature, + args, + thread); + + } + } + + // Future: object initializer should take a cause argument + if (h_cause() != NULL) { + assert(h_cause->is_a(SystemDictionary::throwable_klass()), + "exception cause is not a subclass of java/lang/Throwable"); + JavaValue result1(T_OBJECT); + JavaCallArguments args1; + args1.set_receiver(h_exception); + args1.push_oop(h_cause); + JavaCalls::call_virtual(&result1, klass, + vmSymbolHandles::initCause_name(), + vmSymbolHandles::throwable_throwable_signature(), + &args1, + thread); + } + } + + // Check if another exception was thrown in the process, if so rethrow that one + if (thread->has_pending_exception()) { + h_exception = Handle(thread, thread->pending_exception()); + thread->clear_pending_exception(); + } + return h_exception; +} + +// Convenience method. Calls either the <init>() or <init>(String) method when +// creating a new exception +Handle Exceptions::new_exception(Thread* thread, symbolHandle h_name, + const char* message, Handle h_cause, + Handle h_loader, + Handle h_protection_domain, + ExceptionMsgToUtf8Mode to_utf8_safe) { + JavaCallArguments args; + symbolHandle signature; + if (message == NULL) { + signature = vmSymbolHandles::void_method_signature(); + } else { + // We want to allocate storage, but we can't do that if there's + // a pending exception, so we preserve any pending exception + // around the allocation. + // If we get an exception from the allocation, prefer that to + // the exception we are trying to build, or the pending exception. + // This is sort of like what PRESERVE_EXCEPTION_MARK does, except + // for the preferencing and the early returns. + Handle incoming_exception (thread, NULL); + if (thread->has_pending_exception()) { + incoming_exception = Handle(thread, thread->pending_exception()); + thread->clear_pending_exception(); + } + Handle msg; + if (to_utf8_safe == safe_to_utf8) { + // Make a java UTF8 string. + msg = java_lang_String::create_from_str(message, thread); + } else { + // Make a java string keeping the encoding scheme of the original string. + msg = java_lang_String::create_from_platform_dependent_str(message, thread); + } + if (thread->has_pending_exception()) { + Handle exception(thread, thread->pending_exception()); + thread->clear_pending_exception(); + return exception; + } + if (incoming_exception.not_null()) { + return incoming_exception; + } + args.push_oop(msg); + signature = vmSymbolHandles::string_void_signature(); + } + return new_exception(thread, h_name, signature, &args, h_cause, h_loader, h_protection_domain); +} + +// Another convenience method that creates handles for null class loaders and +// protection domains and null causes. +// If the last parameter 'to_utf8_mode' is safe_to_utf8, +// it means we can safely ignore the encoding scheme of the message string and +// convert it directly to a java UTF8 string. Otherwise, we need to take the +// encoding scheme of the string into account. One thing we should do at some +// point is to push this flag down to class java_lang_String since other +// classes may need similar functionalities. +Handle Exceptions::new_exception(Thread* thread, + symbolOop name, + const char* message, + ExceptionMsgToUtf8Mode to_utf8_safe) { + + symbolHandle h_name(thread, name); + Handle h_loader(thread, NULL); + Handle h_prot(thread, NULL); + Handle h_cause(thread, NULL); + return Exceptions::new_exception(thread, h_name, message, h_cause, h_loader, + h_prot, to_utf8_safe); +} + +// Implementation of ExceptionMark + +ExceptionMark::ExceptionMark(Thread*& thread) { + thread = Thread::current(); + _thread = thread; + if (_thread->has_pending_exception()) { + oop exception = _thread->pending_exception(); + _thread->clear_pending_exception(); // Needed to avoid infinite recursion + exception->print(); + fatal("ExceptionMark constructor expects no pending exceptions"); + } +} + + +ExceptionMark::~ExceptionMark() { + if (_thread->has_pending_exception()) { + Handle exception(_thread, _thread->pending_exception()); + _thread->clear_pending_exception(); // Needed to avoid infinite recursion + if (is_init_completed()) { + exception->print(); + fatal("ExceptionMark destructor expects no pending exceptions"); + } else { + vm_exit_during_initialization(exception); + } + } +} + +// ---------------------------------------------------------------------------------------- + +#ifndef PRODUCT +// caller frees value_string if necessary +void Exceptions::debug_check_abort(const char *value_string) { + if (AbortVMOnException != NULL && value_string != NULL && + strstr(value_string, AbortVMOnException)) { + fatal1("Saw %s, aborting", value_string); + } +} + +void Exceptions::debug_check_abort(Handle exception) { + if (AbortVMOnException != NULL) { + ResourceMark rm; + debug_check_abort(instanceKlass::cast(exception()->klass())->external_name()); + } +} +#endif diff --git a/src/share/vm/utilities/exceptions.hpp b/src/share/vm/utilities/exceptions.hpp new file mode 100644 index 000000000..e8388ad4a --- /dev/null +++ b/src/share/vm/utilities/exceptions.hpp @@ -0,0 +1,275 @@ +/* + * Copyright 1998-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. + * + */ + +// This file provides the basic support for exception handling in the VM. +// Note: We do not use C++ exceptions to avoid compiler dependencies and +// unpredictable performance. +// +// Scheme: Exceptions are stored with the thread. There is never more +// than one pending exception per thread. All functions that can throw +// an exception carry a THREAD argument (usually the last argument and +// declared with the TRAPS macro). Throwing an exception means setting +// a pending exception in the thread. Upon return from a function that +// can throw an exception, we must check if an exception is pending. +// The CHECK macros do this in a convenient way. Carrying around the +// thread provides also convenient access to it (e.g. for Handle +// creation, w/o the need for recomputation). + + + +// Forward declarations to be independent of the include structure. +// This allows us to have exceptions.hpp included in top.hpp. + +class Thread; +class Handle; +class symbolHandle; +class symbolOopDesc; +class JavaCallArguments; + +// The ThreadShadow class is a helper class to access the _pending_exception +// field of the Thread class w/o having access to the Thread's interface (for +// include hierachy reasons). + +class ThreadShadow: public CHeapObj { + protected: + oop _pending_exception; // Thread has gc actions. + const char* _exception_file; // file information for exception (debugging only) + int _exception_line; // line information for exception (debugging only) + friend void check_ThreadShadow(); // checks _pending_exception offset + + // The following virtual exists only to force creation of a vtable. + // We need ThreadShadow to have a vtable, even in product builds, + // so that its layout will start at an offset of zero relative to Thread. + // Some C++ compilers are so "clever" that they put the ThreadShadow + // base class at offset 4 in Thread (after Thread's vtable), if they + // notice that Thread has a vtable but ThreadShadow does not. + virtual void unused_initial_virtual() { } + + public: + oop pending_exception() const { return _pending_exception; } + bool has_pending_exception() const { return _pending_exception != NULL; } + const char* exception_file() const { return _exception_file; } + int exception_line() const { return _exception_line; } + + // Code generation support + static ByteSize pending_exception_offset() { return byte_offset_of(ThreadShadow, _pending_exception); } + + // use THROW whenever possible! + void set_pending_exception(oop exception, const char* file, int line); + + // use CLEAR_PENDING_EXCEPTION whenever possible! + void clear_pending_exception(); + + ThreadShadow() : _pending_exception(NULL), + _exception_file(NULL), _exception_line(0) {} +}; + + +// Exceptions is a helper class that encapsulates all operations +// that require access to the thread interface and which are +// relatively rare. The Exceptions operations should only be +// used directly if the macros below are insufficient. + +class Exceptions { + static bool special_exception(Thread *thread, const char* file, int line, Handle exception); + static bool special_exception(Thread* thread, const char* file, int line, symbolHandle name, const char* message); + public: + // this enum is defined to indicate whether it is safe to + // ignore the encoding scheme of the original message string. + typedef enum { + safe_to_utf8 = 0, + unsafe_to_utf8 = 1 + } ExceptionMsgToUtf8Mode; + // Throw exceptions: w/o message, w/ message & with formatted message. + static void _throw_oop(Thread* thread, const char* file, int line, oop exception); + static void _throw(Thread* thread, const char* file, int line, Handle exception); + static void _throw_msg(Thread* thread, const char* file, int line, + symbolHandle name, const char* message, Handle loader, + Handle protection_domain); + static void _throw_msg(Thread* thread, const char* file, int line, + symbolOop name, const char* message); + static void _throw_msg(Thread* thread, const char* file, int line, + symbolHandle name, const char* message); + static void _throw_args(Thread* thread, const char* file, int line, + symbolHandle name, symbolHandle signature, + JavaCallArguments* args); + static void _throw_msg_cause(Thread* thread, const char* file, + int line, symbolHandle h_name, const char* message, + Handle h_cause, Handle h_loader, Handle h_protection_domain); + static void _throw_msg_cause(Thread* thread, const char* file, int line, + symbolHandle name, const char* message, Handle cause); + + // There is no THROW... macro for this method. Caller should remember + // to do a return after calling it. + static void fthrow(Thread* thread, const char* file, int line, symbolHandle name, + const char* format, ...); + + // Create and initialize a new exception + static Handle new_exception(Thread* thread, symbolHandle name, + symbolHandle signature, JavaCallArguments* args, + Handle cause, Handle loader, + Handle protection_domain); + + static Handle new_exception(Thread* thread, symbolHandle name, + const char* message, Handle cause, Handle loader, + Handle protection_domain, + ExceptionMsgToUtf8Mode to_utf8_safe = safe_to_utf8); + + static Handle new_exception(Thread* thread, symbolOop name, + const char* message, + ExceptionMsgToUtf8Mode to_utf8_safe = safe_to_utf8); + + static void throw_stack_overflow_exception(Thread* thread, const char* file, int line); + + // for AbortVMOnException flag + NOT_PRODUCT(static void debug_check_abort(Handle exception);) + NOT_PRODUCT(static void debug_check_abort(const char *value_string);) +}; + + +// The THREAD & TRAPS macros facilitate the declaration of functions that throw exceptions. +// Convention: Use the TRAPS macro as the last argument of such a function; e.g.: +// +// int this_function_may_trap(int x, float y, TRAPS) + +#define THREAD __the_thread__ +#define TRAPS Thread* THREAD + + +// The CHECK... macros should be used to pass along a THREAD reference and to check for pending +// exceptions. In special situations it is necessary to handle pending exceptions explicitly, +// in these cases the PENDING_EXCEPTION helper macros should be used. +// +// Macro naming conventions: Macros that end with _ require a result value to be returned. They +// are for functions with non-void result type. The result value is usually ignored because of +// the exception and is only needed for syntactic correctness. The _0 ending is a shortcut for +// _(0) since this is a frequent case. Example: +// +// int result = this_function_may_trap(x_arg, y_arg, CHECK_0); +// +// CAUTION: make sure that the function call using a CHECK macro is not the only statement of a +// conditional branch w/o enclosing {} braces, since the CHECK macros expand into several state- +// ments! + +#define PENDING_EXCEPTION (((ThreadShadow*)THREAD)->pending_exception()) +#define HAS_PENDING_EXCEPTION (((ThreadShadow*)THREAD)->has_pending_exception()) +#define CLEAR_PENDING_EXCEPTION (((ThreadShadow*)THREAD)->clear_pending_exception()) + +#define CHECK THREAD); if (HAS_PENDING_EXCEPTION) return ; (0 +#define CHECK_(result) THREAD); if (HAS_PENDING_EXCEPTION) return result; (0 +#define CHECK_0 CHECK_(0) +#define CHECK_NH CHECK_(Handle()) +#define CHECK_NULL CHECK_(NULL) +#define CHECK_false CHECK_(false) + +// The THROW... macros should be used to throw an exception. They require a THREAD variable to be +// visible within the scope containing the THROW. Usually this is achieved by declaring the function +// with a TRAPS argument. + +#define THREAD_AND_LOCATION THREAD, __FILE__, __LINE__ + +#define THROW_OOP(e) \ + { Exceptions::_throw_oop(THREAD_AND_LOCATION, e); return; } + +#define THROW_HANDLE(e) \ + { Exceptions::_throw(THREAD_AND_LOCATION, e); return; } + +#define THROW(name) \ + { Exceptions::_throw_msg(THREAD_AND_LOCATION, name, NULL); return; } + +#define THROW_MSG(name, message) \ + { Exceptions::_throw_msg(THREAD_AND_LOCATION, name, message); return; } + +#define THROW_MSG_LOADER(name, message, loader, protection_domain) \ + { Exceptions::_throw_msg(THREAD_AND_LOCATION, name, message, loader, protection_domain); return; } + +#define THROW_ARG(name, signature, args) \ + { Exceptions::_throw_args(THREAD_AND_LOCATION, name, signature, args); return; } + +#define THROW_OOP_(e, result) \ + { Exceptions::_throw_oop(THREAD_AND_LOCATION, e); return result; } + +#define THROW_HANDLE_(e, result) \ + { Exceptions::_throw(THREAD_AND_LOCATION, e); return result; } + +#define THROW_(name, result) \ + { Exceptions::_throw_msg(THREAD_AND_LOCATION, name, NULL); return result; } + +#define THROW_MSG_(name, message, result) \ + { Exceptions::_throw_msg(THREAD_AND_LOCATION, name, message); return result; } + +#define THROW_MSG_LOADER_(name, message, loader, protection_domain, result) \ + { Exceptions::_throw_msg(THREAD_AND_LOCATION, name, message, loader, protection_domain); return result; } + +#define THROW_ARG_(name, signature, args, result) \ + { Exceptions::_throw_args(THREAD_AND_LOCATION, name, signature, args); return result; } + +#define THROW_MSG_CAUSE_(name, message, cause, result) \ + { Exceptions::_throw_msg_cause(THREAD_AND_LOCATION, name, message, cause); return result; } + + +#define THROW_OOP_0(e) THROW_OOP_(e, 0) +#define THROW_HANDLE_0(e) THROW_HANDLE_(e, 0) +#define THROW_0(name) THROW_(name, 0) +#define THROW_MSG_0(name, message) THROW_MSG_(name, message, 0) +#define THROW_WRAPPED_0(name, oop_to_wrap) THROW_WRAPPED_(name, oop_to_wrap, 0) +#define THROW_ARG_0(name, signature, arg) THROW_ARG_(name, signature, arg, 0) +#define THROW_MSG_CAUSE_0(name, message, cause) THROW_MSG_CAUSE_(name, message, cause, 0) + +// The CATCH macro checks that no exception has been thrown by a function; it is used at +// call sites about which is statically known that the callee cannot throw an exception +// even though it is declared with TRAPS. + +#define CATCH \ + THREAD); if (HAS_PENDING_EXCEPTION) { \ + oop ex = PENDING_EXCEPTION; \ + CLEAR_PENDING_EXCEPTION; \ + ex->print(); \ + ShouldNotReachHere(); \ + } (0 + + +// ExceptionMark is a stack-allocated helper class for local exception handling. +// It is used with the EXCEPTION_MARK macro. + +class ExceptionMark { + private: + Thread* _thread; + + public: + ExceptionMark(Thread*& thread); + ~ExceptionMark(); +}; + + + +// Use an EXCEPTION_MARK for 'local' exceptions. EXCEPTION_MARK makes sure that no +// pending exception exists upon entering its scope and tests that no pending exception +// exists when leaving the scope. + +// See also preserveException.hpp for PRESERVE_EXCEPTION_MARK macro, +// which preserves pre-existing exceptions and does not allow new +// exceptions. + +#define EXCEPTION_MARK Thread* THREAD; ExceptionMark __em(THREAD); diff --git a/src/share/vm/utilities/globalDefinitions.cpp b/src/share/vm/utilities/globalDefinitions.cpp new file mode 100644 index 000000000..12edbeff7 --- /dev/null +++ b/src/share/vm/utilities/globalDefinitions.cpp @@ -0,0 +1,297 @@ +/* + * Copyright 1997-2006 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/_globalDefinitions.cpp.incl" + + +// Basic error support + +void basic_fatal(const char* msg) { + fatal(msg); +} + + +// Something to help porters sleep at night + +void check_basic_types() { +#ifdef ASSERT +#ifdef _LP64 + assert(min_intx == (intx)CONST64(0x8000000000000000), "correct constant"); + assert(max_intx == CONST64(0x7FFFFFFFFFFFFFFF), "correct constant"); + assert(max_uintx == CONST64(0xFFFFFFFFFFFFFFFF), "correct constant"); + assert( 8 == sizeof( intx), "wrong size for basic type"); + assert( 8 == sizeof( jobject), "wrong size for basic type"); +#else + assert(min_intx == (intx)0x80000000, "correct constant"); + assert(max_intx == 0x7FFFFFFF, "correct constant"); + assert(max_uintx == 0xFFFFFFFF, "correct constant"); + assert( 4 == sizeof( intx), "wrong size for basic type"); + assert( 4 == sizeof( jobject), "wrong size for basic type"); +#endif + assert( (~max_juint) == 0, "max_juint has all its bits"); + assert( (~max_uintx) == 0, "max_uintx has all its bits"); + assert( (~max_julong) == 0, "max_julong has all its bits"); + assert( 1 == sizeof( jbyte), "wrong size for basic type"); + assert( 2 == sizeof( jchar), "wrong size for basic type"); + assert( 2 == sizeof( jshort), "wrong size for basic type"); + assert( 4 == sizeof( juint), "wrong size for basic type"); + assert( 4 == sizeof( jint), "wrong size for basic type"); + assert( 1 == sizeof( jboolean), "wrong size for basic type"); + assert( 8 == sizeof( jlong), "wrong size for basic type"); + assert( 4 == sizeof( jfloat), "wrong size for basic type"); + assert( 8 == sizeof( jdouble), "wrong size for basic type"); + assert( 1 == sizeof( u1), "wrong size for basic type"); + assert( 2 == sizeof( u2), "wrong size for basic type"); + assert( 4 == sizeof( u4), "wrong size for basic type"); + + int num_type_chars = 0; + for (int i = 0; i < 99; i++) { + if (type2char((BasicType)i) != 0) { + assert(char2type(type2char((BasicType)i)) == i, "proper inverses"); + num_type_chars++; + } + } + assert(num_type_chars == 11, "must have tested the right number of mappings"); + assert(char2type(0) == T_ILLEGAL, "correct illegality"); + + { + for (int i = T_BOOLEAN; i <= T_CONFLICT; i++) { + BasicType vt = (BasicType)i; + BasicType ft = type2field[vt]; + switch (vt) { + // the following types might plausibly show up in memory layouts: + case T_BOOLEAN: + case T_BYTE: + case T_CHAR: + case T_SHORT: + case T_INT: + case T_FLOAT: + case T_DOUBLE: + case T_LONG: + case T_OBJECT: + case T_ADDRESS: // random raw pointer + case T_CONFLICT: // might as well support a bottom type + case T_VOID: // padding or other unaddressed word + // layout type must map to itself + assert(vt == ft, ""); + break; + default: + // non-layout type must map to a (different) layout type + assert(vt != ft, ""); + assert(ft == type2field[ft], ""); + } + // every type must map to same-sized layout type: + assert(type2size[vt] == type2size[ft], ""); + } + } + // These are assumed, e.g., when filling HeapWords with juints. + assert(is_power_of_2(sizeof(juint)), "juint must be power of 2"); + assert(is_power_of_2(HeapWordSize), "HeapWordSize must be power of 2"); + assert((size_t)HeapWordSize >= sizeof(juint), + "HeapWord should be at least as large as juint"); + assert(sizeof(NULL) == sizeof(char*), "NULL must be same size as pointer"); +#endif + + if( JavaPriority1_To_OSPriority != -1 ) + os::java_to_os_priority[1] = JavaPriority1_To_OSPriority; + if( JavaPriority2_To_OSPriority != -1 ) + os::java_to_os_priority[2] = JavaPriority2_To_OSPriority; + if( JavaPriority3_To_OSPriority != -1 ) + os::java_to_os_priority[3] = JavaPriority3_To_OSPriority; + if( JavaPriority4_To_OSPriority != -1 ) + os::java_to_os_priority[4] = JavaPriority4_To_OSPriority; + if( JavaPriority5_To_OSPriority != -1 ) + os::java_to_os_priority[5] = JavaPriority5_To_OSPriority; + if( JavaPriority6_To_OSPriority != -1 ) + os::java_to_os_priority[6] = JavaPriority6_To_OSPriority; + if( JavaPriority7_To_OSPriority != -1 ) + os::java_to_os_priority[7] = JavaPriority7_To_OSPriority; + if( JavaPriority8_To_OSPriority != -1 ) + os::java_to_os_priority[8] = JavaPriority8_To_OSPriority; + if( JavaPriority9_To_OSPriority != -1 ) + os::java_to_os_priority[9] = JavaPriority9_To_OSPriority; + if(JavaPriority10_To_OSPriority != -1 ) + os::java_to_os_priority[10] = JavaPriority10_To_OSPriority; +} + + +// Map BasicType to signature character +char type2char_tab[T_CONFLICT+1]={ 0, 0, 0, 0, 'Z', 'C', 'F', 'D', 'B', 'S', 'I', 'J', 'L', '[', 'V', 0, 0}; + +// Map BasicType to Java type name +const char* type2name_tab[T_CONFLICT+1] = { + NULL, NULL, NULL, NULL, + "boolean", + "char", + "float", + "double", + "byte", + "short", + "int", + "long", + "object", + "array", + "void", + "*address*", + "*conflict*" +}; + + +BasicType name2type(const char* name) { + for (int i = T_BOOLEAN; i <= T_VOID; i++) { + BasicType t = (BasicType)i; + if (type2name_tab[t] != NULL && 0 == strcmp(type2name_tab[t], name)) + return t; + } + return T_ILLEGAL; +} + + +// Map BasicType to size in words +int type2size[T_CONFLICT+1]={ -1, 0, 0, 0, 1, 1, 1, 2, 1, 1, 1, 2, 1, 1, 0, 1, -1}; + +BasicType type2field[T_CONFLICT+1] = { + (BasicType)0, // 0, + (BasicType)0, // 1, + (BasicType)0, // 2, + (BasicType)0, // 3, + T_BOOLEAN, // T_BOOLEAN = 4, + T_CHAR, // T_CHAR = 5, + T_FLOAT, // T_FLOAT = 6, + T_DOUBLE, // T_DOUBLE = 7, + T_BYTE, // T_BYTE = 8, + T_SHORT, // T_SHORT = 9, + T_INT, // T_INT = 10, + T_LONG, // T_LONG = 11, + T_OBJECT, // T_OBJECT = 12, + T_OBJECT, // T_ARRAY = 13, + T_VOID, // T_VOID = 14, + T_ADDRESS, // T_ADDRESS = 15, + T_CONFLICT // T_CONFLICT = 16, +}; + + +BasicType type2wfield[T_CONFLICT+1] = { + (BasicType)0, // 0, + (BasicType)0, // 1, + (BasicType)0, // 2, + (BasicType)0, // 3, + T_INT, // T_BOOLEAN = 4, + T_INT, // T_CHAR = 5, + T_FLOAT, // T_FLOAT = 6, + T_DOUBLE, // T_DOUBLE = 7, + T_INT, // T_BYTE = 8, + T_INT, // T_SHORT = 9, + T_INT, // T_INT = 10, + T_LONG, // T_LONG = 11, + T_OBJECT, // T_OBJECT = 12, + T_OBJECT, // T_ARRAY = 13, + T_VOID, // T_VOID = 14, + T_ADDRESS, // T_ADDRESS = 15, + T_CONFLICT // T_CONFLICT = 16, +}; + + +int type2aelembytes[T_CONFLICT+1] = { + 0, // 0 + 0, // 1 + 0, // 2 + 0, // 3 + T_BOOLEAN_aelem_bytes, // T_BOOLEAN = 4, + T_CHAR_aelem_bytes, // T_CHAR = 5, + T_FLOAT_aelem_bytes, // T_FLOAT = 6, + T_DOUBLE_aelem_bytes, // T_DOUBLE = 7, + T_BYTE_aelem_bytes, // T_BYTE = 8, + T_SHORT_aelem_bytes, // T_SHORT = 9, + T_INT_aelem_bytes, // T_INT = 10, + T_LONG_aelem_bytes, // T_LONG = 11, + T_OBJECT_aelem_bytes, // T_OBJECT = 12, + T_ARRAY_aelem_bytes, // T_ARRAY = 13, + 0, // T_VOID = 14, + T_INT_aelem_bytes, // T_ADDRESS = 15, + 0 // T_CONFLICT = 16, +}; + + +// Support for 64-bit integer arithmetic + +// The following code is mostly taken from JVM typedefs_md.h and system_md.c + +static const jlong high_bit = (jlong)1 << (jlong)63; +static const jlong other_bits = ~high_bit; + +jlong float2long(jfloat f) { + jlong tmp = (jlong) f; + if (tmp != high_bit) { + return tmp; + } else { + if (g_isnan((jdouble)f)) { + return 0; + } + if (f < 0) { + return high_bit; + } else { + return other_bits; + } + } +} + + +jlong double2long(jdouble f) { + jlong tmp = (jlong) f; + if (tmp != high_bit) { + return tmp; + } else { + if (g_isnan(f)) { + return 0; + } + if (f < 0) { + return high_bit; + } else { + return other_bits; + } + } +} + +// least common multiple +size_t lcm(size_t a, size_t b) { + size_t cur, div, next; + + cur = MAX2(a, b); + div = MIN2(a, b); + + assert(div != 0, "lcm requires positive arguments"); + + + while ((next = cur % div) != 0) { + cur = div; div = next; + } + + + julong result = julong(a) * b / div; + assert(result <= (size_t)max_uintx, "Integer overflow in lcm"); + + return size_t(result); +} diff --git a/src/share/vm/utilities/globalDefinitions.hpp b/src/share/vm/utilities/globalDefinitions.hpp new file mode 100644 index 000000000..460cd1f34 --- /dev/null +++ b/src/share/vm/utilities/globalDefinitions.hpp @@ -0,0 +1,1101 @@ +/* + * 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. + * + */ + +// This file holds all globally used constants & types, class (forward) +// declarations and a few frequently used utility functions. + +//---------------------------------------------------------------------------------------------------- +// Constants + +const int LogBytesPerShort = 1; +const int LogBytesPerInt = 2; +#ifdef _LP64 +const int LogBytesPerWord = 3; +#else +const int LogBytesPerWord = 2; +#endif +const int LogBytesPerLong = 3; + +const int BytesPerShort = 1 << LogBytesPerShort; +const int BytesPerInt = 1 << LogBytesPerInt; +const int BytesPerWord = 1 << LogBytesPerWord; +const int BytesPerLong = 1 << LogBytesPerLong; + +const int LogBitsPerByte = 3; +const int LogBitsPerShort = LogBitsPerByte + LogBytesPerShort; +const int LogBitsPerInt = LogBitsPerByte + LogBytesPerInt; +const int LogBitsPerWord = LogBitsPerByte + LogBytesPerWord; +const int LogBitsPerLong = LogBitsPerByte + LogBytesPerLong; + +const int BitsPerByte = 1 << LogBitsPerByte; +const int BitsPerShort = 1 << LogBitsPerShort; +const int BitsPerInt = 1 << LogBitsPerInt; +const int BitsPerWord = 1 << LogBitsPerWord; +const int BitsPerLong = 1 << LogBitsPerLong; + +const int WordAlignmentMask = (1 << LogBytesPerWord) - 1; +const int LongAlignmentMask = (1 << LogBytesPerLong) - 1; + +const int WordsPerLong = 2; // Number of stack entries for longs + +const int oopSize = sizeof(char*); +const int wordSize = sizeof(char*); +const int longSize = sizeof(jlong); +const int jintSize = sizeof(jint); +const int size_tSize = sizeof(size_t); + +// Size of a char[] needed to represent a jint as a string in decimal. +const int jintAsStringSize = 12; + +const int LogBytesPerOop = LogBytesPerWord; +const int LogBitsPerOop = LogBitsPerWord; +const int BytesPerOop = 1 << LogBytesPerOop; +const int BitsPerOop = 1 << LogBitsPerOop; + +const int BitsPerJavaInteger = 32; +const int BitsPerSize_t = size_tSize * BitsPerByte; + +// In fact this should be +// log2_intptr(sizeof(class JavaThread)) - log2_intptr(64); +// see os::set_memory_serialize_page() +#ifdef _LP64 +const int SerializePageShiftCount = 4; +#else +const int SerializePageShiftCount = 3; +#endif + +// An opaque struct of heap-word width, so that HeapWord* can be a generic +// pointer into the heap. We require that object sizes be measured in +// units of heap words, so that that +// HeapWord* hw; +// hw += oop(hw)->foo(); +// works, where foo is a method (like size or scavenge) that returns the +// object size. +class HeapWord { + friend class VMStructs; +private: + char* i; +}; + +// HeapWordSize must be 2^LogHeapWordSize. +const int HeapWordSize = sizeof(HeapWord); +#ifdef _LP64 +const int LogHeapWordSize = 3; +#else +const int LogHeapWordSize = 2; +#endif +const int HeapWordsPerOop = oopSize / HeapWordSize; +const int HeapWordsPerLong = BytesPerLong / HeapWordSize; + +// The larger HeapWordSize for 64bit requires larger heaps +// for the same application running in 64bit. See bug 4967770. +// The minimum alignment to a heap word size is done. Other +// parts of the memory system may required additional alignment +// and are responsible for those alignments. +#ifdef _LP64 +#define ScaleForWordSize(x) align_size_down_((x) * 13 / 10, HeapWordSize) +#else +#define ScaleForWordSize(x) (x) +#endif + +// The minimum number of native machine words necessary to contain "byte_size" +// bytes. +inline size_t heap_word_size(size_t byte_size) { + return (byte_size + (HeapWordSize-1)) >> LogHeapWordSize; +} + + +const size_t K = 1024; +const size_t M = K*K; +const size_t G = M*K; +const size_t HWperKB = K / sizeof(HeapWord); + +const jint min_jint = (jint)1 << (sizeof(jint)*BitsPerByte-1); // 0x80000000 == smallest jint +const jint max_jint = (juint)min_jint - 1; // 0x7FFFFFFF == largest jint + +// Constants for converting from a base unit to milli-base units. For +// example from seconds to milliseconds and microseconds + +const int MILLIUNITS = 1000; // milli units per base unit +const int MICROUNITS = 1000000; // micro units per base unit +const int NANOUNITS = 1000000000; // nano units per base unit + +inline const char* proper_unit_for_byte_size(size_t s) { + if (s >= 10*M) { + return "M"; + } else if (s >= 10*K) { + return "K"; + } else { + return "B"; + } +} + +inline size_t byte_size_in_proper_unit(size_t s) { + if (s >= 10*M) { + return s/M; + } else if (s >= 10*K) { + return s/K; + } else { + return s; + } +} + + +//---------------------------------------------------------------------------------------------------- +// VM type definitions + +// intx and uintx are the 'extended' int and 'extended' unsigned int types; +// they are 32bit wide on a 32-bit platform, and 64bit wide on a 64bit platform. + +typedef intptr_t intx; +typedef uintptr_t uintx; + +const intx min_intx = (intx)1 << (sizeof(intx)*BitsPerByte-1); +const intx max_intx = (uintx)min_intx - 1; +const uintx max_uintx = (uintx)-1; + +// Table of values: +// sizeof intx 4 8 +// min_intx 0x80000000 0x8000000000000000 +// max_intx 0x7FFFFFFF 0x7FFFFFFFFFFFFFFF +// max_uintx 0xFFFFFFFF 0xFFFFFFFFFFFFFFFF + +typedef unsigned int uint; NEEDS_CLEANUP + + +//---------------------------------------------------------------------------------------------------- +// Java type definitions + +// All kinds of 'plain' byte addresses +typedef signed char s_char; +typedef unsigned char u_char; +typedef u_char* address; +typedef uintptr_t address_word; // unsigned integer which will hold a pointer + // except for some implementations of a C++ + // linkage pointer to function. Should never + // need one of those to be placed in this + // type anyway. + +// Utility functions to "portably" (?) bit twiddle pointers +// Where portable means keep ANSI C++ compilers quiet + +inline address set_address_bits(address x, int m) { return address(intptr_t(x) | m); } +inline address clear_address_bits(address x, int m) { return address(intptr_t(x) & ~m); } + +// Utility functions to "portably" make cast to/from function pointers. + +inline address_word mask_address_bits(address x, int m) { return address_word(x) & m; } +inline address_word castable_address(address x) { return address_word(x) ; } +inline address_word castable_address(void* x) { return address_word(x) ; } + +// Pointer subtraction. +// The idea here is to avoid ptrdiff_t, which is signed and so doesn't have +// the range we might need to find differences from one end of the heap +// to the other. +// A typical use might be: +// if (pointer_delta(end(), top()) >= size) { +// // enough room for an object of size +// ... +// and then additions like +// ... top() + size ... +// are safe because we know that top() is at least size below end(). +inline size_t pointer_delta(const void* left, + const void* right, + size_t element_size) { + return (((uintptr_t) left) - ((uintptr_t) right)) / element_size; +} +// A version specialized for HeapWord*'s. +inline size_t pointer_delta(const HeapWord* left, const HeapWord* right) { + return pointer_delta(left, right, sizeof(HeapWord)); +} + +// +// ANSI C++ does not allow casting from one pointer type to a function pointer +// directly without at best a warning. This macro accomplishes it silently +// In every case that is present at this point the value be cast is a pointer +// to a C linkage function. In somecase the type used for the cast reflects +// that linkage and a picky compiler would not complain. In other cases because +// there is no convenient place to place a typedef with extern C linkage (i.e +// a platform dependent header file) it doesn't. At this point no compiler seems +// picky enough to catch these instances (which are few). It is possible that +// using templates could fix these for all cases. This use of templates is likely +// so far from the middle of the road that it is likely to be problematic in +// many C++ compilers. +// +#define CAST_TO_FN_PTR(func_type, value) ((func_type)(castable_address(value))) +#define CAST_FROM_FN_PTR(new_type, func_ptr) ((new_type)((address_word)(func_ptr))) + +// Unsigned byte types for os and stream.hpp + +// Unsigned one, two, four and eigth byte quantities used for describing +// the .class file format. See JVM book chapter 4. + +typedef jubyte u1; +typedef jushort u2; +typedef juint u4; +typedef julong u8; + +const jubyte max_jubyte = (jubyte)-1; // 0xFF largest jubyte +const jushort max_jushort = (jushort)-1; // 0xFFFF largest jushort +const juint max_juint = (juint)-1; // 0xFFFFFFFF largest juint +const julong max_julong = (julong)-1; // 0xFF....FF largest julong + +//---------------------------------------------------------------------------------------------------- +// JVM spec restrictions + +const int max_method_code_size = 64*K - 1; // JVM spec, 2nd ed. section 4.8.1 (p.134) + + +//---------------------------------------------------------------------------------------------------- +// HotSwap - for JVMTI aka Class File Replacement and PopFrame +// +// Determines whether on-the-fly class replacement and frame popping are enabled. + +#define HOTSWAP + +//---------------------------------------------------------------------------------------------------- +// Object alignment, in units of HeapWords. +// +// Minimum is max(BytesPerLong, BytesPerDouble, BytesPerOop) / HeapWordSize, so jlong, jdouble and +// reference fields can be naturally aligned. + +const int MinObjAlignment = HeapWordsPerLong; +const int MinObjAlignmentInBytes = MinObjAlignment * HeapWordSize; +const int MinObjAlignmentInBytesMask = MinObjAlignmentInBytes - 1; + +// Machine dependent stuff + +#include "incls/_globalDefinitions_pd.hpp.incl" + +// The byte alignment to be used by Arena::Amalloc. See bugid 4169348. +// Note: this value must be a power of 2 + +#define ARENA_AMALLOC_ALIGNMENT (2*BytesPerWord) + +// Signed variants of alignment helpers. There are two versions of each, a macro +// for use in places like enum definitions that require compile-time constant +// expressions and a function for all other places so as to get type checking. + +#define align_size_up_(size, alignment) (((size) + ((alignment) - 1)) & ~((alignment) - 1)) + +inline intptr_t align_size_up(intptr_t size, intptr_t alignment) { + return align_size_up_(size, alignment); +} + +#define align_size_down_(size, alignment) ((size) & ~((alignment) - 1)) + +inline intptr_t align_size_down(intptr_t size, intptr_t alignment) { + return align_size_down_(size, alignment); +} + +// Align objects by rounding up their size, in HeapWord units. + +#define align_object_size_(size) align_size_up_(size, MinObjAlignment) + +inline intptr_t align_object_size(intptr_t size) { + return align_size_up(size, MinObjAlignment); +} + +// Pad out certain offsets to jlong alignment, in HeapWord units. + +#define align_object_offset_(offset) align_size_up_(offset, HeapWordsPerLong) + +inline intptr_t align_object_offset(intptr_t offset) { + return align_size_up(offset, HeapWordsPerLong); +} + +inline bool is_object_aligned(intptr_t offset) { + return offset == align_object_offset(offset); +} + + +//---------------------------------------------------------------------------------------------------- +// Utility macros for compilers +// used to silence compiler warnings + +#define Unused_Variable(var) var + + +//---------------------------------------------------------------------------------------------------- +// Miscellaneous + +// 6302670 Eliminate Hotspot __fabsf dependency +// All fabs() callers should call this function instead, which will implicitly +// convert the operand to double, avoiding a dependency on __fabsf which +// doesn't exist in early versions of Solaris 8. +inline double fabsd(double value) { + return fabs(value); +} + +inline jint low (jlong value) { return jint(value); } +inline jint high(jlong value) { return jint(value >> 32); } + +// the fancy casts are a hopefully portable way +// to do unsigned 32 to 64 bit type conversion +inline void set_low (jlong* value, jint low ) { *value &= (jlong)0xffffffff << 32; + *value |= (jlong)(julong)(juint)low; } + +inline void set_high(jlong* value, jint high) { *value &= (jlong)(julong)(juint)0xffffffff; + *value |= (jlong)high << 32; } + +inline jlong jlong_from(jint h, jint l) { + jlong result = 0; // initialization to avoid warning + set_high(&result, h); + set_low(&result, l); + return result; +} + +union jlong_accessor { + jint words[2]; + jlong long_value; +}; + +void check_basic_types(); // cannot define here; uses assert + + +// NOTE: replicated in SA in vm/agent/sun/jvm/hotspot/runtime/BasicType.java +enum BasicType { + T_BOOLEAN = 4, + T_CHAR = 5, + T_FLOAT = 6, + T_DOUBLE = 7, + T_BYTE = 8, + T_SHORT = 9, + T_INT = 10, + T_LONG = 11, + T_OBJECT = 12, + T_ARRAY = 13, + T_VOID = 14, + T_ADDRESS = 15, + T_CONFLICT = 16, // for stack value type with conflicting contents + T_ILLEGAL = 99 +}; + +// Convert a char from a classfile signature to a BasicType +inline BasicType char2type(char c) { + switch( c ) { + case 'B': return T_BYTE; + case 'C': return T_CHAR; + case 'D': return T_DOUBLE; + case 'F': return T_FLOAT; + case 'I': return T_INT; + case 'J': return T_LONG; + case 'S': return T_SHORT; + case 'Z': return T_BOOLEAN; + case 'V': return T_VOID; + case 'L': return T_OBJECT; + case '[': return T_ARRAY; + } + return T_ILLEGAL; +} + +extern char type2char_tab[T_CONFLICT+1]; // Map a BasicType to a jchar +inline char type2char(BasicType t) { return (uint)t < T_CONFLICT+1 ? type2char_tab[t] : 0; } +extern int type2size[T_CONFLICT+1]; // Map BasicType to result stack elements +extern const char* type2name_tab[T_CONFLICT+1]; // Map a BasicType to a jchar +inline const char* type2name(BasicType t) { return (uint)t < T_CONFLICT+1 ? type2name_tab[t] : NULL; } +extern BasicType name2type(const char* name); + +// Auxilary math routines +// least common multiple +extern size_t lcm(size_t a, size_t b); + + +// NOTE: replicated in SA in vm/agent/sun/jvm/hotspot/runtime/BasicType.java +enum BasicTypeSize { + T_BOOLEAN_size = 1, + T_CHAR_size = 1, + T_FLOAT_size = 1, + T_DOUBLE_size = 2, + T_BYTE_size = 1, + T_SHORT_size = 1, + T_INT_size = 1, + T_LONG_size = 2, + T_OBJECT_size = 1, + T_ARRAY_size = 1, + T_VOID_size = 0 +}; + + +// maps a BasicType to its instance field storage type: +// all sub-word integral types are widened to T_INT +extern BasicType type2field[T_CONFLICT+1]; +extern BasicType type2wfield[T_CONFLICT+1]; + + +// size in bytes +enum ArrayElementSize { + T_BOOLEAN_aelem_bytes = 1, + T_CHAR_aelem_bytes = 2, + T_FLOAT_aelem_bytes = 4, + T_DOUBLE_aelem_bytes = 8, + T_BYTE_aelem_bytes = 1, + T_SHORT_aelem_bytes = 2, + T_INT_aelem_bytes = 4, + T_LONG_aelem_bytes = 8, +#ifdef _LP64 + T_OBJECT_aelem_bytes = 8, + T_ARRAY_aelem_bytes = 8, +#else + T_OBJECT_aelem_bytes = 4, + T_ARRAY_aelem_bytes = 4, +#endif + T_VOID_aelem_bytes = 0 +}; + +extern int type2aelembytes[T_CONFLICT+1]; // maps a BasicType to nof bytes used by its array element + + +// JavaValue serves as a container for arbitrary Java values. + +class JavaValue { + + public: + typedef union JavaCallValue { + jfloat f; + jdouble d; + jint i; + jlong l; + jobject h; + } JavaCallValue; + + private: + BasicType _type; + JavaCallValue _value; + + public: + JavaValue(BasicType t = T_ILLEGAL) { _type = t; } + + JavaValue(jfloat value) { + _type = T_FLOAT; + _value.f = value; + } + + JavaValue(jdouble value) { + _type = T_DOUBLE; + _value.d = value; + } + + jfloat get_jfloat() const { return _value.f; } + jdouble get_jdouble() const { return _value.d; } + jint get_jint() const { return _value.i; } + jlong get_jlong() const { return _value.l; } + jobject get_jobject() const { return _value.h; } + JavaCallValue* get_value_addr() { return &_value; } + BasicType get_type() const { return _type; } + + void set_jfloat(jfloat f) { _value.f = f;} + void set_jdouble(jdouble d) { _value.d = d;} + void set_jint(jint i) { _value.i = i;} + void set_jlong(jlong l) { _value.l = l;} + void set_jobject(jobject h) { _value.h = h;} + void set_type(BasicType t) { _type = t; } + + jboolean get_jboolean() const { return (jboolean) (_value.i);} + jbyte get_jbyte() const { return (jbyte) (_value.i);} + jchar get_jchar() const { return (jchar) (_value.i);} + jshort get_jshort() const { return (jshort) (_value.i);} + +}; + + +#define STACK_BIAS 0 +// V9 Sparc CPU's running in 64 Bit mode use a stack bias of 7ff +// in order to extend the reach of the stack pointer. +#if defined(SPARC) && defined(_LP64) +#undef STACK_BIAS +#define STACK_BIAS 0x7ff +#endif + + +// TosState describes the top-of-stack state before and after the execution of +// a bytecode or method. The top-of-stack value may be cached in one or more CPU +// registers. The TosState corresponds to the 'machine represention' of this cached +// value. There's 4 states corresponding to the JAVA types int, long, float & double +// as well as a 5th state in case the top-of-stack value is actually on the top +// of stack (in memory) and thus not cached. The atos state corresponds to the itos +// state when it comes to machine representation but is used separately for (oop) +// type specific operations (e.g. verification code). + +enum TosState { // describes the tos cache contents + btos = 0, // byte, bool tos cached + ctos = 1, // short, char tos cached + stos = 2, // short, char tos cached + itos = 3, // int tos cached + ltos = 4, // long tos cached + ftos = 5, // float tos cached + dtos = 6, // double tos cached + atos = 7, // object cached + vtos = 8, // tos not cached + number_of_states, + ilgl // illegal state: should not occur +}; + + +inline TosState as_TosState(BasicType type) { + switch (type) { + case T_BYTE : return btos; + case T_BOOLEAN: return btos; + case T_CHAR : return ctos; + case T_SHORT : return stos; + case T_INT : return itos; + case T_LONG : return ltos; + case T_FLOAT : return ftos; + case T_DOUBLE : return dtos; + case T_VOID : return vtos; + case T_ARRAY : // fall through + case T_OBJECT : return atos; + } + return ilgl; +} + + +// Helper function to convert BasicType info into TosState +// Note: Cannot define here as it uses global constant at the time being. +TosState as_TosState(BasicType type); + + +// ReferenceType is used to distinguish between java/lang/ref/Reference subclasses + +enum ReferenceType { + REF_NONE, // Regular class + REF_OTHER, // Subclass of java/lang/ref/Reference, but not subclass of one of the classes below + REF_SOFT, // Subclass of java/lang/ref/SoftReference + REF_WEAK, // Subclass of java/lang/ref/WeakReference + REF_FINAL, // Subclass of java/lang/ref/FinalReference + REF_PHANTOM // Subclass of java/lang/ref/PhantomReference +}; + + +// JavaThreadState keeps track of which part of the code a thread is executing in. This +// information is needed by the safepoint code. +// +// There are 4 essential states: +// +// _thread_new : Just started, but not executed init. code yet (most likely still in OS init code) +// _thread_in_native : In native code. This is a safepoint region, since all oops will be in jobject handles +// _thread_in_vm : Executing in the vm +// _thread_in_Java : Executing either interpreted or compiled Java code (or could be in a stub) +// +// Each state has an associated xxxx_trans state, which is an intermediate state used when a thread is in +// a transition from one state to another. These extra states makes it possible for the safepoint code to +// handle certain thread_states without having to suspend the thread - making the safepoint code faster. +// +// Given a state, the xxx_trans state can always be found by adding 1. +// +enum JavaThreadState { + _thread_uninitialized = 0, // should never happen (missing initialization) + _thread_new = 2, // just starting up, i.e., in process of being initialized + _thread_new_trans = 3, // corresponding transition state (not used, included for completness) + _thread_in_native = 4, // running in native code + _thread_in_native_trans = 5, // corresponding transition state + _thread_in_vm = 6, // running in VM + _thread_in_vm_trans = 7, // corresponding transition state + _thread_in_Java = 8, // running in Java or in stub code + _thread_in_Java_trans = 9, // corresponding transition state (not used, included for completness) + _thread_blocked = 10, // blocked in vm + _thread_blocked_trans = 11, // corresponding transition state + _thread_max_state = 12 // maximum thread state+1 - used for statistics allocation +}; + + +// Handy constants for deciding which compiler mode to use. +enum MethodCompilation { + InvocationEntryBci = -1, // i.e., not a on-stack replacement compilation + InvalidOSREntryBci = -2 +}; + +// Enumeration to distinguish tiers of compilation +enum CompLevel { + CompLevel_none = 0, + CompLevel_fast_compile = 1, + CompLevel_full_optimization = 2, + + CompLevel_highest_tier = CompLevel_full_optimization, +#ifdef TIERED + CompLevel_initial_compile = CompLevel_fast_compile +#else + CompLevel_initial_compile = CompLevel_full_optimization +#endif // TIERED +}; + +inline bool is_tier1_compile(int comp_level) { + return comp_level == CompLevel_fast_compile; +} +inline bool is_tier2_compile(int comp_level) { + return comp_level == CompLevel_full_optimization; +} +inline bool is_highest_tier_compile(int comp_level) { + return comp_level == CompLevel_highest_tier; +} + +//---------------------------------------------------------------------------------------------------- +// 'Forward' declarations of frequently used classes +// (in order to reduce interface dependencies & reduce +// number of unnecessary compilations after changes) + +class symbolTable; +class ClassFileStream; + +class Event; + +class Thread; +class VMThread; +class JavaThread; +class Threads; + +class VM_Operation; +class VMOperationQueue; + +class CodeBlob; +class nmethod; +class OSRAdapter; +class I2CAdapter; +class C2IAdapter; +class CompiledIC; +class relocInfo; +class ScopeDesc; +class PcDesc; + +class Recompiler; +class Recompilee; +class RecompilationPolicy; +class RFrame; +class CompiledRFrame; +class InterpretedRFrame; + +class frame; + +class vframe; +class javaVFrame; +class interpretedVFrame; +class compiledVFrame; +class deoptimizedVFrame; +class externalVFrame; +class entryVFrame; + +class RegisterMap; + +class Mutex; +class Monitor; +class BasicLock; +class BasicObjectLock; + +class PeriodicTask; + +class JavaCallWrapper; + +class oopDesc; + +class NativeCall; + +class zone; + +class StubQueue; + +class outputStream; + +class ResourceArea; + +class DebugInformationRecorder; +class ScopeValue; +class CompressedStream; +class DebugInfoReadStream; +class DebugInfoWriteStream; +class LocationValue; +class ConstantValue; +class IllegalValue; + +class PrivilegedElement; +class MonitorArray; + +class MonitorInfo; + +class OffsetClosure; +class OopMapCache; +class InterpreterOopMap; +class OopMapCacheEntry; +class OSThread; + +typedef int (*OSThreadStartFunc)(void*); + +class Space; + +class JavaValue; +class methodHandle; +class JavaCallArguments; + +// Basic support for errors (general debug facilities not defined at this point fo the include phase) + +extern void basic_fatal(const char* msg); + + +//---------------------------------------------------------------------------------------------------- +// Special constants for debugging + +const jint badInt = -3; // generic "bad int" value +const long badAddressVal = -2; // generic "bad address" value +const long badOopVal = -1; // generic "bad oop" value +const intptr_t badHeapOopVal = (intptr_t) CONST64(0x2BAD4B0BBAADBABE); // value used to zap heap after GC +const int badHandleValue = 0xBC; // value used to zap vm handle area +const int badResourceValue = 0xAB; // value used to zap resource area +const int freeBlockPad = 0xBA; // value used to pad freed blocks. +const int uninitBlockPad = 0xF1; // value used to zap newly malloc'd blocks. +const intptr_t badJNIHandleVal = (intptr_t) CONST64(0xFEFEFEFEFEFEFEFE); // value used to zap jni handle area +const juint badHeapWordVal = 0xBAADBABE; // value used to zap heap after GC +const int badCodeHeapNewVal= 0xCC; // value used to zap Code heap at allocation +const int badCodeHeapFreeVal = 0xDD; // value used to zap Code heap at deallocation + + +// (These must be implemented as #defines because C++ compilers are +// not obligated to inline non-integral constants!) +#define badAddress ((address)::badAddressVal) +#define badOop ((oop)::badOopVal) +#define badHeapWord (::badHeapWordVal) +#define badJNIHandle ((oop)::badJNIHandleVal) + + +//---------------------------------------------------------------------------------------------------- +// Utility functions for bitfield manipulations + +const intptr_t AllBits = ~0; // all bits set in a word +const intptr_t NoBits = 0; // no bits set in a word +const jlong NoLongBits = 0; // no bits set in a long +const intptr_t OneBit = 1; // only right_most bit set in a word + +// get a word with the n.th or the right-most or left-most n bits set +// (note: #define used only so that they can be used in enum constant definitions) +#define nth_bit(n) (n >= BitsPerWord ? 0 : OneBit << (n)) +#define right_n_bits(n) (nth_bit(n) - 1) +#define left_n_bits(n) (right_n_bits(n) << (n >= BitsPerWord ? 0 : (BitsPerWord - n))) + +// bit-operations using a mask m +inline void set_bits (intptr_t& x, intptr_t m) { x |= m; } +inline void clear_bits (intptr_t& x, intptr_t m) { x &= ~m; } +inline intptr_t mask_bits (intptr_t x, intptr_t m) { return x & m; } +inline jlong mask_long_bits (jlong x, jlong m) { return x & m; } +inline bool mask_bits_are_true (intptr_t flags, intptr_t mask) { return (flags & mask) == mask; } + +// bit-operations using the n.th bit +inline void set_nth_bit(intptr_t& x, int n) { set_bits (x, nth_bit(n)); } +inline void clear_nth_bit(intptr_t& x, int n) { clear_bits(x, nth_bit(n)); } +inline bool is_set_nth_bit(intptr_t x, int n) { return mask_bits (x, nth_bit(n)) != NoBits; } + +// returns the bitfield of x starting at start_bit_no with length field_length (no sign-extension!) +inline intptr_t bitfield(intptr_t x, int start_bit_no, int field_length) { + return mask_bits(x >> start_bit_no, right_n_bits(field_length)); +} + + +//---------------------------------------------------------------------------------------------------- +// Utility functions for integers + +// Avoid use of global min/max macros which may cause unwanted double +// evaluation of arguments. +#ifdef max +#undef max +#endif + +#ifdef min +#undef min +#endif + +#define max(a,b) Do_not_use_max_use_MAX2_instead +#define min(a,b) Do_not_use_min_use_MIN2_instead + +// It is necessary to use templates here. Having normal overloaded +// functions does not work because it is necessary to provide both 32- +// and 64-bit overloaded functions, which does not work, and having +// explicitly-typed versions of these routines (i.e., MAX2I, MAX2L) +// will be even more error-prone than macros. +template<class T> inline T MAX2(T a, T b) { return (a > b) ? a : b; } +template<class T> inline T MIN2(T a, T b) { return (a < b) ? a : b; } +template<class T> inline T MAX3(T a, T b, T c) { return MAX2(MAX2(a, b), c); } +template<class T> inline T MIN3(T a, T b, T c) { return MIN2(MIN2(a, b), c); } +template<class T> inline T MAX4(T a, T b, T c, T d) { return MAX2(MAX3(a, b, c), d); } +template<class T> inline T MIN4(T a, T b, T c, T d) { return MIN2(MIN3(a, b, c), d); } + +template<class T> inline T ABS(T x) { return (x > 0) ? x : -x; } + +// true if x is a power of 2, false otherwise +inline bool is_power_of_2(intptr_t x) { + return ((x != NoBits) && (mask_bits(x, x - 1) == NoBits)); +} + +// long version of is_power_of_2 +inline bool is_power_of_2_long(jlong x) { + return ((x != NoLongBits) && (mask_long_bits(x, x - 1) == NoLongBits)); +} + +//* largest i such that 2^i <= x +// A negative value of 'x' will return '31' +inline int log2_intptr(intptr_t x) { + int i = -1; + uintptr_t p = 1; + while (p != 0 && p <= (uintptr_t)x) { + // p = 2^(i+1) && p <= x (i.e., 2^(i+1) <= x) + i++; p *= 2; + } + // p = 2^(i+1) && x < p (i.e., 2^i <= x < 2^(i+1)) + // (if p = 0 then overflow occured and i = 31) + return i; +} + +//* largest i such that 2^i <= x +// A negative value of 'x' will return '63' +inline int log2_long(jlong x) { + int i = -1; + julong p = 1; + while (p != 0 && p <= (julong)x) { + // p = 2^(i+1) && p <= x (i.e., 2^(i+1) <= x) + i++; p *= 2; + } + // p = 2^(i+1) && x < p (i.e., 2^i <= x < 2^(i+1)) + // (if p = 0 then overflow occured and i = 31) + return i; +} + +//* the argument must be exactly a power of 2 +inline int exact_log2(intptr_t x) { + #ifdef ASSERT + if (!is_power_of_2(x)) basic_fatal("x must be a power of 2"); + #endif + return log2_intptr(x); +} + + +// returns integer round-up to the nearest multiple of s (s must be a power of two) +inline intptr_t round_to(intptr_t x, uintx s) { + #ifdef ASSERT + if (!is_power_of_2(s)) basic_fatal("s must be a power of 2"); + #endif + const uintx m = s - 1; + return mask_bits(x + m, ~m); +} + +// returns integer round-down to the nearest multiple of s (s must be a power of two) +inline intptr_t round_down(intptr_t x, uintx s) { + #ifdef ASSERT + if (!is_power_of_2(s)) basic_fatal("s must be a power of 2"); + #endif + const uintx m = s - 1; + return mask_bits(x, ~m); +} + + +inline bool is_odd (intx x) { return x & 1; } +inline bool is_even(intx x) { return !is_odd(x); } + +// "to" should be greater than "from." +inline intx byte_size(void* from, void* to) { + return (address)to - (address)from; +} + +//---------------------------------------------------------------------------------------------------- +// Avoid non-portable casts with these routines (DEPRECATED) + +// NOTE: USE Bytes class INSTEAD WHERE POSSIBLE +// Bytes is optimized machine-specifically and may be much faster then the portable routines below. + +// Given sequence of four bytes, build into a 32-bit word +// following the conventions used in class files. +// On the 386, this could be realized with a simple address cast. +// + +// This routine takes eight bytes: +inline u8 build_u8_from( u1 c1, u1 c2, u1 c3, u1 c4, u1 c5, u1 c6, u1 c7, u1 c8 ) { + return ( u8(c1) << 56 ) & ( u8(0xff) << 56 ) + | ( u8(c2) << 48 ) & ( u8(0xff) << 48 ) + | ( u8(c3) << 40 ) & ( u8(0xff) << 40 ) + | ( u8(c4) << 32 ) & ( u8(0xff) << 32 ) + | ( u8(c5) << 24 ) & ( u8(0xff) << 24 ) + | ( u8(c6) << 16 ) & ( u8(0xff) << 16 ) + | ( u8(c7) << 8 ) & ( u8(0xff) << 8 ) + | ( u8(c8) << 0 ) & ( u8(0xff) << 0 ); +} + +// This routine takes four bytes: +inline u4 build_u4_from( u1 c1, u1 c2, u1 c3, u1 c4 ) { + return ( u4(c1) << 24 ) & 0xff000000 + | ( u4(c2) << 16 ) & 0x00ff0000 + | ( u4(c3) << 8 ) & 0x0000ff00 + | ( u4(c4) << 0 ) & 0x000000ff; +} + +// And this one works if the four bytes are contiguous in memory: +inline u4 build_u4_from( u1* p ) { + return build_u4_from( p[0], p[1], p[2], p[3] ); +} + +// Ditto for two-byte ints: +inline u2 build_u2_from( u1 c1, u1 c2 ) { + return u2(( u2(c1) << 8 ) & 0xff00 + | ( u2(c2) << 0 ) & 0x00ff); +} + +// And this one works if the two bytes are contiguous in memory: +inline u2 build_u2_from( u1* p ) { + return build_u2_from( p[0], p[1] ); +} + +// Ditto for floats: +inline jfloat build_float_from( u1 c1, u1 c2, u1 c3, u1 c4 ) { + u4 u = build_u4_from( c1, c2, c3, c4 ); + return *(jfloat*)&u; +} + +inline jfloat build_float_from( u1* p ) { + u4 u = build_u4_from( p ); + return *(jfloat*)&u; +} + + +// now (64-bit) longs + +inline jlong build_long_from( u1 c1, u1 c2, u1 c3, u1 c4, u1 c5, u1 c6, u1 c7, u1 c8 ) { + return ( jlong(c1) << 56 ) & ( jlong(0xff) << 56 ) + | ( jlong(c2) << 48 ) & ( jlong(0xff) << 48 ) + | ( jlong(c3) << 40 ) & ( jlong(0xff) << 40 ) + | ( jlong(c4) << 32 ) & ( jlong(0xff) << 32 ) + | ( jlong(c5) << 24 ) & ( jlong(0xff) << 24 ) + | ( jlong(c6) << 16 ) & ( jlong(0xff) << 16 ) + | ( jlong(c7) << 8 ) & ( jlong(0xff) << 8 ) + | ( jlong(c8) << 0 ) & ( jlong(0xff) << 0 ); +} + +inline jlong build_long_from( u1* p ) { + return build_long_from( p[0], p[1], p[2], p[3], p[4], p[5], p[6], p[7] ); +} + + +// Doubles, too! +inline jdouble build_double_from( u1 c1, u1 c2, u1 c3, u1 c4, u1 c5, u1 c6, u1 c7, u1 c8 ) { + jlong u = build_long_from( c1, c2, c3, c4, c5, c6, c7, c8 ); + return *(jdouble*)&u; +} + +inline jdouble build_double_from( u1* p ) { + jlong u = build_long_from( p ); + return *(jdouble*)&u; +} + + +// Portable routines to go the other way: + +inline void explode_short_to( u2 x, u1& c1, u1& c2 ) { + c1 = u1(x >> 8); + c2 = u1(x); +} + +inline void explode_short_to( u2 x, u1* p ) { + explode_short_to( x, p[0], p[1]); +} + +inline void explode_int_to( u4 x, u1& c1, u1& c2, u1& c3, u1& c4 ) { + c1 = u1(x >> 24); + c2 = u1(x >> 16); + c3 = u1(x >> 8); + c4 = u1(x); +} + +inline void explode_int_to( u4 x, u1* p ) { + explode_int_to( x, p[0], p[1], p[2], p[3]); +} + + +// Pack and extract shorts to/from ints: + +inline int extract_low_short_from_int(jint x) { + return x & 0xffff; +} + +inline int extract_high_short_from_int(jint x) { + return (x >> 16) & 0xffff; +} + +inline int build_int_from_shorts( jushort low, jushort high ) { + return ((int)((unsigned int)high << 16) | (unsigned int)low); +} + +// Printf-style formatters for fixed- and variable-width types as pointers and +// integers. +// +// Each compiler-specific definitions file (e.g., globalDefinitions_gcc.hpp) +// must define the macro FORMAT64_MODIFIER, which is the modifier for '%x' or +// '%d' formats to indicate a 64-bit quantity; commonly "l" (in LP64) or "ll" +// (in ILP32). + +// Format 32-bit quantities. +#define INT32_FORMAT "%d" +#define UINT32_FORMAT "%u" +#define INT32_FORMAT_W(width) "%" #width "d" +#define UINT32_FORMAT_W(width) "%" #width "u" + +#define PTR32_FORMAT "0x%08x" + +// Format 64-bit quantities. +#define INT64_FORMAT "%" FORMAT64_MODIFIER "d" +#define UINT64_FORMAT "%" FORMAT64_MODIFIER "u" +#define PTR64_FORMAT "0x%016" FORMAT64_MODIFIER "x" + +#define INT64_FORMAT_W(width) "%" #width FORMAT64_MODIFIER "d" +#define UINT64_FORMAT_W(width) "%" #width FORMAT64_MODIFIER "u" + +// Format macros that allow the field width to be specified. The width must be +// a string literal (e.g., "8") or a macro that evaluates to one. +#ifdef _LP64 +#define SSIZE_FORMAT_W(width) INT64_FORMAT_W(width) +#define SIZE_FORMAT_W(width) UINT64_FORMAT_W(width) +#else +#define SSIZE_FORMAT_W(width) INT32_FORMAT_W(width) +#define SIZE_FORMAT_W(width) UINT32_FORMAT_W(width) +#endif // _LP64 + +// Format pointers and size_t (or size_t-like integer types) which change size +// between 32- and 64-bit. +#ifdef _LP64 +#define PTR_FORMAT PTR64_FORMAT +#define UINTX_FORMAT UINT64_FORMAT +#define INTX_FORMAT INT64_FORMAT +#define SIZE_FORMAT UINT64_FORMAT +#define SSIZE_FORMAT INT64_FORMAT +#else // !_LP64 +#define PTR_FORMAT PTR32_FORMAT +#define UINTX_FORMAT UINT32_FORMAT +#define INTX_FORMAT INT32_FORMAT +#define SIZE_FORMAT UINT32_FORMAT +#define SSIZE_FORMAT INT32_FORMAT +#endif // _LP64 + +#define INTPTR_FORMAT PTR_FORMAT + +// Enable zap-a-lot if in debug version. + +# ifdef ASSERT +# ifdef COMPILER2 +# define ENABLE_ZAP_DEAD_LOCALS +#endif /* COMPILER2 */ +# endif /* ASSERT */ + +#define ARRAY_SIZE(array) (sizeof(array)/sizeof((array)[0])) diff --git a/src/share/vm/utilities/globalDefinitions_gcc.hpp b/src/share/vm/utilities/globalDefinitions_gcc.hpp new file mode 100644 index 000000000..417978f08 --- /dev/null +++ b/src/share/vm/utilities/globalDefinitions_gcc.hpp @@ -0,0 +1,275 @@ +/* + * Copyright 1998-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. + * + */ + +// This file holds compiler-dependent includes, +// globally used constants & types, class (forward) +// declarations and a few frequently used utility functions. + +#include <ctype.h> +#include <string.h> +#include <stdarg.h> +#include <stddef.h> +#include <stdio.h> +#include <stdlib.h> +#include <wchar.h> + +#ifdef SOLARIS +#include <ieeefp.h> +#endif // SOLARIS + +#include <math.h> +#ifndef FP_PZERO +// Linux doesn't have positive/negative zero +#define FP_PZERO FP_ZERO +#endif +#if (!defined fpclass) && ((!defined SPARC) || (!defined SOLARIS)) +#define fpclass fpclassify +#endif + +#include <time.h> +#include <fcntl.h> +#include <dlfcn.h> +#include <pthread.h> + +#ifdef SOLARIS +#include <thread.h> +#endif // SOLARIS + +#include <limits.h> +#include <errno.h> + +#ifdef SOLARIS +#include <sys/trap.h> +#include <sys/regset.h> +#include <sys/procset.h> +#include <ucontext.h> +#include <setjmp.h> +#endif // SOLARIS + +# ifdef SOLARIS_MUTATOR_LIBTHREAD +# include <sys/procfs.h> +# endif + +#ifdef LINUX +#include <inttypes.h> +#include <signal.h> +#include <ucontext.h> +#include <sys/time.h> +#endif // LINUX + +// 4810578: varargs unsafe on 32-bit integer/64-bit pointer architectures +// When __cplusplus is defined, NULL is defined as 0 (32-bit constant) in +// system header files. On 32-bit architectures, there is no problem. +// On 64-bit architectures, defining NULL as a 32-bit constant can cause +// problems with varargs functions: C++ integral promotion rules say for +// varargs, we pass the argument 0 as an int. So, if NULL was passed to a +// varargs function it will remain 32-bits. Depending on the calling +// convention of the machine, if the argument is passed on the stack then +// only 32-bits of the "NULL" pointer may be initialized to zero. The +// other 32-bits will be garbage. If the varargs function is expecting a +// pointer when it extracts the argument, then we have a problem. +// +// Solution: For 64-bit architectures, redefine NULL as 64-bit constant 0. +// +// Note: this fix doesn't work well on Linux because NULL will be overwritten +// whenever a system header file is included. Linux handles NULL correctly +// through a special type '__null'. +#ifdef SOLARIS + #ifdef _LP64 + #undef NULL + #define NULL 0L + #else + #ifndef NULL + #define NULL 0 + #endif + #endif +#endif + +// NULL vs NULL_WORD: +// On Linux NULL is defined as a special type '__null'. Assigning __null to +// integer variable will cause gcc warning. Use NULL_WORD in places where a +// pointer is stored as integer value. On some platforms, sizeof(intptr_t) > +// sizeof(void*), so here we want something which is integer type, but has the +// same size as a pointer. +#ifdef LINUX + #ifdef _LP64 + #define NULL_WORD 0L + #else + #define NULL_WORD 0 + #endif +#else + #define NULL_WORD NULL +#endif + +#ifndef LINUX +// Compiler-specific primitive types +typedef unsigned short uint16_t; +#ifndef _UINT32_T +#define _UINT32_T +typedef unsigned int uint32_t; +#endif // _UINT32_T + +#if !defined(_SYS_INT_TYPES_H) +#ifndef _UINT64_T +#define _UINT64_T +typedef unsigned long long uint64_t; +#endif // _UINT64_T +// %%%% how to access definition of intptr_t portably in 5.5 onward? +typedef int intptr_t; +typedef unsigned int uintptr_t; +// If this gets an error, figure out a symbol XXX that implies the +// prior definition of intptr_t, and add "&& !defined(XXX)" above. +#endif // _SYS_INT_TYPES_H + +#endif // !LINUX + +// Additional Java basic types + +typedef uint8_t jubyte; +typedef uint16_t jushort; +typedef uint32_t juint; +typedef uint64_t julong; + +//---------------------------------------------------------------------------------------------------- +// Special (possibly not-portable) casts +// Cast floats into same-size integers and vice-versa w/o changing bit-pattern +// %%%%%% These seem like standard C++ to me--how about factoring them out? - Ungar + +inline jint jint_cast (jfloat x) { return *(jint* )&x; } +inline jlong jlong_cast (jdouble x) { return *(jlong* )&x; } + +inline jfloat jfloat_cast (jint x) { return *(jfloat* )&x; } +inline jdouble jdouble_cast(jlong x) { return *(jdouble*)&x; } + +//---------------------------------------------------------------------------------------------------- +// Constant for jlong (specifying an long long canstant is C++ compiler specific) + +// Build a 64bit integer constant +#define CONST64(x) (x ## LL) +#define UCONST64(x) (x ## ULL) + +const jlong min_jlong = CONST64(0x8000000000000000); +const jlong max_jlong = CONST64(0x7fffffffffffffff); + + +#ifdef SOLARIS +//---------------------------------------------------------------------------------------------------- +// ANSI C++ fixes +// NOTE:In the ANSI committee's continuing attempt to make each version +// of C++ incompatible with the previous version, you can no longer cast +// pointers to functions without specifying linkage unless you want to get +// warnings. +// +// This also means that pointers to functions can no longer be "hidden" +// in opaque types like void * because at the invokation point warnings +// will be generated. While this makes perfect sense from a type safety +// point of view it causes a lot of warnings on old code using C header +// files. Here are some typedefs to make the job of silencing warnings +// a bit easier. +// +// The final kick in the teeth is that you can only have extern "C" linkage +// specified at file scope. So these typedefs are here rather than in the +// .hpp for the class (os:Solaris usually) that needs them. + +extern "C" { + typedef int (*int_fnP_thread_t_iP_uP_stack_tP_gregset_t)(thread_t, int*, unsigned *, stack_t*, gregset_t); + typedef int (*int_fnP_thread_t_i_gregset_t)(thread_t, int, gregset_t); + typedef int (*int_fnP_thread_t_i)(thread_t, int); + typedef int (*int_fnP_thread_t)(thread_t); + + typedef int (*int_fnP_cond_tP_mutex_tP_timestruc_tP)(cond_t *cv, mutex_t *mx, timestruc_t *abst); + typedef int (*int_fnP_cond_tP_mutex_tP)(cond_t *cv, mutex_t *mx); + + // typedef for missing API in libc + typedef int (*int_fnP_mutex_tP_i_vP)(mutex_t *, int, void *); + typedef int (*int_fnP_mutex_tP)(mutex_t *); + typedef int (*int_fnP_cond_tP_i_vP)(cond_t *cv, int scope, void *arg); + typedef int (*int_fnP_cond_tP)(cond_t *cv); +}; +#endif // SOLARIS + +//---------------------------------------------------------------------------------------------------- +// Debugging + +#define DEBUG_EXCEPTION ::abort(); + +extern "C" void breakpoint(); +#define BREAKPOINT ::breakpoint() + +// checking for nanness +#ifdef SOLARIS +#ifdef SPARC +inline int g_isnan(float f) { return isnanf(f); } +#else +// isnanf() broken on Intel Solaris use isnand() +inline int g_isnan(float f) { return isnand(f); } +#endif +inline int g_isnan(double f) { return isnand(f); } +#elif LINUX +inline int g_isnan(float f) { return isnanf(f); } +inline int g_isnan(double f) { return isnan(f); } +#else +#error "missing platform-specific definition here" +#endif + +// Checking for finiteness + +inline int g_isfinite(jfloat f) { return finite(f); } +inline int g_isfinite(jdouble f) { return finite(f); } + + +// Wide characters + +inline int wcslen(const jchar* x) { return wcslen((const wchar_t*)x); } + + +// Portability macros +#define PRAGMA_INTERFACE #pragma interface +#define PRAGMA_IMPLEMENTATION #pragma implementation +#define VALUE_OBJ_CLASS_SPEC + +#if (__GNUC__ == 2) && (__GNUC_MINOR__ < 95) +#define TEMPLATE_TABLE_BUG +#endif +#if (__GNUC__ == 2) && (__GNUC_MINOR__ >= 96) +#define CONST_SDM_BUG +#endif + +// Formatting. +#ifdef _LP64 +#define FORMAT64_MODIFIER "l" +#else // !_LP64 +#define FORMAT64_MODIFIER "ll" +#endif // _LP64 + +// HACK: gcc warns about applying offsetof() to non-POD object or calculating +// offset directly when base address is NULL. Use 16 to get around the +// warning. gcc-3.4 has an option -Wno-invalid-offsetof to suppress +// this warning. +#define offset_of(klass,field) (size_t)((intx)&(((klass*)16)->field) - 16) + +#ifdef offsetof +# undef offsetof +#endif +#define offsetof(klass,field) offset_of(klass,field) diff --git a/src/share/vm/utilities/globalDefinitions_sparcWorks.hpp b/src/share/vm/utilities/globalDefinitions_sparcWorks.hpp new file mode 100644 index 000000000..16ae1ce9b --- /dev/null +++ b/src/share/vm/utilities/globalDefinitions_sparcWorks.hpp @@ -0,0 +1,215 @@ +/* + * 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. + * + */ + +// This file holds compiler-dependent includes, +// globally used constants & types, class (forward) +// declarations and a few frequently used utility functions. + + +# include <ctype.h> +# include <dirent.h> +# include <string.h> +# include <strings.h> // for bsd'isms +# include <stdarg.h> +# include <stddef.h> // for offsetof +# include <stdio.h> +# include <stdlib.h> +# include <wchar.h> +# include <stdarg.h> +# include <ieeefp.h> +# include <math.h> +# include <time.h> +# include <fcntl.h> +# include <dlfcn.h> +# include <pthread.h> +# include <thread.h> +# include <limits.h> +# include <errno.h> +# include <sys/trap.h> +# include <sys/regset.h> +# include <sys/procset.h> +# include <ucontext.h> +# include <setjmp.h> +# ifdef SOLARIS_MUTATOR_LIBTHREAD +# include <sys/procfs.h> +# endif + +// 4810578: varargs unsafe on 32-bit integer/64-bit pointer architectures +// When __cplusplus is defined, NULL is defined as 0 (32-bit constant) in +// system header files. On 32-bit architectures, there is no problem. +// On 64-bit architectures, defining NULL as a 32-bit constant can cause +// problems with varargs functions: C++ integral promotion rules say for +// varargs, we pass the argument 0 as an int. So, if NULL was passed to a +// varargs function it will remain 32-bits. Depending on the calling +// convention of the machine, if the argument is passed on the stack then +// only 32-bits of the "NULL" pointer may be initialized to zero. The +// other 32-bits will be garbage. If the varargs function is expecting a +// pointer when it extracts the argument, then we have a problem. +// +// Solution: For 64-bit architectures, redefine NULL as 64-bit constant 0. +#ifdef _LP64 +#undef NULL +#define NULL 0L +#else +#ifndef NULL +#define NULL 0 +#endif +#endif + +// NULL vs NULL_WORD: +// On Linux NULL is defined as a special type '__null'. Assigning __null to +// integer variable will cause gcc warning. Use NULL_WORD in places where a +// pointer is stored as integer value. +#define NULL_WORD NULL + +// Compiler-specific primitive types +typedef unsigned short uint16_t; +#ifndef _UINT32_T +#define _UINT32_T +typedef unsigned int uint32_t; +#endif +#if !defined(_SYS_INT_TYPES_H) +#ifndef _UINT64_T +#define _UINT64_T +typedef unsigned long long uint64_t; +#endif +// %%%% how to access definition of intptr_t portably in 5.5 onward? +typedef int intptr_t; +typedef unsigned int uintptr_t; +// If this gets an error, figure out a symbol XXX that implies the +// prior definition of intptr_t, and add "&& !defined(XXX)" above. +#endif + +// Additional Java basic types + +typedef unsigned char jubyte; +typedef unsigned short jushort; +typedef unsigned int juint; +typedef unsigned long long julong; + +//---------------------------------------------------------------------------------------------------- +// Special (possibly not-portable) casts +// Cast floats into same-size integers and vice-versa w/o changing bit-pattern + +inline jint jint_cast (jfloat x) { return *(jint* )&x; } +inline jlong jlong_cast (jdouble x) { return *(jlong* )&x; } + +inline jfloat jfloat_cast (jint x) { return *(jfloat* )&x; } +inline jdouble jdouble_cast(jlong x) { return *(jdouble*)&x; } + +//---------------------------------------------------------------------------------------------------- +// Constant for jlong (specifying an long long constant is C++ compiler specific) + +// Build a 64bit integer constant +#define CONST64(x) (x ## LL) +#define UCONST64(x) (x ## ULL) + +const jlong min_jlong = CONST64(0x8000000000000000); +const jlong max_jlong = CONST64(0x7fffffffffffffff); + + +//---------------------------------------------------------------------------------------------------- +// ANSI C++ fixes +// NOTE:In the ANSI committee's continuing attempt to make each version +// of C++ incompatible with the previous version, you can no longer cast +// pointers to functions without specifying linkage unless you want to get +// warnings. +// +// This also means that pointers to functions can no longer be "hidden" +// in opaque types like void * because at the invokation point warnings +// will be generated. While this makes perfect sense from a type safety +// point of view it causes a lot of warnings on old code using C header +// files. Here are some typedefs to make the job of silencing warnings +// a bit easier. +// +// The final kick in the teeth is that you can only have extern "C" linkage +// specified at file scope. So these typedefs are here rather than in the +// .hpp for the class (os:Solaris usually) that needs them. + +extern "C" { + typedef int (*int_fnP_thread_t_iP_uP_stack_tP_gregset_t)(thread_t, int*, unsigned *, stack_t*, gregset_t); + typedef int (*int_fnP_thread_t_i_gregset_t)(thread_t, int, gregset_t); + typedef int (*int_fnP_thread_t_i)(thread_t, int); + typedef int (*int_fnP_thread_t)(thread_t); + + typedef int (*int_fnP_cond_tP_mutex_tP_timestruc_tP)(cond_t *cv, mutex_t *mx, timestruc_t *abst); + typedef int (*int_fnP_cond_tP_mutex_tP)(cond_t *cv, mutex_t *mx); + + // typedef for missing API in libc + typedef int (*int_fnP_mutex_tP_i_vP)(mutex_t *, int, void *); + typedef int (*int_fnP_mutex_tP)(mutex_t *); + typedef int (*int_fnP_cond_tP_i_vP)(cond_t *cv, int scope, void *arg); + typedef int (*int_fnP_cond_tP)(cond_t *cv); +}; + + +//---------------------------------------------------------------------------------------------------- +// Debugging + +#define DEBUG_EXCEPTION ::abort(); + +extern "C" void breakpoint(); +#define BREAKPOINT ::breakpoint() + +// checking for nanness + +#ifdef SPARC +inline int g_isnan(float f) { return isnanf(f); } +#else +// isnanf() broken on Intel Solaris use isnand() +inline int g_isnan(float f) { return isnand(f); } +#endif + +inline int g_isnan(double f) { return isnand(f); } + +// Checking for finiteness + +inline int g_isfinite(jfloat f) { return finite(f); } +inline int g_isfinite(jdouble f) { return finite(f); } + + +// Wide characters + +inline int wcslen(const jchar* x) { return wcslen((const wchar_t*)x); } + + +// Misc +int local_vsnprintf(char* buf, size_t count, const char* fmt, va_list argptr); +#define vsnprintf local_vsnprintf + + +// Portability macros +#define PRAGMA_INTERFACE +#define PRAGMA_IMPLEMENTATION +#define PRAGMA_IMPLEMENTATION_(arg) +#define VALUE_OBJ_CLASS_SPEC : public _ValueObj + +// Formatting. +#ifdef _LP64 +#define FORMAT64_MODIFIER "l" +#else // !_LP64 +#define FORMAT64_MODIFIER "ll" +#endif // _LP64 + +#define offset_of(klass,field) offsetof(klass,field) diff --git a/src/share/vm/utilities/globalDefinitions_visCPP.hpp b/src/share/vm/utilities/globalDefinitions_visCPP.hpp new file mode 100644 index 000000000..6b4804ec5 --- /dev/null +++ b/src/share/vm/utilities/globalDefinitions_visCPP.hpp @@ -0,0 +1,193 @@ +/* + * 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. + * + */ + +// This file holds compiler-dependent includes, +// globally used constants & types, class (forward) +// declarations and a few frequently used utility functions. + +# include <ctype.h> +# include <string.h> +# include <stdarg.h> +# include <stdlib.h> +# include <stddef.h>// for offsetof +# include <io.h> // for stream.cpp +# include <float.h> // for _isnan +# include <stdio.h> // for va_list +# include <time.h> +# include <fcntl.h> +// Need this on windows to get the math constants (e.g., M_PI). +#define _USE_MATH_DEFINES +# include <math.h> + +// 4810578: varargs unsafe on 32-bit integer/64-bit pointer architectures +// When __cplusplus is defined, NULL is defined as 0 (32-bit constant) in +// system header files. On 32-bit architectures, there is no problem. +// On 64-bit architectures, defining NULL as a 32-bit constant can cause +// problems with varargs functions: C++ integral promotion rules say for +// varargs, we pass the argument 0 as an int. So, if NULL was passed to a +// varargs function it will remain 32-bits. Depending on the calling +// convention of the machine, if the argument is passed on the stack then +// only 32-bits of the "NULL" pointer may be initialized to zero. The +// other 32-bits will be garbage. If the varargs function is expecting a +// pointer when it extracts the argument, then we may have a problem. +// +// Solution: For 64-bit architectures, redefine NULL as 64-bit constant 0. +#ifdef _LP64 +#undef NULL +// 64-bit Windows uses a P64 data model (not LP64, although we define _LP64) +// Since longs are 32-bit we cannot use 0L here. Use the Visual C++ specific +// 64-bit integer-suffix (i64) instead. +#define NULL 0i64 +#else +#ifndef NULL +#define NULL 0 +#endif +#endif + +// NULL vs NULL_WORD: +// On Linux NULL is defined as a special type '__null'. Assigning __null to +// integer variable will cause gcc warning. Use NULL_WORD in places where a +// pointer is stored as integer value. +#define NULL_WORD NULL + +// Compiler-specific primitive types +typedef unsigned __int8 uint8_t; +typedef unsigned __int16 uint16_t; +typedef unsigned __int32 uint32_t; +typedef unsigned __int64 uint64_t; + +#ifdef _WIN64 +typedef unsigned __int64 uintptr_t; +#else +typedef unsigned int uintptr_t; +#endif +typedef signed __int8 int8_t; +typedef signed __int16 int16_t; +typedef signed __int32 int32_t; +typedef signed __int64 int64_t; +#ifdef _WIN64 +typedef signed __int64 intptr_t; +typedef signed __int64 ssize_t; +#else +typedef signed int intptr_t; +typedef signed int ssize_t; +#endif + +//---------------------------------------------------------------------------------------------------- +// Additional Java basic types + +typedef unsigned char jubyte; +typedef unsigned short jushort; +typedef unsigned int juint; +typedef unsigned __int64 julong; + +//---------------------------------------------------------------------------------------------------- +// Special (possibly not-portable) casts +// Cast floats into same-size integers and vice-versa w/o changing bit-pattern + +inline jint jint_cast (jfloat x) { return *(jint* )&x; } +inline jlong jlong_cast (jdouble x) { return *(jlong* )&x; } + +inline jfloat jfloat_cast (jint x) { return *(jfloat* )&x; } +inline jdouble jdouble_cast(jlong x) { return *(jdouble*)&x; } + + +//---------------------------------------------------------------------------------------------------- +// Non-standard stdlib-like stuff: +inline int strcasecmp(const char *s1, const char *s2) { return _stricmp(s1,s2); } + + +//---------------------------------------------------------------------------------------------------- +// Debugging + +#if _WIN64 +extern "C" void breakpoint(); +#define BREAKPOINT ::breakpoint() +#else +#define BREAKPOINT __asm { int 3 } +#endif + +//---------------------------------------------------------------------------------------------------- +// Checking for nanness + +inline int g_isnan(jfloat f) { return _isnan(f); } +inline int g_isnan(jdouble f) { return _isnan(f); } + +//---------------------------------------------------------------------------------------------------- +// Checking for finiteness + +inline int g_isfinite(jfloat f) { return _finite(f); } +inline int g_isfinite(jdouble f) { return _finite(f); } + +//---------------------------------------------------------------------------------------------------- +// Constant for jlong (specifying an long long constant is C++ compiler specific) + +// Build a 64bit integer constant on with Visual C++ +#define CONST64(x) (x ## i64) +#define UCONST64(x) ((uint64_t)CONST64(x)) + +const jlong min_jlong = CONST64(0x8000000000000000); +const jlong max_jlong = CONST64(0x7fffffffffffffff); + +//---------------------------------------------------------------------------------------------------- +// Miscellaneous + +inline int vsnprintf(char* buf, size_t count, const char* fmt, va_list argptr) { + // If number of characters written == count, Windows doesn't write a + // terminating NULL, so we do it ourselves. + int ret = _vsnprintf(buf, count, fmt, argptr); + if (count > 0) buf[count-1] = '\0'; + return ret; +} + +// Visual Studio 2005 deprecates POSIX names - use ISO C++ names instead +#if _MSC_VER >= 1400 && !defined(_WIN64) +#define open _open +#define close _close +#define read _read +#define write _write +#define lseek _lseek +#define unlink _unlink +#define strdup _strdup +#endif + +#pragma warning( disable : 4100 ) // unreferenced formal parameter +#pragma warning( disable : 4127 ) // conditional expression is constant +#pragma warning( disable : 4514 ) // unreferenced inline function has been removed +#pragma warning( disable : 4244 ) // possible loss of data +#pragma warning( disable : 4512 ) // assignment operator could not be generated +#pragma warning( disable : 4201 ) // nonstandard extension used : nameless struct/union (needed in windows.h) +#pragma warning( disable : 4511 ) // copy constructor could not be generated +#pragma warning( disable : 4291 ) // no matching operator delete found; memory will not be freed if initialization thows an exception + +// Portability macros +#define PRAGMA_INTERFACE +#define PRAGMA_IMPLEMENTATION +#define PRAGMA_IMPLEMENTATION_(arg) +#define VALUE_OBJ_CLASS_SPEC : public _ValueObj + +// Formatting. +#define FORMAT64_MODIFIER "I64" + +#define offset_of(klass,field) offsetof(klass,field) diff --git a/src/share/vm/utilities/growableArray.cpp b/src/share/vm/utilities/growableArray.cpp new file mode 100644 index 000000000..eeb259c53 --- /dev/null +++ b/src/share/vm/utilities/growableArray.cpp @@ -0,0 +1,53 @@ +/* + * Copyright 1997-2005 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/_growableArray.cpp.incl" + +#ifdef ASSERT +void GenericGrowableArray::set_nesting() { + if (on_stack()) { + _nesting = Thread::current()->resource_area()->nesting(); + } +} + +void GenericGrowableArray::check_nesting() { + // Check for insidious allocation bug: if a GrowableArray overflows, the + // grown array must be allocated under the same ResourceMark as the original. + // Otherwise, the _data array will be deallocated too early. + if (on_stack() && + _nesting != Thread::current()->resource_area()->nesting()) { + fatal("allocation bug: GrowableArray could grow within nested ResourceMark"); + } +} +#endif + +void* GenericGrowableArray::raw_allocate(int elementSize) { + if (on_stack()) { + return (void*)resource_allocate_bytes(elementSize * _max); + } else if (on_C_heap()) { + return (void*)AllocateHeap(elementSize * _max, "GrET in " __FILE__); + } else { + return _arena->Amalloc(elementSize * _max); + } +} diff --git a/src/share/vm/utilities/growableArray.hpp b/src/share/vm/utilities/growableArray.hpp new file mode 100644 index 000000000..208b14518 --- /dev/null +++ b/src/share/vm/utilities/growableArray.hpp @@ -0,0 +1,331 @@ +/* + * Copyright 1997-2005 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. + * + */ + +// A growable array. + +/*************************************************************************/ +/* */ +/* WARNING WARNING WARNING WARNING WARNING WARNING WARNING WARNING */ +/* */ +/* Should you use GrowableArrays to contain handles you must be certain */ +/* the the GrowableArray does not outlive the HandleMark that contains */ +/* the handles. Since GrowableArrays are typically resource allocated */ +/* the following is an example of INCORRECT CODE, */ +/* */ +/* ResourceMark rm; */ +/* GrowableArray<Handle>* arr = new GrowableArray<Handle>(size); */ +/* if (blah) { */ +/* while (...) { */ +/* HandleMark hm; */ +/* ... */ +/* Handle h(THREAD, some_oop); */ +/* arr->append(h); */ +/* } */ +/* } */ +/* if (arr->length() != 0 ) { */ +/* oop bad_oop = arr->at(0)(); // Handle is BAD HERE. */ +/* ... */ +/* } */ +/* */ +/* If the GrowableArrays you are creating is C_Heap allocated then it */ +/* hould not old handles since the handles could trivially try and */ +/* outlive their HandleMark. In some situations you might need to do */ +/* this and it would be legal but be very careful and see if you can do */ +/* the code in some other manner. */ +/* */ +/*************************************************************************/ + +// To call default constructor the placement operator new() is used. +// It should be empty (it only returns the passed void* pointer). +// The definition of placement operator new(size_t, void*) in the <new>. + +#include <new> + +// Need the correct linkage to call qsort without warnings +extern "C" { + typedef int (*_sort_Fn)(const void *, const void *); +} + +class GenericGrowableArray : public ResourceObj { + protected: + int _len; // current length + int _max; // maximum length + Arena* _arena; // Indicates where allocation occurs: + // 0 means default ResourceArea + // 1 means on C heap + // otherwise, allocate in _arena +#ifdef ASSERT + int _nesting; // resource area nesting at creation + void set_nesting(); + void check_nesting(); +#else +#define set_nesting(); +#define check_nesting(); +#endif + + // Where are we going to allocate memory? + bool on_C_heap() { return _arena == (Arena*)1; } + bool on_stack () { return _arena == NULL; } + bool on_arena () { return _arena > (Arena*)1; } + + // This GA will use the resource stack for storage if c_heap==false, + // Else it will use the C heap. Use clear_and_deallocate to avoid leaks. + GenericGrowableArray(int initial_size, int initial_len, bool c_heap) { + _len = initial_len; + _max = initial_size; + assert(_len >= 0 && _len <= _max, "initial_len too big"); + _arena = (c_heap ? (Arena*)1 : NULL); + set_nesting(); + assert(!c_heap || allocated_on_C_heap(), "growable array must be on C heap if elements are"); + } + + // This GA will use the given arena for storage. + // Consider using new(arena) GrowableArray<T> to allocate the header. + GenericGrowableArray(Arena* arena, int initial_size, int initial_len) { + _len = initial_len; + _max = initial_size; + assert(_len >= 0 && _len <= _max, "initial_len too big"); + _arena = arena; + assert(on_arena(), "arena has taken on reserved value 0 or 1"); + } + + void* raw_allocate(int elementSize); +}; + +template<class E> class GrowableArray : public GenericGrowableArray { + private: + E* _data; // data array + + void grow(int j); + void raw_at_put_grow(int i, const E& p, const E& fill); + void clear_and_deallocate(); + public: + GrowableArray(int initial_size, bool C_heap = false) : GenericGrowableArray(initial_size, 0, C_heap) { + _data = (E*)raw_allocate(sizeof(E)); + for (int i = 0; i < _max; i++) ::new ((void*)&_data[i]) E(); + } + + GrowableArray(int initial_size, int initial_len, const E& filler, bool C_heap = false) : GenericGrowableArray(initial_size, initial_len, C_heap) { + _data = (E*)raw_allocate(sizeof(E)); + int i = 0; + for (; i < _len; i++) ::new ((void*)&_data[i]) E(filler); + for (; i < _max; i++) ::new ((void*)&_data[i]) E(); + } + + GrowableArray(Arena* arena, int initial_size, int initial_len, const E& filler) : GenericGrowableArray(arena, initial_size, initial_len) { + _data = (E*)raw_allocate(sizeof(E)); + int i = 0; + for (; i < _len; i++) ::new ((void*)&_data[i]) E(filler); + for (; i < _max; i++) ::new ((void*)&_data[i]) E(); + } + + GrowableArray() : GenericGrowableArray(2, 0, false) { + _data = (E*)raw_allocate(sizeof(E)); + ::new ((void*)&_data[0]) E(); + ::new ((void*)&_data[1]) E(); + } + + // Does nothing for resource and arena objects + ~GrowableArray() { if (on_C_heap()) clear_and_deallocate(); } + + void clear() { _len = 0; } + int length() const { return _len; } + void trunc_to(int l) { assert(l <= _len,"cannot increase length"); _len = l; } + bool is_empty() const { return _len == 0; } + bool is_nonempty() const { return _len != 0; } + bool is_full() const { return _len == _max; } + DEBUG_ONLY(E* data_addr() const { return _data; }) + + void print(); + + void append(const E& elem) { + check_nesting(); + if (_len == _max) grow(_len); + _data[_len++] = elem; + } + + void append_if_missing(const E& elem) { + if (!contains(elem)) append(elem); + } + + E at(int i) const { + assert(0 <= i && i < _len, "illegal index"); + return _data[i]; + } + + E* adr_at(int i) const { + assert(0 <= i && i < _len, "illegal index"); + return &_data[i]; + } + + E first() const { + assert(_len > 0, "empty list"); + return _data[0]; + } + + E top() const { + assert(_len > 0, "empty list"); + return _data[_len-1]; + } + + void push(const E& elem) { append(elem); } + + E pop() { + assert(_len > 0, "empty list"); + return _data[--_len]; + } + + void at_put(int i, const E& elem) { + assert(0 <= i && i < _len, "illegal index"); + _data[i] = elem; + } + + E at_grow(int i, const E& fill = E()) { + assert(0 <= i, "negative index"); + check_nesting(); + if (i >= _len) { + if (i >= _max) grow(i); + for (int j = _len; j <= i; j++) + _data[j] = fill; + _len = i+1; + } + return _data[i]; + } + + void at_put_grow(int i, const E& elem, const E& fill = E()) { + assert(0 <= i, "negative index"); + check_nesting(); + raw_at_put_grow(i, elem, fill); + } + + bool contains(const E& elem) const { + for (int i = 0; i < _len; i++) { + if (_data[i] == elem) return true; + } + return false; + } + + int find(const E& elem) const { + for (int i = 0; i < _len; i++) { + if (_data[i] == elem) return i; + } + return -1; + } + + int find(void* token, bool f(void*, E)) const { + for (int i = 0; i < _len; i++) { + if (f(token, _data[i])) return i; + } + return -1; + } + + int find_at_end(void* token, bool f(void*, E)) const { + // start at the end of the array + for (int i = _len-1; i >= 0; i--) { + if (f(token, _data[i])) return i; + } + return -1; + } + + void remove(const E& elem) { + for (int i = 0; i < _len; i++) { + if (_data[i] == elem) { + for (int j = i + 1; j < _len; j++) _data[j-1] = _data[j]; + _len--; + return; + } + } + ShouldNotReachHere(); + } + + void remove_at(int index) { + assert(0 <= index && index < _len, "illegal index"); + for (int j = index + 1; j < _len; j++) _data[j-1] = _data[j]; + _len--; + } + + void appendAll(const GrowableArray<E>* l) { + for (int i = 0; i < l->_len; i++) { + raw_at_put_grow(_len, l->_data[i], 0); + } + } + + void sort(int f(E*,E*)) { + qsort(_data, length(), sizeof(E), (_sort_Fn)f); + } + // sort by fixed-stride sub arrays: + void sort(int f(E*,E*), int stride) { + qsort(_data, length() / stride, sizeof(E) * stride, (_sort_Fn)f); + } +}; + +// Global GrowableArray methods (one instance in the library per each 'E' type). + +template<class E> void GrowableArray<E>::grow(int j) { + // grow the array by doubling its size (amortized growth) + int old_max = _max; + if (_max == 0) _max = 1; // prevent endless loop + while (j >= _max) _max = _max*2; + // j < _max + E* newData = (E*)raw_allocate(sizeof(E)); + int i = 0; + for ( ; i < _len; i++) ::new ((void*)&newData[i]) E(_data[i]); + for ( ; i < _max; i++) ::new ((void*)&newData[i]) E(); + for (i = 0; i < old_max; i++) _data[i].~E(); + if (on_C_heap() && _data != NULL) { + FreeHeap(_data); + } + _data = newData; +} + +template<class E> void GrowableArray<E>::raw_at_put_grow(int i, const E& p, const E& fill) { + if (i >= _len) { + if (i >= _max) grow(i); + for (int j = _len; j < i; j++) + _data[j] = fill; + _len = i+1; + } + _data[i] = p; +} + +// This function clears and deallocate the data in the growable array that +// has been allocated on the C heap. It's not public - called by the +// destructor. +template<class E> void GrowableArray<E>::clear_and_deallocate() { + assert(on_C_heap(), + "clear_and_deallocate should only be called when on C heap"); + clear(); + if (_data != NULL) { + for (int i = 0; i < _max; i++) _data[i].~E(); + FreeHeap(_data); + _data = NULL; + } +} + +template<class E> void GrowableArray<E>::print() { + tty->print("Growable Array " INTPTR_FORMAT, this); + tty->print(": length %ld (_max %ld) { ", _len, _max); + for (int i = 0; i < _len; i++) tty->print(INTPTR_FORMAT " ", *(intptr_t*)&(_data[i])); + tty->print("}\n"); +} diff --git a/src/share/vm/utilities/hashtable.cpp b/src/share/vm/utilities/hashtable.cpp new file mode 100644 index 000000000..58d675d00 --- /dev/null +++ b/src/share/vm/utilities/hashtable.cpp @@ -0,0 +1,270 @@ +/* + * Copyright 2003-2005 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/_hashtable.cpp.incl" + +HS_DTRACE_PROBE_DECL4(hs_private, hashtable__new_entry, + void*, unsigned int, oop, void*); + +// This is a generic hashtable, designed to be used for the symbol +// and string tables. +// +// It is implemented as an open hash table with a fixed number of buckets. +// +// %note: +// - HashtableEntrys are allocated in blocks to reduce the space overhead. + +BasicHashtableEntry* BasicHashtable::new_entry(unsigned int hashValue) { + BasicHashtableEntry* entry; + + if (_free_list) { + entry = _free_list; + _free_list = _free_list->next(); + } else { + const int block_size = 500; + if (_first_free_entry == _end_block) { + int len = _entry_size * block_size; + _first_free_entry = NEW_C_HEAP_ARRAY(char, len); + _end_block = _first_free_entry + len; + } + entry = (BasicHashtableEntry*)_first_free_entry; + _first_free_entry += _entry_size; + } + + entry->set_hash(hashValue); + return entry; +} + + +HashtableEntry* Hashtable::new_entry(unsigned int hashValue, oop obj) { + HashtableEntry* entry; + + entry = (HashtableEntry*)BasicHashtable::new_entry(hashValue); + entry->set_literal(obj); // clears literal string field + HS_DTRACE_PROBE4(hs_private, hashtable__new_entry, + this, hashValue, obj, entry); + return entry; +} + + +// GC support + +void Hashtable::unlink(BoolObjectClosure* is_alive) { + // Readers of the table are unlocked, so we should only be removing + // entries at a safepoint. + assert(SafepointSynchronize::is_at_safepoint(), "must be at safepoint"); + for (int i = 0; i < table_size(); ++i) { + for (HashtableEntry** p = bucket_addr(i); *p != NULL; ) { + HashtableEntry* entry = *p; + if (entry->is_shared()) { + break; + } + assert(entry->literal() != NULL, "just checking"); + if (is_alive->do_object_b(entry->literal())) { + p = entry->next_addr(); + } else { + *p = entry->next(); + free_entry(entry); + } + } + } +} + + +void Hashtable::oops_do(OopClosure* f) { + for (int i = 0; i < table_size(); ++i) { + HashtableEntry** p = bucket_addr(i); + HashtableEntry* entry = bucket(i); + while (entry != NULL) { + f->do_oop(entry->literal_addr()); + + // Did the closure remove the literal from the table? + if (entry->literal() == NULL) { + assert(!entry->is_shared(), "immutable hashtable entry?"); + *p = entry->next(); + free_entry(entry); + } else { + p = entry->next_addr(); + } + entry = (HashtableEntry*)HashtableEntry::make_ptr(*p); + } + } +} + + +// Reverse the order of elements in the hash buckets. + +void BasicHashtable::reverse() { + + for (int i = 0; i < _table_size; ++i) { + BasicHashtableEntry* new_list = NULL; + BasicHashtableEntry* p = bucket(i); + while (p != NULL) { + BasicHashtableEntry* next = p->next(); + p->set_next(new_list); + new_list = p; + p = next; + } + *bucket_addr(i) = new_list; + } +} + + +// Copy the table to the shared space. + +void BasicHashtable::copy_table(char** top, char* end) { + + // Dump the hash table entries. + + intptr_t *plen = (intptr_t*)(*top); + *top += sizeof(*plen); + + int i; + for (i = 0; i < _table_size; ++i) { + for (BasicHashtableEntry** p = _buckets[i].entry_addr(); + *p != NULL; + p = (*p)->next_addr()) { + if (*top + entry_size() > end) { + warning("\nThe shared miscellaneous data space is not large " + "enough to \npreload requested classes. Use " + "-XX:SharedMiscDataSize= to increase \nthe initial " + "size of the miscellaneous data space.\n"); + exit(2); + } + *p = (BasicHashtableEntry*)memcpy(*top, *p, entry_size()); + *top += entry_size(); + } + } + *plen = (char*)(*top) - (char*)plen - sizeof(*plen); + + // Set the shared bit. + + for (i = 0; i < _table_size; ++i) { + for (BasicHashtableEntry* p = bucket(i); p != NULL; p = p->next()) { + p->set_shared(); + } + } +} + + + +// Reverse the order of elements in the hash buckets. + +void Hashtable::reverse(void* boundary) { + + for (int i = 0; i < table_size(); ++i) { + HashtableEntry* high_list = NULL; + HashtableEntry* low_list = NULL; + HashtableEntry* last_low_entry = NULL; + HashtableEntry* p = bucket(i); + while (p != NULL) { + HashtableEntry* next = p->next(); + if ((void*)p->literal() >= boundary) { + p->set_next(high_list); + high_list = p; + } else { + p->set_next(low_list); + low_list = p; + if (last_low_entry == NULL) { + last_low_entry = p; + } + } + p = next; + } + if (low_list != NULL) { + *bucket_addr(i) = low_list; + last_low_entry->set_next(high_list); + } else { + *bucket_addr(i) = high_list; + } + } +} + + +// Dump the hash table buckets. + +void BasicHashtable::copy_buckets(char** top, char* end) { + intptr_t len = _table_size * sizeof(HashtableBucket); + *(intptr_t*)(*top) = len; + *top += sizeof(intptr_t); + + *(intptr_t*)(*top) = _number_of_entries; + *top += sizeof(intptr_t); + + if (*top + len > end) { + warning("\nThe shared miscellaneous data space is not large " + "enough to \npreload requested classes. Use " + "-XX:SharedMiscDataSize= to increase \nthe initial " + "size of the miscellaneous data space.\n"); + exit(2); + } + _buckets = (HashtableBucket*)memcpy(*top, _buckets, len); + *top += len; +} + + +#ifndef PRODUCT + +void Hashtable::print() { + ResourceMark rm; + + for (int i = 0; i < table_size(); i++) { + HashtableEntry* entry = bucket(i); + while(entry != NULL) { + tty->print("%d : ", i); + entry->literal()->print(); + tty->cr(); + entry = entry->next(); + } + } +} + + +void BasicHashtable::verify() { + int count = 0; + for (int i = 0; i < table_size(); i++) { + for (BasicHashtableEntry* p = bucket(i); p != NULL; p = p->next()) { + ++count; + } + } + assert(count == number_of_entries(), "number of hashtable entries incorrect"); +} + + +#endif // PRODUCT + + +#ifdef ASSERT + +void BasicHashtable::verify_lookup_length(double load) { + if ((double)_lookup_length / (double)_lookup_count > load * 2.0) { + warning("Performance bug: SystemDictionary lookup_count=%d " + "lookup_length=%d average=%lf load=%f", + _lookup_count, _lookup_length, + (double) _lookup_length / _lookup_count, load); + } +} + +#endif diff --git a/src/share/vm/utilities/hashtable.hpp b/src/share/vm/utilities/hashtable.hpp new file mode 100644 index 000000000..dd30d250d --- /dev/null +++ b/src/share/vm/utilities/hashtable.hpp @@ -0,0 +1,280 @@ +/* + * Copyright 2003-2005 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. + * + */ + +// This is a generic hashtable, designed to be used for the symbol +// and string tables. +// +// It is implemented as an open hash table with a fixed number of buckets. +// +// %note: +// - TableEntrys are allocated in blocks to reduce the space overhead. + + + +class BasicHashtableEntry : public CHeapObj { + friend class VMStructs; +private: + unsigned int _hash; // 32-bit hash for item + + // Link to next element in the linked list for this bucket. EXCEPT + // bit 0 set indicates that this entry is shared and must not be + // unlinked from the table. Bit 0 is set during the dumping of the + // archive. Since shared entries are immutable, _next fields in the + // shared entries will not change. New entries will always be + // unshared and since pointers are align, bit 0 will always remain 0 + // with no extra effort. + BasicHashtableEntry* _next; + + // Windows IA64 compiler requires subclasses to be able to access these +protected: + // Entry objects should not be created, they should be taken from the + // free list with BasicHashtable.new_entry(). + BasicHashtableEntry() { ShouldNotReachHere(); } + // Entry objects should not be destroyed. They should be placed on + // the free list instead with BasicHashtable.free_entry(). + ~BasicHashtableEntry() { ShouldNotReachHere(); } + +public: + + unsigned int hash() const { return _hash; } + void set_hash(unsigned int hash) { _hash = hash; } + unsigned int* hash_addr() { return &_hash; } + + static BasicHashtableEntry* make_ptr(BasicHashtableEntry* p) { + return (BasicHashtableEntry*)((intptr_t)p & -2); + } + + BasicHashtableEntry* next() const { + return make_ptr(_next); + } + + void set_next(BasicHashtableEntry* next) { + _next = next; + } + + BasicHashtableEntry** next_addr() { + return &_next; + } + + bool is_shared() const { + return ((intptr_t)_next & 1) != 0; + } + + void set_shared() { + _next = (BasicHashtableEntry*)((intptr_t)_next | 1); + } +}; + + + +class HashtableEntry : public BasicHashtableEntry { + friend class VMStructs; +private: + oop _literal; // ref to item in table. + +public: + // Literal + oop literal() const { return _literal; } + oop* literal_addr() { return &_literal; } + void set_literal(oop s) { _literal = s; } + + HashtableEntry* next() const { + return (HashtableEntry*)BasicHashtableEntry::next(); + } + HashtableEntry** next_addr() { + return (HashtableEntry**)BasicHashtableEntry::next_addr(); + } +}; + + + +class HashtableBucket : public CHeapObj { + friend class VMStructs; +private: + // Instance variable + BasicHashtableEntry* _entry; + +public: + // Accessing + void clear() { _entry = NULL; } + + // The following methods use order access methods to avoid race + // conditions in multiprocessor systems. + BasicHashtableEntry* get_entry() const; + void set_entry(BasicHashtableEntry* l); + + // The following method is not MT-safe and must be done under lock. + BasicHashtableEntry** entry_addr() { return &_entry; } +}; + + +class BasicHashtable : public CHeapObj { + friend class VMStructs; + +public: + BasicHashtable(int table_size, int entry_size); + BasicHashtable(int table_size, int entry_size, + HashtableBucket* buckets, int number_of_entries); + + // Sharing support. + void copy_buckets(char** top, char* end); + void copy_table(char** top, char* end); + + // Bucket handling + int hash_to_index(unsigned int full_hash) { + int h = full_hash % _table_size; + assert(h >= 0 && h < _table_size, "Illegal hash value"); + return h; + } + + // Reverse the order of elements in each of the buckets. + void reverse(); + +private: + // Instance variables + int _table_size; + HashtableBucket* _buckets; + BasicHashtableEntry* _free_list; + char* _first_free_entry; + char* _end_block; + int _entry_size; + int _number_of_entries; + +protected: + +#ifdef ASSERT + int _lookup_count; + int _lookup_length; + void verify_lookup_length(double load); +#endif + + void initialize(int table_size, int entry_size, int number_of_entries); + + // Accessor + int entry_size() const { return _entry_size; } + int table_size() { return _table_size; } + + // The following method is MT-safe and may be used with caution. + BasicHashtableEntry* bucket(int i); + + // The following method is not MT-safe and must be done under lock. + BasicHashtableEntry** bucket_addr(int i) { return _buckets[i].entry_addr(); } + + // Table entry management + BasicHashtableEntry* new_entry(unsigned int hashValue); + +public: + void set_entry(int index, BasicHashtableEntry* entry); + + void add_entry(int index, BasicHashtableEntry* entry); + + void free_entry(BasicHashtableEntry* entry); + + int number_of_entries() { return _number_of_entries; } + + void verify() PRODUCT_RETURN; +}; + + +class Hashtable : public BasicHashtable { + friend class VMStructs; + +public: + Hashtable(int table_size, int entry_size) + : BasicHashtable(table_size, entry_size) { } + + Hashtable(int table_size, int entry_size, + HashtableBucket* buckets, int number_of_entries) + : BasicHashtable(table_size, entry_size, buckets, number_of_entries) { } + + // Invoke "f->do_oop" on the locations of all oops in the table. + void oops_do(OopClosure* f); + + // Debugging + void print() PRODUCT_RETURN; + + // GC support + // Delete pointers to otherwise-unreachable objects. + void unlink(BoolObjectClosure* cl); + + // Reverse the order of elements in each of the buckets. Hashtable + // entries which refer to objects at a lower address than 'boundary' + // are separated from those which refer to objects at higher + // addresses, and appear first in the list. + void reverse(void* boundary = NULL); + +protected: + + static unsigned int hash_symbol(const char* s, int len); + + unsigned int compute_hash(symbolHandle name) { + return (unsigned int) name->identity_hash(); + } + + int index_for(symbolHandle name) { + return hash_to_index(compute_hash(name)); + } + + // Table entry management + HashtableEntry* new_entry(unsigned int hashValue, oop obj); + + // The following method is MT-safe and may be used with caution. + HashtableEntry* bucket(int i) { + return (HashtableEntry*)BasicHashtable::bucket(i); + } + + // The following method is not MT-safe and must be done under lock. + HashtableEntry** bucket_addr(int i) { + return (HashtableEntry**)BasicHashtable::bucket_addr(i); + } +}; + + +// Verions of hashtable where two handles are used to compute the index. + +class TwoOopHashtable : public Hashtable { + friend class VMStructs; +protected: + TwoOopHashtable(int table_size, int entry_size) + : Hashtable(table_size, entry_size) {} + + TwoOopHashtable(int table_size, int entry_size, HashtableBucket* t, + int number_of_entries) + : Hashtable(table_size, entry_size, t, number_of_entries) {} + +public: + unsigned int compute_hash(symbolHandle name, Handle loader) { + // Be careful with identity_hash(), it can safepoint and if this + // were one expression, the compiler could choose to unhandle each + // oop before calling identity_hash() for either of them. If the first + // causes a GC, the next would fail. + unsigned int name_hash = name->identity_hash(); + unsigned int loader_hash = loader.is_null() ? 0 : loader->identity_hash(); + return name_hash ^ loader_hash; + } + + int index_for(symbolHandle name, Handle loader) { + return hash_to_index(compute_hash(name, loader)); + } +}; diff --git a/src/share/vm/utilities/hashtable.inline.hpp b/src/share/vm/utilities/hashtable.inline.hpp new file mode 100644 index 000000000..9d8980632 --- /dev/null +++ b/src/share/vm/utilities/hashtable.inline.hpp @@ -0,0 +1,126 @@ +/* + * Copyright 2003 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. + * + */ + +// Inline function definitions for hashtable.hpp. + + +// -------------------------------------------------------------------------- +// Hash function + +// We originally used hashpjw, but hash P(31) gives just as good results +// and is slighly faster. We would like a hash function that looks at every +// character, since package names have large common prefixes, and also because +// hash_or_fail does error checking while iterating. + +// hash P(31) from Kernighan & Ritchie + +inline unsigned int Hashtable::hash_symbol(const char* s, int len) { + unsigned int h = 0; + while (len-- > 0) { + h = 31*h + (unsigned) *s; + s++; + } + return h; +} + + +// -------------------------------------------------------------------------- + +// Initialize a table. + +inline BasicHashtable::BasicHashtable(int table_size, int entry_size) { + // Called on startup, no locking needed + initialize(table_size, entry_size, 0); + _buckets = NEW_C_HEAP_ARRAY(HashtableBucket, table_size); + for (int index = 0; index < _table_size; index++) { + _buckets[index].clear(); + } +} + + +inline BasicHashtable::BasicHashtable(int table_size, int entry_size, + HashtableBucket* buckets, + int number_of_entries) { + // Called on startup, no locking needed + initialize(table_size, entry_size, number_of_entries); + _buckets = buckets; +} + + +inline void BasicHashtable::initialize(int table_size, int entry_size, + int number_of_entries) { + // Called on startup, no locking needed + _table_size = table_size; + _entry_size = entry_size; + _free_list = NULL; + _first_free_entry = NULL; + _end_block = NULL; + _number_of_entries = number_of_entries; +#ifdef ASSERT + _lookup_count = 0; + _lookup_length = 0; +#endif +} + + +// The following method is MT-safe and may be used with caution. +inline BasicHashtableEntry* BasicHashtable::bucket(int i) { + return _buckets[i].get_entry(); +} + + +inline void HashtableBucket::set_entry(BasicHashtableEntry* l) { + // Warning: Preserve store ordering. The SystemDictionary is read + // without locks. The new SystemDictionaryEntry must be + // complete before other threads can be allowed to see it + // via a store to _buckets[index]. + OrderAccess::release_store_ptr(&_entry, l); +} + + +inline BasicHashtableEntry* HashtableBucket::get_entry() const { + // Warning: Preserve load ordering. The SystemDictionary is read + // without locks. The new SystemDictionaryEntry must be + // complete before other threads can be allowed to see it + // via a store to _buckets[index]. + return (BasicHashtableEntry*) OrderAccess::load_ptr_acquire(&_entry); +} + + +inline void BasicHashtable::set_entry(int index, BasicHashtableEntry* entry) { + _buckets[index].set_entry(entry); +} + + +inline void BasicHashtable::add_entry(int index, BasicHashtableEntry* entry) { + entry->set_next(bucket(index)); + _buckets[index].set_entry(entry); + ++_number_of_entries; +} + +inline void BasicHashtable::free_entry(BasicHashtableEntry* entry) { + entry->set_next(_free_list); + _free_list = entry; + --_number_of_entries; +} diff --git a/src/share/vm/utilities/histogram.cpp b/src/share/vm/utilities/histogram.cpp new file mode 100644 index 000000000..f60269f1c --- /dev/null +++ b/src/share/vm/utilities/histogram.cpp @@ -0,0 +1,100 @@ +/* + * Copyright 1998-2004 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/_histogram.cpp.incl" + +#ifdef ASSERT + +////////////////// HistogramElement //////////////////////// + +HistogramElement::HistogramElement() { + _count = 0; +} + +int HistogramElement::count() { + return _count; +} + +const char* HistogramElement::name() { + return _name; +} + +void HistogramElement::increment_count() { + // We can't use the accessor :-(. + Atomic::inc(&_count); +} + +int HistogramElement::compare(HistogramElement* e1,HistogramElement* e2) { + if(e1->count() > e2->count()) { + return -1; + } else if(e1->count() < e2->count()) { + return 1; + } + return 0; +} + +void HistogramElement::print_on(outputStream* st) const { + st->print("%10d ",((HistogramElement*)this)->count()); + st->print_cr("%s",((HistogramElement*)this)->name()); +} + +////////////////// Histogram //////////////////////// + +int Histogram::sort_helper(HistogramElement** e1, HistogramElement** e2) { + return (*e1)->compare(*e1,*e2); +} + +Histogram::Histogram(const char* title,int estimatedCount) { + _title = title; + _elements = new (ResourceObj::C_HEAP) GrowableArray<HistogramElement*>(estimatedCount,true); +} + +void Histogram::add_element(HistogramElement* element) { + // Note, we need to add locking ! + elements()->append(element); +} + +void Histogram::print_header(outputStream* st) { + st->print_cr("%s",title()); + st->print_cr("--------------------------------------------------"); +} + +void Histogram::print_elements(outputStream* st) { + elements()->sort(Histogram::sort_helper); + jint total = 0; + for(int i=0; i < elements()->length(); i++) { + elements()->at(i)->print(); + total += elements()->at(i)->count(); + } + st->print("%10d ", total); + st->print_cr("Total"); +} + +void Histogram::print_on(outputStream* st) const { + ((Histogram*)this)->print_header(st); + ((Histogram*)this)->print_elements(st); +} + +#endif diff --git a/src/share/vm/utilities/histogram.hpp b/src/share/vm/utilities/histogram.hpp new file mode 100644 index 000000000..0e15d8207 --- /dev/null +++ b/src/share/vm/utilities/histogram.hpp @@ -0,0 +1,91 @@ +/* + * Copyright 1998-2000 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. + * + */ + +// This class provides a framework for collecting various statistics. +// The current implementation is oriented towards counting invocations +// of various types, but that can be easily changed. +// +// To use it, you need to declare a Histogram*, and a subtype of +// HistogramElement: +// +// HistogramElement* MyHistogram; +// +// class MyHistogramElement : public HistogramElement { +// public: +// MyHistogramElement(char* name); +// }; +// +// MyHistogramElement::MyHistogramElement(char* elementName) { +// _name = elementName; +// +// if(MyHistogram == NULL) +// MyHistogram = new Histogram("My Call Counts",100); +// +// MyHistogram->add_element(this); +// } +// +// #define MyCountWrapper(arg) static MyHistogramElement* e = new MyHistogramElement(arg); e->increment_count() +// +// This gives you a simple way to count invocations of specfic functions: +// +// void a_function_that_is_being_counted() { +// MyCountWrapper("FunctionName"); +// ... +// } +// +// To print the results, invoke print() on your Histogram*. + +#ifdef ASSERT + +class HistogramElement : public CHeapObj { + protected: + jint _count; + const char* _name; + + public: + HistogramElement(); + virtual int count(); + virtual const char* name(); + virtual void increment_count(); + void print_on(outputStream* st) const; + virtual int compare(HistogramElement* e1,HistogramElement* e2); +}; + +class Histogram : public CHeapObj { + protected: + GrowableArray<HistogramElement*>* _elements; + GrowableArray<HistogramElement*>* elements() { return _elements; } + const char* _title; + const char* title() { return _title; } + static int sort_helper(HistogramElement** e1,HistogramElement** e2); + virtual void print_header(outputStream* st); + virtual void print_elements(outputStream* st); + + public: + Histogram(const char* title,int estimatedSize); + virtual void add_element(HistogramElement* element); + void print_on(outputStream* st) const; +}; + +#endif diff --git a/src/share/vm/utilities/macros.hpp b/src/share/vm/utilities/macros.hpp new file mode 100644 index 000000000..2f495efac --- /dev/null +++ b/src/share/vm/utilities/macros.hpp @@ -0,0 +1,181 @@ +/* + * 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. + * + */ + +// Use this to mark code that needs to be cleaned up (for development only) +#define NEEDS_CLEANUP + +// Makes a string of the argument (which is not macro-expanded) +#define STR(a) #a + +// Makes a string of the macro expansion of a +#define XSTR(a) STR(a) + +// KERNEL variant +#ifdef KERNEL +#define COMPILER1 +#define SERIALGC + +#define JVMTI_KERNEL +#define FPROF_KERNEL +#define VM_STRUCTS_KERNEL +#define JNICHECK_KERNEL +#define SERVICES_KERNEL + +#define KERNEL_RETURN {} +#define KERNEL_RETURN_(code) { code } + +#else // KERNEL + +#define KERNEL_RETURN /* next token must be ; */ +#define KERNEL_RETURN_(code) /* next token must be ; */ + +#endif // KERNEL + +// COMPILER1 variant +#ifdef COMPILER1 +#ifdef COMPILER2 + #define TIERED +#endif +#define COMPILER1_PRESENT(code) code +#else // COMPILER1 +#define COMPILER1_PRESENT(code) +#endif // COMPILER1 + +// COMPILER2 variant +#ifdef COMPILER2 +#define COMPILER2_PRESENT(code) code +#else // COMPILER2 +#define COMPILER2_PRESENT(code) +#endif // COMPILER2 + + +// PRODUCT variant +#ifdef PRODUCT +#define PRODUCT_ONLY(code) code +#define NOT_PRODUCT(code) +#define PRODUCT_RETURN {} +#define PRODUCT_RETURN0 { return 0; } +#define PRODUCT_RETURN_(code) { code } +#else // PRODUCT +#define PRODUCT_ONLY(code) +#define NOT_PRODUCT(code) code +#define PRODUCT_RETURN /*next token must be ;*/ +#define PRODUCT_RETURN0 /*next token must be ;*/ +#define PRODUCT_RETURN_(code) /*next token must be ;*/ +#endif // PRODUCT + +#ifdef CHECK_UNHANDLED_OOPS +#define CHECK_UNHANDLED_OOPS_ONLY(code) code +#define NOT_CHECK_UNHANDLED_OOPS(code) +#else +#define CHECK_UNHANDLED_OOPS_ONLY(code) +#define NOT_CHECK_UNHANDLED_OOPS(code) code +#endif // CHECK_UNHANDLED_OOPS + +#ifdef CC_INTERP +#define CC_INTERP_ONLY(code) code +#define NOT_CC_INTERP(code) +#else +#define CC_INTERP_ONLY(code) +#define NOT_CC_INTERP(code) code +#endif // CC_INTERP + +#ifdef ASSERT +#define DEBUG_ONLY(code) code +#define NOT_DEBUG(code) +// Historical. +#define debug_only(code) code +#else // ASSERT +#define DEBUG_ONLY(code) +#define NOT_DEBUG(code) code +#define debug_only(code) +#endif // ASSERT + +#ifdef _LP64 +#define LP64_ONLY(code) code +#define NOT_LP64(code) +#else // !_LP64 +#define LP64_ONLY(code) +#define NOT_LP64(code) code +#endif // _LP64 + +#ifdef LINUX +#define LINUX_ONLY(code) code +#define NOT_LINUX(code) +#else +#define LINUX_ONLY(code) +#define NOT_LINUX(code) code +#endif + +#ifdef SOLARIS +#define SOLARIS_ONLY(code) code +#define NOT_SOLARIS(code) +#else +#define SOLARIS_ONLY(code) +#define NOT_SOLARIS(code) code +#endif + +#ifdef _WINDOWS +#define WINDOWS_ONLY(code) code +#define NOT_WINDOWS(code) +#else +#define WINDOWS_ONLY(code) +#define NOT_WINDOWS(code) code +#endif + +#ifdef IA32 +#define IA32_ONLY(code) code +#define NOT_IA32(code) +#else +#define IA32_ONLY(code) +#define NOT_IA32(code) code +#endif + +#ifdef IA64 +#define IA64_ONLY(code) code +#define NOT_IA64(code) +#else +#define IA64_ONLY(code) +#define NOT_IA64(code) code +#endif + +#ifdef AMD64 +#define AMD64_ONLY(code) code +#define NOT_AMD64(code) +#else +#define AMD64_ONLY(code) +#define NOT_AMD64(code) code +#endif + +#ifdef SPARC +#define SPARC_ONLY(code) code +#define NOT_SPARC(code) +#else +#define SPARC_ONLY(code) +#define NOT_SPARC(code) code +#endif + +#define FIX_THIS(code) report_assertion_failure("FIX_THIS",__FILE__, __LINE__, "") + +#define define_pd_global(type, name, value) const type pd_##name = value; diff --git a/src/share/vm/utilities/ostream.cpp b/src/share/vm/utilities/ostream.cpp new file mode 100644 index 000000000..d5ad211fc --- /dev/null +++ b/src/share/vm/utilities/ostream.cpp @@ -0,0 +1,850 @@ +/* + * 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/_ostream.cpp.incl" + +extern "C" void jio_print(const char* s); // Declarationtion of jvm method + +outputStream::outputStream(int width) { + _width = width; + _position = 0; + _newlines = 0; + _precount = 0; + _indentation = 0; +} + +outputStream::outputStream(int width, bool has_time_stamps) { + _width = width; + _position = 0; + _newlines = 0; + _precount = 0; + _indentation = 0; + if (has_time_stamps) _stamp.update(); +} + +void outputStream::update_position(const char* s, size_t len) { + for (size_t i = 0; i < len; i++) { + char ch = s[i]; + if (ch == '\n') { + _newlines += 1; + _precount += _position + 1; + _position = 0; + } else if (ch == '\t') { + _position += 8; + _precount -= 7; // invariant: _precount + _position == total count + } else { + _position += 1; + } + } +} + +// Execute a vsprintf, using the given buffer if necessary. +// Return a pointer to the formatted string. +const char* outputStream::do_vsnprintf(char* buffer, size_t buflen, + const char* format, va_list ap, + bool add_cr, + size_t& result_len) { + const char* result; + if (add_cr) buflen--; + if (!strchr(format, '%')) { + // constant format string + result = format; + result_len = strlen(result); + if (add_cr && result_len >= buflen) result_len = buflen-1; // truncate + } else if (format[0] == '%' && format[1] == 's' && format[2] == '\0') { + // trivial copy-through format string + result = va_arg(ap, const char*); + result_len = strlen(result); + if (add_cr && result_len >= buflen) result_len = buflen-1; // truncate + } else if (vsnprintf(buffer, buflen, format, ap) >= 0) { + result = buffer; + result_len = strlen(result); + } else { + DEBUG_ONLY(warning("increase O_BUFLEN in ostream.hpp -- output truncated");) + result = buffer; + result_len = buflen - 1; + buffer[result_len] = 0; + } + if (add_cr) { + if (result != buffer) { + strncpy(buffer, result, buflen); + result = buffer; + } + buffer[result_len++] = '\n'; + buffer[result_len] = 0; + } + return result; +} + +void outputStream::print(const char* format, ...) { + char buffer[O_BUFLEN]; + va_list ap; + va_start(ap, format); + size_t len; + const char* str = do_vsnprintf(buffer, O_BUFLEN, format, ap, false, len); + write(str, len); + va_end(ap); +} + +void outputStream::print_cr(const char* format, ...) { + char buffer[O_BUFLEN]; + va_list ap; + va_start(ap, format); + size_t len; + const char* str = do_vsnprintf(buffer, O_BUFLEN, format, ap, true, len); + write(str, len); + va_end(ap); +} + +void outputStream::vprint(const char *format, va_list argptr) { + char buffer[O_BUFLEN]; + size_t len; + const char* str = do_vsnprintf(buffer, O_BUFLEN, format, argptr, false, len); + write(str, len); +} + +void outputStream::vprint_cr(const char* format, va_list argptr) { + char buffer[O_BUFLEN]; + size_t len; + const char* str = do_vsnprintf(buffer, O_BUFLEN, format, argptr, true, len); + write(str, len); +} + +void outputStream::fill_to(int col) { + while (position() < col) sp(); +} + +void outputStream::put(char ch) { + assert(ch != 0, "please fix call site"); + char buf[] = { ch, '\0' }; + write(buf, 1); +} + +void outputStream::sp() { + this->write(" ", 1); +} + +void outputStream::cr() { + this->write("\n", 1); +} + +void outputStream::stamp() { + if (! _stamp.is_updated()) { + _stamp.update(); // start at 0 on first call to stamp() + } + + // outputStream::stamp() may get called by ostream_abort(), use snprintf + // to avoid allocating large stack buffer in print(). + char buf[40]; + jio_snprintf(buf, sizeof(buf), "%.3f", _stamp.seconds()); + print_raw(buf); +} + +void outputStream::date_stamp(bool guard, + const char* prefix, + const char* suffix) { + if (!guard) { + return; + } + print_raw(prefix); + static const char error_time[] = "yyyy-mm-ddThh:mm:ss.mmm+zzzz"; + static const int buffer_length = 32; + char buffer[buffer_length]; + const char* iso8601_result = os::iso8601_time(buffer, buffer_length); + if (iso8601_result != NULL) { + print_raw(buffer); + } else { + print_raw(error_time); + } + print_raw(suffix); + return; +} + +void outputStream::indent() { + while (_position < _indentation) sp(); +} + +void outputStream::print_jlong(jlong value) { + // N.B. Same as INT64_FORMAT + print(os::jlong_format_specifier(), value); +} + +void outputStream::print_julong(julong value) { + // N.B. Same as UINT64_FORMAT + print(os::julong_format_specifier(), value); +} + +stringStream::stringStream(size_t initial_size) : outputStream() { + buffer_length = initial_size; + buffer = NEW_RESOURCE_ARRAY(char, buffer_length); + buffer_pos = 0; + buffer_fixed = false; +} + +// useful for output to fixed chunks of memory, such as performance counters +stringStream::stringStream(char* fixed_buffer, size_t fixed_buffer_size) : outputStream() { + buffer_length = fixed_buffer_size; + buffer = fixed_buffer; + buffer_pos = 0; + buffer_fixed = true; +} + +void stringStream::write(const char* s, size_t len) { + size_t write_len = len; // number of non-null bytes to write + size_t end = buffer_pos + len + 1; // position after write and final '\0' + if (end > buffer_length) { + if (buffer_fixed) { + // if buffer cannot resize, silently truncate + end = buffer_length; + write_len = end - buffer_pos - 1; // leave room for the final '\0' + } else { + // For small overruns, double the buffer. For larger ones, + // increase to the requested size. + if (end < buffer_length * 2) { + end = buffer_length * 2; + } + char* oldbuf = buffer; + buffer = NEW_RESOURCE_ARRAY(char, end); + strncpy(buffer, oldbuf, buffer_pos); + buffer_length = end; + } + } + // invariant: buffer is always null-terminated + guarantee(buffer_pos + write_len + 1 <= buffer_length, "stringStream oob"); + buffer[buffer_pos + write_len] = 0; + strncpy(buffer + buffer_pos, s, write_len); + buffer_pos += write_len; + + // Note that the following does not depend on write_len. + // This means that position and count get updated + // even when overflow occurs. + update_position(s, len); +} + +char* stringStream::as_string() { + char* copy = NEW_RESOURCE_ARRAY(char, buffer_pos+1); + strncpy(copy, buffer, buffer_pos); + copy[buffer_pos] = 0; // terminating null + return copy; +} + +stringStream::~stringStream() {} + +xmlStream* xtty; +outputStream* tty; +outputStream* gclog_or_tty; +extern Mutex* tty_lock; + +fileStream::fileStream(const char* file_name) { + _file = fopen(file_name, "w"); + _need_close = true; +} + +void fileStream::write(const char* s, size_t len) { + if (_file != NULL) fwrite(s, 1, len, _file); + update_position(s, len); +} + +fileStream::~fileStream() { + if (_file != NULL) { + if (_need_close) fclose(_file); + _file = NULL; + } +} + +void fileStream::flush() { + fflush(_file); +} + +fdStream::fdStream(const char* file_name) { + _fd = open(file_name, O_WRONLY | O_CREAT | O_TRUNC, 0666); + _need_close = true; +} + +fdStream::~fdStream() { + if (_fd != -1) { + if (_need_close) close(_fd); + _fd = -1; + } +} + +void fdStream::write(const char* s, size_t len) { + if (_fd != -1) ::write(_fd, s, (int)len); + update_position(s, len); +} + +defaultStream* defaultStream::instance = NULL; +int defaultStream::_output_fd = 1; +int defaultStream::_error_fd = 2; +FILE* defaultStream::_output_stream = stdout; +FILE* defaultStream::_error_stream = stderr; + +#define LOG_MAJOR_VERSION 160 +#define LOG_MINOR_VERSION 1 + +void defaultStream::init() { + _inited = true; + if (LogVMOutput || LogCompilation) { + init_log(); + } +} + +bool defaultStream::has_log_file() { + // lazily create log file (at startup, LogVMOutput is false even + // if +LogVMOutput is used, because the flags haven't been parsed yet) + // For safer printing during fatal error handling, do not init logfile + // if a VM error has been reported. + if (!_inited && !is_error_reported()) init(); + return _log_file != NULL; +} + +static const char* make_log_name(const char* log_name, const char* force_directory, char* buf) { + const char* basename = log_name; + char file_sep = os::file_separator()[0]; + const char* cp; + for (cp = log_name; *cp != '\0'; cp++) { + if (*cp == '/' || *cp == file_sep) { + basename = cp+1; + } + } + const char* nametail = log_name; + + strcpy(buf, ""); + if (force_directory != NULL) { + strcat(buf, force_directory); + strcat(buf, os::file_separator()); + nametail = basename; // completely skip directory prefix + } + + const char* star = strchr(basename, '*'); + int star_pos = (star == NULL) ? -1 : (star - nametail); + + if (star_pos >= 0) { + // convert foo*bar.log to foo123bar.log + int buf_pos = (int) strlen(buf); + strncpy(&buf[buf_pos], nametail, star_pos); + sprintf(&buf[buf_pos + star_pos], "%u", os::current_process_id()); + nametail += star_pos + 1; // skip prefix and star + } + + strcat(buf, nametail); // append rest of name, or all of name + return buf; +} + +void defaultStream::init_log() { + // %%% Need a MutexLocker? + const char* log_name = LogFile != NULL ? LogFile : "hotspot.log"; + char buf[O_BUFLEN*2]; + const char* try_name = make_log_name(log_name, NULL, buf); + fileStream* file = new(ResourceObj::C_HEAP) fileStream(try_name); + if (!file->is_open()) { + // Try again to open the file. + char warnbuf[O_BUFLEN*2]; + sprintf(warnbuf, "Warning: Cannot open log file: %s\n", try_name); + // Note: This feature is for maintainer use only. No need for L10N. + jio_print(warnbuf); + try_name = make_log_name("hs_pid*.log", os::get_temp_directory(), buf); + sprintf(warnbuf, "Warning: Forcing option -XX:LogFile=%s\n", try_name); + jio_print(warnbuf); + delete file; + file = new(ResourceObj::C_HEAP) fileStream(try_name); + } + if (file->is_open()) { + _log_file = file; + xmlStream* xs = new(ResourceObj::C_HEAP) xmlStream(file); + _outer_xmlStream = xs; + if (this == tty) xtty = xs; + // Write XML header. + xs->print_cr("<?xml version='1.0' encoding='UTF-8'?>"); + // (For now, don't bother to issue a DTD for this private format.) + jlong time_ms = os::javaTimeMillis() - tty->time_stamp().milliseconds(); + // %%% Should be: jlong time_ms = os::start_time_milliseconds(), if + // we ever get round to introduce that method on the os class + xs->head("hotspot_log version='%d %d'" + " process='%d' time_ms='"INT64_FORMAT"'", + LOG_MAJOR_VERSION, LOG_MINOR_VERSION, + os::current_process_id(), time_ms); + // Write VM version header immediately. + xs->head("vm_version"); + xs->head("name"); xs->text("%s", VM_Version::vm_name()); xs->cr(); + xs->tail("name"); + xs->head("release"); xs->text("%s", VM_Version::vm_release()); xs->cr(); + xs->tail("release"); + xs->head("info"); xs->text("%s", VM_Version::internal_vm_info_string()); xs->cr(); + xs->tail("info"); + xs->tail("vm_version"); + // Record information about the command-line invocation. + xs->head("vm_arguments"); // Cf. Arguments::print_on() + if (Arguments::num_jvm_flags() > 0) { + xs->head("flags"); + Arguments::print_jvm_flags_on(xs->text()); + xs->tail("flags"); + } + if (Arguments::num_jvm_args() > 0) { + xs->head("args"); + Arguments::print_jvm_args_on(xs->text()); + xs->tail("args"); + } + if (Arguments::java_command() != NULL) { + xs->head("command"); xs->text()->print_cr("%s", Arguments::java_command()); + xs->tail("command"); + } + if (Arguments::sun_java_launcher() != NULL) { + xs->head("launcher"); xs->text()->print_cr("%s", Arguments::sun_java_launcher()); + xs->tail("launcher"); + } + if (Arguments::system_properties() != NULL) { + xs->head("properties"); + // Print it as a java-style property list. + // System properties don't generally contain newlines, so don't bother with unparsing. + for (SystemProperty* p = Arguments::system_properties(); p != NULL; p = p->next()) { + xs->text()->print_cr("%s=%s", p->key(), p->value()); + } + xs->tail("properties"); + } + xs->tail("vm_arguments"); + // tty output per se is grouped under the <tty>...</tty> element. + xs->head("tty"); + // All further non-markup text gets copied to the tty: + xs->_text = this; // requires friend declaration! + } else { + delete(file); + // and leave xtty as NULL + LogVMOutput = false; + DisplayVMOutput = true; + LogCompilation = false; + } +} + +// finish_log() is called during normal VM shutdown. finish_log_on_error() is +// called by ostream_abort() after a fatal error. +// +void defaultStream::finish_log() { + xmlStream* xs = _outer_xmlStream; + xs->done("tty"); + + // Other log forks are appended here, at the End of Time: + CompileLog::finish_log(xs->out()); // write compile logging, if any, now + + xs->done("hotspot_log"); + xs->flush(); + + fileStream* file = _log_file; + _log_file = NULL; + + delete _outer_xmlStream; + _outer_xmlStream = NULL; + + file->flush(); + delete file; +} + +void defaultStream::finish_log_on_error(char *buf, int buflen) { + xmlStream* xs = _outer_xmlStream; + + if (xs && xs->out()) { + + xs->done_raw("tty"); + + // Other log forks are appended here, at the End of Time: + CompileLog::finish_log_on_error(xs->out(), buf, buflen); // write compile logging, if any, now + + xs->done_raw("hotspot_log"); + xs->flush(); + + fileStream* file = _log_file; + _log_file = NULL; + _outer_xmlStream = NULL; + + if (file) { + file->flush(); + + // Can't delete or close the file because delete and fclose aren't + // async-safe. We are about to die, so leave it to the kernel. + // delete file; + } + } +} + +intx defaultStream::hold(intx writer_id) { + bool has_log = has_log_file(); // check before locking + if (// impossible, but who knows? + writer_id == NO_WRITER || + + // bootstrap problem + tty_lock == NULL || + + // can't grab a lock or call Thread::current() if TLS isn't initialized + ThreadLocalStorage::thread() == NULL || + + // developer hook + !SerializeVMOutput || + + // VM already unhealthy + is_error_reported() || + + // safepoint == global lock (for VM only) + (SafepointSynchronize::is_synchronizing() && + Thread::current()->is_VM_thread()) + ) { + // do not attempt to lock unless we know the thread and the VM is healthy + return NO_WRITER; + } + if (_writer == writer_id) { + // already held, no need to re-grab the lock + return NO_WRITER; + } + tty_lock->lock_without_safepoint_check(); + // got the lock + if (writer_id != _last_writer) { + if (has_log) { + _log_file->bol(); + // output a hint where this output is coming from: + _log_file->print_cr("<writer thread='"INTX_FORMAT"'/>", writer_id); + } + _last_writer = writer_id; + } + _writer = writer_id; + return writer_id; +} + +void defaultStream::release(intx holder) { + if (holder == NO_WRITER) { + // nothing to release: either a recursive lock, or we scribbled (too bad) + return; + } + if (_writer != holder) { + return; // already unlocked, perhaps via break_tty_lock_for_safepoint + } + _writer = NO_WRITER; + tty_lock->unlock(); +} + + +// Yuck: jio_print does not accept char*/len. +static void call_jio_print(const char* s, size_t len) { + char buffer[O_BUFLEN+100]; + if (len > sizeof(buffer)-1) { + warning("increase O_BUFLEN in ostream.cpp -- output truncated"); + len = sizeof(buffer)-1; + } + strncpy(buffer, s, len); + buffer[len] = '\0'; + jio_print(buffer); +} + + +void defaultStream::write(const char* s, size_t len) { + intx thread_id = os::current_thread_id(); + intx holder = hold(thread_id); + + if (DisplayVMOutput && + (_outer_xmlStream == NULL || !_outer_xmlStream->inside_attrs())) { + // print to output stream. It can be redirected by a vfprintf hook + if (s[len] == '\0') { + jio_print(s); + } else { + call_jio_print(s, len); + } + } + + // print to log file + if (has_log_file()) { + int nl0 = _newlines; + xmlTextStream::write(s, len); + // flush the log file too, if there were any newlines + if (nl0 != _newlines){ + flush(); + } + } else { + update_position(s, len); + } + + release(holder); +} + +intx ttyLocker::hold_tty() { + if (defaultStream::instance == NULL) return defaultStream::NO_WRITER; + intx thread_id = os::current_thread_id(); + return defaultStream::instance->hold(thread_id); +} + +void ttyLocker::release_tty(intx holder) { + if (holder == defaultStream::NO_WRITER) return; + defaultStream::instance->release(holder); +} + +void ttyLocker::break_tty_lock_for_safepoint(intx holder) { + if (defaultStream::instance != NULL && + defaultStream::instance->writer() == holder) { + if (xtty != NULL) { + xtty->print_cr("<!-- safepoint while printing -->"); + } + defaultStream::instance->release(holder); + } + // (else there was no lock to break) +} + +void ostream_init() { + if (defaultStream::instance == NULL) { + defaultStream::instance = new(ResourceObj::C_HEAP) defaultStream(); + tty = defaultStream::instance; + + // We want to ensure that time stamps in GC logs consider time 0 + // the time when the JVM is initialized, not the first time we ask + // for a time stamp. So, here, we explicitly update the time stamp + // of tty. + tty->time_stamp().update_to(1); + } +} + +void ostream_init_log() { + // For -Xloggc:<file> option - called in runtime/thread.cpp + // Note : this must be called AFTER ostream_init() + + gclog_or_tty = tty; // default to tty + if (Arguments::gc_log_filename() != NULL) { + fileStream * gclog = new(ResourceObj::C_HEAP) + fileStream(Arguments::gc_log_filename()); + if (gclog->is_open()) { + // now we update the time stamp of the GC log to be synced up + // with tty. + gclog->time_stamp().update_to(tty->time_stamp().ticks()); + gclog_or_tty = gclog; + } + } + + // If we haven't lazily initialized the logfile yet, do it now, + // to avoid the possibility of lazy initialization during a VM + // crash, which can affect the stability of the fatal error handler. + defaultStream::instance->has_log_file(); +} + +// ostream_exit() is called during normal VM exit to finish log files, flush +// output and free resource. +void ostream_exit() { + static bool ostream_exit_called = false; + if (ostream_exit_called) return; + ostream_exit_called = true; + if (gclog_or_tty != tty) { + delete gclog_or_tty; + } + { + // we temporaly disable PrintMallocFree here + // as otherwise it'll lead to using of almost deleted + // tty or defaultStream::instance in logging facility + // of HeapFree(), see 6391258 + DEBUG_ONLY(FlagSetting fs(PrintMallocFree, false);) + if (tty != defaultStream::instance) { + delete tty; + } + if (defaultStream::instance != NULL) { + delete defaultStream::instance; + } + } + tty = NULL; + xtty = NULL; + gclog_or_tty = NULL; + defaultStream::instance = NULL; +} + +// ostream_abort() is called by os::abort() when VM is about to die. +void ostream_abort() { + // Here we can't delete gclog_or_tty and tty, just flush their output + if (gclog_or_tty) gclog_or_tty->flush(); + if (tty) tty->flush(); + + if (defaultStream::instance != NULL) { + static char buf[4096]; + defaultStream::instance->finish_log_on_error(buf, sizeof(buf)); + } +} + +staticBufferStream::staticBufferStream(char* buffer, size_t buflen, + outputStream *outer_stream) { + _buffer = buffer; + _buflen = buflen; + _outer_stream = outer_stream; +} + +void staticBufferStream::write(const char* c, size_t len) { + _outer_stream->print_raw(c, (int)len); +} + +void staticBufferStream::flush() { + _outer_stream->flush(); +} + +void staticBufferStream::print(const char* format, ...) { + va_list ap; + va_start(ap, format); + size_t len; + const char* str = do_vsnprintf(_buffer, _buflen, format, ap, false, len); + write(str, len); + va_end(ap); +} + +void staticBufferStream::print_cr(const char* format, ...) { + va_list ap; + va_start(ap, format); + size_t len; + const char* str = do_vsnprintf(_buffer, _buflen, format, ap, true, len); + write(str, len); + va_end(ap); +} + +void staticBufferStream::vprint(const char *format, va_list argptr) { + size_t len; + const char* str = do_vsnprintf(_buffer, _buflen, format, argptr, false, len); + write(str, len); +} + +void staticBufferStream::vprint_cr(const char* format, va_list argptr) { + size_t len; + const char* str = do_vsnprintf(_buffer, _buflen, format, argptr, true, len); + write(str, len); +} + +bufferedStream::bufferedStream(size_t initial_size) : outputStream() { + buffer_length = initial_size; + buffer = NEW_C_HEAP_ARRAY(char, buffer_length); + buffer_pos = 0; + buffer_fixed = false; +} + +bufferedStream::bufferedStream(char* fixed_buffer, size_t fixed_buffer_size) : outputStream() { + buffer_length = fixed_buffer_size; + buffer = fixed_buffer; + buffer_pos = 0; + buffer_fixed = true; +} + +void bufferedStream::write(const char* s, size_t len) { + size_t end = buffer_pos + len; + if (end >= buffer_length) { + if (buffer_fixed) { + // if buffer cannot resize, silently truncate + len = buffer_length - buffer_pos - 1; + } else { + // For small overruns, double the buffer. For larger ones, + // increase to the requested size. + if (end < buffer_length * 2) { + end = buffer_length * 2; + } + buffer = REALLOC_C_HEAP_ARRAY(char, buffer, end); + buffer_length = end; + } + } + memcpy(buffer + buffer_pos, s, len); + buffer_pos += len; + update_position(s, len); +} + +char* bufferedStream::as_string() { + char* copy = NEW_RESOURCE_ARRAY(char, buffer_pos+1); + strncpy(copy, buffer, buffer_pos); + copy[buffer_pos] = 0; // terminating null + return copy; +} + +bufferedStream::~bufferedStream() { + if (!buffer_fixed) { + FREE_C_HEAP_ARRAY(char, buffer); + } +} + +#ifndef PRODUCT + +#if defined(SOLARIS) || defined(LINUX) +#include <sys/types.h> +#include <sys/socket.h> +#include <netinet/in.h> +#include <arpa/inet.h> +#endif + +// Network access +networkStream::networkStream() { + + _socket = -1; + + hpi::initialize_socket_library(); + + int result = hpi::socket(AF_INET, SOCK_STREAM, 0); + if (result <= 0) { + assert(false, "Socket could not be created!"); + } else { + _socket = result; + } +} + +int networkStream::read(char *buf, size_t len) { + return hpi::recv(_socket, buf, (int)len, 0); +} + +void networkStream::flush() { + if (size() != 0) { + hpi::send(_socket, (char *)base(), (int)size(), 0); + } + reset(); +} + +networkStream::~networkStream() { + close(); +} + +void networkStream::close() { + if (_socket != -1) { + flush(); + hpi::socket_close(_socket); + _socket = -1; + } +} + +bool networkStream::connect(const char *ip, short port) { + + struct sockaddr_in server; + server.sin_family = AF_INET; + server.sin_port = htons(port); + + server.sin_addr.s_addr = inet_addr(ip); + if (server.sin_addr.s_addr == (unsigned long)-1) { +#ifdef _WINDOWS + struct hostent* host = hpi::get_host_by_name((char*)ip); +#else + struct hostent* host = gethostbyname(ip); +#endif + if (host != NULL) { + memcpy(&server.sin_addr, host->h_addr_list[0], host->h_length); + } else { + return false; + } + } + + + int result = hpi::connect(_socket, (struct sockaddr*)&server, sizeof(struct sockaddr_in)); + return (result >= 0); +} + +#endif diff --git a/src/share/vm/utilities/ostream.hpp b/src/share/vm/utilities/ostream.hpp new file mode 100644 index 000000000..3d50c6988 --- /dev/null +++ b/src/share/vm/utilities/ostream.hpp @@ -0,0 +1,241 @@ +/* + * Copyright 1997-2005 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. + * + */ + +// Output streams for printing +// +// Printing guidelines: +// Where possible, please use tty->print() and tty->print_cr(). +// For product mode VM warnings use warning() which internally uses tty. +// In places where tty is not initialized yet or too much overhead, +// we may use jio_printf: +// jio_fprintf(defaultStream::output_stream(), "Message"); +// This allows for redirection via -XX:+DisplayVMOutputToStdout and +// -XX:+DisplayVMOutputToStderr +class outputStream : public ResourceObj { + protected: + int _indentation; // current indentation + int _width; // width of the page + int _position; // position on the current line + int _newlines; // number of '\n' output so far + julong _precount; // number of chars output, less _position + TimeStamp _stamp; // for time stamps + + void update_position(const char* s, size_t len); + static const char* do_vsnprintf(char* buffer, size_t buflen, + const char* format, va_list ap, + bool add_cr, + size_t& result_len); + + public: + // creation + outputStream(int width = 80); + outputStream(int width, bool has_time_stamps); + + // indentation + void indent(); + void inc() { _indentation++; }; + void dec() { _indentation--; }; + int indentation() const { return _indentation; } + void set_indentation(int i) { _indentation = i; } + void fill_to(int col); + + // sizing + int width() const { return _width; } + int position() const { return _position; } + int newlines() const { return _newlines; } + julong count() const { return _precount + _position; } + void set_count(julong count) { _precount = count - _position; } + void set_position(int pos) { _position = pos; } + + // printing + void print(const char* format, ...); + void print_cr(const char* format, ...); + void vprint(const char *format, va_list argptr); + void vprint_cr(const char* format, va_list argptr); + void print_raw(const char* str) { write(str, strlen(str)); } + void print_raw(const char* str, int len) { write(str, len); } + 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 cr(); + void bol() { if (_position > 0) cr(); } + + // Time stamp + TimeStamp& time_stamp() { return _stamp; } + void stamp(); + // Date stamp + void date_stamp(bool guard, const char* prefix, const char* suffix); + // A simplified call that includes a suffix of ": " + void date_stamp(bool guard) { + date_stamp(guard, "", ": "); + } + + // portable printing of 64 bit integers + void print_jlong(jlong value); + void print_julong(julong value); + + // flushing + virtual void flush() {} + virtual void write(const char* str, size_t len) = 0; + virtual ~outputStream() {} // close properly on deletion + + void dec_cr() { dec(); cr(); } + void inc_cr() { inc(); cr(); } +}; + +// standard output + // ANSI C++ name collision +extern outputStream* tty; // tty output +extern outputStream* gclog_or_tty; // stream for gc log if -Xloggc:<f>, or tty + +// advisory locking for the shared tty stream: +class ttyLocker: StackObj { + private: + intx _holder; + + public: + static intx hold_tty(); // returns a "holder" token + static void release_tty(intx holder); // must witness same token + static void break_tty_lock_for_safepoint(intx holder); + + ttyLocker() { _holder = hold_tty(); } + ~ttyLocker() { release_tty(_holder); } +}; + +// for writing to strings; buffer will expand automatically +class stringStream : public outputStream { + protected: + char* buffer; + size_t buffer_pos; + size_t buffer_length; + bool buffer_fixed; + public: + stringStream(size_t initial_bufsize = 256); + stringStream(char* fixed_buffer, size_t fixed_buffer_size); + ~stringStream(); + virtual void write(const char* c, size_t len); + size_t size() { return buffer_pos; } + const char* base() { return buffer; } + void reset() { buffer_pos = 0; _precount = 0; _position = 0; } + char* as_string(); +}; + +class fileStream : public outputStream { + protected: + FILE* _file; + bool _need_close; + public: + fileStream(const char* file_name); + fileStream(FILE* file) { _file = file; _need_close = false; } + ~fileStream(); + bool is_open() const { return _file != NULL; } + virtual void write(const char* c, size_t len); + void flush(); +}; + +// unlike fileStream, fdStream does unbuffered I/O by calling +// open() and write() directly. It is async-safe, but output +// from multiple thread may be mixed together. Used by fatal +// error handler. +class fdStream : public outputStream { + protected: + int _fd; + bool _need_close; + public: + fdStream(const char* file_name); + fdStream(int fd = -1) { _fd = fd; _need_close = false; } + ~fdStream(); + bool is_open() const { return _fd != -1; } + void set_fd(int fd) { _fd = fd; _need_close = false; } + int fd() const { return _fd; } + virtual void write(const char* c, size_t len); + void flush() {}; +}; + +void ostream_init(); +void ostream_init_log(); +void ostream_exit(); +void ostream_abort(); + +// staticBufferStream uses a user-supplied buffer for all formatting. +// Used for safe formatting during fatal error handling. Not MT safe. +// Do not share the stream between multiple threads. +class staticBufferStream : public outputStream { + private: + char* _buffer; + size_t _buflen; + outputStream* _outer_stream; + public: + staticBufferStream(char* buffer, size_t buflen, + outputStream *outer_stream); + ~staticBufferStream() {}; + virtual void write(const char* c, size_t len); + void flush(); + void print(const char* format, ...); + void print_cr(const char* format, ...); + void vprint(const char *format, va_list argptr); + void vprint_cr(const char* format, va_list argptr); +}; + +// In the non-fixed buffer case an underlying buffer will be created and +// managed in C heap. Not MT-safe. +class bufferedStream : public outputStream { + protected: + char* buffer; + size_t buffer_pos; + size_t buffer_length; + bool buffer_fixed; + public: + bufferedStream(size_t initial_bufsize = 256); + bufferedStream(char* fixed_buffer, size_t fixed_buffer_size); + ~bufferedStream(); + virtual void write(const char* c, size_t len); + size_t size() { return buffer_pos; } + const char* base() { return buffer; } + void reset() { buffer_pos = 0; _precount = 0; _position = 0; } + char* as_string(); +}; + +#define O_BUFLEN 2000 // max size of output of individual print() methods + +#ifndef PRODUCT + +class networkStream : public bufferedStream { + + private: + int _socket; + + public: + networkStream(); + ~networkStream(); + + bool connect(const char *host, short port); + bool is_open() const { return _socket != -1; } + int read(char *buf, size_t len); + void close(); + virtual void flush(); +}; + +#endif diff --git a/src/share/vm/utilities/preserveException.cpp b/src/share/vm/utilities/preserveException.cpp new file mode 100644 index 000000000..46d8dc96a --- /dev/null +++ b/src/share/vm/utilities/preserveException.cpp @@ -0,0 +1,89 @@ +/* + * Copyright 1998-2006 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/_preserveException.cpp.incl" + +// TODO: These three classes should be refactored + +PreserveExceptionMark::PreserveExceptionMark(Thread*& thread) { + thread = Thread::current(); + _thread = thread; + _preserved_exception_oop = Handle(thread, _thread->pending_exception()); + _thread->clear_pending_exception(); // Needed to avoid infinite recursion + _preserved_exception_line = _thread->exception_line(); + _preserved_exception_file = _thread->exception_file(); +} + + +PreserveExceptionMark::~PreserveExceptionMark() { + if (_thread->has_pending_exception()) { + oop exception = _thread->pending_exception(); + _thread->clear_pending_exception(); // Needed to avoid infinite recursion + exception->print(); + fatal("PreserveExceptionMark destructor expects no pending exceptions"); + } + if (_preserved_exception_oop() != NULL) { + _thread->set_pending_exception(_preserved_exception_oop(), _preserved_exception_file, _preserved_exception_line); + } +} + + +// This code is cloned from PreserveExceptionMark, except that: +// returned pending exceptions do not cause a crash. +// thread is passed in, not set (not a reference parameter) +// and bug 6431341 has been addressed. + +CautiouslyPreserveExceptionMark::CautiouslyPreserveExceptionMark(Thread* thread) { + _thread = thread; + _preserved_exception_oop = Handle(thread, _thread->pending_exception()); + _preserved_exception_line = _thread->exception_line(); + _preserved_exception_file = _thread->exception_file(); + _thread->clear_pending_exception(); // Pending exceptions are checked in the destructor +} + + +CautiouslyPreserveExceptionMark::~CautiouslyPreserveExceptionMark() { + assert(!_thread->has_pending_exception(), "unexpected exception generated"); + if (_thread->has_pending_exception()) { + _thread->clear_pending_exception(); + } + if (_preserved_exception_oop() != NULL) { + _thread->set_pending_exception(_preserved_exception_oop(), _preserved_exception_file, _preserved_exception_line); + } +} + + +void WeakPreserveExceptionMark::preserve() { + _preserved_exception_oop = Handle(_thread, _thread->pending_exception()); + _preserved_exception_line = _thread->exception_line(); + _preserved_exception_file = _thread->exception_file(); + _thread->clear_pending_exception(); +} + +void WeakPreserveExceptionMark::restore() { + if (!_thread->has_pending_exception()) { + _thread->set_pending_exception(_preserved_exception_oop(), _preserved_exception_file, _preserved_exception_line); + } +} diff --git a/src/share/vm/utilities/preserveException.hpp b/src/share/vm/utilities/preserveException.hpp new file mode 100644 index 000000000..9ed3797d3 --- /dev/null +++ b/src/share/vm/utilities/preserveException.hpp @@ -0,0 +1,85 @@ +/* + * Copyright 1998-2006 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. + * + */ + +// This file provides more support for exception handling; see also exceptions.hpp +class PreserveExceptionMark { + private: + Thread* _thread; + Handle _preserved_exception_oop; + int _preserved_exception_line; + const char* _preserved_exception_file; + + public: + PreserveExceptionMark(Thread*& thread); + ~PreserveExceptionMark(); +}; + + +// This is a clone of PreserveExceptionMark which asserts instead +// of failing when what it wraps generates a pending exception. +// It also addresses bug 6431341. +class CautiouslyPreserveExceptionMark { + private: + Thread* _thread; + Handle _preserved_exception_oop; + int _preserved_exception_line; + const char* _preserved_exception_file; + + public: + CautiouslyPreserveExceptionMark(Thread* thread); + ~CautiouslyPreserveExceptionMark(); +}; + + +// Like PreserveExceptionMark but allows new exceptions to be generated in +// the body of the mark. If a new exception is generated then the original one +// is discarded. +class WeakPreserveExceptionMark { +private: + Thread* _thread; + Handle _preserved_exception_oop; + int _preserved_exception_line; + const char* _preserved_exception_file; + + void preserve(); + void restore(); + + public: + WeakPreserveExceptionMark(Thread* pThread) : _thread(pThread), _preserved_exception_oop() { + if (pThread->has_pending_exception()) { + preserve(); + } + } + ~WeakPreserveExceptionMark() { + if (_preserved_exception_oop.not_null()) { + restore(); + } + } +}; + + + +// use global exception mark when allowing pending exception to be set and +// saving and restoring them +#define PRESERVE_EXCEPTION_MARK Thread* THREAD; PreserveExceptionMark __em(THREAD); diff --git a/src/share/vm/utilities/sizes.cpp b/src/share/vm/utilities/sizes.cpp new file mode 100644 index 000000000..62d02473a --- /dev/null +++ b/src/share/vm/utilities/sizes.cpp @@ -0,0 +1,26 @@ +/* + * Copyright 2000 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/_sizes.cpp.incl" diff --git a/src/share/vm/utilities/sizes.hpp b/src/share/vm/utilities/sizes.hpp new file mode 100644 index 000000000..7b805f18a --- /dev/null +++ b/src/share/vm/utilities/sizes.hpp @@ -0,0 +1,144 @@ +/* + * Copyright 2000-2004 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. + * + */ + +// The following two classes are used to represent 'sizes' and 'offsets' in the VM; +// they serve as 'unit' types. ByteSize is used for sizes measured in bytes, while +// WordSize is used for sizes measured in machine words (i.e., 32bit or 64bit words +// depending on platform). +// +// The classes are defined with friend functions operating on them instead of member +// functions so that they (the classes) can be re-#define'd to int types in optimized +// mode. This allows full type checking and maximum safety in debug mode, and full +// optimizations (constant folding) and zero overhead (time and space wise) in the +// optimized build (some compilers do not optimize one-element value classes but +// instead create an object in memory - thus the overhead may be significant). +// +// Note: 1) DO NOT add new overloaded friend functions that do not have a unique function +// function name but require signature types for resolution. This will not work +// in optimized mode as both, ByteSize and WordSize are mapped to the same type +// and thus the distinction would not be possible anymore (=> compiler errors). +// +// 2) DO NOT add non-static member functions as they cannot be mapped so something +// compilable in the optimized build. Static member functions could be added +// but require a corresponding class definition in the optimized build. +// +// These classes should help doing a transition from (currently) word-size based offsets +// to byte-size based offsets in the VM (this will be important if we desire to pack +// objects more densely in the VM for 64bit machines). Such a transition should proceed +// in two steps to minimize the risk of introducing hard-to-find bugs: +// +// a) first transition the whole VM into a form where all sizes are strongly typed +// b) change all WordSize's to ByteSize's where desired and fix the compilation errors + + +#ifdef ASSERT + +class ByteSize VALUE_OBJ_CLASS_SPEC { + private: + int _size; + + // Note: This constructor must be private to avoid implicit conversions! + ByteSize(int size) { _size = size; } + + public: + // constructors + inline friend ByteSize in_ByteSize(int size); + + // accessors + inline friend int in_bytes(ByteSize x); + + // operators + friend ByteSize operator + (ByteSize x, ByteSize y) { return ByteSize(in_bytes(x) + in_bytes(y)); } + friend ByteSize operator - (ByteSize x, ByteSize y) { return ByteSize(in_bytes(x) - in_bytes(y)); } + friend ByteSize operator * (ByteSize x, int y) { return ByteSize(in_bytes(x) * y ); } + + // comparison + friend bool operator == (ByteSize x, ByteSize y) { return in_bytes(x) == in_bytes(y); } + friend bool operator != (ByteSize x, ByteSize y) { return in_bytes(x) != in_bytes(y); } + friend bool operator < (ByteSize x, ByteSize y) { return in_bytes(x) < in_bytes(y); } + friend bool operator <= (ByteSize x, ByteSize y) { return in_bytes(x) <= in_bytes(y); } + friend bool operator > (ByteSize x, ByteSize y) { return in_bytes(x) > in_bytes(y); } + friend bool operator >= (ByteSize x, ByteSize y) { return in_bytes(x) >= in_bytes(y); } +}; + +inline ByteSize in_ByteSize(int size) { return ByteSize(size); } +inline int in_bytes(ByteSize x) { return x._size; } + + +class WordSize VALUE_OBJ_CLASS_SPEC { + private: + int _size; + + // Note: This constructor must be private to avoid implicit conversions! + WordSize(int size) { _size = size; } + + public: + // constructors + inline friend WordSize in_WordSize(int size); + + // accessors + inline friend int in_words(WordSize x); + + // operators + friend WordSize operator + (WordSize x, WordSize y) { return WordSize(in_words(x) + in_words(y)); } + friend WordSize operator - (WordSize x, WordSize y) { return WordSize(in_words(x) - in_words(y)); } + friend WordSize operator * (WordSize x, int y) { return WordSize(in_words(x) * y ); } + + // comparison + friend bool operator == (WordSize x, WordSize y) { return in_words(x) == in_words(y); } + friend bool operator != (WordSize x, WordSize y) { return in_words(x) != in_words(y); } + friend bool operator < (WordSize x, WordSize y) { return in_words(x) < in_words(y); } + friend bool operator <= (WordSize x, WordSize y) { return in_words(x) <= in_words(y); } + friend bool operator > (WordSize x, WordSize y) { return in_words(x) > in_words(y); } + friend bool operator >= (WordSize x, WordSize y) { return in_words(x) >= in_words(y); } +}; + +inline WordSize in_WordSize(int size) { return WordSize(size); } +inline int in_words(WordSize x) { return x._size; } + + +#else // ASSERT + +// The following definitions must match the corresponding friend declarations +// in the Byte/WordSize classes if they are typedef'ed to be int. This will +// be the case in optimized mode to ensure zero overhead for these types. +// +// Note: If a compiler does not inline these function calls away, one may +// want to use #define's to make sure full optimization (constant +// folding in particular) is possible. + +typedef int ByteSize; +inline ByteSize in_ByteSize(int size) { return size; } +inline int in_bytes (ByteSize x) { return x; } + +typedef int WordSize; +inline WordSize in_WordSize(int size) { return size; } +inline int in_words (WordSize x) { return x; } + +#endif // ASSERT + + +// Use the following #define to get C++ field member offsets + +#define byte_offset_of(klass,field) in_ByteSize((int)offset_of(klass, field)) diff --git a/src/share/vm/utilities/taskqueue.cpp b/src/share/vm/utilities/taskqueue.cpp new file mode 100644 index 000000000..691a85031 --- /dev/null +++ b/src/share/vm/utilities/taskqueue.cpp @@ -0,0 +1,178 @@ +/* + * Copyright 2001-2006 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/_taskqueue.cpp.incl" + +bool TaskQueueSuper::peek() { + return _bottom != _age.top(); +} + +int TaskQueueSetSuper::randomParkAndMiller(int *seed0) { + const int a = 16807; + const int m = 2147483647; + const int q = 127773; /* m div a */ + const int r = 2836; /* m mod a */ + assert(sizeof(int) == 4, "I think this relies on that"); + int seed = *seed0; + int hi = seed / q; + int lo = seed % q; + int test = a * lo - r * hi; + if (test > 0) + seed = test; + else + seed = test + m; + *seed0 = seed; + return seed; +} + +ParallelTaskTerminator:: +ParallelTaskTerminator(int n_threads, TaskQueueSetSuper* queue_set) : + _n_threads(n_threads), + _queue_set(queue_set), + _offered_termination(0) {} + +bool ParallelTaskTerminator::peek_in_queue_set() { + return _queue_set->peek(); +} + +void ParallelTaskTerminator::yield() { + os::yield(); +} + +void ParallelTaskTerminator::sleep(uint millis) { + os::sleep(Thread::current(), millis, false); +} + +bool ParallelTaskTerminator::offer_termination() { + Atomic::inc(&_offered_termination); + + juint yield_count = 0; + while (true) { + if (_offered_termination == _n_threads) { + //inner_termination_loop(); + return true; + } else { + if (yield_count <= WorkStealingYieldsBeforeSleep) { + yield_count++; + yield(); + } else { + if (PrintGCDetails && Verbose) { + gclog_or_tty->print_cr("ParallelTaskTerminator::offer_termination() " + "thread %d sleeps after %d yields", + Thread::current(), yield_count); + } + yield_count = 0; + // A sleep will cause this processor to seek work on another processor's + // runqueue, if it has nothing else to run (as opposed to the yield + // which may only move the thread to the end of the this processor's + // runqueue). + sleep(WorkStealingSleepMillis); + } + + if (peek_in_queue_set()) { + Atomic::dec(&_offered_termination); + return false; + } + } + } +} + +void ParallelTaskTerminator::reset_for_reuse() { + if (_offered_termination != 0) { + assert(_offered_termination == _n_threads, + "Terminator may still be in use"); + _offered_termination = 0; + } +} + +bool ChunkTaskQueueWithOverflow::is_empty() { + return (_chunk_queue.size() == 0) && + (_overflow_stack->length() == 0); +} + +bool ChunkTaskQueueWithOverflow::stealable_is_empty() { + return _chunk_queue.size() == 0; +} + +bool ChunkTaskQueueWithOverflow::overflow_is_empty() { + return _overflow_stack->length() == 0; +} + +void ChunkTaskQueueWithOverflow::initialize() { + _chunk_queue.initialize(); + assert(_overflow_stack == 0, "Creating memory leak"); + _overflow_stack = + new (ResourceObj::C_HEAP) GrowableArray<ChunkTask>(10, true); +} + +void ChunkTaskQueueWithOverflow::save(ChunkTask t) { + if (TraceChunkTasksQueuing && Verbose) { + gclog_or_tty->print_cr("CTQ: save " PTR_FORMAT, t); + } + if(!_chunk_queue.push(t)) { + _overflow_stack->push(t); + } +} + +// Note that using this method will retrieve all chunks +// that have been saved but that it will always check +// the overflow stack. It may be more efficient to +// check the stealable queue and the overflow stack +// separately. +bool ChunkTaskQueueWithOverflow::retrieve(ChunkTask& chunk_task) { + bool result = retrieve_from_overflow(chunk_task); + if (!result) { + result = retrieve_from_stealable_queue(chunk_task); + } + if (TraceChunkTasksQueuing && Verbose && result) { + gclog_or_tty->print_cr(" CTQ: retrieve " PTR_FORMAT, result); + } + return result; +} + +bool ChunkTaskQueueWithOverflow::retrieve_from_stealable_queue( + ChunkTask& chunk_task) { + bool result = _chunk_queue.pop_local(chunk_task); + if (TraceChunkTasksQueuing && Verbose) { + gclog_or_tty->print_cr("CTQ: retrieve_stealable " PTR_FORMAT, chunk_task); + } + return result; +} + +bool ChunkTaskQueueWithOverflow::retrieve_from_overflow( + ChunkTask& chunk_task) { + bool result; + if (!_overflow_stack->is_empty()) { + chunk_task = _overflow_stack->pop(); + result = true; + } else { + chunk_task = (ChunkTask) NULL; + result = false; + } + if (TraceChunkTasksQueuing && Verbose) { + gclog_or_tty->print_cr("CTQ: retrieve_stealable " PTR_FORMAT, chunk_task); + } + return result; +} diff --git a/src/share/vm/utilities/taskqueue.hpp b/src/share/vm/utilities/taskqueue.hpp new file mode 100644 index 000000000..7fa983f82 --- /dev/null +++ b/src/share/vm/utilities/taskqueue.hpp @@ -0,0 +1,525 @@ +/* + * Copyright 2001-2006 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 TaskQueueSuper: public CHeapObj { +protected: + // The first free element after the last one pushed (mod _n). + // (For now we'll assume only 32-bit CAS). + volatile juint _bottom; + + // log2 of the size of the queue. + enum SomeProtectedConstants { + Log_n = 14 + }; + + // Size of the queue. + juint n() { return (1 << Log_n); } + // For computing "x mod n" efficiently. + juint n_mod_mask() { return n() - 1; } + + struct Age { + jushort _top; + jushort _tag; + + jushort tag() const { return _tag; } + jushort top() const { return _top; } + + Age() { _tag = 0; _top = 0; } + + friend bool operator ==(const Age& a1, const Age& a2) { + return a1.tag() == a2.tag() && a1.top() == a2.top(); + } + + }; + Age _age; + // These make sure we do single atomic reads and writes. + Age get_age() { + jint res = *(volatile jint*)(&_age); + return *(Age*)(&res); + } + void set_age(Age a) { + *(volatile jint*)(&_age) = *(int*)(&a); + } + + jushort get_top() { + return get_age().top(); + } + + // These both operate mod _n. + juint increment_index(juint ind) { + return (ind + 1) & n_mod_mask(); + } + juint decrement_index(juint ind) { + return (ind - 1) & n_mod_mask(); + } + + // Returns a number in the range [0.._n). If the result is "n-1", it + // should be interpreted as 0. + juint dirty_size(juint bot, juint top) { + return ((jint)bot - (jint)top) & n_mod_mask(); + } + + // Returns the size corresponding to the given "bot" and "top". + juint size(juint bot, juint top) { + juint sz = dirty_size(bot, top); + // Has the queue "wrapped", so that bottom is less than top? + // There's a complicated special case here. A pair of threads could + // perform pop_local and pop_global operations concurrently, starting + // from a state in which _bottom == _top+1. The pop_local could + // succeed in decrementing _bottom, and the pop_global in incrementing + // _top (in which case the pop_global will be awarded the contested + // queue element.) The resulting state must be interpreted as an empty + // queue. (We only need to worry about one such event: only the queue + // owner performs pop_local's, and several concurrent threads + // attempting to perform the pop_global will all perform the same CAS, + // and only one can succeed. Any stealing thread that reads after + // either the increment or decrement will seen an empty queue, and will + // not join the competitors. The "sz == -1 || sz == _n-1" state will + // not be modified by concurrent queues, so the owner thread can reset + // the state to _bottom == top so subsequent pushes will be performed + // normally. + if (sz == (n()-1)) return 0; + else return sz; + } + +public: + TaskQueueSuper() : _bottom(0), _age() {} + + // Return "true" if the TaskQueue contains any tasks. + bool peek(); + + // Return an estimate of the number of elements in the queue. + // The "careful" version admits the possibility of pop_local/pop_global + // races. + juint size() { + return size(_bottom, get_top()); + } + + juint dirty_size() { + return dirty_size(_bottom, get_top()); + } + + // Maximum number of elements allowed in the queue. This is two less + // than the actual queue size, for somewhat complicated reasons. + juint max_elems() { return n() - 2; } + +}; + +template<class E> class GenericTaskQueue: public TaskQueueSuper { +private: + // Slow paths for push, pop_local. (pop_global has no fast path.) + bool push_slow(E t, juint dirty_n_elems); + bool pop_local_slow(juint localBot, Age oldAge); + +public: + // Initializes the queue to empty. + GenericTaskQueue(); + + void initialize(); + + // Push the task "t" on the queue. Returns "false" iff the queue is + // full. + inline bool push(E t); + + // If succeeds in claiming a task (from the 'local' end, that is, the + // most recently pushed task), returns "true" and sets "t" to that task. + // Otherwise, the queue is empty and returns false. + inline bool pop_local(E& t); + + // If succeeds in claiming a task (from the 'global' end, that is, the + // least recently pushed task), returns "true" and sets "t" to that task. + // Otherwise, the queue is empty and returns false. + bool pop_global(E& t); + + // Delete any resource associated with the queue. + ~GenericTaskQueue(); + +private: + // Element array. + volatile E* _elems; +}; + +template<class E> +GenericTaskQueue<E>::GenericTaskQueue():TaskQueueSuper() { + assert(sizeof(Age) == sizeof(jint), "Depends on this."); +} + +template<class E> +void GenericTaskQueue<E>::initialize() { + _elems = NEW_C_HEAP_ARRAY(E, n()); + guarantee(_elems != NULL, "Allocation failed."); +} + +template<class E> +bool GenericTaskQueue<E>::push_slow(E t, juint dirty_n_elems) { + if (dirty_n_elems == n() - 1) { + // Actually means 0, so do the push. + juint localBot = _bottom; + _elems[localBot] = t; + _bottom = increment_index(localBot); + return true; + } else + return false; +} + +template<class E> +bool GenericTaskQueue<E>:: +pop_local_slow(juint localBot, Age oldAge) { + // This queue was observed to contain exactly one element; either this + // thread will claim it, or a competing "pop_global". In either case, + // the queue will be logically empty afterwards. Create a new Age value + // that represents the empty queue for the given value of "_bottom". (We + // must also increment "tag" because of the case where "bottom == 1", + // "top == 0". A pop_global could read the queue element in that case, + // then have the owner thread do a pop followed by another push. Without + // the incrementing of "tag", the pop_global's CAS could succeed, + // allowing it to believe it has claimed the stale element.) + Age newAge; + newAge._top = localBot; + newAge._tag = oldAge.tag() + 1; + // Perhaps a competing pop_global has already incremented "top", in which + // case it wins the element. + if (localBot == oldAge.top()) { + Age tempAge; + // No competing pop_global has yet incremented "top"; we'll try to + // install new_age, thus claiming the element. + assert(sizeof(Age) == sizeof(jint) && sizeof(jint) == sizeof(juint), + "Assumption about CAS unit."); + *(jint*)&tempAge = Atomic::cmpxchg(*(jint*)&newAge, (volatile jint*)&_age, *(jint*)&oldAge); + if (tempAge == oldAge) { + // We win. + assert(dirty_size(localBot, get_top()) != n() - 1, + "Shouldn't be possible..."); + return true; + } + } + // We fail; a completing pop_global gets the element. But the queue is + // empty (and top is greater than bottom.) Fix this representation of + // the empty queue to become the canonical one. + set_age(newAge); + assert(dirty_size(localBot, get_top()) != n() - 1, + "Shouldn't be possible..."); + return false; +} + +template<class E> +bool GenericTaskQueue<E>::pop_global(E& t) { + Age newAge; + Age oldAge = get_age(); + juint localBot = _bottom; + juint n_elems = size(localBot, oldAge.top()); + if (n_elems == 0) { + return false; + } + t = _elems[oldAge.top()]; + newAge = oldAge; + newAge._top = increment_index(newAge.top()); + if ( newAge._top == 0 ) newAge._tag++; + Age resAge; + *(jint*)&resAge = Atomic::cmpxchg(*(jint*)&newAge, (volatile jint*)&_age, *(jint*)&oldAge); + // Note that using "_bottom" here might fail, since a pop_local might + // have decremented it. + assert(dirty_size(localBot, newAge._top) != n() - 1, + "Shouldn't be possible..."); + return (resAge == oldAge); +} + +template<class E> +GenericTaskQueue<E>::~GenericTaskQueue() { + FREE_C_HEAP_ARRAY(E, _elems); +} + +// Inherits the typedef of "Task" from above. +class TaskQueueSetSuper: public CHeapObj { +protected: + static int randomParkAndMiller(int* seed0); +public: + // Returns "true" if some TaskQueue in the set contains a task. + virtual bool peek() = 0; +}; + +template<class E> class GenericTaskQueueSet: public TaskQueueSetSuper { +private: + int _n; + GenericTaskQueue<E>** _queues; + +public: + GenericTaskQueueSet(int n) : _n(n) { + typedef GenericTaskQueue<E>* GenericTaskQueuePtr; + _queues = NEW_C_HEAP_ARRAY(GenericTaskQueuePtr, n); + guarantee(_queues != NULL, "Allocation failure."); + for (int i = 0; i < n; i++) { + _queues[i] = NULL; + } + } + + bool steal_1_random(int queue_num, int* seed, E& t); + bool steal_best_of_2(int queue_num, int* seed, E& t); + bool steal_best_of_all(int queue_num, int* seed, E& t); + + void register_queue(int i, GenericTaskQueue<E>* q); + + GenericTaskQueue<E>* queue(int n); + + // The thread with queue number "queue_num" (and whose random number seed + // is at "seed") is trying to steal a task from some other queue. (It + // may try several queues, according to some configuration parameter.) + // If some steal succeeds, returns "true" and sets "t" the stolen task, + // otherwise returns false. + bool steal(int queue_num, int* seed, E& t); + + bool peek(); +}; + +template<class E> +void GenericTaskQueueSet<E>::register_queue(int i, GenericTaskQueue<E>* q) { + assert(0 <= i && i < _n, "index out of range."); + _queues[i] = q; +} + +template<class E> +GenericTaskQueue<E>* GenericTaskQueueSet<E>::queue(int i) { + return _queues[i]; +} + +template<class E> +bool GenericTaskQueueSet<E>::steal(int queue_num, int* seed, E& t) { + for (int i = 0; i < 2 * _n; i++) + if (steal_best_of_2(queue_num, seed, t)) + return true; + return false; +} + +template<class E> +bool GenericTaskQueueSet<E>::steal_best_of_all(int queue_num, int* seed, E& t) { + if (_n > 2) { + int best_k; + jint best_sz = 0; + for (int k = 0; k < _n; k++) { + if (k == queue_num) continue; + jint sz = _queues[k]->size(); + if (sz > best_sz) { + best_sz = sz; + best_k = k; + } + } + return best_sz > 0 && _queues[best_k]->pop_global(t); + } else if (_n == 2) { + // Just try the other one. + int k = (queue_num + 1) % 2; + return _queues[k]->pop_global(t); + } else { + assert(_n == 1, "can't be zero."); + return false; + } +} + +template<class E> +bool GenericTaskQueueSet<E>::steal_1_random(int queue_num, int* seed, E& t) { + if (_n > 2) { + int k = queue_num; + while (k == queue_num) k = randomParkAndMiller(seed) % _n; + return _queues[2]->pop_global(t); + } else if (_n == 2) { + // Just try the other one. + int k = (queue_num + 1) % 2; + return _queues[k]->pop_global(t); + } else { + assert(_n == 1, "can't be zero."); + return false; + } +} + +template<class E> +bool GenericTaskQueueSet<E>::steal_best_of_2(int queue_num, int* seed, E& t) { + if (_n > 2) { + int k1 = queue_num; + while (k1 == queue_num) k1 = randomParkAndMiller(seed) % _n; + int k2 = queue_num; + while (k2 == queue_num || k2 == k1) k2 = randomParkAndMiller(seed) % _n; + // Sample both and try the larger. + juint sz1 = _queues[k1]->size(); + juint sz2 = _queues[k2]->size(); + if (sz2 > sz1) return _queues[k2]->pop_global(t); + else return _queues[k1]->pop_global(t); + } else if (_n == 2) { + // Just try the other one. + int k = (queue_num + 1) % 2; + return _queues[k]->pop_global(t); + } else { + assert(_n == 1, "can't be zero."); + return false; + } +} + +template<class E> +bool GenericTaskQueueSet<E>::peek() { + // Try all the queues. + for (int j = 0; j < _n; j++) { + if (_queues[j]->peek()) + return true; + } + return false; +} + +// A class to aid in the termination of a set of parallel tasks using +// TaskQueueSet's for work stealing. + +class ParallelTaskTerminator: public StackObj { +private: + int _n_threads; + TaskQueueSetSuper* _queue_set; + jint _offered_termination; + + bool peek_in_queue_set(); +protected: + virtual void yield(); + void sleep(uint millis); + +public: + + // "n_threads" is the number of threads to be terminated. "queue_set" is a + // queue sets of work queues of other threads. + ParallelTaskTerminator(int n_threads, TaskQueueSetSuper* queue_set); + + // The current thread has no work, and is ready to terminate if everyone + // else is. If returns "true", all threads are terminated. If returns + // "false", available work has been observed in one of the task queues, + // so the global task is not complete. + bool offer_termination(); + + // Reset the terminator, so that it may be reused again. + // The caller is responsible for ensuring that this is done + // in an MT-safe manner, once the previous round of use of + // the terminator is finished. + void reset_for_reuse(); + +}; + +#define SIMPLE_STACK 0 + +template<class E> inline bool GenericTaskQueue<E>::push(E t) { +#if SIMPLE_STACK + juint localBot = _bottom; + if (_bottom < max_elems()) { + _elems[localBot] = t; + _bottom = localBot + 1; + return true; + } else { + return false; + } +#else + juint localBot = _bottom; + assert((localBot >= 0) && (localBot < n()), "_bottom out of range."); + jushort top = get_top(); + juint dirty_n_elems = dirty_size(localBot, top); + assert((dirty_n_elems >= 0) && (dirty_n_elems < n()), + "n_elems out of range."); + if (dirty_n_elems < max_elems()) { + _elems[localBot] = t; + _bottom = increment_index(localBot); + return true; + } else { + return push_slow(t, dirty_n_elems); + } +#endif +} + +template<class E> inline bool GenericTaskQueue<E>::pop_local(E& t) { +#if SIMPLE_STACK + juint localBot = _bottom; + assert(localBot > 0, "precondition."); + localBot--; + t = _elems[localBot]; + _bottom = localBot; + return true; +#else + juint localBot = _bottom; + // This value cannot be n-1. That can only occur as a result of + // the assignment to bottom in this method. If it does, this method + // resets the size( to 0 before the next call (which is sequential, + // since this is pop_local.) + juint dirty_n_elems = dirty_size(localBot, get_top()); + assert(dirty_n_elems != n() - 1, "Shouldn't be possible..."); + if (dirty_n_elems == 0) return false; + localBot = decrement_index(localBot); + _bottom = localBot; + // This is necessary to prevent any read below from being reordered + // before the store just above. + OrderAccess::fence(); + t = _elems[localBot]; + // This is a second read of "age"; the "size()" above is the first. + // If there's still at least one element in the queue, based on the + // "_bottom" and "age" we've read, then there can be no interference with + // a "pop_global" operation, and we're done. + juint tp = get_top(); + if (size(localBot, tp) > 0) { + assert(dirty_size(localBot, tp) != n() - 1, + "Shouldn't be possible..."); + return true; + } else { + // Otherwise, the queue contained exactly one element; we take the slow + // path. + return pop_local_slow(localBot, get_age()); + } +#endif +} + +typedef oop Task; +typedef GenericTaskQueue<Task> OopTaskQueue; +typedef GenericTaskQueueSet<Task> OopTaskQueueSet; + +typedef oop* StarTask; +typedef GenericTaskQueue<StarTask> OopStarTaskQueue; +typedef GenericTaskQueueSet<StarTask> OopStarTaskQueueSet; + +typedef size_t ChunkTask; // index for chunk +typedef GenericTaskQueue<ChunkTask> ChunkTaskQueue; +typedef GenericTaskQueueSet<ChunkTask> ChunkTaskQueueSet; + +class ChunkTaskQueueWithOverflow: public CHeapObj { + protected: + ChunkTaskQueue _chunk_queue; + GrowableArray<ChunkTask>* _overflow_stack; + + public: + ChunkTaskQueueWithOverflow() : _overflow_stack(NULL) {} + // Initialize both stealable queue and overflow + void initialize(); + // Save first to stealable queue and then to overflow + void save(ChunkTask t); + // Retrieve first from overflow and then from stealable queue + bool retrieve(ChunkTask& chunk_index); + // Retrieve from stealable queue + bool retrieve_from_stealable_queue(ChunkTask& chunk_index); + // Retrieve from overflow + bool retrieve_from_overflow(ChunkTask& chunk_index); + bool is_empty(); + bool stealable_is_empty(); + bool overflow_is_empty(); + juint stealable_size() { return _chunk_queue.size(); } + ChunkTaskQueue* task_queue() { return &_chunk_queue; } +}; + +#define USE_ChunkTaskQueueWithOverflow diff --git a/src/share/vm/utilities/top.hpp b/src/share/vm/utilities/top.hpp new file mode 100644 index 000000000..2b709bb33 --- /dev/null +++ b/src/share/vm/utilities/top.hpp @@ -0,0 +1,26 @@ +/* + * Copyright 1997 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. + * + */ + +// THIS FILE IS INTESIONALLY LEFT EMPTY +// IT IS USED TO MINIMIZE THE NUMBER OF DEPENDENCIES IN includeDB diff --git a/src/share/vm/utilities/utf8.cpp b/src/share/vm/utilities/utf8.cpp new file mode 100644 index 000000000..120b55277 --- /dev/null +++ b/src/share/vm/utilities/utf8.cpp @@ -0,0 +1,244 @@ +/* + * Copyright 1997-2004 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/_utf8.cpp.incl" + +// Assume the utf8 string is in legal form and has been +// checked in the class file parser/format checker. +char* UTF8::next(const char* str, jchar* value) { + unsigned const char *ptr = (const unsigned char *)str; + unsigned char ch, ch2, ch3; + int length = -1; /* bad length */ + jchar result; + switch ((ch = ptr[0]) >> 4) { + default: + result = ch; + length = 1; + break; + + case 0x8: case 0x9: case 0xA: case 0xB: case 0xF: + /* Shouldn't happen. */ + break; + + case 0xC: case 0xD: + /* 110xxxxx 10xxxxxx */ + if (((ch2 = ptr[1]) & 0xC0) == 0x80) { + unsigned char high_five = ch & 0x1F; + unsigned char low_six = ch2 & 0x3F; + result = (high_five << 6) + low_six; + length = 2; + break; + } + break; + + case 0xE: + /* 1110xxxx 10xxxxxx 10xxxxxx */ + if (((ch2 = ptr[1]) & 0xC0) == 0x80) { + if (((ch3 = ptr[2]) & 0xC0) == 0x80) { + unsigned char high_four = ch & 0x0f; + unsigned char mid_six = ch2 & 0x3f; + unsigned char low_six = ch3 & 0x3f; + result = (((high_four << 6) + mid_six) << 6) + low_six; + length = 3; + } + } + break; + } /* end of switch */ + + if (length <= 0) { + *value = ptr[0]; /* default bad result; */ + return (char*)(ptr + 1); // make progress somehow + } + + *value = result; + + // The assert is correct but the .class file is wrong + // assert(UNICODE::utf8_size(result) == length, "checking reverse computation"); + return (char *)(ptr + length); +} + +char* UTF8::next_character(const char* str, jint* value) { + unsigned const char *ptr = (const unsigned char *)str; + /* See if it's legal supplementary character: + 11101101 1010xxxx 10xxxxxx 11101101 1011xxxx 10xxxxxx */ + if (is_supplementary_character(ptr)) { + *value = get_supplementary_character(ptr); + return (char *)(ptr + 6); + } + jchar result; + char* next_ch = next(str, &result); + *value = result; + return next_ch; +} + +// Count bytes of the form 10xxxxxx and deduct this count +// from the total byte count. The utf8 string must be in +// legal form which has been verified in the format checker. +int UTF8::unicode_length(const char* str, int len) { + int num_chars = len; + for (int i = 0; i < len; i++) { + if ((str[i] & 0xC0) == 0x80) { + --num_chars; + } + } + return num_chars; +} + +// Count bytes of the utf8 string except those in form +// 10xxxxxx which only appear in multibyte characters. +// The utf8 string must be in legal form and has been +// verified in the format checker. +int UTF8::unicode_length(const char* str) { + int num_chars = 0; + for (const char* p = str; *p; p++) { + if (((*p) & 0xC0) != 0x80) { + num_chars++; + } + } + return num_chars; +} + +// Writes a jchar a utf8 and returns the end +static u_char* utf8_write(u_char* base, jchar ch) { + if ((ch != 0) && (ch <=0x7f)) { + base[0] = (u_char) ch; + return base + 1; + } + + if (ch <= 0x7FF) { + /* 11 bits or less. */ + unsigned char high_five = ch >> 6; + unsigned char low_six = ch & 0x3F; + base[0] = high_five | 0xC0; /* 110xxxxx */ + base[1] = low_six | 0x80; /* 10xxxxxx */ + return base + 2; + } + /* possibly full 16 bits. */ + char high_four = ch >> 12; + char mid_six = (ch >> 6) & 0x3F; + char low_six = ch & 0x3f; + base[0] = high_four | 0xE0; /* 1110xxxx */ + base[1] = mid_six | 0x80; /* 10xxxxxx */ + base[2] = low_six | 0x80; /* 10xxxxxx */ + return base + 3; +} + +void UTF8::convert_to_unicode(const char* utf8_str, jchar* unicode_str, int unicode_length) { + unsigned char ch; + const char *ptr = (const char *)utf8_str; + int index = 0; + + /* ASCII case loop optimization */ + for (; index < unicode_length; index++) { + if((ch = ptr[0]) > 0x7F) { break; } + unicode_str[index] = ch; + ptr = (const char *)(ptr + 1); + } + + for (; index < unicode_length; index++) { + ptr = UTF8::next(ptr, &unicode_str[index]); + } +} + +// Returns NULL if 'c' it not found. This only works as long +// as 'c' is an ASCII character +jbyte* UTF8::strrchr(jbyte* base, int length, jbyte c) { + assert(length >= 0, "sanity check"); + assert(c >= 0, "does not work for non-ASCII characters"); + // Skip backwards in string until 'c' is found or end is reached + while(--length >= 0 && base[length] != c); + return (length < 0) ? NULL : &base[length]; +} + +bool UTF8::equal(jbyte* base1, int length1, jbyte* base2, int length2) { + // Length must be the same + if (length1 != length2) return false; + for (int i = 0; i < length1; i++) { + if (base1[i] != base2[i]) return false; + } + return true; +} + +bool UTF8::is_supplementary_character(const unsigned char* str) { + return ((str[0] & 0xFF) == 0xED) && ((str[1] & 0xF0) == 0xA0) && ((str[2] & 0xC0) == 0x80) + && ((str[3] & 0xFF) == 0xED) && ((str[4] & 0xF0) == 0xB0) && ((str[5] & 0xC0) == 0x80); +} + +jint UTF8::get_supplementary_character(const unsigned char* str) { + return 0x10000 + ((str[1] & 0x0f) << 16) + ((str[2] & 0x3f) << 10) + + ((str[4] & 0x0f) << 6) + (str[5] & 0x3f); +} + + +//------------------------------------------------------------------------------------- + + +int UNICODE::utf8_size(jchar c) { + if ((0x0001 <= c) && (c <= 0x007F)) return 1; + if (c <= 0x07FF) return 2; + return 3; +} + +int UNICODE::utf8_length(jchar* base, int length) { + int result = 0; + for (int index = 0; index < length; index++) { + jchar c = base[index]; + if ((0x0001 <= c) && (c <= 0x007F)) result += 1; + else if (c <= 0x07FF) result += 2; + else result += 3; + } + return result; +} + +char* UNICODE::as_utf8(jchar* base, int length) { + int utf8_len = utf8_length(base, length); + u_char* result = NEW_RESOURCE_ARRAY(u_char, utf8_len + 1); + u_char* p = result; + for (int index = 0; index < length; index++) { + p = utf8_write(p, base[index]); + } + *p = '\0'; + assert(p == &result[utf8_len], "length prediction must be correct"); + return (char*) result; +} + +char* UNICODE::as_utf8(jchar* base, int length, char* buf, int buflen) { + u_char* p = (u_char*)buf; + u_char* end = (u_char*)buf + buflen; + for (int index = 0; index < length; index++) { + jchar c = base[index]; + if (p + utf8_size(c) >= end) break; // string is truncated + p = utf8_write(p, base[index]); + } + *p = '\0'; + return buf; +} + +void UNICODE::convert_to_utf8(const jchar* base, int length, char* utf8_buffer) { + for(int index = 0; index < length; index++) { + utf8_buffer = (char*)utf8_write((u_char*)utf8_buffer, base[index]); + } + *utf8_buffer = '\0'; +} diff --git a/src/share/vm/utilities/utf8.hpp b/src/share/vm/utilities/utf8.hpp new file mode 100644 index 000000000..baa8e7795 --- /dev/null +++ b/src/share/vm/utilities/utf8.hpp @@ -0,0 +1,76 @@ +/* + * Copyright 1997-2003 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. + * + */ + +// Low-level interface for UTF8 strings + +class UTF8 : AllStatic { + public: + // returns the unicode length of a 0-terminated uft8 string + static int unicode_length(const char* uft8_str); + + // returns the unicode length of a non-0-terminated uft8 string + static int unicode_length(const char* uft8_str, int len); + + // converts a uft8 string to a unicode string + static void convert_to_unicode(const char* utf8_str, jchar* unicode_buffer, int unicode_length); + + // decodes the current utf8 character, stores the result in value, + // and returns the end of the current uft8 chararacter. + static char* next(const char* str, jchar* value); + + // decodes the current utf8 character, gets the supplementary character instead of + // the surrogate pair when seeing a supplementary character in string, + // stores the result in value, and returns the end of the current uft8 chararacter. + static char* next_character(const char* str, jint* value); + + // Utility methods + static jbyte* strrchr(jbyte* base, int length, jbyte c); + static bool equal(jbyte* base1, int length1, jbyte* base2, int length2); + static bool is_supplementary_character(const unsigned char* str); + static jint get_supplementary_character(const unsigned char* str); +}; + + +// Low-level interface for UNICODE strings + +// A unicode string represents a string in the UTF-16 format in which supplementary +// characters are represented by surrogate pairs. Index values refer to char code +// units, so a supplementary character uses two positions in a unicode string. + +class UNICODE : AllStatic { + public: + // returns the utf8 size of a unicode character + static int utf8_size(jchar c); + + // returns the utf8 length of a unicode string + static int utf8_length(jchar* base, int length); + + // converts a unicode string to utf8 string + static void convert_to_utf8(const jchar* base, int length, char* utf8_buffer); + + // converts a unicode string to a utf8 string; result is allocated + // in resource area unless a buffer is provided. + static char* as_utf8(jchar* base, int length); + static char* as_utf8(jchar* base, int length, char* buf, int buflen); +}; diff --git a/src/share/vm/utilities/vmError.cpp b/src/share/vm/utilities/vmError.cpp new file mode 100644 index 000000000..17dfb6b0c --- /dev/null +++ b/src/share/vm/utilities/vmError.cpp @@ -0,0 +1,875 @@ +/* + * Copyright 2003-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/_vmError.cpp.incl" + +// List of environment variables that should be reported in error log file. +const char *env_list[] = { + // All platforms + "JAVA_HOME", "JRE_HOME", "JAVA_TOOL_OPTIONS", "_JAVA_OPTIONS", "CLASSPATH", + "JAVA_COMPILER", "PATH", "USERNAME", + + // Env variables that are defined on Solaris/Linux + "LD_LIBRARY_PATH", "LD_PRELOAD", "SHELL", "DISPLAY", + "HOSTTYPE", "OSTYPE", "ARCH", "MACHTYPE", + + // defined on Linux + "LD_ASSUME_KERNEL", "_JAVA_SR_SIGNUM", + + // defined on Windows + "OS", "PROCESSOR_IDENTIFIER", "_ALT_JAVA_HOME_DIR", + + (const char *)0 +}; + +// Fatal error handler for internal errors and crashes. +// +// The default behavior of fatal error handler is to print a brief message +// to standard out (defaultStream::output_fd()), then save detailed information +// into an error report file (hs_err_pid<pid>.log) and abort VM. If multiple +// threads are having troubles at the same time, only one error is reported. +// The thread that is reporting error will abort VM when it is done, all other +// threads are blocked forever inside report_and_die(). + +// Constructor for crashes +VMError::VMError(Thread* thread, int sig, address pc, void* siginfo, void* context) { + _thread = thread; + _id = sig; + _pc = pc; + _siginfo = siginfo; + _context = context; + + _verbose = false; + _current_step = 0; + _current_step_info = NULL; + + _message = ""; + _filename = NULL; + _lineno = 0; + + _size = 0; +} + +// Constructor for internal errors +VMError::VMError(Thread* thread, const char* message, const char* filename, int lineno) { + _thread = thread; + _id = internal_error; // set it to a value that's not an OS exception/signal + _filename = filename; + _lineno = lineno; + _message = message; + + _verbose = false; + _current_step = 0; + _current_step_info = NULL; + + _pc = NULL; + _siginfo = NULL; + _context = NULL; + + _size = 0; +} + +// Constructor for OOM errors +VMError::VMError(Thread* thread, size_t size, const char* message, const char* filename, int lineno) { + _thread = thread; + _id = oom_error; // set it to a value that's not an OS exception/signal + _filename = filename; + _lineno = lineno; + _message = message; + + _verbose = false; + _current_step = 0; + _current_step_info = NULL; + + _pc = NULL; + _siginfo = NULL; + _context = NULL; + + _size = size; +} + + +// Constructor for non-fatal errors +VMError::VMError(const char* message) { + _thread = NULL; + _id = internal_error; // set it to a value that's not an OS exception/signal + _filename = NULL; + _lineno = 0; + _message = message; + + _verbose = false; + _current_step = 0; + _current_step_info = NULL; + + _pc = NULL; + _siginfo = NULL; + _context = NULL; + + _size = 0; +} + +// -XX:OnError=<string>, where <string> can be a list of commands, separated +// by ';'. "%p" is replaced by current process id (pid); "%%" is replaced by +// a single "%". Some examples: +// +// -XX:OnError="pmap %p" // show memory map +// -XX:OnError="gcore %p; dbx - %p" // dump core and launch debugger +// -XX:OnError="cat hs_err_pid%p.log | mail my_email@sun.com" +// -XX:OnError="kill -9 %p" // ?#!@# + +// A simple parser for -XX:OnError, usage: +// ptr = OnError; +// while ((cmd = next_OnError_command(buffer, sizeof(buffer), &ptr) != NULL) +// ... ... +static char* next_OnError_command(char* buf, int buflen, const char** ptr) { + if (ptr == NULL || *ptr == NULL) return NULL; + + const char* cmd = *ptr; + + // skip leading blanks or ';' + while (*cmd == ' ' || *cmd == ';') cmd++; + + if (*cmd == '\0') return NULL; + + const char * cmdend = cmd; + while (*cmdend != '\0' && *cmdend != ';') cmdend++; + + Arguments::copy_expand_pid(cmd, cmdend - cmd, buf, buflen); + + *ptr = (*cmdend == '\0' ? cmdend : cmdend + 1); + return buf; +} + + +static void print_bug_submit_message(outputStream *out, Thread *thread) { + if (out == NULL) return; + out->print_raw_cr("# If you would like to submit a bug report, please visit:"); + out->print_raw ("# "); + out->print_raw_cr(Arguments::java_vendor_url_bug()); + // If the crash is in native code, encourage user to submit a bug to the + // provider of that code. + if (thread && thread->is_Java_thread()) { + JavaThread* jt = (JavaThread*)thread; + if (jt->thread_state() == _thread_in_native) { + out->print_cr("# The crash happened outside the Java Virtual Machine in native code.\n# See problematic frame for where to report the bug."); + } + } + out->print_raw_cr("#"); +} + + +// Return a string to describe the error +char* VMError::error_string(char* buf, int buflen) { + char signame_buf[64]; + const char *signame = os::exception_name(_id, signame_buf, sizeof(signame_buf)); + + if (signame) { + jio_snprintf(buf, buflen, + "%s (0x%x) at pc=" PTR_FORMAT ", pid=%d, tid=" UINTX_FORMAT, + signame, _id, _pc, + os::current_process_id(), os::current_thread_id()); + } else { + if (_filename != NULL && _lineno > 0) { + // skip directory names + char separator = os::file_separator()[0]; + const char *p = strrchr(_filename, separator); + + jio_snprintf(buf, buflen, + "Internal Error at %s:%d, pid=%d, tid=" UINTX_FORMAT " \nError: %s", + p ? p + 1 : _filename, _lineno, + os::current_process_id(), os::current_thread_id(), + _message ? _message : ""); + } else { + jio_snprintf(buf, buflen, + "Internal Error (0x%x), pid=%d, tid=" UINTX_FORMAT, + _id, os::current_process_id(), os::current_thread_id()); + } + } + + return buf; +} + + +// This is the main function to report a fatal error. Only one thread can +// call this function, so we don't need to worry about MT-safety. But it's +// possible that the error handler itself may crash or die on an internal +// error, for example, when the stack/heap is badly damaged. We must be +// able to handle recursive errors that happen inside error handler. +// +// Error reporting is done in several steps. If a crash or internal error +// occurred when reporting an error, the nested signal/exception handler +// can skip steps that are already (or partially) done. Error reporting will +// continue from the next step. This allows us to retrieve and print +// information that may be unsafe to get after a fatal error. If it happens, +// you may find nested report_and_die() frames when you look at the stack +// in a debugger. +// +// In general, a hang in error handler is much worse than a crash or internal +// error, as it's harder to recover from a hang. Deadlock can happen if we +// try to grab a lock that is already owned by current thread, or if the +// owner is blocked forever (e.g. in os::infinite_sleep()). If possible, the +// error handler and all the functions it called should avoid grabbing any +// lock. An important thing to notice is that memory allocation needs a lock. +// +// We should avoid using large stack allocated buffers. Many errors happen +// when stack space is already low. Making things even worse is that there +// could be nested report_and_die() calls on stack (see above). Only one +// thread can report error, so large buffers are statically allocated in data +// segment. + +void VMError::report(outputStream* st) { +# define BEGIN if (_current_step == 0) { _current_step = 1; +# define STEP(n, s) } if (_current_step < n) { _current_step = n; _current_step_info = s; +# define END } + + // don't allocate large buffer on stack + static char buf[O_BUFLEN]; + + BEGIN + + STEP(10, "(printing unexpected error message)") + + st->print_cr("#"); + st->print_cr("# An unexpected error has been detected by Java Runtime Environment:"); + + STEP(15, "(printing type of error)") + + switch(_id) { + case oom_error: + st->print_cr("#"); + st->print("# java.lang.OutOfMemoryError: "); + if (_size) { + st->print("requested "); + sprintf(buf,"%d",_size); + st->print(buf); + st->print(" bytes"); + if (_message != NULL) { + st->print(" for "); + st->print(_message); + } + st->print_cr(". Out of swap space?"); + } else { + if (_message != NULL) + st->print_cr(_message); + } + break; + case internal_error: + default: + break; + } + + STEP(20, "(printing exception/signal name)") + + st->print_cr("#"); + st->print("# "); + // Is it an OS exception/signal? + if (os::exception_name(_id, buf, sizeof(buf))) { + st->print("%s", buf); + st->print(" (0x%x)", _id); // signal number + st->print(" at pc=" PTR_FORMAT, _pc); + } else { + st->print("Internal Error"); + if (_filename != NULL && _lineno > 0) { +#ifdef PRODUCT + // In product mode chop off pathname? + char separator = os::file_separator()[0]; + const char *p = strrchr(_filename, separator); + const char *file = p ? p+1 : _filename; +#else + const char *file = _filename; +#endif + size_t len = strlen(file); + size_t buflen = sizeof(buf); + + strncpy(buf, file, buflen); + if (len + 10 < buflen) { + sprintf(buf + len, ":" SIZE_FORMAT, _lineno); + } + st->print(" (%s)", buf); + } else { + st->print(" (0x%x)", _id); + } + } + + STEP(30, "(printing current thread and pid)") + + // process id, thread id + st->print(", pid=%d", os::current_process_id()); + st->print(", tid=" UINTX_FORMAT, os::current_thread_id()); + st->cr(); + + STEP(40, "(printing error message)") + + // error message + if (_message && _message[0] != '\0') { + st->print_cr("# Error: %s", _message); + } + + STEP(50, "(printing Java version string)") + + // VM version + st->print_cr("#"); + st->print_cr("# Java VM: %s (%s %s %s)", + Abstract_VM_Version::vm_name(), + Abstract_VM_Version::vm_release(), + Abstract_VM_Version::vm_info_string(), + Abstract_VM_Version::vm_platform_string() + ); + + STEP(60, "(printing problematic frame)") + + // Print current frame if we have a context (i.e. it's a crash) + if (_context) { + st->print_cr("# Problematic frame:"); + st->print("# "); + frame fr = os::fetch_frame_from_context(_context); + fr.print_on_error(st, buf, sizeof(buf)); + st->cr(); + st->print_cr("#"); + } + + STEP(65, "(printing bug submit message)") + + if (_verbose) print_bug_submit_message(st, _thread); + + STEP(70, "(printing thread)" ) + + if (_verbose) { + st->cr(); + st->print_cr("--------------- T H R E A D ---------------"); + st->cr(); + } + + STEP(80, "(printing current thread)" ) + + // current thread + if (_verbose) { + if (_thread) { + st->print("Current thread (" PTR_FORMAT "): ", _thread); + _thread->print_on_error(st, buf, sizeof(buf)); + st->cr(); + } else { + st->print_cr("Current thread is native thread"); + } + st->cr(); + } + + STEP(90, "(printing siginfo)" ) + + // signal no, signal code, address that caused the fault + if (_verbose && _siginfo) { + os::print_siginfo(st, _siginfo); + st->cr(); + } + + STEP(100, "(printing registers, top of stack, instructions near pc)") + + // registers, top of stack, instructions near pc + if (_verbose && _context) { + os::print_context(st, _context); + st->cr(); + } + + STEP(110, "(printing stack bounds)" ) + + if (_verbose) { + st->print("Stack: "); + + address stack_top; + size_t stack_size; + + if (_thread) { + stack_top = _thread->stack_base(); + stack_size = _thread->stack_size(); + } else { + stack_top = os::current_stack_base(); + stack_size = os::current_stack_size(); + } + + address stack_bottom = stack_top - stack_size; + st->print("[" PTR_FORMAT "," PTR_FORMAT "]", stack_bottom, stack_top); + + frame fr = _context ? os::fetch_frame_from_context(_context) + : os::current_frame(); + + if (fr.sp()) { + st->print(", sp=" PTR_FORMAT, fr.sp()); + st->print(", free space=%dk", + ((intptr_t)fr.sp() - (intptr_t)stack_bottom) >> 10); + } + + st->cr(); + } + + STEP(120, "(printing native stack)" ) + + if (_verbose) { + frame fr = _context ? os::fetch_frame_from_context(_context) + : os::current_frame(); + + // see if it's a valid frame + if (fr.pc()) { + st->print_cr("Native frames: (J=compiled Java code, j=interpreted, Vv=VM code, C=native code)"); + + int count = 0; + + while (count++ < StackPrintLimit) { + fr.print_on_error(st, buf, sizeof(buf)); + st->cr(); + if (os::is_first_C_frame(&fr)) break; + fr = os::get_sender_for_C_frame(&fr); + } + + if (count > StackPrintLimit) { + st->print_cr("...<more frames>..."); + } + + st->cr(); + } + } + + STEP(130, "(printing Java stack)" ) + + if (_verbose && _thread && _thread->is_Java_thread()) { + JavaThread* jt = (JavaThread*)_thread; + if (jt->has_last_Java_frame()) { + st->print_cr("Java frames: (J=compiled Java code, j=interpreted, Vv=VM code)"); + for(StackFrameStream sfs(jt); !sfs.is_done(); sfs.next()) { + sfs.current()->print_on_error(st, buf, sizeof(buf)); + st->cr(); + } + } + } + + STEP(140, "(printing VM operation)" ) + + if (_verbose && _thread && _thread->is_VM_thread()) { + VMThread* t = (VMThread*)_thread; + VM_Operation* op = t->vm_operation(); + if (op) { + op->print_on_error(st); + st->cr(); + st->cr(); + } + } + + STEP(150, "(printing current compile task)" ) + + if (_verbose && _thread && _thread->is_Compiler_thread()) { + CompilerThread* t = (CompilerThread*)_thread; + if (t->task()) { + st->cr(); + st->print_cr("Current CompileTask:"); + t->task()->print_line_on_error(st, buf, sizeof(buf)); + st->cr(); + } + } + + STEP(160, "(printing process)" ) + + if (_verbose) { + st->cr(); + st->print_cr("--------------- P R O C E S S ---------------"); + st->cr(); + } + + STEP(170, "(printing all threads)" ) + + // all threads + if (_verbose && _thread) { + Threads::print_on_error(st, _thread, buf, sizeof(buf)); + st->cr(); + } + + STEP(175, "(printing VM state)" ) + + if (_verbose) { + // Safepoint state + st->print("VM state:"); + + if (SafepointSynchronize::is_synchronizing()) st->print("synchronizing"); + else if (SafepointSynchronize::is_at_safepoint()) st->print("at safepoint"); + else st->print("not at safepoint"); + + // Also see if error occurred during initialization or shutdown + if (!Universe::is_fully_initialized()) { + st->print(" (not fully initialized)"); + } else if (VM_Exit::vm_exited()) { + st->print(" (shutting down)"); + } else { + st->print(" (normal execution)"); + } + st->cr(); + st->cr(); + } + + STEP(180, "(printing owned locks on error)" ) + + // mutexes/monitors that currently have an owner + if (_verbose) { + print_owned_locks_on_error(st); + st->cr(); + } + + STEP(190, "(printing heap information)" ) + + if (_verbose && Universe::is_fully_initialized()) { + // print heap information before vm abort + Universe::print_on(st); + st->cr(); + } + + STEP(200, "(printing dynamic libraries)" ) + + if (_verbose) { + // dynamic libraries, or memory map + os::print_dll_info(st); + st->cr(); + } + + STEP(210, "(printing VM options)" ) + + if (_verbose) { + // VM options + Arguments::print_on(st); + st->cr(); + } + + STEP(220, "(printing environment variables)" ) + + if (_verbose) { + os::print_environment_variables(st, env_list, buf, sizeof(buf)); + st->cr(); + } + + STEP(225, "(printing signal handlers)" ) + + if (_verbose) { + os::print_signal_handlers(st, buf, sizeof(buf)); + st->cr(); + } + + STEP(230, "" ) + + if (_verbose) { + st->cr(); + st->print_cr("--------------- S Y S T E M ---------------"); + st->cr(); + } + + STEP(240, "(printing OS information)" ) + + if (_verbose) { + os::print_os_info(st); + st->cr(); + } + + STEP(250, "(printing CPU info)" ) + if (_verbose) { + os::print_cpu_info(st); + st->cr(); + } + + STEP(260, "(printing memory info)" ) + + if (_verbose) { + os::print_memory_info(st); + st->cr(); + } + + STEP(270, "(printing internal vm info)" ) + + if (_verbose) { + st->print_cr("vm_info: %s", Abstract_VM_Version::internal_vm_info_string()); + st->cr(); + } + + STEP(280, "(printing date and time)" ) + + if (_verbose) { + os::print_date_and_time(st); + st->cr(); + } + + END + +# undef BEGIN +# undef STEP +# undef END +} + + +void VMError::report_and_die() { + // Don't allocate large buffer on stack + static char buffer[O_BUFLEN]; + + // First error, and its thread id. We must be able to handle native thread, + // so use thread id instead of Thread* to identify thread. + static VMError* first_error; + static jlong first_error_tid; + + // An error could happen before tty is initialized or after it has been + // destroyed. Here we use a very simple unbuffered fdStream for printing. + // Only out.print_raw() and out.print_raw_cr() should be used, as other + // printing methods need to allocate large buffer on stack. To format a + // string, use jio_snprintf() with a static buffer or use staticBufferStream. + static fdStream out(defaultStream::output_fd()); + + // How many errors occurred in error handler when reporting first_error. + static int recursive_error_count; + + // We will first print a brief message to standard out (verbose = false), + // then save detailed information in log file (verbose = true). + static bool out_done = false; // done printing to standard out + static bool log_done = false; // done saving error log + static fdStream log; // error log + + if (SuppressFatalErrorMessage) { + os::abort(); + } + jlong mytid = os::current_thread_id(); + if (first_error == NULL && + Atomic::cmpxchg_ptr(this, &first_error, NULL) == NULL) { + + // first time + first_error_tid = mytid; + set_error_reported(); + + if (ShowMessageBoxOnError) { + show_message_box(buffer, sizeof(buffer)); + + // User has asked JVM to abort. Reset ShowMessageBoxOnError so the + // WatcherThread can kill JVM if the error handler hangs. + ShowMessageBoxOnError = false; + } + + // reset signal handlers or exception filter; make sure recursive crashes + // are handled properly. + reset_signal_handlers(); + + } else { + // This is not the first error, see if it happened in a different thread + // or in the same thread during error reporting. + if (first_error_tid != mytid) { + jio_snprintf(buffer, sizeof(buffer), + "[thread " INT64_FORMAT " also had an error]", + mytid); + out.print_raw_cr(buffer); + + // error reporting is not MT-safe, block current thread + os::infinite_sleep(); + + } else { + if (recursive_error_count++ > 30) { + out.print_raw_cr("[Too many errors, abort]"); + os::die(); + } + + jio_snprintf(buffer, sizeof(buffer), + "[error occurred during error reporting %s, id 0x%x]", + first_error ? first_error->_current_step_info : "", + _id); + if (log.is_open()) { + log.cr(); + log.print_raw_cr(buffer); + log.cr(); + } else { + out.cr(); + out.print_raw_cr(buffer); + out.cr(); + } + } + } + + // print to screen + if (!out_done) { + first_error->_verbose = false; + + staticBufferStream sbs(buffer, sizeof(buffer), &out); + first_error->report(&sbs); + + out_done = true; + + first_error->_current_step = 0; // reset current_step + first_error->_current_step_info = ""; // reset current_step string + } + + // print to error log file + if (!log_done) { + first_error->_verbose = true; + + // see if log file is already open + if (!log.is_open()) { + // open log file + int fd = -1; + + if (ErrorFile != NULL) { + bool copy_ok = + Arguments::copy_expand_pid(ErrorFile, strlen(ErrorFile), buffer, sizeof(buffer)); + if (copy_ok) { + fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, 0666); + } + } + + if (fd == -1) { + const char *cwd = os::get_current_directory(buffer, sizeof(buffer)); + size_t len = strlen(cwd); + // either user didn't specify, or the user's location failed, + // so use the default name in the current directory + jio_snprintf(&buffer[len], sizeof(buffer)-len, "%shs_err_pid%u.log", + os::file_separator(), os::current_process_id()); + fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, 0666); + } + + if (fd == -1) { + // try temp directory + const char * tmpdir = os::get_temp_directory(); + jio_snprintf(buffer, sizeof(buffer), "%shs_err_pid%u.log", + (tmpdir ? tmpdir : ""), os::current_process_id()); + fd = open(buffer, O_WRONLY | O_CREAT | O_TRUNC, 0666); + } + + if (fd != -1) { + out.print_raw("# An error report file with more information is saved as:\n# "); + out.print_raw_cr(buffer); + os::set_error_file(buffer); + + log.set_fd(fd); + } else { + out.print_raw_cr("# Can not save log file, dump to screen.."); + log.set_fd(defaultStream::output_fd()); + } + } + + staticBufferStream sbs(buffer, O_BUFLEN, &log); + first_error->report(&sbs); + first_error->_current_step = 0; // reset current_step + first_error->_current_step_info = ""; // reset current_step string + + if (log.fd() != defaultStream::output_fd()) { + close(log.fd()); + } + + log.set_fd(-1); + log_done = true; + } + + + static bool skip_OnError = false; + if (!skip_OnError && OnError && OnError[0]) { + skip_OnError = true; + + out.print_raw_cr("#"); + out.print_raw ("# -XX:OnError=\""); + out.print_raw (OnError); + out.print_raw_cr("\""); + + char* cmd; + const char* ptr = OnError; + while ((cmd = next_OnError_command(buffer, sizeof(buffer), &ptr)) != NULL){ + out.print_raw ("# Executing "); +#if defined(LINUX) + out.print_raw ("/bin/sh -c "); +#elif defined(SOLARIS) + out.print_raw ("/usr/bin/sh -c "); +#endif + out.print_raw ("\""); + out.print_raw (cmd); + out.print_raw_cr("\" ..."); + + os::fork_and_exec(cmd); + } + + // done with OnError + OnError = NULL; + } + + static bool skip_bug_url = false; + if (!skip_bug_url) { + skip_bug_url = true; + + out.print_raw_cr("#"); + print_bug_submit_message(&out, _thread); + } + + if (!UseOSErrorReporting) { + // os::abort() will call abort hooks, try it first. + static bool skip_os_abort = false; + if (!skip_os_abort) { + skip_os_abort = true; + os::abort(); + } + + // if os::abort() doesn't abort, try os::die(); + os::die(); + } +} + +/* + * OnOutOfMemoryError scripts/commands executed while VM is a safepoint - this + * ensures utilities such as jmap can observe the process is a consistent state. + */ +class VM_ReportJavaOutOfMemory : public VM_Operation { + private: + VMError *_err; + public: + VM_ReportJavaOutOfMemory(VMError *err) { _err = err; } + VMOp_Type type() const { return VMOp_ReportJavaOutOfMemory; } + void doit(); +}; + +void VM_ReportJavaOutOfMemory::doit() { + // Don't allocate large buffer on stack + static char buffer[O_BUFLEN]; + + tty->print_cr("#"); + tty->print_cr("# java.lang.OutOfMemoryError: %s", _err->message()); + tty->print_cr("# -XX:OnOutOfMemoryError=\"%s\"", OnOutOfMemoryError); + + // make heap parsability + Universe::heap()->ensure_parsability(false); // no need to retire TLABs + + char* cmd; + const char* ptr = OnOutOfMemoryError; + while ((cmd = next_OnError_command(buffer, sizeof(buffer), &ptr)) != NULL){ + tty->print("# Executing "); +#if defined(LINUX) + tty->print ("/bin/sh -c "); +#elif defined(SOLARIS) + tty->print ("/usr/bin/sh -c "); +#endif + tty->print_cr("\"%s\"...", cmd); + + os::fork_and_exec(cmd); + } +} + +void VMError::report_java_out_of_memory() { + if (OnOutOfMemoryError && OnOutOfMemoryError[0]) { + MutexLocker ml(Heap_lock); + VM_ReportJavaOutOfMemory op(this); + VMThread::execute(&op); + } +} diff --git a/src/share/vm/utilities/vmError.hpp b/src/share/vm/utilities/vmError.hpp new file mode 100644 index 000000000..414bc7f21 --- /dev/null +++ b/src/share/vm/utilities/vmError.hpp @@ -0,0 +1,103 @@ +/* + * Copyright 2003-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. + * + */ + + +class VM_ReportJavaOutOfMemory; + +class VMError : public StackObj { + friend class VM_ReportJavaOutOfMemory; + + enum ErrorType { + internal_error = 0xe0000000, + oom_error = 0xe0000001 + }; + int _id; // Solaris/Linux signals: 0 - SIGRTMAX + // Windows exceptions: 0xCxxxxxxx system errors + // 0x8xxxxxxx system warnings + + const char * _message; + + Thread * _thread; // NULL if it's native thread + + + // additional info for crashes + address _pc; // faulting PC + void * _siginfo; // ExceptionRecord on Windows, + // siginfo_t on Solaris/Linux + void * _context; // ContextRecord on Windows, + // ucontext_t on Solaris/Linux + + // additional info for VM internal errors + const char * _filename; + int _lineno; + + // used by fatal error handler + int _current_step; + const char * _current_step_info; + int _verbose; + + // used by reporting about OOM + size_t _size; + + // set signal handlers on Solaris/Linux or the default exception filter + // on Windows, to handle recursive crashes. + void reset_signal_handlers(); + + // handle -XX:+ShowMessageBoxOnError. buf is used to format the message string + void show_message_box(char* buf, int buflen); + + // generate an error report + void report(outputStream* st); + + // accessor + const char* message() { return _message; } + +public: + // Constructor for crashes + VMError(Thread* thread, int sig, address pc, void* siginfo, void* context); + // Constructor for VM internal errors + VMError(Thread* thread, const char* message, const char* filename, int lineno); + + // Constructors for VM OOM errors + VMError(Thread* thread, size_t size, const char* message, const char* filename, int lineno); + // Constructor for non-fatal errors + VMError(const char* message); + + // return a string to describe the error + char *error_string(char* buf, int buflen); + + // main error reporting function + void report_and_die(); + + // reporting OutOfMemoryError + void report_java_out_of_memory(); + + // returns original flags for signal, if it was resetted, or -1 if + // signal was not changed by error reporter + static int get_resetted_sigflags(int sig); + + // returns original handler for signal, if it was resetted, or NULL if + // signal was not changed by error reporter + static address get_resetted_sighandler(int sig); +}; diff --git a/src/share/vm/utilities/workgroup.cpp b/src/share/vm/utilities/workgroup.cpp new file mode 100644 index 000000000..bdf650bbc --- /dev/null +++ b/src/share/vm/utilities/workgroup.cpp @@ -0,0 +1,444 @@ +/* + * Copyright 2001-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/_workgroup.cpp.incl" + +// Definitions of WorkGang methods. + +AbstractWorkGang::AbstractWorkGang(const char* name, + bool are_GC_threads) : + _name(name), + _are_GC_threads(are_GC_threads) { + // Other initialization. + _monitor = new Monitor(/* priority */ Mutex::leaf, + /* name */ "WorkGroup monitor", + /* allow_vm_block */ are_GC_threads); + assert(monitor() != NULL, "Failed to allocate monitor"); + _terminate = false; + _task = NULL; + _sequence_number = 0; + _started_workers = 0; + _finished_workers = 0; +} + +WorkGang::WorkGang(const char* name, + int workers, + bool are_GC_threads) : + AbstractWorkGang(name, are_GC_threads) { + // Save arguments. + _total_workers = workers; + if (TraceWorkGang) { + tty->print_cr("Constructing work gang %s with %d threads", name, workers); + } + _gang_workers = NEW_C_HEAP_ARRAY(GangWorker*, workers); + assert(gang_workers() != NULL, "Failed to allocate gang workers"); + for (int worker = 0; worker < total_workers(); worker += 1) { + GangWorker* new_worker = new GangWorker(this, worker); + assert(new_worker != NULL, "Failed to allocate GangWorker"); + _gang_workers[worker] = new_worker; + if (new_worker == NULL || !os::create_thread(new_worker, os::pgc_thread)) + vm_exit_out_of_memory(0, "Cannot create worker GC thread. Out of system resources."); + if (!DisableStartThread) { + os::start_thread(new_worker); + } + } +} + +AbstractWorkGang::~AbstractWorkGang() { + if (TraceWorkGang) { + tty->print_cr("Destructing work gang %s", name()); + } + stop(); // stop all the workers + for (int worker = 0; worker < total_workers(); worker += 1) { + delete gang_worker(worker); + } + delete gang_workers(); + delete monitor(); +} + +GangWorker* AbstractWorkGang::gang_worker(int i) const { + // Array index bounds checking. + GangWorker* result = NULL; + assert(gang_workers() != NULL, "No workers for indexing"); + assert(((i >= 0) && (i < total_workers())), "Worker index out of bounds"); + result = _gang_workers[i]; + assert(result != NULL, "Indexing to null worker"); + return result; +} + +void WorkGang::run_task(AbstractGangTask* task) { + // This thread is executed by the VM thread which does not block + // on ordinary MutexLocker's. + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + if (TraceWorkGang) { + tty->print_cr("Running work gang %s task %s", name(), task->name()); + } + // Tell all the workers to run a task. + assert(task != NULL, "Running a null task"); + // Initialize. + _task = task; + _sequence_number += 1; + _started_workers = 0; + _finished_workers = 0; + // Tell the workers to get to work. + monitor()->notify_all(); + // Wait for them to be finished + while (finished_workers() < total_workers()) { + if (TraceWorkGang) { + tty->print_cr("Waiting in work gang %s: %d/%d finished sequence %d", + name(), finished_workers(), total_workers(), + _sequence_number); + } + monitor()->wait(/* no_safepoint_check */ true); + } + _task = NULL; + if (TraceWorkGang) { + tty->print_cr("/nFinished work gang %s: %d/%d sequence %d", + name(), finished_workers(), total_workers(), + _sequence_number); + } +} + +void AbstractWorkGang::stop() { + // Tell all workers to terminate, then wait for them to become inactive. + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + if (TraceWorkGang) { + tty->print_cr("Stopping work gang %s task %s", name(), task()->name()); + } + _task = NULL; + _terminate = true; + monitor()->notify_all(); + while (finished_workers() < total_workers()) { + if (TraceWorkGang) { + tty->print_cr("Waiting in work gang %s: %d/%d finished", + name(), finished_workers(), total_workers()); + } + monitor()->wait(/* no_safepoint_check */ true); + } +} + +void AbstractWorkGang::internal_worker_poll(WorkData* data) const { + assert(monitor()->owned_by_self(), "worker_poll is an internal method"); + assert(data != NULL, "worker data is null"); + data->set_terminate(terminate()); + data->set_task(task()); + data->set_sequence_number(sequence_number()); +} + +void AbstractWorkGang::internal_note_start() { + assert(monitor()->owned_by_self(), "note_finish is an internal method"); + _started_workers += 1; +} + +void AbstractWorkGang::internal_note_finish() { + assert(monitor()->owned_by_self(), "note_finish is an internal method"); + _finished_workers += 1; +} + +void AbstractWorkGang::print_worker_threads_on(outputStream* st) const { + uint num_thr = total_workers(); + for (uint i = 0; i < num_thr; i++) { + gang_worker(i)->print_on(st); + st->cr(); + } +} + +void AbstractWorkGang::threads_do(ThreadClosure* tc) const { + assert(tc != NULL, "Null ThreadClosure"); + uint num_thr = total_workers(); + for (uint i = 0; i < num_thr; i++) { + tc->do_thread(gang_worker(i)); + } +} + +// GangWorker methods. + +GangWorker::GangWorker(AbstractWorkGang* gang, uint id) { + _gang = gang; + set_id(id); + set_name("Gang worker#%d (%s)", id, gang->name()); +} + +void GangWorker::run() { + initialize(); + loop(); +} + +void GangWorker::initialize() { + this->initialize_thread_local_storage(); + assert(_gang != NULL, "No gang to run in"); + os::set_priority(this, NearMaxPriority); + if (TraceWorkGang) { + tty->print_cr("Running gang worker for gang %s id %d", + gang()->name(), id()); + } + // The VM thread should not execute here because MutexLocker's are used + // as (opposed to MutexLockerEx's). + assert(!Thread::current()->is_VM_thread(), "VM thread should not be part" + " of a work gang"); +} + +void GangWorker::loop() { + int previous_sequence_number = 0; + Monitor* gang_monitor = gang()->monitor(); + for ( ; /* !terminate() */; ) { + WorkData data; + int part; // Initialized below. + { + // Grab the gang mutex. + MutexLocker ml(gang_monitor); + // Wait for something to do. + // Polling outside the while { wait } avoids missed notifies + // in the outer loop. + gang()->internal_worker_poll(&data); + if (TraceWorkGang) { + tty->print("Polled outside for work in gang %s worker %d", + gang()->name(), id()); + tty->print(" terminate: %s", + data.terminate() ? "true" : "false"); + tty->print(" sequence: %d (prev: %d)", + data.sequence_number(), previous_sequence_number); + if (data.task() != NULL) { + tty->print(" task: %s", data.task()->name()); + } else { + tty->print(" task: NULL"); + } + tty->cr(); + } + for ( ; /* break or return */; ) { + // Terminate if requested. + if (data.terminate()) { + gang()->internal_note_finish(); + gang_monitor->notify_all(); + return; + } + // Check for new work. + if ((data.task() != NULL) && + (data.sequence_number() != previous_sequence_number)) { + gang()->internal_note_start(); + gang_monitor->notify_all(); + part = gang()->started_workers() - 1; + break; + } + // Nothing to do. + gang_monitor->wait(/* no_safepoint_check */ true); + gang()->internal_worker_poll(&data); + if (TraceWorkGang) { + tty->print("Polled inside for work in gang %s worker %d", + gang()->name(), id()); + tty->print(" terminate: %s", + data.terminate() ? "true" : "false"); + tty->print(" sequence: %d (prev: %d)", + data.sequence_number(), previous_sequence_number); + if (data.task() != NULL) { + tty->print(" task: %s", data.task()->name()); + } else { + tty->print(" task: NULL"); + } + tty->cr(); + } + } + // Drop gang mutex. + } + if (TraceWorkGang) { + tty->print("Work for work gang %s id %d task %s part %d", + gang()->name(), id(), data.task()->name(), part); + } + assert(data.task() != NULL, "Got null task"); + data.task()->work(part); + { + if (TraceWorkGang) { + tty->print("Finish for work gang %s id %d task %s part %d", + gang()->name(), id(), data.task()->name(), part); + } + // Grab the gang mutex. + MutexLocker ml(gang_monitor); + gang()->internal_note_finish(); + // Tell the gang you are done. + gang_monitor->notify_all(); + // Drop the gang mutex. + } + previous_sequence_number = data.sequence_number(); + } +} + +bool GangWorker::is_GC_task_thread() const { + return gang()->are_GC_threads(); +} + +void GangWorker::print_on(outputStream* st) const { + st->print("\"%s\" ", name()); + Thread::print_on(st); + st->cr(); +} + +// Printing methods + +const char* AbstractWorkGang::name() const { + return _name; +} + +#ifndef PRODUCT + +const char* AbstractGangTask::name() const { + return _name; +} + +#endif /* PRODUCT */ + +// *** WorkGangBarrierSync + +WorkGangBarrierSync::WorkGangBarrierSync() + : _monitor(Mutex::safepoint, "work gang barrier sync", true), + _n_workers(0), _n_completed(0) { +} + +WorkGangBarrierSync::WorkGangBarrierSync(int n_workers, const char* name) + : _monitor(Mutex::safepoint, name, true), + _n_workers(n_workers), _n_completed(0) { +} + +void WorkGangBarrierSync::set_n_workers(int n_workers) { + _n_workers = n_workers; + _n_completed = 0; +} + +void WorkGangBarrierSync::enter() { + MutexLockerEx x(monitor(), Mutex::_no_safepoint_check_flag); + inc_completed(); + if (n_completed() == n_workers()) { + monitor()->notify_all(); + } + else { + while (n_completed() != n_workers()) { + monitor()->wait(/* no_safepoint_check */ true); + } + } +} + +// SubTasksDone functions. + +SubTasksDone::SubTasksDone(int n) : + _n_tasks(n), _n_threads(1), _tasks(NULL) { + _tasks = NEW_C_HEAP_ARRAY(jint, n); + guarantee(_tasks != NULL, "alloc failure"); + clear(); +} + +bool SubTasksDone::valid() { + return _tasks != NULL; +} + +void SubTasksDone::set_par_threads(int t) { +#ifdef ASSERT + assert(_claimed == 0 || _threads_completed == _n_threads, + "should not be called while tasks are being processed!"); +#endif + _n_threads = (t == 0 ? 1 : t); +} + +void SubTasksDone::clear() { + for (int i = 0; i < _n_tasks; i++) { + _tasks[i] = 0; + } + _threads_completed = 0; +#ifdef ASSERT + _claimed = 0; +#endif +} + +bool SubTasksDone::is_task_claimed(int t) { + assert(0 <= t && t < _n_tasks, "bad task id."); + jint old = _tasks[t]; + if (old == 0) { + old = Atomic::cmpxchg(1, &_tasks[t], 0); + } + assert(_tasks[t] == 1, "What else?"); + bool res = old != 0; +#ifdef ASSERT + if (!res) { + assert(_claimed < _n_tasks, "Too many tasks claimed; missing clear?"); + Atomic::inc(&_claimed); + } +#endif + return res; +} + +void SubTasksDone::all_tasks_completed() { + jint observed = _threads_completed; + jint old; + do { + old = observed; + observed = Atomic::cmpxchg(old+1, &_threads_completed, old); + } while (observed != old); + // If this was the last thread checking in, clear the tasks. + if (observed+1 == _n_threads) clear(); +} + + +SubTasksDone::~SubTasksDone() { + if (_tasks != NULL) FREE_C_HEAP_ARRAY(jint, _tasks); +} + +// *** SequentialSubTasksDone + +void SequentialSubTasksDone::clear() { + _n_tasks = _n_claimed = 0; + _n_threads = _n_completed = 0; +} + +bool SequentialSubTasksDone::valid() { + return _n_threads > 0; +} + +bool SequentialSubTasksDone::is_task_claimed(int& t) { + jint* n_claimed_ptr = &_n_claimed; + t = *n_claimed_ptr; + while (t < _n_tasks) { + jint res = Atomic::cmpxchg(t+1, n_claimed_ptr, t); + if (res == t) { + return false; + } + t = *n_claimed_ptr; + } + return true; +} + +bool SequentialSubTasksDone::all_tasks_completed() { + jint* n_completed_ptr = &_n_completed; + jint complete = *n_completed_ptr; + while (true) { + jint res = Atomic::cmpxchg(complete+1, n_completed_ptr, complete); + if (res == complete) { + break; + } + complete = res; + } + if (complete+1 == _n_threads) { + clear(); + return true; + } + return false; +} diff --git a/src/share/vm/utilities/workgroup.hpp b/src/share/vm/utilities/workgroup.hpp new file mode 100644 index 000000000..3797a3f76 --- /dev/null +++ b/src/share/vm/utilities/workgroup.hpp @@ -0,0 +1,345 @@ +/* + * Copyright 2002-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. + * + */ + +// Forward declarations of classes defined here + +class WorkGang; +class GangWorker; +class YieldingFlexibleGangWorker; +class YieldingFlexibleGangTask; +class WorkData; + +// An abstract task to be worked on by a gang. +// You subclass this to supply your own work() method +class AbstractGangTask: public CHeapObj { +public: + // The abstract work method. + // The argument tells you which member of the gang you are. + virtual void work(int i) = 0; + + // Debugging accessor for the name. + const char* name() const PRODUCT_RETURN_(return NULL;); + int counter() { return _counter; } + void set_counter(int value) { _counter = value; } + int *address_of_counter() { return &_counter; } + + // RTTI + NOT_PRODUCT(virtual bool is_YieldingFlexibleGang_task() const { + return false; + }) + +private: + NOT_PRODUCT(const char* _name;) + // ??? Should a task have a priority associated with it? + // ??? Or can the run method adjust priority as needed? + int _counter; + +protected: + // Constructor and desctructor: only construct subclasses. + AbstractGangTask(const char* name) { + NOT_PRODUCT(_name = name); + _counter = 0; + } + virtual ~AbstractGangTask() { } +}; + + +// Class AbstractWorkGang: +// An abstract class representing a gang of workers. +// You subclass this to supply an implementation of run_task(). +class AbstractWorkGang: public CHeapObj { + // Here's the public interface to this class. +public: + // Constructor and destructor. + AbstractWorkGang(const char* name, bool are_GC_threads); + ~AbstractWorkGang(); + // Run a task, returns when the task is done (or terminated). + virtual void run_task(AbstractGangTask* task) = 0; + // Stop and terminate all workers. + virtual void stop(); +public: + // Debugging. + const char* name() const; +protected: + // Initialize only instance data. + const bool _are_GC_threads; + // Printing support. + const char* _name; + // The monitor which protects these data, + // and notifies of changes in it. + Monitor* _monitor; + // The count of the number of workers in the gang. + int _total_workers; + // Whether the workers should terminate. + bool _terminate; + // The array of worker threads for this gang. + // This is only needed for cleaning up. + GangWorker** _gang_workers; + // The task for this gang. + AbstractGangTask* _task; + // A sequence number for the current task. + int _sequence_number; + // The number of started workers. + int _started_workers; + // The number of finished workers. + int _finished_workers; +public: + // Accessors for fields + Monitor* monitor() const { + return _monitor; + } + int total_workers() const { + return _total_workers; + } + bool terminate() const { + return _terminate; + } + GangWorker** gang_workers() const { + return _gang_workers; + } + AbstractGangTask* task() const { + return _task; + } + int sequence_number() const { + return _sequence_number; + } + int started_workers() const { + return _started_workers; + } + int finished_workers() const { + return _finished_workers; + } + bool are_GC_threads() const { + return _are_GC_threads; + } + // Predicates. + bool is_idle() const { + return (task() == NULL); + } + // Return the Ith gang worker. + GangWorker* gang_worker(int i) const; + + void threads_do(ThreadClosure* tc) const; + + // Printing + void print_worker_threads_on(outputStream *st) const; + void print_worker_threads() const { + print_worker_threads_on(tty); + } + +protected: + friend class GangWorker; + friend class YieldingFlexibleGangWorker; + // Note activation and deactivation of workers. + // These methods should only be called with the mutex held. + void internal_worker_poll(WorkData* data) const; + void internal_note_start(); + void internal_note_finish(); +}; + +class WorkData: public StackObj { + // This would be a struct, but I want accessor methods. +private: + bool _terminate; + AbstractGangTask* _task; + int _sequence_number; +public: + // Constructor and destructor + WorkData() { + _terminate = false; + _task = NULL; + _sequence_number = 0; + } + ~WorkData() { + } + // Accessors and modifiers + bool terminate() const { return _terminate; } + void set_terminate(bool value) { _terminate = value; } + AbstractGangTask* task() const { return _task; } + void set_task(AbstractGangTask* value) { _task = value; } + int sequence_number() const { return _sequence_number; } + void set_sequence_number(int value) { _sequence_number = value; } + + YieldingFlexibleGangTask* yf_task() const { + return (YieldingFlexibleGangTask*)_task; + } +}; + +// Class WorkGang: +class WorkGang: public AbstractWorkGang { +public: + // Constructor + WorkGang(const char* name, int workers, bool are_GC_threads); + // Run a task, returns when the task is done (or terminated). + virtual void run_task(AbstractGangTask* task); +}; + +// Class GangWorker: +// Several instances of this class run in parallel as workers for a gang. +class GangWorker: public WorkerThread { +public: + // Constructors and destructor. + GangWorker(AbstractWorkGang* gang, uint id); + + // The only real method: run a task for the gang. + virtual void run(); + // Predicate for Thread + virtual bool is_GC_task_thread() const; + // Printing + void print_on(outputStream* st) const; + virtual void print() const { print_on(tty); } +protected: + AbstractWorkGang* _gang; + + virtual void initialize(); + virtual void loop(); + +public: + AbstractWorkGang* gang() const { return _gang; } +}; + +// A class that acts as a synchronisation barrier. Workers enter +// the barrier and must wait until all other workers have entered +// before any of them may leave. + +class WorkGangBarrierSync : public StackObj { +protected: + Monitor _monitor; + int _n_workers; + int _n_completed; + + Monitor* monitor() { return &_monitor; } + int n_workers() { return _n_workers; } + int n_completed() { return _n_completed; } + + void inc_completed() { _n_completed++; } + +public: + WorkGangBarrierSync(); + WorkGangBarrierSync(int n_workers, const char* name); + + // Set the number of workers that will use the barrier. + // Must be called before any of the workers start running. + void set_n_workers(int n_workers); + + // Enter the barrier. A worker that enters the barrier will + // not be allowed to leave until all other threads have + // also entered the barrier. + void enter(); +}; + +// A class to manage claiming of subtasks within a group of tasks. The +// subtasks will be identified by integer indices, usually elements of an +// enumeration type. + +class SubTasksDone: public CHeapObj { + jint* _tasks; + int _n_tasks; + int _n_threads; + jint _threads_completed; +#ifdef ASSERT + jint _claimed; +#endif + + // Set all tasks to unclaimed. + void clear(); + +public: + // Initializes "this" to a state in which there are "n" tasks to be + // processed, none of the which are originally claimed. The number of + // threads doing the tasks is initialized 1. + SubTasksDone(int n); + + // True iff the object is in a valid state. + bool valid(); + + // Set the number of parallel threads doing the tasks to "t". Can only + // be called before tasks start or after they are complete. + void set_par_threads(int t); + + // Returns "false" if the task "t" is unclaimed, and ensures that task is + // claimed. The task "t" is required to be within the range of "this". + bool is_task_claimed(int t); + + // The calling thread asserts that it has attempted to claim all the + // tasks that it will try to claim. Every thread in the parallel task + // must execute this. (When the last thread does so, the task array is + // cleared.) + void all_tasks_completed(); + + // Destructor. + ~SubTasksDone(); +}; + +// As above, but for sequential tasks, i.e. instead of claiming +// sub-tasks from a set (possibly an enumeration), claim sub-tasks +// in sequential order. This is ideal for claiming dynamically +// partitioned tasks (like striding in the parallel remembered +// set scanning). Note that unlike the above class this is +// a stack object - is there any reason for it not to be? + +class SequentialSubTasksDone : public StackObj { +protected: + jint _n_tasks; // Total number of tasks available. + jint _n_claimed; // Number of tasks claimed. + jint _n_threads; // Total number of parallel threads. + jint _n_completed; // Number of completed threads. + + void clear(); + +public: + SequentialSubTasksDone() { clear(); } + ~SequentialSubTasksDone() {} + + // True iff the object is in a valid state. + bool valid(); + + // number of tasks + jint n_tasks() const { return _n_tasks; } + + // Set the number of parallel threads doing the tasks to t. + // Should be called before the task starts but it is safe + // to call this once a task is running provided that all + // threads agree on the number of threads. + void set_par_threads(int t) { _n_threads = t; } + + // Set the number of tasks to be claimed to t. As above, + // should be called before the tasks start but it is safe + // to call this once a task is running provided all threads + // agree on the number of tasks. + void set_n_tasks(int t) { _n_tasks = t; } + + // Returns false if the next task in the sequence is unclaimed, + // and ensures that it is claimed. Will set t to be the index + // of the claimed task in the sequence. Will return true if + // the task cannot be claimed and there are none left to claim. + bool is_task_claimed(int& t); + + // The calling thread asserts that it has attempted to claim + // all the tasks it possibly can in the sequence. Every thread + // claiming tasks must promise call this. Returns true if this + // is the last thread to complete so that the thread can perform + // cleanup if necessary. + bool all_tasks_completed(); +}; diff --git a/src/share/vm/utilities/xmlstream.cpp b/src/share/vm/utilities/xmlstream.cpp new file mode 100644 index 000000000..b7098c857 --- /dev/null +++ b/src/share/vm/utilities/xmlstream.cpp @@ -0,0 +1,470 @@ +/* + * Copyright 2002-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/_xmlstream.cpp.incl" + +void xmlStream::initialize(outputStream* out) { + _out = out; + _last_flush = 0; + _markup_state = BODY; + _text_init._outer_xmlStream = this; + _text = &_text_init; + +#ifdef ASSERT + _element_depth = 0; + int init_len = 100; + char* init_buf = NEW_C_HEAP_ARRAY(char, init_len); + _element_close_stack_low = init_buf; + _element_close_stack_high = init_buf + init_len; + _element_close_stack_ptr = init_buf + init_len - 1; + _element_close_stack_ptr[0] = '\0'; +#endif + + // Make sure each log uses the same base for time stamps. + if (is_open()) { + _out->time_stamp().update_to(1); + } +} + +#ifdef ASSERT +xmlStream::~xmlStream() { + FREE_C_HEAP_ARRAY(char, _element_close_stack_low); +} +#endif + +// Pass the given chars directly to _out. +void xmlStream::write(const char* s, size_t len) { + if (!is_open()) return; + + out()->write(s, len); +} + + +// Pass the given chars directly to _out, except that +// we watch for special "<&>" chars. +// This is suitable for either attribute text or for body text. +// We don't fool with "<![CDATA[" quotes, just single-character entities. +// This makes it easier for dumb tools to parse the output. +void xmlStream::write_text(const char* s, size_t len) { + if (!is_open()) return; + + size_t written = 0; + // All normally printed material goes inside XML quotes. + // This leaves the output free to include markup also. + // Scan the string looking for inadvertant "<&>" chars + for (size_t i = 0; i < len; i++) { + char ch = s[i]; + // Escape special chars. + const char* esc = NULL; + switch (ch) { + // These are important only in attrs, but we do them always: + case '\'': esc = "'"; break; + case '"': esc = """; break; + case '<': esc = "<"; break; + case '&': esc = "&"; break; + // This is a freebie. + case '>': esc = ">"; break; + } + if (esc != NULL) { + if (written < i) { + out()->write(&s[written], i - written); + written = i; + } + out()->print_raw(esc); + written++; + } + } + + // Print the clean remainder. Usually, it is all of s. + if (written < len) { + out()->write(&s[written], len - written); + } +} + +// ------------------------------------------------------------------ +// Outputs XML text, with special characters quoted. +void xmlStream::text(const char* format, ...) { + va_list ap; + va_start(ap, format); + va_text(format, ap); + va_end(ap); +} + +#define BUFLEN 2*K /* max size of output of individual print methods */ + +// ------------------------------------------------------------------ +void xmlStream::va_tag(bool push, const char* format, va_list ap) { + assert_if_no_error(!inside_attrs(), "cannot print tag inside attrs"); + char buffer[BUFLEN]; + size_t len; + const char* kind = do_vsnprintf(buffer, BUFLEN, format, ap, false, len); + see_tag(kind, push); + print_raw("<"); + write(kind, len); + _markup_state = (push ? HEAD : ELEM); +} + +#ifdef ASSERT +/// Debugging goo to make sure element tags nest properly. + +// ------------------------------------------------------------------ +void xmlStream::see_tag(const char* tag, bool push) { + assert_if_no_error(!inside_attrs(), "cannot start new element inside attrs"); + if (!push) return; + + // tag goes up until either null or space: + const char* tag_end = strchr(tag, ' '); + size_t tag_len = (tag_end == NULL) ? strlen(tag) : tag_end - tag; + assert(tag_len > 0, "tag must not be empty"); + // push the tag onto the stack, pulling down the pointer + char* old_ptr = _element_close_stack_ptr; + char* old_low = _element_close_stack_low; + char* push_ptr = old_ptr - (tag_len+1); + if (push_ptr < old_low) { + int old_len = _element_close_stack_high - old_ptr; + int new_len = old_len * 2; + if (new_len < 100) new_len = 100; + char* new_low = NEW_C_HEAP_ARRAY(char, new_len); + char* new_high = new_low + new_len; + char* new_ptr = new_high - old_len; + memcpy(new_ptr, old_ptr, old_len); + _element_close_stack_high = new_high; + _element_close_stack_low = new_low; + _element_close_stack_ptr = new_ptr; + FREE_C_HEAP_ARRAY(char, old_low); + push_ptr = new_ptr - (tag_len+1); + } + assert(push_ptr >= _element_close_stack_low, "in range"); + memcpy(push_ptr, tag, tag_len); + push_ptr[tag_len] = 0; + _element_close_stack_ptr = push_ptr; + _element_depth += 1; +} + +// ------------------------------------------------------------------ +void xmlStream::pop_tag(const char* tag) { + assert_if_no_error(!inside_attrs(), "cannot close element inside attrs"); + assert(_element_depth > 0, "must be in an element to close"); + assert(*tag != 0, "tag must not be empty"); + char* cur_tag = _element_close_stack_ptr; + bool bad_tag = false; + while (*cur_tag != 0 && strcmp(cur_tag, tag) != 0) { + this->print_cr("</%s> <!-- missing closing tag -->", cur_tag); + _element_close_stack_ptr = (cur_tag += strlen(cur_tag) + 1); + _element_depth -= 1; + bad_tag = true; + } + if (*cur_tag == 0) { + bad_tag = true; + } else { + // Pop the stack, by skipping over the tag and its null. + _element_close_stack_ptr = cur_tag + strlen(cur_tag) + 1; + _element_depth -= 1; + } + if (bad_tag && !VMThread::should_terminate() && !is_error_reported()) + assert(false, "bad tag in log"); +} +#endif + + +// ------------------------------------------------------------------ +// First word in formatted string is element kind, and any subsequent +// words must be XML attributes. Outputs "<kind .../>". +void xmlStream::elem(const char* format, ...) { + va_list ap; + va_start(ap, format); + va_elem(format, ap); + va_end(ap); +} + +// ------------------------------------------------------------------ +void xmlStream::va_elem(const char* format, va_list ap) { + va_begin_elem(format, ap); + end_elem(); +} + + +// ------------------------------------------------------------------ +// First word in formatted string is element kind, and any subsequent +// words must be XML attributes. Outputs "<kind ...", not including "/>". +void xmlStream::begin_elem(const char* format, ...) { + va_list ap; + va_start(ap, format); + va_tag(false, format, ap); + va_end(ap); +} + +// ------------------------------------------------------------------ +void xmlStream::va_begin_elem(const char* format, va_list ap) { + va_tag(false, format, ap); +} + +// ------------------------------------------------------------------ +// Outputs "/>". +void xmlStream::end_elem() { + assert(_markup_state == ELEM, "misplaced end_elem"); + print_raw("/>\n"); + _markup_state = BODY; +} + +// ------------------------------------------------------------------ +// Outputs formatted text, followed by "/>". +void xmlStream::end_elem(const char* format, ...) { + va_list ap; + va_start(ap, format); + out()->vprint(format, ap); + va_end(ap); + end_elem(); +} + + +// ------------------------------------------------------------------ +// First word in formatted string is element kind, and any subsequent +// words must be XML attributes. Outputs "<kind ...>". +void xmlStream::head(const char* format, ...) { + va_list ap; + va_start(ap, format); + va_head(format, ap); + va_end(ap); +} + +// ------------------------------------------------------------------ +void xmlStream::va_head(const char* format, va_list ap) { + va_begin_head(format, ap); + end_head(); +} + +// ------------------------------------------------------------------ +// First word in formatted string is element kind, and any subsequent +// words must be XML attributes. Outputs "<kind ...", not including ">". +void xmlStream::begin_head(const char* format, ...) { + va_list ap; + va_start(ap, format); + va_tag(true, format, ap); + va_end(ap); +} + +// ------------------------------------------------------------------ +void xmlStream::va_begin_head(const char* format, va_list ap) { + va_tag(true, format, ap); +} + +// ------------------------------------------------------------------ +// Outputs ">". +void xmlStream::end_head() { + assert(_markup_state == HEAD, "misplaced end_head"); + print_raw(">\n"); + _markup_state = BODY; +} + + +// ------------------------------------------------------------------ +// Outputs formatted text, followed by ">". +void xmlStream::end_head(const char* format, ...) { + va_list ap; + va_start(ap, format); + out()->vprint(format, ap); + va_end(ap); + end_head(); +} + + +// ------------------------------------------------------------------ +// Outputs "</kind>". +void xmlStream::tail(const char* kind) { + pop_tag(kind); + print_raw("</"); + print_raw(kind); + print_raw(">\n"); +} + +// ------------------------------------------------------------------ +// Outputs "<kind_done ... stamp='D.DD'/> </kind>". +void xmlStream::done(const char* format, ...) { + va_list ap; + va_start(ap, format); + va_done(format, ap); + va_end(ap); +} + +// ------------------------------------------------------------------ +// Outputs "<kind_done stamp='D.DD'/> </kind>". +// Because done_raw() doesn't need to format strings, it's simpler than +// done(), and can be called safely by fatal error handler. +void xmlStream::done_raw(const char* kind) { + print_raw("<"); + print_raw(kind); + print_raw("_done stamp='"); + out()->stamp(); + print_raw_cr("'/>"); + print_raw("</"); + print_raw(kind); + print_raw_cr(">"); +} + +// ------------------------------------------------------------------ +void xmlStream::va_done(const char* format, va_list ap) { + char buffer[200]; + guarantee(strlen(format) + 10 < sizeof(buffer), "bigger format buffer") + const char* kind = format; + const char* kind_end = strchr(kind, ' '); + size_t kind_len = (kind_end != NULL) ? (kind_end - kind) : strlen(kind); + strncpy(buffer, kind, kind_len); + strcpy(buffer + kind_len, "_done"); + strcat(buffer, format + kind_len); + // Output the trailing event with the timestamp. + va_begin_elem(buffer, ap); + stamp(); + end_elem(); + // Output the tail-tag of the enclosing element. + buffer[kind_len] = 0; + tail(buffer); +} + +// Output a timestamp attribute. +void xmlStream::stamp() { + assert_if_no_error(inside_attrs(), "stamp must be an attribute"); + print_raw(" stamp='"); + out()->stamp(); + print_raw("'"); +} + + +// ------------------------------------------------------------------ +// Output a method attribute, in the form " method='pkg/cls name sig'". +// This is used only when there is no ciMethod available. +void xmlStream::method(methodHandle method) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (method.is_null()) return; + print_raw(" method='"); + method_text(method); + print("' bytes='%d'", method->code_size()); + print(" count='%d'", method->invocation_count()); + int bec = method->backedge_count(); + if (bec != 0) print(" backedge_count='%d'", bec); + print(" iicount='%d'", method->interpreter_invocation_count()); + int throwouts = method->interpreter_throwout_count(); + if (throwouts != 0) print(" throwouts='%d'", throwouts); + methodDataOop mdo = method->method_data(); + if (mdo != NULL) { + uint cnt; + cnt = mdo->decompile_count(); + if (cnt != 0) print(" decompiles='%d'", cnt); + for (uint reason = 0; reason < mdo->trap_reason_limit(); reason++) { + cnt = mdo->trap_count(reason); + if (cnt != 0) print(" %s_traps='%d'", Deoptimization::trap_reason_name(reason), cnt); + } + cnt = mdo->overflow_trap_count(); + if (cnt != 0) print(" overflow_traps='%d'", cnt); + cnt = mdo->overflow_recompile_count(); + if (cnt != 0) print(" overflow_recompiles='%d'", cnt); + } +} + +void xmlStream::method_text(methodHandle method) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (method.is_null()) return; + //method->print_short_name(text()); + method->method_holder()->klass_part()->name()->print_symbol_on(text()); + print_raw(" "); // " " is easier for tools to parse than "::" + method->name()->print_symbol_on(text()); + print_raw(" "); // separator + method->signature()->print_symbol_on(text()); +} + + +// ------------------------------------------------------------------ +// Output a klass attribute, in the form " klass='pkg/cls'". +// This is used only when there is no ciKlass available. +void xmlStream::klass(KlassHandle klass) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (klass.is_null()) return; + print_raw(" klass='"); + klass_text(klass); + print_raw("'"); +} + +void xmlStream::klass_text(KlassHandle klass) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (klass.is_null()) return; + //klass->print_short_name(log->out()); + klass->name()->print_symbol_on(out()); +} + +void xmlStream::name(symbolHandle name) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (name.is_null()) return; + print_raw(" name='"); + name_text(name); + print_raw("'"); +} + +void xmlStream::name_text(symbolHandle name) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (name.is_null()) return; + //name->print_short_name(text()); + name->print_symbol_on(text()); +} + +void xmlStream::object(const char* attr, Handle x) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (x.is_null()) return; + print_raw(" "); + print_raw(attr); + print_raw("='"); + object_text(x); + print_raw("'"); +} + +void xmlStream::object_text(Handle x) { + assert_if_no_error(inside_attrs(), "printing attributes"); + if (x.is_null()) return; + //x->print_value_on(text()); + if (x->is_method()) + method_text(methodOop(x())); + else if (x->is_klass()) + klass_text(klassOop(x())); + else if (x->is_symbol()) + name_text(symbolOop(x())); + else + x->print_value_on(text()); +} + + +void xmlStream::flush() { + out()->flush(); + _last_flush = count(); +} + +void xmlTextStream::flush() { + if (_outer_xmlStream == NULL) return; + _outer_xmlStream->flush(); +} + +void xmlTextStream::write(const char* str, size_t len) { + if (_outer_xmlStream == NULL) return; + _outer_xmlStream->write_text(str, len); + update_position(str, len); +} diff --git a/src/share/vm/utilities/xmlstream.hpp b/src/share/vm/utilities/xmlstream.hpp new file mode 100644 index 000000000..27839700f --- /dev/null +++ b/src/share/vm/utilities/xmlstream.hpp @@ -0,0 +1,177 @@ +/* + * Copyright 2002-2005 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 xmlStream; +class defaultStream; + +// Sub-stream for writing quoted text, as opposed to markup. +// Characters written to this stream are subject to quoting, +// as '<' => "<", etc. +class xmlTextStream : public outputStream { + friend class xmlStream; + friend class defaultStream; // tty + private: + + xmlStream* _outer_xmlStream; + + xmlTextStream() { _outer_xmlStream = NULL; } + + public: + virtual void flush(); // _outer.flush(); + virtual void write(const char* str, size_t len); // _outer->write_text() +}; + + +// Output stream for writing XML-structured logs. +// To write markup, use special calls elem, head/tail, etc. +// Use the xmlStream::text() stream to write unmarked text. +// Text written that way will be quoted as necessary using '<', etc. +// Characters written directly to an xmlStream via print_cr, etc., +// are directly written to the encapsulated stream, xmlStream::out(). +// This can be used to produce markup directly, character by character. +// (Such writes are not checked for markup syntax errors.) + +class xmlStream : public outputStream { + friend class defaultStream; // tty + public: + enum MarkupState { BODY, // after end_head() call, in text + HEAD, // after begin_head() call, in attrs + ELEM }; // after begin_elem() call, in attrs + + protected: + outputStream* _out; // file stream by which it goes + julong _last_flush; // last position of flush + MarkupState _markup_state; // where in the elem/head/tail dance + outputStream* _text; // text stream + xmlTextStream _text_init; + + // for subclasses + xmlStream() {} + void initialize(outputStream* out); + + // protect this from public use: + outputStream* out() { return _out; } + + // helpers for writing XML elements + void va_tag(bool push, const char* format, va_list ap); + virtual void see_tag(const char* tag, bool push) NOT_DEBUG({}); + virtual void pop_tag(const char* tag) NOT_DEBUG({}); + +#ifdef ASSERT + // in debug mode, we verify matching of opening and closing tags + int _element_depth; // number of unfinished elements + char* _element_close_stack_high; // upper limit of down-growing stack + char* _element_close_stack_low; // upper limit of down-growing stack + char* _element_close_stack_ptr; // pointer of down-growing stack +#endif + + public: + // creation + xmlStream(outputStream* out) { initialize(out); } + DEBUG_ONLY(virtual ~xmlStream();) + + bool is_open() { return _out != NULL; } + + // text output + bool inside_attrs() { return _markup_state != BODY; } + + // flushing + virtual void flush(); // flushes out, sets _last_flush = count() + virtual void write(const char* s, size_t len); + void write_text(const char* s, size_t len); // used by xmlTextStream + int unflushed_count() { return (int)(out()->count() - _last_flush); } + + // writing complete XML elements + void elem(const char* format, ...); + void begin_elem(const char* format, ...); + void end_elem(const char* format, ...); + void end_elem(); + void head(const char* format, ...); + void begin_head(const char* format, ...); + void end_head(const char* format, ...); + void end_head(); + void done(const char* format, ...); // xxx_done event, plus tail + void done_raw(const char * kind); + void tail(const char* kind); + + // va_list versions + void va_elem(const char* format, va_list ap); + void va_begin_elem(const char* format, va_list ap); + void va_head(const char* format, va_list ap); + void va_begin_head(const char* format, va_list ap); + void va_done(const char* format, va_list ap); + + // write text (with quoting of special XML characters <>&'" etc.) + outputStream* text() { return _text; } + void text(const char* format, ...); + void va_text(const char* format, va_list ap) { + text()->vprint(format, ap); + } + + // commonly used XML attributes + void stamp(); // stamp='1.234' + void method(methodHandle m); // method='k n s' ... + void klass(KlassHandle k); // klass='name' + void name(symbolHandle s); // name='name' + void object(const char* attr, Handle val); + + // print the text alone (sans ''): + void method_text(methodHandle m); + void klass_text(KlassHandle k); // klass='name' + void name_text(symbolHandle s); // name='name' + void object_text(Handle x); + + /* Example uses: + + // Empty element, simple case. + elem("X Y='Z'"); <X Y='Z'/> \n + + // Empty element, general case. + begin_elem("X Y='Z'"); <X Y='Z' + ...attrs... ...attrs... + end_elem(); /> + + // Compound element, simple case. + head("X Y='Z'"); <X Y='Z'> \n + ...body... ...body... + tail("X"); </X> \n + + // Compound element, general case. + begin_head("X Y='Z'"); <X Y='Z' + ...attrs... ...attrs... + end_head(); > \n + ...body... ...body... + tail("X"); </X> \n + + // Printf-style formatting: + elem("X Y='%s'", "Z"); <X Y='Z'/> \n + + */ + +}; + +// Standard log file, null if no logging is happening. +extern xmlStream* xtty; + +// Note: If ::xtty != NULL, ::tty == ::xtty->text(). diff --git a/src/share/vm/utilities/yieldingWorkgroup.cpp b/src/share/vm/utilities/yieldingWorkgroup.cpp new file mode 100644 index 000000000..d4c0ea92d --- /dev/null +++ b/src/share/vm/utilities/yieldingWorkgroup.cpp @@ -0,0 +1,396 @@ +/* + * Copyright 2005 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/_yieldingWorkgroup.cpp.incl" + +// Forward declaration of classes declared here. + +class GangWorker; +class WorkData; + +YieldingFlexibleWorkGang::YieldingFlexibleWorkGang( + const char* name, int workers, bool are_GC_threads) : + AbstractWorkGang(name, are_GC_threads) { + // Save arguments. + _total_workers = workers; + assert(_total_workers > 0, "Must have more than 1 worker"); + + _yielded_workers = 0; + + if (TraceWorkGang) { + tty->print_cr("Constructing work gang %s with %d threads", name, workers); + } + _gang_workers = NEW_C_HEAP_ARRAY(GangWorker*, workers); + assert(gang_workers() != NULL, "Failed to allocate gang workers"); + for (int worker = 0; worker < total_workers(); worker += 1) { + YieldingFlexibleGangWorker* new_worker = + new YieldingFlexibleGangWorker(this, worker); + assert(new_worker != NULL, "Failed to allocate YieldingFlexibleGangWorker"); + _gang_workers[worker] = new_worker; + if (new_worker == NULL || !os::create_thread(new_worker, os::pgc_thread)) + vm_exit_out_of_memory(0, "Cannot create worker GC thread. Out of system resources."); + if (!DisableStartThread) { + os::start_thread(new_worker); + } + } +} + +// Run a task; returns when the task is done, or the workers yield, +// or the task is aborted, or the work gang is terminated via stop(). +// A task that has been yielded can be continued via this interface +// by using the same task repeatedly as the argument to the call. +// It is expected that the YieldingFlexibleGangTask carries the appropriate +// continuation information used by workers to continue the task +// from its last yield point. Thus, a completed task will return +// immediately with no actual work having been done by the workers. +///////////////////// +// Implementatiuon notes: remove before checking XXX +/* +Each gang is working on a task at a certain time. +Some subset of workers may have yielded and some may +have finished their quota of work. Until this task has +been completed, the workers are bound to that task. +Once the task has been completed, the gang unbounds +itself from the task. + +The yielding work gang thus exports two invokation +interfaces: run_task() and continue_task(). The +first is used to initiate a new task and bind it +to the workers; the second is used to continue an +already bound task that has yielded. Upon completion +the binding is released and a new binding may be +created. + +The shape of a yielding work gang is as follows: + +Overseer invokes run_task(*task). + Lock gang monitor + Check that there is no existing binding for the gang + If so, abort with an error + Else, create a new binding of this gang to the given task + Set number of active workers (as asked) + Notify workers that work is ready to be done + [the requisite # workers would then start up + and do the task] + Wait on the monitor until either + all work is completed or the task has yielded + -- this is normally done through + yielded + completed == active + [completed workers are rest to idle state by overseer?] + return appropriate status to caller + +Overseer invokes continue_task(*task), + Lock gang monitor + Check that task is the same as current binding + If not, abort with an error + Else, set the number of active workers as requested? + Notify workers that they can continue from yield points + New workers can also start up as required + while satisfying the constraint that + active + yielded does not exceed required number + Wait (as above). + +NOTE: In the above, for simplicity in a first iteration + our gangs will be of fixed population and will not + therefore be flexible work gangs, just yielding work + gangs. Once this works well, we will in a second + iteration.refinement introduce flexibility into + the work gang. + +NOTE: we can always create a new gang per each iteration + in order to get the flexibility, but we will for now + desist that simplified route. + + */ +///////////////////// +void YieldingFlexibleWorkGang::start_task(YieldingFlexibleGangTask* new_task) { + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + assert(task() == NULL, "Gang currently tied to a task"); + assert(new_task != NULL, "Null task"); + // Bind task to gang + _task = new_task; + new_task->set_gang(this); // Establish 2-way binding to support yielding + _sequence_number++; + + int requested_size = new_task->requested_size(); + assert(requested_size >= 0, "Should be non-negative"); + if (requested_size != 0) { + _active_workers = MIN2(requested_size, total_workers()); + } else { + _active_workers = total_workers(); + } + new_task->set_actual_size(_active_workers); + + assert(_started_workers == 0, "Tabula rasa non"); + assert(_finished_workers == 0, "Tabula rasa non"); + assert(_yielded_workers == 0, "Tabula rasa non"); + yielding_task()->set_status(ACTIVE); + + // Wake up all the workers, the first few will get to work, + // and the rest will go back to sleep + monitor()->notify_all(); + wait_for_gang(); +} + +void YieldingFlexibleWorkGang::wait_for_gang() { + + assert(monitor()->owned_by_self(), "Data race"); + // Wait for task to complete or yield + for (Status status = yielding_task()->status(); + status != COMPLETED && status != YIELDED && status != ABORTED; + status = yielding_task()->status()) { + assert(started_workers() <= active_workers(), "invariant"); + assert(finished_workers() <= active_workers(), "invariant"); + assert(yielded_workers() <= active_workers(), "invariant"); + monitor()->wait(Mutex::_no_safepoint_check_flag); + } + switch (yielding_task()->status()) { + case COMPLETED: + case ABORTED: { + assert(finished_workers() == active_workers(), "Inconsistent status"); + assert(yielded_workers() == 0, "Invariant"); + reset(); // for next task; gang<->task binding released + break; + } + case YIELDED: { + assert(yielded_workers() > 0, "Invariant"); + assert(yielded_workers() + finished_workers() == active_workers(), + "Inconsistent counts"); + break; + } + case ACTIVE: + case INACTIVE: + case COMPLETING: + case YIELDING: + case ABORTING: + default: + ShouldNotReachHere(); + } +} + +void YieldingFlexibleWorkGang::continue_task( + YieldingFlexibleGangTask* gang_task) { + + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + assert(task() != NULL && task() == gang_task, "Incorrect usage"); + // assert(_active_workers == total_workers(), "For now"); + assert(_started_workers == _active_workers, "Precondition"); + assert(_yielded_workers > 0 && yielding_task()->status() == YIELDED, + "Else why are we calling continue_task()"); + // Restart the yielded gang workers + yielding_task()->set_status(ACTIVE); + monitor()->notify_all(); + wait_for_gang(); +} + +void YieldingFlexibleWorkGang::reset() { + _started_workers = 0; + _finished_workers = 0; + _active_workers = 0; + yielding_task()->set_gang(NULL); + _task = NULL; // unbind gang from task +} + +void YieldingFlexibleWorkGang::yield() { + assert(task() != NULL, "Inconsistency; should have task binding"); + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + assert(yielded_workers() < active_workers(), "Consistency check"); + if (yielding_task()->status() == ABORTING) { + // Do not yield; we need to abort as soon as possible + // XXX NOTE: This can cause a performance pathology in the + // current implementation in Mustang, as of today, and + // pre-Mustang in that as soon as an overflow occurs, + // yields will not be honoured. The right way to proceed + // of course is to fix bug # TBF, so that abort's cause + // us to return at each potential yield point. + return; + } + if (++_yielded_workers + finished_workers() == active_workers()) { + yielding_task()->set_status(YIELDED); + monitor()->notify_all(); + } else { + yielding_task()->set_status(YIELDING); + } + + while (true) { + switch (yielding_task()->status()) { + case YIELDING: + case YIELDED: { + monitor()->wait(Mutex::_no_safepoint_check_flag); + break; // from switch + } + case ACTIVE: + case ABORTING: + case COMPLETING: { + assert(_yielded_workers > 0, "Else why am i here?"); + _yielded_workers--; + return; + } + case INACTIVE: + case ABORTED: + case COMPLETED: + default: { + ShouldNotReachHere(); + } + } + } + // Only return is from inside switch statement above + ShouldNotReachHere(); +} + +void YieldingFlexibleWorkGang::abort() { + assert(task() != NULL, "Inconsistency; should have task binding"); + MutexLockerEx ml(monitor(), Mutex::_no_safepoint_check_flag); + assert(yielded_workers() < active_workers(), "Consistency check"); + #ifndef PRODUCT + switch (yielding_task()->status()) { + // allowed states + case ACTIVE: + case ABORTING: + case COMPLETING: + case YIELDING: + break; + // not allowed states + case INACTIVE: + case ABORTED: + case COMPLETED: + case YIELDED: + default: + ShouldNotReachHere(); + } + #endif // !PRODUCT + Status prev_status = yielding_task()->status(); + yielding_task()->set_status(ABORTING); + if (prev_status == YIELDING) { + assert(yielded_workers() > 0, "Inconsistency"); + // At least one thread has yielded, wake it up + // so it can go back to waiting stations ASAP. + monitor()->notify_all(); + } +} + +/////////////////////////////// +// YieldingFlexibleGangTask +/////////////////////////////// +void YieldingFlexibleGangTask::yield() { + assert(gang() != NULL, "No gang to signal"); + gang()->yield(); +} + +void YieldingFlexibleGangTask::abort() { + assert(gang() != NULL, "No gang to signal"); + gang()->abort(); +} + +/////////////////////////////// +// YieldingFlexibleGangWorker +/////////////////////////////// +void YieldingFlexibleGangWorker::loop() { + int previous_sequence_number = 0; + Monitor* gang_monitor = gang()->monitor(); + MutexLockerEx ml(gang_monitor, Mutex::_no_safepoint_check_flag); + WorkData data; + int id; + while (true) { + // Check if there is work to do or if we have been asked + // to terminate + gang()->internal_worker_poll(&data); + if (data.terminate()) { + // We have been asked to terminate. + assert(gang()->task() == NULL, "No task binding"); + // set_status(TERMINATED); + return; + } else if (data.task() != NULL && + data.sequence_number() != previous_sequence_number) { + // There is work to be done. + // First check if we need to become active or if there + // are already the requisite number of workers + if (gang()->started_workers() == yf_gang()->active_workers()) { + // There are already enough workers, we do not need to + // to run; fall through and wait on monitor. + } else { + // We need to pitch in and do the work. + assert(gang()->started_workers() < yf_gang()->active_workers(), + "Unexpected state"); + id = gang()->started_workers(); + gang()->internal_note_start(); + // Now, release the gang mutex and do the work. + { + MutexUnlockerEx mul(gang_monitor, Mutex::_no_safepoint_check_flag); + data.task()->work(id); // This might include yielding + } + // Reacquire monitor and note completion of this worker + gang()->internal_note_finish(); + // Update status of task based on whether all workers have + // finished or some have yielded + assert(data.task() == gang()->task(), "Confused task binding"); + if (gang()->finished_workers() == yf_gang()->active_workers()) { + switch (data.yf_task()->status()) { + case ABORTING: { + data.yf_task()->set_status(ABORTED); + break; + } + case ACTIVE: + case COMPLETING: { + data.yf_task()->set_status(COMPLETED); + break; + } + default: + ShouldNotReachHere(); + } + gang_monitor->notify_all(); // Notify overseer + } else { // at least one worker is still working or yielded + assert(gang()->finished_workers() < yf_gang()->active_workers(), + "Counts inconsistent"); + switch (data.yf_task()->status()) { + case ACTIVE: { + // first, but not only thread to complete + data.yf_task()->set_status(COMPLETING); + break; + } + case YIELDING: { + if (gang()->finished_workers() + yf_gang()->yielded_workers() + == yf_gang()->active_workers()) { + data.yf_task()->set_status(YIELDED); + gang_monitor->notify_all(); // notify overseer + } + break; + } + case ABORTING: + case COMPLETING: { + break; // nothing to do + } + default: // everything else: INACTIVE, YIELDED, ABORTED, COMPLETED + ShouldNotReachHere(); + } + } + } + } + // Remember the sequence number + previous_sequence_number = data.sequence_number(); + // Wait for more work + gang_monitor->wait(Mutex::_no_safepoint_check_flag); + } +} diff --git a/src/share/vm/utilities/yieldingWorkgroup.hpp b/src/share/vm/utilities/yieldingWorkgroup.hpp new file mode 100644 index 000000000..d7890f17d --- /dev/null +++ b/src/share/vm/utilities/yieldingWorkgroup.hpp @@ -0,0 +1,203 @@ +/* + * Copyright 2005 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. + * + */ + + +// Forward declarations +class YieldingFlexibleWorkGang; + +// Status of tasks +enum Status { + INACTIVE, + ACTIVE, + YIELDING, + YIELDED, + ABORTING, + ABORTED, + COMPLETING, + COMPLETED +}; + +// Class YieldingFlexibleGangWorker: +// Several instances of this class run in parallel as workers for a gang. +class YieldingFlexibleGangWorker: public GangWorker { +public: + // Ctor + YieldingFlexibleGangWorker(AbstractWorkGang* gang, int id) : + GangWorker(gang, id) { } + +public: + YieldingFlexibleWorkGang* yf_gang() const + { return (YieldingFlexibleWorkGang*)gang(); } + +protected: // Override from parent class + virtual void loop(); +}; + +// An abstract task to be worked on by a flexible work gang, +// and where the workers will periodically yield, usually +// in response to some condition that is signalled by means +// that are specific to the task at hand. +// You subclass this to supply your own work() method. +// A second feature of this kind of work gang is that +// it allows for the signalling of certain exceptional +// conditions that may be encountered during the performance +// of the task and that may require the task at hand to be +// `aborted' forthwith. Finally, these gangs are `flexible' +// in that they can operate at partial capacity with some +// gang workers waiting on the bench; in other words, the +// size of the active worker pool can flex (up to an apriori +// maximum) in response to task requests at certain points. +// The last part (the flexible part) has not yet been fully +// fleshed out and is a work in progress. +class YieldingFlexibleGangTask: public AbstractGangTask { + Status _status; + YieldingFlexibleWorkGang* _gang; + int _actual_size; // size of gang obtained + +protected: + int _requested_size; // size of gang requested + + // Constructor and desctructor: only construct subclasses. + YieldingFlexibleGangTask(const char* name): AbstractGangTask(name), + _status(INACTIVE), + _gang(NULL), + _requested_size(0) { } + + virtual ~YieldingFlexibleGangTask() { } + + friend class YieldingFlexibleWorkGang; + friend class YieldingFlexibleGangWorker; + NOT_PRODUCT(virtual bool is_YieldingFlexibleGang_task() const { + return true; + }) + + void set_status(Status s) { + _status = s; + } + YieldingFlexibleWorkGang* gang() { + return _gang; + } + void set_gang(YieldingFlexibleWorkGang* gang) { + assert(_gang == NULL || gang == NULL, "Clobber without intermediate reset?"); + _gang = gang; + } + +public: + // The abstract work method. + // The argument tells you which member of the gang you are. + virtual void work(int i) = 0; + + // Subclasses should call the parent's yield() method + // after having done any work specific to the subclass. + virtual void yield(); + + // An abstract method supplied by + // a concrete sub-class which is used by the coordinator + // to do any "central yielding" work. + virtual void coordinator_yield() = 0; + + // Subclasses should call the parent's abort() method + // after having done any work specific to the sunbclass. + virtual void abort(); + + Status status() const { return _status; } + bool yielded() const { return _status == YIELDED; } + bool completed() const { return _status == COMPLETED; } + bool aborted() const { return _status == ABORTED; } + bool active() const { return _status == ACTIVE; } + + int requested_size() const { return _requested_size; } + int actual_size() const { return _actual_size; } + + void set_requested_size(int sz) { _requested_size = sz; } + void set_actual_size(int sz) { _actual_size = sz; } +}; + +// Class YieldingWorkGang: A subclass of WorkGang. +// In particular, a YieldingWorkGang is made up of +// YieldingGangWorkers, and provides infrastructure +// supporting yielding to the "GangOverseer", +// being the thread that orchestrates the WorkGang via run_task(). +class YieldingFlexibleWorkGang: public AbstractWorkGang { + // Here's the public interface to this class. +public: + // Constructor and destructor. + YieldingFlexibleWorkGang(const char* name, int workers, bool are_GC_threads); + + YieldingFlexibleGangTask* yielding_task() const { + assert(task() == NULL || task()->is_YieldingFlexibleGang_task(), + "Incorrect cast"); + return (YieldingFlexibleGangTask*)task(); + } + // Run a task; returns when the task is done, or the workers yield, + // or the task is aborted, or the work gang is terminated via stop(). + // A task that has been yielded can be continued via this same interface + // by using the same task repeatedly as the argument to the call. + // It is expected that the YieldingFlexibleGangTask carries the appropriate + // continuation information used by workers to continue the task + // from its last yield point. Thus, a completed task will return + // immediately with no actual work having been done by the workers. + void run_task(AbstractGangTask* task) { + guarantee(false, "Use start_task instead"); + } + void start_task(YieldingFlexibleGangTask* new_task); + void continue_task(YieldingFlexibleGangTask* gang_task); + + // Abort a currently running task, if any; returns when all the workers + // have stopped working on the current task and have returned to their + // waiting stations. + void abort_task(); + + // Yield: workers wait at their current working stations + // until signalled to proceed by the overseer. + void yield(); + + // Abort: workers are expected to return to their waiting + // stations, whence they are ready for the next task dispatched + // by the overseer. + void abort(); + +private: + // The currently active workers in this gang. + // This is a number that is dynamically adjusted by + // the run_task() method at each subsequent invocation, + // using data in the YieldingFlexibleGangTask. + int _active_workers; + int _yielded_workers; + void wait_for_gang(); + +public: + // Accessors for fields + int active_workers() const { + return _active_workers; + } + + int yielded_workers() const { + return _yielded_workers; + } + +private: + friend class YieldingFlexibleGangWorker; + void reset(); // NYI +}; |