1 /* 2 * Copyright (c) 2018, Google 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 package MyPackage; 25 26 /** 27 * @test 28 * @build Frame HeapMonitor 29 * @summary Verifies the JVMTI Heap Monitor Thread information sanity 30 * @compile HeapMonitorThreadTest.java 31 * @run main/othervm/native -agentlib:HeapMonitor MyPackage.HeapMonitorThreadTest 32 */ 33 34 import java.util.ArrayList; 35 import java.util.List; 36 import java.util.concurrent.BlockingQueue; 37 import java.util.concurrent.LinkedBlockingQueue; 38 39 public class HeapMonitorThreadTest { 40 private native static boolean checkSamples(int numThreads); 41 42 public static void main(String[] args) { 43 final int numThreads = 24; 44 ArrayList<ThreadInformation> threadList = new ArrayList<>(); 45 46 HeapMonitor.enableSamplingEvents(); 47 48 // The test creates numThreads threads, each allocate a lot. Then they inform the main thread 49 // they are done allocating. The test looks if all threads are available in the collected event 50 // information and then proceeds to tell each thread to stop running before leaving the test. 51 for (int i = 0 ; i < numThreads; i++) { 52 BlockingQueue<Object> queue = new LinkedBlockingQueue<>(); 53 Allocator allocator = new Allocator(i, queue); 54 Thread thread = new Thread(allocator, "Allocator" + i); 55 thread.start(); 56 57 ThreadInformation info = new ThreadInformation(thread, queue, allocator); 58 threadList.add(info); 59 } 60 61 // Wait until all threads have put an object in the queue. 62 for (ThreadInformation info : threadList) { 63 info.waitForAllocations(); 64 } 65 66 if (!checkSamples(numThreads)) { 67 throw new RuntimeException("Problem with checkSamples..."); 68 } 69 70 // Now inform each thread we are done and wait for them to be done. 71 for (ThreadInformation info : threadList) { 72 info.stop(); 73 } 74 } 75 } 76 77 class ThreadInformation { 78 private Thread thread; 79 private BlockingQueue<Object> finishedAllocationQueue; 80 private Allocator allocator; 81 82 public ThreadInformation(Thread thread, BlockingQueue<Object> finishedAllocationQueue, 83 Allocator allocator) { 84 this.thread = thread; 85 this.finishedAllocationQueue = finishedAllocationQueue; 86 this.allocator = allocator; 87 } 88 89 public void waitForAllocations() { 90 try { 91 finishedAllocationQueue.take(); 92 } catch(InterruptedException e) { 93 throw new RuntimeException("Thread got interrupted..."); 94 } 95 } 96 97 public void stop() { 98 try { 99 allocator.stopRun(); 100 thread.join(); 101 102 if (!allocator.endedNormally()) { 103 throw new RuntimeException("Thread did not end normally..."); 104 } 105 106 } catch(InterruptedException e) { 107 throw new RuntimeException("Thread got interrupted..."); 108 } 109 } 110 } 111 112 class Allocator implements Runnable { 113 private int depth; 114 private List<int[]> currentList; 115 private BlockingQueue<Object> finishedAllocationQueue; 116 private BlockingQueue<Object> jobCanStop; 117 private boolean failed; 118 119 public Allocator(int depth, BlockingQueue<Object> finishedAllocationQueue) { 120 jobCanStop = new LinkedBlockingQueue<>(); 121 this.finishedAllocationQueue = finishedAllocationQueue; 122 this.depth = depth; 123 } 124 125 public boolean endedNormally() { 126 return !failed; 127 } 128 129 private void helper() { 130 List<int[]> newList = new ArrayList<>(); 131 // Let us assume that the array is 24 bytes of memory, by default we sample at 512k, keep in 132 // memory at least 2MB without counting the link-list itself, which adds to this. 133 int iterations = (1 << 21) / 24; 134 for (int i = 0; i < iterations; i++) { 135 int newTmp[] = new int[1]; 136 // Force it to be kept. 137 newList.add(newTmp); 138 } 139 140 // Replace old list with new list, which provokes two things: 141 // Old list will get GC'd at some point. 142 // New list forces that this thread has some allocations still sampled. 143 currentList = newList; 144 } 145 146 private void recursiveWrapper(int depth) { 147 if (depth > 0) { 148 recursiveWrapper(depth - 1); 149 } 150 helper(); 151 } 152 153 public void stopRun() throws InterruptedException { 154 jobCanStop.put(new Object()); 155 } 156 157 public void run() { 158 for (int j = 0; j < 50; j++) { 159 recursiveWrapper(depth); 160 } 161 162 try { 163 // Tell the main thread we are done. 164 finishedAllocationQueue.put(new Object()); 165 166 // Wait until the main thread says we can stop. 167 jobCanStop.take(); 168 } catch (InterruptedException e) { 169 failed = true; 170 } 171 } 172 }