diff --git a/src/hotspot/share/nmt/memReporter.cpp b/src/hotspot/share/nmt/memReporter.cpp index 48470478b9b..964911bb595 100644 --- a/src/hotspot/share/nmt/memReporter.cpp +++ b/src/hotspot/share/nmt/memReporter.cpp @@ -55,7 +55,7 @@ void MemReporterBase::print_total(size_t reserved, size_t committed, size_t peak output()->print("reserved=" SIZE_FORMAT "%s, committed=" SIZE_FORMAT "%s", amount_in_current_scale(reserved), scale, amount_in_current_scale(committed), scale); if (peak != 0) { - output()->print(", largest_committed=" SIZE_FORMAT "%s", amount_in_current_scale(peak), scale); + output()->print(", peak=" SIZE_FORMAT "%s", amount_in_current_scale(peak), scale); } } @@ -93,9 +93,15 @@ void MemReporterBase::print_malloc(const MemoryCounter* c, MEMFLAGS flag) const } void MemReporterBase::print_virtual_memory(size_t reserved, size_t committed, size_t peak) const { + outputStream* out = output(); const char* scale = current_scale(); - output()->print("(mmap: reserved=" SIZE_FORMAT "%s, committed=" SIZE_FORMAT "%s, largest_committed=" SIZE_FORMAT "%s)", - amount_in_current_scale(reserved), scale, amount_in_current_scale(committed), scale, amount_in_current_scale(peak), scale); + out->print("(mmap: reserved=" SIZE_FORMAT "%s, committed=" SIZE_FORMAT "%s, ", + amount_in_current_scale(reserved), scale, amount_in_current_scale(committed), scale); + if (peak == committed) { + out->print_raw("at peak)"); + } else { + out->print("peak=" SIZE_FORMAT "%s)", amount_in_current_scale(peak), scale); + } } void MemReporterBase::print_malloc_line(const MemoryCounter* c) const { @@ -204,74 +210,79 @@ void MemSummaryReporter::report_summary_of_type(MEMFLAGS flag, committed_amount += _malloc_snapshot->malloc_overhead(); } - if (amount_in_current_scale(reserved_amount) > 0) { - outputStream* out = output(); - const char* scale = current_scale(); - out->print("-%26s (", NMTUtil::flag_to_name(flag)); - print_total(reserved_amount, committed_amount); -#if INCLUDE_CDS - if (flag == mtClassShared) { - size_t read_only_bytes = FileMapInfo::readonly_total(); - output()->print(", readonly=" SIZE_FORMAT "%s", - amount_in_current_scale(read_only_bytes), scale); - } -#endif - out->print_cr(")"); + // Omit printing if the current reserved value as well as all historical peaks (malloc, mmap committed, arena) + // fall below scale threshold + const size_t pk_vm = virtual_memory->peak_size(); + const size_t pk_malloc = malloc_memory->malloc_peak_size(); + const size_t pk_arena = malloc_memory->arena_peak_size(); - 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); - } else if (flag == mtThread) { - if (ThreadStackTracker::track_as_vm()) { - 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: ", " "); - print_total(thread_stack_usage->reserved(), thread_stack_usage->committed(), thread_stack_usage->peak_size()); - } else { - MallocMemory* thread_stack_memory = _malloc_snapshot->by_type(mtThreadStack); - const char* scale = current_scale(); - // report thread count - out->print_cr("%27s (threads #" SIZE_FORMAT ")", " ", thread_stack_memory->malloc_count()); - out->print("%27s (Stack: " SIZE_FORMAT "%s", " ", - amount_in_current_scale(thread_stack_memory->malloc_size()), scale); - } - out->print_cr(")"); - } - - // report malloc'd memory - if (amount_in_current_scale(malloc_memory->malloc_size()) > 0 - || amount_in_current_scale(malloc_memory->malloc_peak_size()) > 0) { - print_malloc_line(malloc_memory->malloc_counter()); - } - - if (amount_in_current_scale(virtual_memory->reserved()) > 0 - DEBUG_ONLY(|| amount_in_current_scale(virtual_memory->peak_size()) > 0)) { - print_virtual_memory_line(virtual_memory->reserved(), virtual_memory->committed(), virtual_memory->peak_size()); - } - - if (amount_in_current_scale(malloc_memory->arena_size()) > 0 - DEBUG_ONLY(|| amount_in_current_scale(malloc_memory->arena_peak_size()) > 0)) { - print_arena_line(malloc_memory->arena_counter()); - } - - 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); - } else if (flag == mtClass) { - // Metadata information - report_metadata(Metaspace::NonClassType); - if (Metaspace::using_class_space()) { - report_metadata(Metaspace::ClassType); - } - } - out->print_cr(" "); + if (amount_in_current_scale(MAX4(reserved_amount, pk_vm, pk_malloc, pk_arena)) == 0) { + return; } + + outputStream* out = output(); + const char* scale = current_scale(); + out->print("-%26s (", NMTUtil::flag_to_name(flag)); + print_total(reserved_amount, committed_amount); +#if INCLUDE_CDS + if (flag == mtClassShared) { + size_t read_only_bytes = FileMapInfo::readonly_total(); + output()->print(", readonly=" SIZE_FORMAT "%s", + amount_in_current_scale(read_only_bytes), scale); + } +#endif + out->print_cr(")"); + + 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); + } else if (flag == mtThread) { + if (ThreadStackTracker::track_as_vm()) { + 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: ", " "); + print_total(thread_stack_usage->reserved(), thread_stack_usage->committed(), thread_stack_usage->peak_size()); + } else { + MallocMemory* thread_stack_memory = _malloc_snapshot->by_type(mtThreadStack); + const char* scale = current_scale(); + // report thread count + out->print_cr("%27s (threads #" SIZE_FORMAT ")", " ", thread_stack_memory->malloc_count()); + out->print("%27s (Stack: " SIZE_FORMAT "%s", " ", + amount_in_current_scale(thread_stack_memory->malloc_size()), scale); + } + 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()); + } + + 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()); + } + + if (amount_in_current_scale(MAX2(malloc_memory->arena_size(), pk_arena)) > 0) { + print_arena_line(malloc_memory->arena_counter()); + } + + 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); + } else if (flag == mtClass) { + // Metadata information + report_metadata(Metaspace::NonClassType); + if (Metaspace::using_class_space()) { + report_metadata(Metaspace::ClassType); + } + } + out->print_cr(" "); } void MemSummaryReporter::report_metadata(Metaspace::MetadataType type) const { @@ -321,9 +332,8 @@ int MemDetailReporter::report_malloc_sites() { const MallocSite* malloc_site; int num_omitted = 0; while ((malloc_site = malloc_itr.next()) != nullptr) { - // Don't report if site has never allocated less than one unit of whatever our scale is - if (scale() > 1 && amount_in_current_scale(malloc_site->size()) == 0 - DEBUG_ONLY(&& amount_in_current_scale(malloc_site->peak_size()) == 0)) { + // Omit printing if the current value and the historic peak value both fall below the reporting scale threshold + if (amount_in_current_scale(MAX2(malloc_site->size(), malloc_site->peak_size())) == 0) { num_omitted ++; continue; } @@ -353,8 +363,10 @@ int MemDetailReporter::report_virtual_memory_allocation_sites() { if (virtual_memory_site->reserved() == 0) { continue; } - // Don't report if site has reserved less than one unit of whatever our scale is - if (scale() > 1 && amount_in_current_scale(virtual_memory_site->reserved()) == 0) { + // Omit printing if the current value and the historic peak value both fall below the + // reporting scale threshold + if (amount_in_current_scale(MAX2(virtual_memory_site->reserved(), + virtual_memory_site->peak_size())) == 0) { num_omitted++; continue; } @@ -386,7 +398,16 @@ void MemDetailReporter::report_virtual_memory_map() { void MemDetailReporter::report_virtual_memory_region(const ReservedMemoryRegion* reserved_rgn) { assert(reserved_rgn != nullptr, "null pointer"); - // Don't report if size is too small + // We don't bother about reporting peaks here. + // That is because peaks - in the context of virtual memory, peak of committed areas - make little sense + // when we report *by region*, which are identified by their location in memory. There is a philosophical + // question about identity here: e.g. a committed region that has been split into three regions by + // uncommitting a middle section of it, should that still count as "having peaked" before the split? If + // yes, which of the three new regions would be the spiritual successor? Rather than introducing more + // complexity, we avoid printing peaks altogether. Note that peaks should still be printed when reporting + // usage *by callsite*. + + // Don't report if size is too small. if (amount_in_current_scale(reserved_rgn->size()) == 0) return; outputStream* out = output(); diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.cpp b/src/hotspot/share/nmt/virtualMemoryTracker.cpp index 65bd9afa2d6..5bb90726660 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.cpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.cpp @@ -34,7 +34,6 @@ size_t VirtualMemorySummary::_snapshot[CALC_OBJ_SIZE_IN_TYPE(VirtualMemorySnapshot, size_t)]; -#ifdef ASSERT void VirtualMemory::update_peak(size_t size) { size_t peak_sz = peak_size(); while (peak_sz < size) { @@ -46,7 +45,6 @@ void VirtualMemory::update_peak(size_t size) { } } } -#endif // ASSERT void VirtualMemorySummary::initialize() { assert(sizeof(_snapshot) >= sizeof(VirtualMemorySnapshot), "Sanity Check"); diff --git a/src/hotspot/share/nmt/virtualMemoryTracker.hpp b/src/hotspot/share/nmt/virtualMemoryTracker.hpp index b6f89c278ce..06ca960febe 100644 --- a/src/hotspot/share/nmt/virtualMemoryTracker.hpp +++ b/src/hotspot/share/nmt/virtualMemoryTracker.hpp @@ -42,21 +42,17 @@ class VirtualMemory { size_t _reserved; size_t _committed; -#ifdef ASSERT volatile size_t _peak_size; void update_peak(size_t size); -#endif // ASSERT public: - VirtualMemory() : _reserved(0), _committed(0) { - DEBUG_ONLY(_peak_size = 0;) - } + VirtualMemory() : _reserved(0), _committed(0), _peak_size(0) {} inline void reserve_memory(size_t sz) { _reserved += sz; } inline void commit_memory (size_t sz) { _committed += sz; - DEBUG_ONLY(update_peak(sz);) assert(_committed <= _reserved, "Sanity check"); + update_peak(_committed); } inline void release_memory (size_t sz) { @@ -72,7 +68,7 @@ class VirtualMemory { inline size_t reserved() const { return _reserved; } inline size_t committed() const { return _committed; } inline size_t peak_size() const { - return DEBUG_ONLY(Atomic::load(&_peak_size)) NOT_DEBUG(0); + return Atomic::load(&_peak_size); } }; @@ -85,10 +81,9 @@ class VirtualMemoryAllocationSite : public AllocationSite { inline void reserve_memory(size_t sz) { _c.reserve_memory(sz); } inline void commit_memory (size_t sz) { _c.commit_memory(sz); } - inline void uncommit_memory(size_t sz) { _c.uncommit_memory(sz); } - inline void release_memory(size_t sz) { _c.release_memory(sz); } inline size_t reserved() const { return _c.reserved(); } inline size_t committed() const { return _c.committed(); } + inline size_t peak_size() const { return _c.peak_size(); } }; class VirtualMemorySummary; diff --git a/test/hotspot/jtreg/runtime/NMT/HugeArenaTracking.java b/test/hotspot/jtreg/runtime/NMT/HugeArenaTracking.java index e5a38114f6c..adccbd01170 100644 --- a/test/hotspot/jtreg/runtime/NMT/HugeArenaTracking.java +++ b/test/hotspot/jtreg/runtime/NMT/HugeArenaTracking.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2019, 2020, Red Hat, Inc. All rights reserved. + * Copyright (c) 2019, 2023, Red Hat, 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 @@ -31,59 +31,81 @@ * java.management * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail HugeArenaTracking + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=summary HugeArenaTracking */ import java.util.Random; import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; import jdk.test.lib.Utils; import jdk.test.whitebox.WhiteBox; public class HugeArenaTracking { - private static final long GB = 1024 * 1024 * 1024; + private static final long MB = 1024 * 1024; + private static final long GB = MB * 1024; public static void main(String args[]) throws Exception { - OutputAnalyzer output; final WhiteBox wb = WhiteBox.getWhiteBox(); - // Grab my own PID - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); - long arena1 = wb.NMTNewArena(1024); long arena2 = wb.NMTNewArena(1024); - // Run 'jcmd VM.native_memory summary' - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary"}); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=2KB, committed=2KB)"); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=K" }, + new String[] { "Test (reserved=2KB, committed=2KB)", + "(arena=2KB #2) (at peak)" }); Random rand = Utils.getRandomInstance(); // Allocate 2GB+ from arena long total = 0; while (total < 2 * GB) { - // Cap to 10M - long inc = rand.nextInt(10 * 1024 * 1024); - wb.NMTArenaMalloc(arena1, inc); - total += inc; + wb.NMTArenaMalloc(arena1, MB); + total += MB; } - ProcessBuilder pb2 = new ProcessBuilder(); - // Run 'jcmd VM.native_memory summary' - pb2.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary", "scale=GB"}); - output = new OutputAnalyzer(pb2.start()); - output.shouldContain("Test (reserved=2GB, committed=2GB)"); + // run a report at GB level. We should see our allocations; since they are rounded + // to GB, we expect an exact output match + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=G" }, + new String[] { "Test (reserved=2GB, committed=2GB)", + "(arena=2GB #2) (at peak)" }); + + // Repeat at MB level; we expect the same behavior + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=M" }, + new String[] { "Test (reserved=2048MB, committed=2048MB)", + "(arena=2048MB #2) (at peak)" }); wb.NMTFreeArena(arena1); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=1KB, committed=1KB)"); + // Repeat report at GB level. Reserved should be 0 now. Current usage is 1KB, since arena2 is left, but that + // is below GB scale threshold, so should show up as 0. + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=G" }, + new String[] { "Test (reserved=0GB, committed=0GB)", + "(arena=0GB #1) (peak=2GB #2)" }); + + // Same, for MB scale + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=M" }, + new String[] { "Test (reserved=0MB, committed=0MB)", + "(arena=0MB #1) (peak=2048MB #2)" }); + + // At KB level we should see the remaining 1KB. Note that we refrain from testing peak here + // since the number gets fuzzy: it depends on the size of the initially allocated chunk. At MB + // and GB scale, these differences don't matter. + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=K" }, + new String[] { "Test (reserved=1KB, committed=1KB)", + "(arena=1KB #1) (peak=" }); + wb.NMTFreeArena(arena2); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved"); + // Everything free'd, current usage 0, peak should be preserved. + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[] { "scale=G" }, + new String[] { "Test (reserved=0GB, committed=0GB)", + "(arena=0GB #0) (peak=2GB #2)" }); } } diff --git a/test/hotspot/jtreg/runtime/NMT/MallocRoundingReportTest.java b/test/hotspot/jtreg/runtime/NMT/MallocRoundingReportTest.java index 653f8354e65..30a03f973bf 100644 --- a/test/hotspot/jtreg/runtime/NMT/MallocRoundingReportTest.java +++ b/test/hotspot/jtreg/runtime/NMT/MallocRoundingReportTest.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Red Hat, 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 @@ -33,23 +34,14 @@ * */ -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; - import jdk.test.whitebox.WhiteBox; public class MallocRoundingReportTest { private static long K = 1024; public static void main(String args[]) throws Exception { - OutputAnalyzer output; WhiteBox wb = WhiteBox.getWhiteBox(); - // Grab my own PID - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); - long[] additionalBytes = {0, 1, 512, 650}; long[] kByteSize = {1024, 2048}; long mallocd_total = 0; @@ -63,17 +55,18 @@ public class MallocRoundingReportTest { mallocd_total = wb.NMTMalloc(curKB); // Run 'jcmd VM.native_memory summary', check for expected output // NMT does not track memory allocations less than 1KB, and rounds to the nearest KB - String expectedOut = ("Test (reserved=" + numKB + "KB, committed=" + numKB + "KB)"); - - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary" }); - output = new OutputAnalyzer(pb.start()); - output.shouldContain(expectedOut); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + "Test (reserved=" + numKB + "KB, committed=" + numKB + "KB)", + "(malloc=" + numKB + "KB #1) (at peak)" + ); wb.NMTFree(mallocd_total); + // Run 'jcmd VM.native_memory summary', check for expected output - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary" }); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + "Test (reserved=0KB, committed=0KB)", + "(malloc=0KB) (peak=" + numKB + "KB #1)" + ); } } } diff --git a/test/hotspot/jtreg/runtime/NMT/MallocStressTest.java b/test/hotspot/jtreg/runtime/NMT/MallocStressTest.java index 55531a68b96..6af14268c7e 100644 --- a/test/hotspot/jtreg/runtime/NMT/MallocStressTest.java +++ b/test/hotspot/jtreg/runtime/NMT/MallocStressTest.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Red Hat, 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 @@ -130,7 +131,7 @@ public class MallocStressTest { // All test memory allocated should be released output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + output.shouldContain("Test (reserved=0KB, committed=0KB)"); // Verify that tracking level has not been downgraded pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "statistics"}); diff --git a/test/hotspot/jtreg/runtime/NMT/MallocTestType.java b/test/hotspot/jtreg/runtime/NMT/MallocTestType.java index e091ab0f7a7..67df864e7b4 100644 --- a/test/hotspot/jtreg/runtime/NMT/MallocTestType.java +++ b/test/hotspot/jtreg/runtime/NMT/MallocTestType.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Red Hat, 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 @@ -32,37 +33,34 @@ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail MallocTestType */ -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; import jdk.test.whitebox.WhiteBox; public class MallocTestType { public static void main(String args[]) throws Exception { - OutputAnalyzer output; WhiteBox wb = WhiteBox.getWhiteBox(); - // Grab my own PID - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); - // Use WB API to alloc and free with the mtTest type - long memAlloc3 = wb.NMTMalloc(128 * 1024); - long memAlloc2 = wb.NMTMalloc(256 * 1024); - wb.NMTFree(memAlloc3); - long memAlloc1 = wb.NMTMalloc(512 * 1024); - wb.NMTFree(memAlloc2); + long memAlloc3 = wb.NMTMalloc(128 * 1024); // current +128K #1 peak +128K #1 + long memAlloc2 = wb.NMTMalloc(256 * 1024); // current +384K #2 peak +384K #2 - // Run 'jcmd VM.native_memory summary' - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary"}); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=512KB, committed=512KB)"); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[]{"Test (reserved=384KB, committed=384KB)", + "(malloc=384KB #2) (at peak)"}); + + wb.NMTFree(memAlloc3); // current +256K #1 peak +384K #2 + long memAlloc1 = wb.NMTMalloc(512 * 1024); // current +768K #2 peak +768K #2 + wb.NMTFree(memAlloc2); // current +512K #1 peak +768K #2 + + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[]{"Test (reserved=512KB, committed=512KB)", + "(malloc=512KB #1) (peak=768KB #2)"}); // Free the memory allocated by NMTAllocTest - wb.NMTFree(memAlloc1); + wb.NMTFree(memAlloc1); // current 0K #0 peak +768K #2 - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + new String[]{"Test (reserved=0KB, committed=0KB)", + "(malloc=0KB) (peak=768KB #2)"}); } } diff --git a/test/hotspot/jtreg/runtime/NMT/MallocTrackingVerify.java b/test/hotspot/jtreg/runtime/NMT/MallocTrackingVerify.java index f89f15a764e..0c08e07fd25 100644 --- a/test/hotspot/jtreg/runtime/NMT/MallocTrackingVerify.java +++ b/test/hotspot/jtreg/runtime/NMT/MallocTrackingVerify.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Red Hat, 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 @@ -31,7 +32,7 @@ * java.management * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail MallocTrackingVerify + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=summary MallocTrackingVerify * */ @@ -53,11 +54,6 @@ public class MallocTrackingVerify { public static WhiteBox wb = WhiteBox.getWhiteBox(); public static void main(String args[]) throws Exception { - OutputAnalyzer output; - - // Grab my own PID - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); Random random = Utils.getRandomInstance(); // Allocate small amounts of memory with random pseudo call stack @@ -74,9 +70,10 @@ public class MallocTrackingVerify { } } - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary" }); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4KB, committed=4KB)"); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + "Test (reserved=4KB, committed=4KB)", + "(malloc=4KB #" + mallocd_memory.size() + ") (at peak)" + ); // Free for (MallocMemory mem : mallocd_memory) { @@ -84,10 +81,11 @@ public class MallocTrackingVerify { } // Run 'jcmd VM.native_memory summary', check for expected output - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, - "VM.native_memory", "summary" }); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + "Test (reserved=0KB, committed=0KB)", + "(malloc=0KB) (peak=4KB #" + + mallocd_memory.size() + ")" + ); + } static class MallocMemory { diff --git a/test/hotspot/jtreg/runtime/NMT/NMTTestUtils.java b/test/hotspot/jtreg/runtime/NMT/NMTTestUtils.java new file mode 100644 index 00000000000..6cb0d727328 --- /dev/null +++ b/test/hotspot/jtreg/runtime/NMT/NMTTestUtils.java @@ -0,0 +1,70 @@ +/* + * Copyright (c) 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023 Red Hat, 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 Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import jdk.test.lib.JDKToolFinder; +import jdk.test.lib.process.OutputAnalyzer; +import jdk.test.lib.process.ProcessTools; + +public class NMTTestUtils { + + public static OutputAnalyzer startJcmdVMNativeMemory(String... additional_args) throws Exception { + if (additional_args == null) { + additional_args = new String[] {}; + } + String fullargs[] = new String[3 + additional_args.length]; + fullargs[0] = JDKToolFinder.getJDKTool("jcmd"); + fullargs[1] = Long.toString(ProcessTools.getProcessId()); + fullargs[2] = "VM.native_memory"; + System.arraycopy(additional_args, 0, fullargs, 3, additional_args.length); + ProcessBuilder pb = new ProcessBuilder(); + pb.command(fullargs); + OutputAnalyzer output = new OutputAnalyzer(pb.start()); + return output; + } + + public static OutputAnalyzer startJcmdVMNativeMemoryDetail(String... additional_args) throws Exception { + return startJcmdVMNativeMemory("detail"); + } + + public static void runJcmdSummaryReportAndCheckOutput(String[] additional_args, String[] pattern, boolean verbose) throws Exception { + OutputAnalyzer output = startJcmdVMNativeMemory(additional_args); + output.stdoutShouldContainMultiLinePattern(pattern, true); + } + + public static void runJcmdSummaryReportAndCheckOutput(String[] additional_args, String[] pattern) throws Exception { + runJcmdSummaryReportAndCheckOutput(additional_args, pattern, true); + } + + public static void runJcmdSummaryReportAndCheckOutput(String... pattern) throws Exception { + runJcmdSummaryReportAndCheckOutput(null, pattern, true); + } + + public static void checkReservedCommittedSummary(OutputAnalyzer output, long reservedKB, long committedKB, long peakKB) { + String peakString = (committedKB == peakKB) ? "at peak" : "peak=" + peakKB + "KB"; + output.stdoutShouldContainMultiLinePattern( + "Test (reserved=" + reservedKB + "KB, committed=" + committedKB + "KB)", + "(mmap: reserved=" + reservedKB + "KB, committed=" + committedKB + "KB, " + peakString + ")" + ); + } +} diff --git a/test/hotspot/jtreg/runtime/NMT/ThreadedMallocTestType.java b/test/hotspot/jtreg/runtime/NMT/ThreadedMallocTestType.java index 2b1faf9fb43..290984c4185 100644 --- a/test/hotspot/jtreg/runtime/NMT/ThreadedMallocTestType.java +++ b/test/hotspot/jtreg/runtime/NMT/ThreadedMallocTestType.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Red Hat, 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 @@ -28,12 +29,9 @@ * java.management * @build jdk.test.whitebox.WhiteBox * @run driver jdk.test.lib.helpers.ClassFileInstaller jdk.test.whitebox.WhiteBox - * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail ThreadedMallocTestType + * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=summary ThreadedMallocTestType */ -import jdk.test.lib.process.ProcessTools; -import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; import jdk.test.whitebox.WhiteBox; public class ThreadedMallocTestType { @@ -42,13 +40,8 @@ public class ThreadedMallocTestType { public static long memAlloc3; public static void main(String args[]) throws Exception { - OutputAnalyzer output; final WhiteBox wb = WhiteBox.getWhiteBox(); - // Grab my own PID - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); - Thread allocThread = new Thread() { public void run() { // Alloc memory using the WB api @@ -66,9 +59,10 @@ public class ThreadedMallocTestType { System.out.println("memAlloc3:"+memAlloc3); // Run 'jcmd VM.native_memory summary' - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "summary"}); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=896KB, committed=896KB)"); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + "Test (reserved=896KB, committed=896KB)", + "(malloc=896KB #3) (at peak)" + ); Thread freeThread = new Thread() { public void run() { @@ -82,7 +76,9 @@ public class ThreadedMallocTestType { freeThread.start(); freeThread.join(); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + NMTTestUtils.runJcmdSummaryReportAndCheckOutput( + "Test (reserved=0KB, committed=0KB)", + "(malloc=0KB) (peak=896KB #3)" + ); } } diff --git a/test/hotspot/jtreg/runtime/NMT/ThreadedVirtualAllocTestType.java b/test/hotspot/jtreg/runtime/NMT/ThreadedVirtualAllocTestType.java index 4bc6ffc90cc..60a493ea728 100644 --- a/test/hotspot/jtreg/runtime/NMT/ThreadedVirtualAllocTestType.java +++ b/test/hotspot/jtreg/runtime/NMT/ThreadedVirtualAllocTestType.java @@ -1,5 +1,6 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2023, Red Hat, 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 @@ -31,9 +32,7 @@ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail ThreadedVirtualAllocTestType */ -import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; import jdk.test.whitebox.WhiteBox; public class ThreadedVirtualAllocTestType { @@ -45,8 +44,6 @@ public class ThreadedVirtualAllocTestType { public static void main(String args[]) throws Exception { OutputAnalyzer output; - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); Thread reserveThread = new Thread() { public void run() { @@ -56,9 +53,8 @@ public class ThreadedVirtualAllocTestType { reserveThread.start(); reserveThread.join(); - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "detail"}); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=512KB, committed=0KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output,512, 0); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 512KB for Test"); Thread commitThread = new Thread() { @@ -69,8 +65,8 @@ public class ThreadedVirtualAllocTestType { commitThread.start(); commitThread.join(); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=512KB, committed=128KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output,512, 128); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + commitSize) + "\\] committed 128KB"); Thread uncommitThread = new Thread() { @@ -81,7 +77,8 @@ public class ThreadedVirtualAllocTestType { uncommitThread.start(); uncommitThread.join(); - output = new OutputAnalyzer(pb.start()); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output,512, 0); output.shouldContain("Test (reserved=512KB, committed=0KB)"); output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + commitSize) + "\\] committed"); @@ -93,9 +90,18 @@ public class ThreadedVirtualAllocTestType { releaseThread.start(); releaseThread.join(); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output,0, 0); output.shouldNotContain("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved"); } + static long peakKB = 0; + + public static void checkReservedCommittedSummary(OutputAnalyzer output, long reservedKB, long committedKB) { + if (committedKB > peakKB) { + peakKB = committedKB; + } + NMTTestUtils.checkReservedCommittedSummary(output, reservedKB, committedKB, peakKB); + } + } diff --git a/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitMerge.java b/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitMerge.java index 9cc8c39d897..4d691eb920a 100644 --- a/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitMerge.java +++ b/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitMerge.java @@ -36,9 +36,7 @@ * */ -import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; import jdk.test.lib.Platform; import jdk.test.whitebox.WhiteBox; @@ -53,16 +51,10 @@ public class VirtualAllocCommitMerge { long reserveSize = 4 * 1024 * 1024; // 4096KB long addr; - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); - // reserve addr = wb.NMTReserveMemory(reserveSize); - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, - "VM.native_memory", "detail" }); - - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemory("detail"); + checkReservedCommittedSummary(output, 4096, 0); checkReserved(output, addr, reserveSize, "4096KB"); long addrA = addr + (0 * commitSize); @@ -75,8 +67,8 @@ public class VirtualAllocCommitMerge { // commit overlapping ABC, A, B, C wb.NMTCommitMemory(addrA, 3 * commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -84,8 +76,8 @@ public class VirtualAllocCommitMerge { wb.NMTCommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -93,16 +85,16 @@ public class VirtualAllocCommitMerge { wb.NMTCommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); wb.NMTCommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -110,8 +102,8 @@ public class VirtualAllocCommitMerge { // uncommit wb.NMTUncommitMemory(addrA, 3 * commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } // Test discontigous areas @@ -121,8 +113,8 @@ public class VirtualAllocCommitMerge { wb.NMTCommitMemory(addrC, commitSize); wb.NMTCommitMemory(addrE, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, commitSize, "128KB"); @@ -134,8 +126,8 @@ public class VirtualAllocCommitMerge { wb.NMTUncommitMemory(addrC, commitSize); wb.NMTUncommitMemory(addrE, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } // Test contiguous areas @@ -144,8 +136,8 @@ public class VirtualAllocCommitMerge { wb.NMTCommitMemory(addrA, commitSize); wb.NMTCommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "256KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 256); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 2 * commitSize, "256KB"); @@ -154,8 +146,8 @@ public class VirtualAllocCommitMerge { wb.NMTUncommitMemory(addrA, commitSize); wb.NMTUncommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -163,8 +155,8 @@ public class VirtualAllocCommitMerge { wb.NMTCommitMemory(addrB, commitSize); wb.NMTCommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "256KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 256); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 2 * commitSize, "256KB"); @@ -173,8 +165,8 @@ public class VirtualAllocCommitMerge { wb.NMTUncommitMemory(addrB, commitSize); wb.NMTUncommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -183,8 +175,8 @@ public class VirtualAllocCommitMerge { wb.NMTCommitMemory(addrB, commitSize); wb.NMTCommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -194,8 +186,8 @@ public class VirtualAllocCommitMerge { wb.NMTUncommitMemory(addrB, commitSize); wb.NMTUncommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -204,8 +196,8 @@ public class VirtualAllocCommitMerge { wb.NMTCommitMemory(addrC, commitSize); wb.NMTCommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -215,8 +207,8 @@ public class VirtualAllocCommitMerge { wb.NMTUncommitMemory(addrC, commitSize); wb.NMTUncommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -225,8 +217,8 @@ public class VirtualAllocCommitMerge { wb.NMTCommitMemory(addrA, commitSize); wb.NMTCommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -236,8 +228,8 @@ public class VirtualAllocCommitMerge { wb.NMTUncommitMemory(addrA, commitSize); wb.NMTUncommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -246,8 +238,8 @@ public class VirtualAllocCommitMerge { wb.NMTCommitMemory(addrC, commitSize); wb.NMTCommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -257,8 +249,8 @@ public class VirtualAllocCommitMerge { wb.NMTUncommitMemory(addrC, commitSize); wb.NMTUncommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -267,8 +259,8 @@ public class VirtualAllocCommitMerge { wb.NMTCommitMemory(addrA, commitSize); wb.NMTCommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -278,8 +270,8 @@ public class VirtualAllocCommitMerge { wb.NMTUncommitMemory(addrA, commitSize); wb.NMTUncommitMemory(addrB, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } { @@ -288,8 +280,8 @@ public class VirtualAllocCommitMerge { wb.NMTCommitMemory(addrB, commitSize); wb.NMTCommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "384KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); checkReserved(output, addr, reserveSize, "4096KB"); checkCommitted(output, addrA, 3 * commitSize, "384KB"); @@ -299,20 +291,26 @@ public class VirtualAllocCommitMerge { wb.NMTUncommitMemory(addrB, commitSize); wb.NMTUncommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - checkReservedCommittedSummary(output, "4096KB", "0KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); } // release wb.NMTReleaseMemory(addr, reserveSize); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 0, 0); output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); } - public static void checkReservedCommittedSummary(OutputAnalyzer output, String reservedString, String committedString) { - output.shouldContain("Test (reserved=" + reservedString + ", committed=" + committedString + ")"); + // running peak counter + static long peakKB = 0; + + public static void checkReservedCommittedSummary(OutputAnalyzer output, long reservedKB, long committedKB) { + if (committedKB > peakKB) { + peakKB = committedKB; + } + NMTTestUtils.checkReservedCommittedSummary(output, reservedKB, committedKB, peakKB); } public static void checkReserved(OutputAnalyzer output, long addr, long size, String sizeString) { diff --git a/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitUncommitRecommit.java b/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitUncommitRecommit.java index bfa4de6e0c2..ef6ee6f8838 100644 --- a/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitUncommitRecommit.java +++ b/test/hotspot/jtreg/runtime/NMT/VirtualAllocCommitUncommitRecommit.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2014, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2014, 2023, 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 @@ -33,10 +33,7 @@ * */ -import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; - import jdk.test.whitebox.WhiteBox; public class VirtualAllocCommitUncommitRecommit { @@ -49,16 +46,10 @@ public class VirtualAllocCommitUncommitRecommit { long reserveSize = 4 * 1024 * 1024; // 4096KB long addr; - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); - // reserve addr = wb.NMTReserveMemory(reserveSize); - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, - "VM.native_memory", "detail" }); - - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=0KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); @@ -76,8 +67,8 @@ public class VirtualAllocCommitUncommitRecommit { wb.NMTCommitMemory(addrC, commitSize); wb.NMTCommitMemory(addrD, commitSize); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=512KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 512); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) @@ -86,9 +77,8 @@ public class VirtualAllocCommitUncommitRecommit { wb.NMTUncommitMemory(addrB, commitSize); wb.NMTUncommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=256KB)"); - + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 256); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); @@ -97,8 +87,8 @@ public class VirtualAllocCommitUncommitRecommit { wb.NMTCommitMemory(addrE, commitSize); wb.NMTCommitMemory(addrF, commitSize); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=512KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 512); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); @@ -106,8 +96,8 @@ public class VirtualAllocCommitUncommitRecommit { // uncommit A wb.NMTUncommitMemory(addrA, commitSize); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=384KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 384); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); @@ -117,8 +107,8 @@ public class VirtualAllocCommitUncommitRecommit { wb.NMTCommitMemory(addrB, commitSize); wb.NMTCommitMemory(addrC, commitSize); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=768KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 768); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); @@ -131,17 +121,27 @@ public class VirtualAllocCommitUncommitRecommit { wb.NMTUncommitMemory(addrE, commitSize); wb.NMTUncommitMemory(addrF, commitSize); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=4096KB, committed=0KB)"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 4096, 0); output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); // release wb.NMTReleaseMemory(addr, reserveSize); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 0, 0); output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 4096KB for Test"); } + + // running peak counter + static long peakKB = 0; + + public static void checkReservedCommittedSummary(OutputAnalyzer output, long reservedKB, long committedKB) { + if (committedKB > peakKB) { + peakKB = committedKB; + } + NMTTestUtils.checkReservedCommittedSummary(output, reservedKB, committedKB, peakKB); + } } diff --git a/test/hotspot/jtreg/runtime/NMT/VirtualAllocTestType.java b/test/hotspot/jtreg/runtime/NMT/VirtualAllocTestType.java index 081ba6aafb6..f4321e80b22 100644 --- a/test/hotspot/jtreg/runtime/NMT/VirtualAllocTestType.java +++ b/test/hotspot/jtreg/runtime/NMT/VirtualAllocTestType.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2013, 2022, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2013, 2023, 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 @@ -32,9 +32,7 @@ * @run main/othervm -Xbootclasspath/a:. -XX:+UnlockDiagnosticVMOptions -XX:+WhiteBoxAPI -XX:NativeMemoryTracking=detail VirtualAllocTestType */ -import jdk.test.lib.process.ProcessTools; import jdk.test.lib.process.OutputAnalyzer; -import jdk.test.lib.JDKToolFinder; import jdk.test.whitebox.WhiteBox; public class VirtualAllocTestType { @@ -44,36 +42,120 @@ public class VirtualAllocTestType { OutputAnalyzer output; long commitSize = 128 * 1024; long reserveSize = 256 * 1024; - long addr; + long addr1, addr2; - String pid = Long.toString(ProcessTools.getProcessId()); - ProcessBuilder pb = new ProcessBuilder(); + String info = "start"; - addr = wb.NMTReserveMemory(reserveSize); - pb.command(new String[] { JDKToolFinder.getJDKTool("jcmd"), pid, "VM.native_memory", "detail"}); + try { + // ------ + // Reserve first mapping + addr1 = wb.NMTReserveMemory(reserveSize); + info = "reserve 1: addr1=" + addr1; - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=256KB, committed=0KB)"); - output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved 256KB for Test"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 256, 0); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); - wb.NMTCommitMemory(addr, commitSize); + // ------ + // Reserve second mapping + addr2 = wb.NMTReserveMemory(reserveSize); + info = "reserve 2: addr2=" + addr2; + // If the second mapping happens to be adjacent to the first mapping, reserve another mapping and release the second mapping; for + // this test, we want to see two disjunct mappings. + if (addr2 == addr1 + reserveSize) { + long tmp = wb.NMTReserveMemory(reserveSize); + wb.NMTReleaseMemory(addr2, reserveSize); + addr2 = tmp; + } - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=256KB, committed=128KB)"); - output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + commitSize) + "\\] committed 128KB"); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 512, 0); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); - wb.NMTUncommitMemory(addr, commitSize); + // ------ + // Now commit the first mapping + wb.NMTCommitMemory(addr1, commitSize); + info = "commit 1"; + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 512, 128); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + commitSize) + "\\] committed 128KB"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); - output = new OutputAnalyzer(pb.start()); - output.shouldContain("Test (reserved=256KB, committed=0KB)"); - output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + commitSize) + "\\] committed"); + // ------ + // Now commit the second mapping + wb.NMTCommitMemory(addr2, commitSize); + info = "commit 2"; - wb.NMTReleaseMemory(addr, reserveSize); + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 512, 256); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + commitSize) + "\\] committed 128KB"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + commitSize) + "\\] committed 128KB"); - output = new OutputAnalyzer(pb.start()); - output.shouldNotContain("Test (reserved="); - output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr) + " - 0x[0]*" + Long.toHexString(addr + reserveSize) + "\\] reserved"); + // ------ + // Now uncommit the second mapping + wb.NMTUncommitMemory(addr2, commitSize); + info = "uncommit 2"; + + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 512, 128); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + commitSize) + "\\] committed 128KB"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + commitSize) + "\\] committed 128KB"); + + // ------ + // Now uncommit the first mapping + wb.NMTUncommitMemory(addr1, commitSize); + info = "uncommit 1"; + + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 512, 0); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + commitSize) + "\\] committed 128KB"); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + commitSize) + "\\] committed 128KB"); + + // ---------- + // Release second mapping + wb.NMTReleaseMemory(addr2, reserveSize); + info = "release 2"; + + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 256, 0); + output.shouldMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + commitSize) + "\\] committed 128KB"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + commitSize) + "\\] committed 128KB"); + + // ---------- + // Release first mapping + wb.NMTReleaseMemory(addr1, reserveSize); + info = "release 1"; + + output = NMTTestUtils.startJcmdVMNativeMemoryDetail(); + checkReservedCommittedSummary(output, 0, 0); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr1) + " - 0x[0]*" + Long.toHexString(addr1 + commitSize) + "\\] committed 128KB"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + reserveSize) + "\\] reserved 256KB for Test"); + output.shouldNotMatch("\\[0x[0]*" + Long.toHexString(addr2) + " - 0x[0]*" + Long.toHexString(addr2 + commitSize) + "\\] committed 128KB"); + + } catch (Exception e) { + throw new RuntimeException(e.getMessage() + " (" + info + ")"); + } + } + + static long peakKB = 0; + + public static void checkReservedCommittedSummary(OutputAnalyzer output, long reservedKB, long committedKB) { + if (committedKB > peakKB) { + peakKB = committedKB; + } + NMTTestUtils.checkReservedCommittedSummary(output, reservedKB, committedKB, peakKB); } } diff --git a/test/lib/jdk/test/lib/process/OutputAnalyzer.java b/test/lib/jdk/test/lib/process/OutputAnalyzer.java index fab7b99196d..ae04d9d1bc4 100644 --- a/test/lib/jdk/test/lib/process/OutputAnalyzer.java +++ b/test/lib/jdk/test/lib/process/OutputAnalyzer.java @@ -621,6 +621,14 @@ public final class OutputAnalyzer { return asLines(getOutput()); } + public List stdoutAsLines() { + return asLines(getStdout()); + } + + public List stderrAsLines() { + return asLines(getStderr()); + } + private List asLines(String buffer) { return Arrays.asList(buffer.split("\\R")); } @@ -786,4 +794,72 @@ public final class OutputAnalyzer { return -1; } + private void searchLinesForMultiLinePattern(String[] haystack, String[] needles, boolean verbose) { + + if (needles.length == 0) { + return; + } + + int firstNeedlePos = 0; + for (int i = 0; i < haystack.length; i++) { + if (verbose) { + System.out.println("" + i + ":" + haystack[i]); + } + if (haystack[i].contains(needles[0])) { + if (verbose) { + System.out.println("Matches pattern 0 (\"" + needles[0] + "\")"); + } + firstNeedlePos = i; + break; + } + } + + for (int i = 1; i < needles.length; i++) { + int haystackPos = firstNeedlePos + i; + if (haystackPos < haystack.length) { + if (verbose) { + System.out.println("" + haystackPos + ":" + haystack[haystackPos]); + } + if (haystack[haystackPos].contains(needles[i])) { + if (verbose) { + System.out.println("Matches pattern " + i + "(\"" + needles[i] + "\")"); + } + } else { + String err = "First unmatched pattern: " + i + " (\"" + needles[i] + "\")"; + if (!verbose) { // don't print twice + reportDiagnosticSummary(); + } + throw new RuntimeException(err); + } + } + } + } + + public void stdoutShouldContainMultiLinePattern(String[] needles, boolean verbose) { + String [] stdoutLines = stdoutAsLines().toArray(new String[0]); + searchLinesForMultiLinePattern(stdoutLines, needles, verbose); + } + + public void stdoutShouldContainMultiLinePattern(String... needles) { + stdoutShouldContainMultiLinePattern(needles, true); + } + + public void stderrShouldContainMultiLinePattern(String[] needles, boolean verbose) { + String [] stderrLines = stdoutAsLines().toArray(new String[0]); + searchLinesForMultiLinePattern(stderrLines, needles, verbose); + } + + public void stderrShouldContainMultiLinePattern(String... needles) { + stderrShouldContainMultiLinePattern(needles, true); + } + + public void shouldContainMultiLinePattern(String[] needles, boolean verbose) { + String [] lines = asLines().toArray(new String[0]); + searchLinesForMultiLinePattern(lines, needles, verbose); + } + + public void shouldContainMultiLinePattern(String... needles) { + shouldContainMultiLinePattern(needles, true); + } + }