Mercurial > hg > truffle
view src/share/vm/utilities/ostream.cpp @ 453:c96030fff130
6684579: SoftReference processing can be made more efficient
Summary: For current soft-ref clearing policies, we can decide at marking time if a soft-reference will definitely not be cleared, postponing the decision of whether it will definitely be cleared to the final reference processing phase. This can be especially beneficial in the case of concurrent collectors where the marking is usually concurrent but reference processing is usually not.
Reviewed-by: jmasa
author | ysr |
---|---|
date | Thu, 20 Nov 2008 16:56:09 -0800 |
parents | 1ee8caae33af |
children | 2328d1d3f8cf |
line wrap: on
line source
/* * Copyright 1997-2008 Sun Microsystems, Inc. All Rights Reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it * 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') { int tw = 8 - (_position & 7); _position += tw; _precount -= tw-1; // 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) { int need_fill = col - position(); sp(need_fill); } void outputStream::move_to(int col, int slop, int min_space) { if (position() >= col + slop) cr(); int need_fill = col - position(); if (need_fill < min_space) need_fill = min_space; sp(need_fill); } void outputStream::put(char ch) { assert(ch != 0, "please fix call site"); char buf[] = { ch, '\0' }; write(buf, 1); } #define SP_USE_TABS false void outputStream::sp(int count) { if (count < 0) return; if (SP_USE_TABS && count >= 8) { int target = position() + count; while (count >= 8) { this->write("\t", 1); count -= 8; } count = target - position(); } while (count > 0) { int nw = (count > 8) ? 8 : count; this->write(" ", nw); count -= nw; } } void outputStream::cr() { 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::stamp(bool guard, const char* prefix, const char* suffix) { if (!guard) { return; } print_raw(prefix); stamp(); print_raw(suffix); } 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, size_t bufmax) : outputStream() { buffer_length = initial_size; buffer = NEW_C_HEAP_ARRAY(char, buffer_length); buffer_pos = 0; buffer_fixed = false; buffer_max = bufmax; } bufferedStream::bufferedStream(char* fixed_buffer, size_t fixed_buffer_size, size_t bufmax) : outputStream() { buffer_length = fixed_buffer_size; buffer = fixed_buffer; buffer_pos = 0; buffer_fixed = true; buffer_max = bufmax; } void bufferedStream::write(const char* s, size_t len) { if(buffer_pos + len > buffer_max) { flush(); } 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() : bufferedStream(1024*10, 1024*10) { _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) { int result = hpi::raw_send(_socket, (char *)base(), (int)size(), 0); assert(result != -1, "connection error"); assert(result == (int)size(), "didn't send enough data"); } 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 == (uint32_t)-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