diff -r fd16c54261b3 -r 489c9b5090e2 hotspot/src/share/vm/utilities/xmlstream.cpp --- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/hotspot/src/share/vm/utilities/xmlstream.cpp Sat Dec 01 00:00:00 2007 +0000 @@ -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 "" 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(" ", 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 "". +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 "". +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 "". +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 "". +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 "". +void xmlStream::tail(const char* kind) { + pop_tag(kind); + print_raw("\n"); +} + +// ------------------------------------------------------------------ +// Outputs " ". +void xmlStream::done(const char* format, ...) { + va_list ap; + va_start(ap, format); + va_done(format, ap); + va_end(ap); +} + +// ------------------------------------------------------------------ +// Outputs " ". +// 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(""); +} + +// ------------------------------------------------------------------ +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); +}