/* * Copyright (c) 1997, 2022, Oracle and/or its affiliates. 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA * or visit www.oracle.com if you need additional information or have any * questions. *
*/
// Execute a vsprintf, using the given buffer if necessary. // Return a pointer to the formatted string. constchar* outputStream::do_vsnprintf(char* buffer, size_t buflen, constchar* format, va_list ap, bool add_cr,
size_t& result_len) {
assert(buflen >= 2, "buffer too small");
constchar* 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
} elseif (format[0] == '%' && format[1] == 's' && format[2] == '\0') { // trivial copy-through format string
result = va_arg(ap, constchar*);
result_len = strlen(result); if (add_cr && result_len >= buflen) result_len = buflen-1; // truncate
} else { int required_len = os::vsnprintf(buffer, buflen, format, ap);
assert(required_len >= 0, "vsnprintf encoding error");
result = buffer; if ((size_t)required_len < buflen) {
result_len = required_len;
} else {
DEBUG_ONLY(warning("outputStream::do_vsnprintf output truncated -- buffer length is %d bytes but %d bytes are needed.",
add_cr ? (int)buflen + 1 : (int)buflen, add_cr ? required_len + 2 : required_len + 1);)
result_len = buflen - 1;
}
} if (add_cr) { if (result != buffer) {
memcpy(buffer, result, result_len);
result = buffer;
}
buffer[result_len++] = '\n';
buffer[result_len] = 0;
} return result;
}
void outputStream::vprint_cr(constchar* format, va_list argptr) {
do_vsnprintf_and_write(format, argptr, true);
}
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::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);
}
// 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(bool c_heap) const { char* copy = c_heap ?
NEW_C_HEAP_ARRAY(char, _written + 1, mtInternal) : NEW_RESOURCE_ARRAY(char, _written + 1);
::memcpy(copy, _buffer, _written);
copy[_written] = 0; // terminating null if (c_heap) { // Need to ensure our content is written to memory before we return // the pointer to it.
OrderAccess::storestore();
} return copy;
}
// tty needs to be always accessible since there are code paths that may write to it // outside of the VM lifespan. // Examples for pre-VM-init accesses: Early NMT init, Early UL init // Examples for post-VM-exit accesses: many, e.g. NMT C-heap bounds checker, signal handling, AGCT, ... // During lifetime tty is served by an instance of defaultStream. That instance's deletion cannot // be (easily) postponed or omitted since it has ties to the JVM infrastructure. // The policy followed here is a compromise reached during review of JDK-8292351: // - pre-init: we silently swallow all output. We won't see anything, but at least won't crash // - post-exit: we write to a simple fdStream, but somewhat mimic the behavior of the real defaultStream static nullStream tty_preinit_stream;
outputStream* tty = &tty_preinit_stream;
xmlStream* xtty; extern Mutex* tty_lock;
#define EXTRACHARLEN 32 #define CURRENTAPPX ".current" // convert YYYY-MM-DD HH:MM:SS to YYYY-MM-DD_HH-MM-SS char* get_datetime_string(char *buf, size_t len) {
os::local_time_string(buf, len); int i = (int)strlen(buf); while (--i >= 0) { if (buf[i] == ' ') buf[i] = '_'; elseif (buf[i] == ':') buf[i] = '-';
} return buf;
}
// who is first, %p or %t? int first = -1, second = -1; constchar *p1st = NULL; constchar *p2nd = NULL;
if (pid_pos >= 0 && tms_pos >= 0) { // contains both %p and %t if (pid_pos < tms_pos) { // case foo%pbar%tmonkey.log
first = pid_pos;
p1st = pid_text;
second = tms_pos;
p2nd = tms;
} else { // case foo%tbar%pmonkey.log
first = tms_pos;
p1st = tms;
second = pid_pos;
p2nd = pid_text;
}
} elseif (pid_pos >= 0) { // contains %p only
first = pid_pos;
p1st = pid_text;
} elseif (tms_pos >= 0) { // contains %t only
first = tms_pos;
p1st = tms;
}
int buf_pos = (int)strlen(buf); constchar* tail = nametail;
if (first >= 0) {
tail = nametail + first + 2;
strncpy(&buf[buf_pos], nametail, first);
strcpy(&buf[buf_pos + first], p1st);
buf_pos = (int)strlen(buf); if (second >= 0) {
strncpy(&buf[buf_pos], tail, second - first - 2);
strcpy(&buf[buf_pos + second - first - 2], p2nd);
tail = nametail + second + 2;
}
}
strcat(buf, tail); // append rest of name, or all of name return buf;
}
// log_name comes from -XX:LogFile=log_name or // -XX:DumpLoadedClassList=<file_name> // in log_name, %p => pid1234 and // %t => YYYY-MM-DD_HH-MM-SS constchar* make_log_name(constchar* log_name, constchar* force_directory) { char timestr[32];
get_datetime_string(timestr, sizeof(timestr)); return make_log_name_internal(log_name, force_directory, os::current_process_id(),
timestr);
}
fileStream::fileStream(constchar* file_name) {
_file = os::fopen(file_name, "w"); if (_file != NULL) {
_need_close = true;
} else {
warning("Cannot open file %s due to %s\n", file_name, os::strerror(errno));
_need_close = false;
}
}
fileStream::fileStream(constchar* file_name, constchar* opentype) {
_file = os::fopen(file_name, opentype); if (_file != NULL) {
_need_close = true;
} else {
warning("Cannot open file %s due to %s\n", file_name, os::strerror(errno));
_need_close = false;
}
}
void fileStream::write(constchar* s, size_t len) { if (_file != NULL) { // Make an unused local variable to avoid warning from gcc compiler.
size_t count = fwrite(s, 1, len, _file);
update_position(s, len);
}
}
long fileStream::fileSize() { long size = -1; if (_file != NULL) { long pos = ::ftell(_file); if (pos < 0) return pos; if (::fseek(_file, 0, SEEK_END) == 0) {
size = ::ftell(_file);
}
::fseek(_file, pos, SEEK_SET);
} return size;
}
char* fileStream::readln(char *data, int count ) { char * ret = NULL; if (_file != NULL) {
ret = ::fgets(data, count, _file); // Get rid of annoying \n char only if it is present.
size_t len = ::strlen(data); if (len > 0 && data[len - 1] == '\n') {
data[len - 1] = '\0';
}
} return ret;
}
fileStream::~fileStream() { if (_file != NULL) { if (_need_close) fclose(_file);
_file = NULL;
}
}
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 && !VMError::is_error_reported()) init(); return _log_file != NULL;
}
fileStream* defaultStream::open_file(constchar* log_name) { constchar* try_name = make_log_name(log_name, NULL); if (try_name == NULL) {
warning("Cannot open file %s: file name is too long.\n", log_name); return NULL;
}
fileStream* file = new (mtInternal) fileStream(try_name);
FREE_C_HEAP_ARRAY(char, try_name); if (file->is_open()) { return file;
}
// Try again to open the file in the temp directory. delete file; // Note: This feature is for maintainer use only. No need for L10N.
jio_printf("Warning: Cannot open log file: %s\n", log_name);
try_name = make_log_name(log_name, os::get_temp_directory()); if (try_name == NULL) {
warning("Cannot open file %s: file name is too long for directory %s.\n", log_name, os::get_temp_directory()); return NULL;
}
void defaultStream::start_log() {
xmlStream*xs = _outer_xmlStream; if (this == tty) xtty = xs; // Write XML header.
xs->print_cr(""); // (For now, don't bother to issue a DTD for this private format.)
// Calculate the start time of the log as ms since the epoch: this is // the current time in ms minus the uptime in ms.
jlong time_ms = os::javaTimeMillis() - tty->time_stamp().milliseconds();
xs->head("hotspot_log version='%d %d'" " process='%d' time_ms='" INT64_FORMAT "'",
LOG_MAJOR_VERSION, LOG_MINOR_VERSION,
os::current_process_id(), (int64_t)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.
outputStream *text = xs->text(); for (SystemProperty* p = Arguments::system_properties(); p != NULL; p = p->next()) {
assert(p->key() != NULL, "p->key() is NULL"); if (p->readable()) { // Print in two stages to avoid problems with long // keys/values.
text->print_raw(p->key());
text->put('=');
assert(p->value() != NULL, "p->value() is NULL");
text->print_raw_cr(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!
}
// 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
// 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 if current Thread isn't set
Thread::current_or_null() == NULL ||
// developer hook
!SerializeVMOutput ||
// VM already unhealthy
VMError::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_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();
}
if (DisplayVMOutput &&
(_outer_xmlStream == nullptr || !_outer_xmlStream->inside_attrs())) { // print to output stream. It can be redirected by a vfprintf hook
jio_print(s, len);
}
// print to log file if (has_log_file() && _outer_xmlStream != nullptr) {
_outer_xmlStream->write_text(s, len); bool nl = update_position(s, len); // flush the log file too, if there were any newlines if (nl) {
flush();
}
} else {
update_position(s, len);
}
// 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() { // Note : this must be called AFTER ostream_init()
ClassListWriter::init();
// 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() { staticbool ostream_exit_called = false; if (ostream_exit_called) return;
ostream_exit_called = true;
ClassListWriter::delete_classlist(); // Make sure tty works after VM exit by assigning an always-on functioning fdStream.
outputStream* tmp = tty;
tty = DisplayVMOutputToStderr ? fdStream::stdout_stream() : fdStream::stderr_stream(); if (tmp != &tty_preinit_stream && tmp != defaultStream::instance) { delete tmp;
} delete defaultStream::instance;
xtty = 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 tty, just flush its output if (tty) tty->flush();
if(buffer_pos + len > buffer_max) {
flush(); // Note: may be a noop.
}
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;
truncated = true;
} else { // For small overruns, double the buffer. For larger ones, // increase to the requested size. if (end < buffer_length * 2) {
end = buffer_length * 2;
} // Impose a cap beyond which the buffer cannot grow - a size which // in all probability indicates a real error, e.g. faulty printing // code looping, while not affecting cases of just-very-large-but-its-normal // output. const size_t reasonable_cap = MAX2(100 * M, buffer_max * 2); if (end > reasonable_cap) { // In debug VM, assert right away.
assert(false, "Exceeded max buffer size for this string."); // Release VM: silently truncate. We do this since these kind of errors // are both difficult to predict with testing (depending on logging content) // and usually not serious enough to kill a production VM for it.
end = reasonable_cap;
size_t remaining = end - buffer_pos; if (len >= remaining) {
len = remaining - 1;
truncated = true;
}
} if (buffer_length < end) {
buffer = REALLOC_C_HEAP_ARRAY(char, buffer, end, mtInternal);
buffer_length = end;
}
}
} if (len > 0) {
memcpy(buffer + buffer_pos, s, len);
buffer_pos += len;
update_position(s, len);
}
}
Die Informationen auf dieser Webseite wurden
nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit,
noch Qualität der bereit gestellten Informationen zugesichert.
Bemerkung:
Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.