8333211: NMT Reports: replace manual indentation handling with auto indent

Reviewed-by: jsjolen, asmehra
This commit is contained in:
Thomas Stuefe 2024-06-07 07:34:58 +00:00
parent 8ffc35d117
commit bf7f1c41cc
9 changed files with 215 additions and 136 deletions

@ -87,7 +87,7 @@ public:
class Arena : public CHeapObjBase {
public:
enum class Tag {
enum class Tag : uint8_t {
tag_other = 0,
tag_ra, // resource area
tag_ha, // handle area
@ -101,6 +101,7 @@ protected:
MEMFLAGS _flags; // Memory tracking flags
const Tag _tag;
uint32_t _init_size;
Chunk* _first; // First chunk
Chunk* _chunk; // current chunk
char* _hwm; // High water mark

@ -31,7 +31,14 @@
#include "nmt/memoryFileTracker.hpp"
#include "nmt/threadStackTracker.hpp"
#include "nmt/virtualMemoryTracker.hpp"
#include "utilities/debug.hpp"
#include "utilities/globalDefinitions.hpp"
#include "utilities/ostream.hpp"
#define INDENT_BY(num_chars, CODE) { \
streamIndentor si(out, num_chars); \
{ CODE } \
}
// Diff two counters, express them as signed, with range checks
static ssize_t counter_diff(size_t c1, size_t c2) {
@ -43,6 +50,15 @@ static ssize_t counter_diff(size_t c1, size_t c2) {
return c1 - c2;
}
MemReporterBase::MemReporterBase(outputStream* out, size_t scale) :
_scale(scale), _output(out) {
_output->set_autoindent(true);
}
MemReporterBase::~MemReporterBase() {
_output->set_autoindent(false);
}
size_t MemReporterBase::reserved_total(const MallocMemory* malloc, const VirtualMemory* vm) {
return malloc->malloc_size() + malloc->arena_size() + vm->reserved();
}
@ -105,27 +121,15 @@ void MemReporterBase::print_virtual_memory(size_t reserved, size_t committed, si
}
}
void MemReporterBase::print_malloc_line(const MemoryCounter* c) const {
output()->print("%28s", " ");
print_malloc(c);
output()->print_cr(" ");
}
void MemReporterBase::print_virtual_memory_line(size_t reserved, size_t committed, size_t peak) const {
output()->print("%28s", " ");
print_virtual_memory(reserved, committed, peak);
output()->print_cr(" ");
}
void MemReporterBase::print_arena_line(const MemoryCounter* c) const {
void MemReporterBase::print_arena(const MemoryCounter* c) const {
const char* scale = current_scale();
outputStream* out = output();
const size_t amount = c->size();
const size_t count = c->count();
out->print("%27s (arena=" SIZE_FORMAT "%s #" SIZE_FORMAT ")", "",
amount_in_current_scale(amount), scale, count);
out->print("(arena=" SIZE_FORMAT "%s #" SIZE_FORMAT ")",
amount_in_current_scale(amount), scale, count);
size_t pk_amount = c->peak_size();
if (pk_amount == amount) {
@ -135,8 +139,6 @@ void MemReporterBase::print_arena_line(const MemoryCounter* c) const {
out->print(" (peak=" SIZE_FORMAT "%s #" SIZE_FORMAT ")",
amount_in_current_scale(pk_amount), scale, pk_count);
}
out->cr();
}
void MemReporterBase::print_virtual_memory_region(const char* type, address base, size_t size) const {
@ -156,7 +158,9 @@ void MemSummaryReporter::report() {
size_t total_committed_amount = total_malloced_bytes + total_mmap_committed_bytes;
// Overall total
out->print_cr("\nNative Memory Tracking:\n");
out->cr();
out->print_cr("Native Memory Tracking:");
out->cr();
if (scale() > 1) {
out->print_cr("(Omitting categories weighting less than 1%s)", current_scale());
@ -166,13 +170,15 @@ void MemSummaryReporter::report() {
out->print("Total: ");
print_total(total_reserved_amount, total_committed_amount);
out->cr();
out->print_cr(" malloc: " SIZE_FORMAT "%s #" SIZE_FORMAT ", peak=" SIZE_FORMAT "%s #" SIZE_FORMAT,
amount_in_current_scale(total_malloced_bytes), current_scale(),
_malloc_snapshot->total_count(),
amount_in_current_scale(_malloc_snapshot->total_peak()),
current_scale(), _malloc_snapshot->total_peak_count());
out->print(" mmap: ");
print_total(total_mmap_reserved_bytes, total_mmap_committed_bytes);
INDENT_BY(7,
out->print_cr("malloc: " SIZE_FORMAT "%s #" SIZE_FORMAT ", peak=" SIZE_FORMAT "%s #" SIZE_FORMAT,
amount_in_current_scale(total_malloced_bytes), current_scale(),
_malloc_snapshot->total_count(),
amount_in_current_scale(_malloc_snapshot->total_peak()),
current_scale(), _malloc_snapshot->total_peak_count());
out->print("mmap: ");
print_total(total_mmap_reserved_bytes, total_mmap_committed_bytes);
)
out->cr();
out->cr();
@ -218,7 +224,8 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag,
outputStream* out = output();
const char* scale = current_scale();
out->print("-%26s (", NMTUtil::flag_to_name(flag));
constexpr int indent = 28;
out->print("-%*s (", indent - 2, NMTUtil::flag_to_name(flag));
print_total(reserved_amount, committed_amount);
#if INCLUDE_CDS
if (flag == mtClassShared) {
@ -229,39 +236,43 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag,
#endif
out->print_cr(")");
streamIndentor si(out, indent);
if (flag == mtClass) {
// report class count
out->print_cr("%27s (classes #" SIZE_FORMAT ")",
" ", (_instance_class_count + _array_class_count));
out->print_cr("%27s ( instance classes #" SIZE_FORMAT ", array classes #" SIZE_FORMAT ")",
" ", _instance_class_count, _array_class_count);
out->print_cr("(classes #" SIZE_FORMAT ")", (_instance_class_count + _array_class_count));
out->print_cr("( instance classes #" SIZE_FORMAT ", array classes #" SIZE_FORMAT ")",
_instance_class_count, _array_class_count);
} else if (flag == mtThread) {
const VirtualMemory* thread_stack_usage =
_vm_snapshot->by_type(mtThreadStack);
// report thread count
out->print_cr("%27s (threads #" SIZE_FORMAT ")", " ", ThreadStackTracker::thread_count());
out->print("%27s (stack: ", " ");
out->print_cr("(threads #" SIZE_FORMAT ")", ThreadStackTracker::thread_count());
out->print("(stack: ");
print_total(thread_stack_usage->reserved(), thread_stack_usage->committed(), thread_stack_usage->peak_size());
out->print_cr(")");
}
// report malloc'd memory
if (amount_in_current_scale(MAX2(malloc_memory->malloc_size(), pk_malloc)) > 0) {
print_malloc_line(malloc_memory->malloc_counter());
print_malloc(malloc_memory->malloc_counter());
out->cr();
}
if (amount_in_current_scale(MAX2(virtual_memory->reserved(), pk_vm)) > 0) {
print_virtual_memory_line(virtual_memory->reserved(), virtual_memory->committed(), virtual_memory->peak_size());
print_virtual_memory(virtual_memory->reserved(), virtual_memory->committed(), virtual_memory->peak_size());
out->cr();
}
if (amount_in_current_scale(MAX2(malloc_memory->arena_size(), pk_arena)) > 0) {
print_arena_line(malloc_memory->arena_counter());
print_arena(malloc_memory->arena_counter());
out->cr();
}
if (flag == mtNMT &&
amount_in_current_scale(_malloc_snapshot->malloc_overhead()) > 0) {
out->print_cr("%27s (tracking overhead=" SIZE_FORMAT "%s)", " ",
amount_in_current_scale(_malloc_snapshot->malloc_overhead()), scale);
out->print_cr("(tracking overhead=" SIZE_FORMAT "%s)",
amount_in_current_scale(_malloc_snapshot->malloc_overhead()), scale);
} else if (flag == mtClass) {
// Metadata information
report_metadata(Metaspace::NonClassType);
@ -269,7 +280,7 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag,
report_metadata(Metaspace::ClassType);
}
}
out->print_cr(" ");
out->cr();
}
void MemSummaryReporter::report_metadata(Metaspace::MetadataType type) const {
@ -292,13 +303,13 @@ void MemSummaryReporter::report_metadata(Metaspace::MetadataType type) const {
size_t waste = stats.committed() - stats.used();
float waste_percentage = stats.committed() > 0 ? (((float)waste * 100)/(float)stats.committed()) : 0.0f;
out->print_cr("%27s ( %s)", " ", name);
out->print("%27s ( ", " ");
out->print_cr("( %s)", name);
out->print("( ");
print_total(stats.reserved(), stats.committed());
out->print_cr(")");
out->print_cr("%27s ( used=" SIZE_FORMAT "%s)", " ", amount_in_current_scale(stats.used()), scale);
out->print_cr("%27s ( waste=" SIZE_FORMAT "%s =%2.2f%%)", " ", amount_in_current_scale(waste),
scale, waste_percentage);
out->print_cr("( used=" SIZE_FORMAT "%s)", amount_in_current_scale(stats.used()), scale);
out->print_cr("( waste=" SIZE_FORMAT "%s =%2.2f%%)", amount_in_current_scale(waste),
scale, waste_percentage);
}
void MemDetailReporter::report_detail() {
@ -333,12 +344,15 @@ int MemDetailReporter::report_malloc_sites() {
}
const NativeCallStack* stack = malloc_site->call_stack();
stack->print_on(out);
out->print("%29s", " ");
MEMFLAGS flag = malloc_site->flag();
assert(NMTUtil::flag_is_valid(flag) && flag != mtNone,
"Must have a valid memory type");
print_malloc(malloc_site->counter(), flag);
out->print_cr("\n");
INDENT_BY(29,
out->print("(");
print_malloc(malloc_site->counter(), flag);
out->print_cr(")");
)
out->cr();
}
return num_omitted;
}
@ -350,6 +364,7 @@ int MemDetailReporter::report_virtual_memory_allocation_sites() {
if (virtual_memory_itr.is_empty()) return 0;
outputStream* out = output();
const VirtualMemoryAllocationSite* virtual_memory_site;
int num_omitted = 0;
while ((virtual_memory_site = virtual_memory_itr.next()) != nullptr) {
@ -366,13 +381,16 @@ int MemDetailReporter::report_virtual_memory_allocation_sites() {
}
const NativeCallStack* stack = virtual_memory_site->call_stack();
stack->print_on(out);
out->print("%28s (", " ");
print_total(virtual_memory_site->reserved(), virtual_memory_site->committed());
MEMFLAGS flag = virtual_memory_site->flag();
if (flag != mtNone) {
out->print(" Type=%s", NMTUtil::flag_to_name(flag));
}
out->print_cr(")\n");
INDENT_BY(29,
out->print("(");
print_total(virtual_memory_site->reserved(), virtual_memory_site->committed());
const MEMFLAGS flag = virtual_memory_site->flag();
if (flag != mtNone) {
out->print(" Type=%s", NMTUtil::flag_to_name(flag));
}
out->print_cr(")");
)
out->cr();
}
return num_omitted;
}
@ -409,14 +427,14 @@ void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion*
const NativeCallStack* stack = reserved_rgn->call_stack();
bool all_committed = reserved_rgn->size() == reserved_rgn->committed_size();
const char* region_type = (all_committed ? "reserved and committed" : "reserved");
out->print_cr(" ");
out->cr();
print_virtual_memory_region(region_type, reserved_rgn->base(), reserved_rgn->size());
out->print(" for %s", NMTUtil::flag_to_name(reserved_rgn->flag()));
if (stack->is_empty()) {
out->print_cr(" ");
out->cr();
} else {
out->print_cr(" from");
stack->print_on(out, 4);
INDENT_BY(4, stack->print_on(out);)
}
if (all_committed) {
@ -437,20 +455,33 @@ void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion*
// Don't report if size is too small
if (amount_in_current_scale(committed_rgn->size()) == 0) continue;
stack = committed_rgn->call_stack();
out->print("\n\t");
print_virtual_memory_region("committed", committed_rgn->base(), committed_rgn->size());
if (stack->is_empty()) {
out->print_cr(" ");
} else {
out->print_cr(" from");
stack->print_on(out, 12);
}
out->cr();
INDENT_BY(8,
print_virtual_memory_region("committed", committed_rgn->base(), committed_rgn->size());
if (stack->is_empty()) {
out->cr();
} else {
out->print_cr(" from");
INDENT_BY(4, stack->print_on(out);)
}
)
}
}
void MemDetailReporter::report_memory_file_allocations() {
stringStream st;
{
MemoryFileTracker::Instance::Locker lock;
MemoryFileTracker::Instance::print_all_reports_on(&st, scale());
}
output()->print_raw(st.freeze());
}
void MemSummaryDiffReporter::report_diff() {
outputStream* out = output();
out->print_cr("\nNative Memory Tracking:\n");
out->cr();
out->print_cr("Native Memory Tracking:");
out->cr();
if (scale() > 1) {
out->print_cr("(Omitting categories weighting less than 1%s)", current_scale());
@ -463,7 +494,8 @@ void MemSummaryDiffReporter::report_diff() {
_current_baseline.total_committed_memory(), _early_baseline.total_reserved_memory(),
_early_baseline.total_committed_memory());
out->print_cr("\n");
out->cr();
out->cr();
// Summary diff by memory type
for (int index = 0; index < mt_number_of_types; index ++) {
@ -548,6 +580,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
outputStream* out = output();
const char* scale = current_scale();
constexpr int indent = 28;
// Total reserved and committed memory in current baseline
size_t current_reserved_amount = reserved_total (current_malloc, current_vm);
@ -581,15 +614,17 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
diff_in_current_scale(current_reserved_amount, early_reserved_amount) != 0) {
// print summary line
out->print("-%26s (", NMTUtil::flag_to_name(flag));
out->print("-%*s (", indent - 2, NMTUtil::flag_to_name(flag));
print_virtual_memory_diff(current_reserved_amount, current_committed_amount,
early_reserved_amount, early_committed_amount);
out->print_cr(")");
streamIndentor si(out, indent);
// detail lines
if (flag == mtClass) {
// report class count
out->print("%27s (classes #" SIZE_FORMAT "", " ", _current_baseline.class_count());
out->print("(classes #" SIZE_FORMAT, _current_baseline.class_count());
const ssize_t class_count_diff =
counter_diff(_current_baseline.class_count(), _early_baseline.class_count());
if (class_count_diff != 0) {
@ -597,7 +632,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
}
out->print_cr(")");
out->print("%27s ( instance classes #" SIZE_FORMAT, " ", _current_baseline.instance_class_count());
out->print("( instance classes #" SIZE_FORMAT, _current_baseline.instance_class_count());
const ssize_t instance_class_count_diff =
counter_diff(_current_baseline.instance_class_count(), _early_baseline.instance_class_count());
if (instance_class_count_diff != 0) {
@ -613,14 +648,14 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
} else if (flag == mtThread) {
// report thread count
out->print("%27s (threads #" SIZE_FORMAT "", " ", _current_baseline.thread_count());
out->print("(threads #" SIZE_FORMAT, _current_baseline.thread_count());
const ssize_t thread_count_diff = counter_diff(_current_baseline.thread_count(), _early_baseline.thread_count());
if (thread_count_diff != 0) {
out->print(" " SSIZE_PLUS_FORMAT, thread_count_diff);
}
out->print_cr(")");
out->print("%27s (stack: ", " ");
out->print("(stack: ");
// report thread stack
const VirtualMemory* current_thread_stack =
_current_baseline.virtual_memory(mtThreadStack);
@ -638,7 +673,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
size_t early_malloc_amount = early_malloc->malloc_size();
if (amount_in_current_scale(current_malloc_amount) > 0 ||
diff_in_current_scale(current_malloc_amount, early_malloc_amount) != 0) {
out->print("%28s(", " ");
out->print("(");
print_malloc_diff(current_malloc_amount, (flag == mtChunk) ? 0 : current_malloc->malloc_count(),
early_malloc_amount, early_malloc->malloc_count(), mtNone);
out->print_cr(")");
@ -647,7 +682,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
// Report virtual memory
if (amount_in_current_scale(current_vm->reserved()) > 0 ||
diff_in_current_scale(current_vm->reserved(), early_vm->reserved()) != 0) {
out->print("%27s (mmap: ", " ");
out->print("(mmap: ");
print_virtual_memory_diff(current_vm->reserved(), current_vm->committed(),
early_vm->reserved(), early_vm->committed());
out->print_cr(")");
@ -656,7 +691,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
// Report arena memory
if (amount_in_current_scale(current_malloc->arena_size()) > 0 ||
diff_in_current_scale(current_malloc->arena_size(), early_malloc->arena_size()) != 0) {
out->print("%28s(", " ");
out->print("(");
print_arena_diff(current_malloc->arena_size(), current_malloc->arena_count(),
early_malloc->arena_size(), early_malloc->arena_count());
out->print_cr(")");
@ -667,8 +702,8 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
size_t current_tracking_overhead = amount_in_current_scale(_current_baseline.malloc_tracking_overhead());
size_t early_tracking_overhead = amount_in_current_scale(_early_baseline.malloc_tracking_overhead());
out->print("%27s (tracking overhead=" SIZE_FORMAT "%s", " ",
amount_in_current_scale(_current_baseline.malloc_tracking_overhead()), scale);
out->print("(tracking overhead=" SIZE_FORMAT "%s",
amount_in_current_scale(_current_baseline.malloc_tracking_overhead()), scale);
int64_t overhead_diff = diff_in_current_scale(_current_baseline.malloc_tracking_overhead(),
_early_baseline.malloc_tracking_overhead());
@ -679,7 +714,7 @@ void MemSummaryDiffReporter::diff_summary_of_type(MEMFLAGS flag,
} else if (flag == mtClass) {
print_metaspace_diff(current_ms, early_ms);
}
out->print_cr(" ");
out->cr();
}
}
@ -697,8 +732,8 @@ void MemSummaryDiffReporter::print_metaspace_diff(const char* header,
outputStream* out = output();
const char* scale = current_scale();
out->print_cr("%27s: ( %s)", " ", header);
out->print("%27s ( ", " ");
out->print_cr("( %s)", header);
out->print("( ");
print_virtual_memory_diff(current_stats.reserved(),
current_stats.committed(),
early_stats.reserved(),
@ -713,8 +748,8 @@ void MemSummaryDiffReporter::print_metaspace_diff(const char* header,
int64_t diff_waste = diff_in_current_scale(current_waste, early_waste);
// Diff used
out->print("%27s ( used=" SIZE_FORMAT "%s", " ",
amount_in_current_scale(current_stats.used()), scale);
out->print("( used=" SIZE_FORMAT "%s",
amount_in_current_scale(current_stats.used()), scale);
if (diff_used != 0) {
out->print(" " INT64_PLUS_FORMAT "%s", diff_used, scale);
}
@ -723,8 +758,8 @@ void MemSummaryDiffReporter::print_metaspace_diff(const char* header,
// Diff waste
const float waste_percentage = current_stats.committed() == 0 ? 0.0f :
((float)current_waste * 100.0f) / (float)current_stats.committed();
out->print("%27s ( waste=" SIZE_FORMAT "%s =%2.2f%%", " ",
amount_in_current_scale(current_waste), scale, waste_percentage);
out->print("( waste=" SIZE_FORMAT "%s =%2.2f%%",
amount_in_current_scale(current_waste), scale, waste_percentage);
if (diff_waste != 0) {
out->print(" " INT64_PLUS_FORMAT "%s", diff_waste, scale);
}
@ -841,11 +876,13 @@ void MemDetailDiffReporter::diff_malloc_site(const NativeCallStack* stack, size_
}
stack->print_on(out);
out->print("%28s (", " ");
print_malloc_diff(current_size, current_count,
early_size, early_count, flags);
INDENT_BY(28,
out->print("(");
print_malloc_diff(current_size, current_count, early_size, early_count, flags);
out->print_cr(")");
)
out->cr();
out->print_cr(")\n");
}
@ -874,22 +911,14 @@ void MemDetailDiffReporter::diff_virtual_memory_site(const NativeCallStack* stac
}
stack->print_on(out);
out->print("%28s (mmap: ", " ");
print_virtual_memory_diff(current_reserved, current_committed,
early_reserved, early_committed);
if (flag != mtNone) {
out->print(" Type=%s", NMTUtil::flag_to_name(flag));
}
out->print_cr(")\n");
INDENT_BY(28,
out->print("(mmap: ");
print_virtual_memory_diff(current_reserved, current_committed, early_reserved, early_committed);
if (flag != mtNone) {
out->print(" Type=%s", NMTUtil::flag_to_name(flag));
}
out->print_cr(")");
)
out->cr();
}
void MemDetailReporter::report_memory_file_allocations() {
stringStream st;
{
MemoryFileTracker::Instance::Locker lock;
MemoryFileTracker::Instance::print_all_reports_on(&st, scale());
}
output()->print_raw(st.freeze());
}

@ -44,9 +44,8 @@ class MemReporterBase : public StackObj {
// Default scale to use if no scale given.
static const size_t default_scale = K;
MemReporterBase(outputStream* out, size_t scale = default_scale) :
_scale(scale), _output(out)
{}
MemReporterBase(outputStream* out, size_t scale = default_scale);
~MemReporterBase();
// Helper functions
// Calculate total reserved and committed amount
@ -109,10 +108,7 @@ class MemReporterBase : public StackObj {
void print_total(size_t reserved, size_t committed, size_t peak = 0) const;
void print_malloc(const MemoryCounter* c, MEMFLAGS flag = mtNone) const;
void print_virtual_memory(size_t reserved, size_t committed, size_t peak) const;
void print_malloc_line(const MemoryCounter* c) const;
void print_virtual_memory_line(size_t reserved, size_t committed, size_t peak) const;
void print_arena_line(const MemoryCounter* c) const;
void print_arena(const MemoryCounter* c) const;
void print_virtual_memory_region(const char* type, address base, size_t size) const;
};

@ -92,7 +92,10 @@ void MemoryFileTracker::print_report_on(const MemoryFile* file, outputStream* st
NMTUtil::amount_in_scale(end_addr - start_addr, scale),
NMTUtil::scale_name(scale),
NMTUtil::flag_to_name(prev->val().out.flag()));
_stack_storage.get(prev->val().out.stack()).print_on(stream, 4);
{
streamIndentor si(stream, 4);
_stack_storage.get(prev->val().out.stack()).print_on(stream);
}
stream->cr();
}
prev = current;

@ -71,24 +71,18 @@ int NativeCallStack::frames() const {
return index;
}
void NativeCallStack::print_on(outputStream* out) const {
print_on(out, 0);
}
// Decode and print this call path
void NativeCallStack::print_on(outputStream* out, int indent) const {
void NativeCallStack::print_on(outputStream* out) const {
DEBUG_ONLY(assert_not_fake();)
address pc;
char buf[1024];
int offset;
if (is_empty()) {
out->fill_to(indent);
out->print("[BOOTSTRAP]");
} else {
for (int frame = 0; frame < NMT_TrackingStackDepth; frame ++) {
pc = get_frame(frame);
if (pc == nullptr) break;
out->fill_to(indent);
out->print("[" PTR_FORMAT "]", p2i(pc));
// Print function and library; shorten library name to just its last component
// for brevity, and omit it completely for libjvm.so

@ -124,7 +124,6 @@ public:
}
void print_on(outputStream* out) const;
void print_on(outputStream* out, int indent) const;
};
#define FAKE_CALLSTACK NativeCallStack(NativeCallStack::FakeMarker::its_fake)

@ -44,18 +44,11 @@
extern "C" void jio_print(const char* s, size_t len);
extern "C" int jio_printf(const char *fmt, ...);
outputStream::outputStream() {
_position = 0;
_precount = 0;
_indentation = 0;
_scratch = nullptr;
_scratch_len = 0;
}
outputStream::outputStream(bool has_time_stamps) {
_position = 0;
_precount = 0;
_indentation = 0;
_autoindent = false;
_scratch = nullptr;
_scratch_len = 0;
if (has_time_stamps) _stamp.update();
@ -159,6 +152,9 @@ void outputStream::do_vsnprintf_and_write_with_scratch_buffer(const char* format
}
void outputStream::do_vsnprintf_and_write(const char* format, va_list ap, bool add_cr) {
if (_autoindent && _position == 0) {
indent();
}
if (_scratch) {
do_vsnprintf_and_write_with_scratch_buffer(format, ap, add_cr);
} else {
@ -188,6 +184,13 @@ void outputStream::vprint_cr(const char* format, va_list argptr) {
do_vsnprintf_and_write(format, argptr, true);
}
void outputStream::print_raw(const char* str, size_t len) {
if (_autoindent && _position == 0) {
indent();
}
write(str, len);
}
void outputStream::fill_to(int col) {
int need_fill = col - position();
sp(need_fill);

@ -46,9 +46,10 @@ DEBUG_ONLY(class ResourceMark;)
class outputStream : public CHeapObjBase {
private:
NONCOPYABLE(outputStream);
int _indentation; // current indentation
bool _autoindent; // if true, every line starts with indentation
protected:
int _indentation; // current indentation
int _position; // visual position on the current line
uint64_t _precount; // number of chars output, less than _position
TimeStamp _stamp; // for time stamps
@ -90,8 +91,7 @@ class outputStream : public CHeapObjBase {
class TestSupport; // Unit test support
// creation
outputStream();
outputStream(bool has_time_stamps);
outputStream(bool has_time_stamps = false);
// indentation
outputStream& indent();
@ -104,6 +104,13 @@ class outputStream : public CHeapObjBase {
void fill_to(int col);
void move_to(int col, int slop = 6, int min_space = 2);
// Automatic indentation:
// If autoindent mode is on, the following APIs will automatically indent
// line starts depending on the current indentation level:
// print(), print_cr(), print_raw(), print_raw_cr()
// Other APIs are unaffected
void set_autoindent(bool value) { _autoindent = value; }
// sizing
int position() const { return _position; }
julong count() const { return _precount + _position; }
@ -119,10 +126,10 @@ class outputStream : public CHeapObjBase {
void print_cr(const char* format, ...) ATTRIBUTE_PRINTF(2, 3);
void vprint(const char *format, va_list argptr) ATTRIBUTE_PRINTF(2, 0);
void vprint_cr(const char* format, va_list argptr) ATTRIBUTE_PRINTF(2, 0);
void print_raw(const char* str) { write(str, strlen(str)); }
void print_raw(const char* str, size_t len) { write(str, len); }
void print_raw_cr(const char* str) { write(str, strlen(str)); cr(); }
void print_raw_cr(const char* str, size_t len){ write(str, len); cr(); }
void print_raw(const char* str) { print_raw(str, strlen(str)); }
void print_raw(const char* str, size_t len);
void print_raw_cr(const char* str) { print_raw(str); cr(); }
void print_raw_cr(const char* str, size_t len) { print_raw(str, len); cr(); }
void print_data(void* data, size_t len, bool with_ascii, bool rel_addr=true);
void put(char ch);
void sp(int count = 1);

@ -104,6 +104,53 @@ TEST_VM(ostream, bufferedStream_dynamic_small) {
}
}
static void test_autoindent(bool on) {
stringStream ss;
ss.set_autoindent(on);
{
streamIndentor si(&ss, 5);
ss.print("ABC");
ss.print("DEF");
ss.cr();
ss.print_cr("0123");
{
streamIndentor si(&ss, 5);
ss.print_cr("4567");
ss.print_raw("89AB");
ss.print_raw("CDEXXXX", 3);
ss.print_raw_cr("XYZ");
}
ss.print("%u", 100);
ss.print_raw("KB");
ss.cr();
}
ss.print("end");
if (on) {
EXPECT_STREQ(ss.base(),
" ABCDEF\n"
" 0123\n"
" 4567\n"
" 89ABCDEXYZ\n"
" 100KB\n"
"end"
);
} else {
// no autoindent: calls should work as always without indentation
EXPECT_STREQ(ss.base(),
"ABCDEF\n"
"0123\n"
"4567\n"
"89ABCDEXYZ\n"
"100KB\n"
"end"
);
}
}
TEST_VM(ostream, autoindent_on) { test_autoindent(true); }
TEST_VM(ostream, autoindent_off) { test_autoindent(false); }
/* Activate to manually test bufferedStream dynamic cap.
TEST_VM(ostream, bufferedStream_dynamic_large) {