1 /* 2 * Copyright (c) 2018, Oracle and/or its affiliates. All rights reserved. 3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. 4 * 5 * This code is free software; you can redistribute it and/or modify it 6 * under the terms of the GNU General Public License version 2 only, as 7 * published by the Free Software Foundation. 8 * 9 * This code is distributed in the hope that it will be useful, but WITHOUT 10 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 11 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 12 * version 2 for more details (a copy is included in the LICENSE file that 13 * accompanied this code). 14 * 15 * You should have received a copy of the GNU General Public License version 16 * 2 along with this work; if not, write to the Free Software Foundation, 17 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. 18 * 19 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA 20 * or visit www.oracle.com if you need additional information or have any 21 * questions. 22 */ 23 24 #include "precompiled.hpp" 25 26 // Included early because the NMT flags don't include it. 27 #include "utilities/macros.hpp" 28 29 #include "runtime/thread.hpp" 30 #include "services/memTracker.hpp" 31 #include "services/virtualMemoryTracker.hpp" 32 #include "utilities/globalDefinitions.hpp" 33 #include "unittest.hpp" 34 35 36 class ThreadStackTrackingTest { 37 public: 38 static void test() { 39 Thread* thr = Thread::current(); 40 address stack_end = thr->stack_end(); 41 size_t stack_size = thr->stack_size(); 42 43 MemTracker::record_thread_stack(stack_end, stack_size); 44 45 VirtualMemoryTracker::add_reserved_region(stack_end, stack_size, CALLER_PC, mtThreadStack); 46 47 // snapshot current stack usage 48 VirtualMemoryTracker::snapshot_thread_stacks(); 49 50 ReservedMemoryRegion* rmr = VirtualMemoryTracker::_reserved_regions->find(ReservedMemoryRegion(stack_end, stack_size)); 51 ASSERT_TRUE(rmr != NULL); 52 53 ASSERT_EQ(rmr->base(), stack_end); 54 ASSERT_EQ(rmr->size(), stack_size); 55 56 CommittedRegionIterator iter = rmr->iterate_committed_regions(); 57 int i = 0; 58 address i_addr = (address)&i; 59 bool found_i_addr = false; 60 61 // stack grows downward 62 address stack_top = stack_end + stack_size; 63 bool found_stack_top = false; 64 65 for (const CommittedMemoryRegion* region = iter.next(); region != NULL; region = iter.next()) { 66 if (region->base() + region->size() == stack_top) { 67 ASSERT_TRUE(region->size() <= stack_size); 68 found_stack_top = true; 69 } 70 71 if(i_addr < stack_top && i_addr >= region->base()) { 72 found_i_addr = true; 73 } 74 75 i++; 76 } 77 78 // stack and guard pages may be contiguous as one region 79 ASSERT_TRUE(i >= 1); 80 ASSERT_TRUE(found_stack_top); 81 ASSERT_TRUE(found_i_addr); 82 } 83 84 static void check_covered_pages(address addr, size_t size, address base, size_t touch_pages, int* page_num) { 85 const size_t page_sz = os::vm_page_size(); 86 for (size_t index = 0; index < touch_pages; index ++) { 87 address page_addr = base + page_num[index] * page_sz; 88 // The range covers this page, marks the page 89 if (page_addr >= addr && page_addr < addr + size) { 90 page_num[index] = -1; 91 } 92 } 93 } 94 95 static void test_committed_region_impl(size_t num_pages, size_t touch_pages, int* page_num) { 96 const size_t page_sz = os::vm_page_size(); 97 const size_t size = num_pages * page_sz; 98 char* base = os::reserve_memory(size, NULL, page_sz, mtThreadStack); 99 ASSERT_NE(base, (char*)NULL); 100 bool result = os::commit_memory(base, size, false); 101 102 for (size_t index = 0; index < touch_pages; index ++) { 103 char* touch_addr = base + page_sz * page_num[index]; 104 *touch_addr = 'a'; 105 } 106 107 address frame = (address)0x1235; 108 NativeCallStack stack(&frame, 1); 109 VirtualMemoryTracker::add_reserved_region((address)base, size, stack, mtThreadStack); 110 111 // trigger the test 112 VirtualMemoryTracker::snapshot_thread_stacks(); 113 114 ReservedMemoryRegion* rmr = VirtualMemoryTracker::_reserved_regions->find(ReservedMemoryRegion((address)base, size)); 115 ASSERT_TRUE(rmr != NULL); 116 117 bool precise_tracking_supported = false; 118 CommittedRegionIterator iter = rmr->iterate_committed_regions(); 119 for (const CommittedMemoryRegion* region = iter.next(); region != NULL; region = iter.next()) { 120 if (region->size() == size) { 121 // platforms that do not support precise tracking. 122 ASSERT_TRUE(iter.next() == NULL); 123 break; 124 } else { 125 precise_tracking_supported = true; 126 check_covered_pages(region->base(), region->size(), (address)base, touch_pages, page_num); 127 } 128 } 129 130 if (precise_tracking_supported) { 131 // All touched pages should be committed 132 for (size_t index = 0; index < touch_pages; index ++) { 133 ASSERT_EQ(page_num[index], -1); 134 } 135 } 136 137 // Cleanup 138 os::free_memory(base, size, page_sz); 139 VirtualMemoryTracker::remove_released_region((address)base, size); 140 141 rmr = VirtualMemoryTracker::_reserved_regions->find(ReservedMemoryRegion((address)base, size)); 142 ASSERT_TRUE(rmr == NULL); 143 } 144 145 static void test_committed_region() { 146 // On Linux, we scan 1024 pages at a time. 147 // Here, we test scenario that scans < 1024 pages. 148 int small_range[] = {3, 9, 46}; 149 test_committed_region_impl(47, 3, small_range); 150 151 int mid_range[] = {0, 45, 100, 399, 400, 1000, 1031}; 152 test_committed_region_impl(1088, 5, mid_range); 153 154 int large_range[] = {100, 301, 1024, 2047, 2048, 2049, 2050, 3000}; 155 test_committed_region_impl(3074, 8, large_range); 156 } 157 }; 158 159 TEST_VM(VirtualMemoryTracker, thread_stack_tracking) { 160 VirtualMemoryTracker::initialize(NMT_detail); 161 VirtualMemoryTracker::late_initialize(NMT_detail); 162 163 ThreadStackTrackingTest::test(); 164 ThreadStackTrackingTest::test_committed_region(); 165 }