Path: blob/master/test/hotspot/jtreg/vmTestbase/nsk/monitoring/share/ThreadController.java
40948 views
/*1* Copyright (c) 2003, 2018, Oracle and/or its affiliates. All rights reserved.2* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.3*4* This code is free software; you can redistribute it and/or modify it5* under the terms of the GNU General Public License version 2 only, as6* published by the Free Software Foundation.7*8* This code is distributed in the hope that it will be useful, but WITHOUT9* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or10* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License11* version 2 for more details (a copy is included in the LICENSE file that12* accompanied this code).13*14* You should have received a copy of the GNU General Public License version15* 2 along with this work; if not, write to the Free Software Foundation,16* Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.17*18* Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA19* or visit www.oracle.com if you need additional information or have any20* questions.21*/2223package nsk.monitoring.share;2425import java.lang.management.*;26import java.util.*;2728import nsk.share.*;2930/**31* The <code>ThreadController</code> class allows to operate with threads.32*/33public class ThreadController extends StateController {3435/**36* Type of threads: pure java.37*/38static public final int JAVA_TYPE = 0;3940/**41* Type of threads: native.42*/43static public final int NATIVE_TYPE = 1;4445/**46* Type of threads: both java and native.47*/48static public final int MIXED_TYPE = 2;4950/**51* Result code: no errors.52*/53static public final int NO_ERROR = 0;5455/**56* Result code: wrong state of the thread.57*/58static public final int ERR_STATE = 1;5960/**61* Result code: error in stack trace.62*/63static public final int ERR_STACKTRACE = 2;6465/**66* Result code: thread not found.67*/68static public final int ERR_THREAD_NOTFOUND = 3;697071// Prefix to print while logging72static final String LOG_PREFIX = "ThreadController> ";7374// Internal trace levels75static final int THREAD_TRACE_LEVEL = 50;7677/**78* Suffix of all started threads.79*/80static final String THREAD_SUFFIX = "_ThreadMM";8182// Number of tested kinds of threads83public static final int THREAD_KIND_COUNT = 4;8485/**86* Index of blocked threads.87*/88static public final int BLOCKED_IDX = 0;8990/**91* Index of waiting threads.92*/93static public final int WAITING_IDX = 1;9495/**96* Index of sleeping threads.97*/98static public final int SLEEPING_IDX = 2;99100/**101* Index of running threads.102*/103static public final int RUNNING_IDX = 3;104105public static final String[] THREAD_KIND_NAMES = {"BLOCKED","WAITING","SLEEPING","RUNNABLE"};106public static final Thread.State[] THREAD_KINDS = {Thread.State.BLOCKED, Thread.State.WAITING, Thread.State.TIMED_WAITING, Thread.State.RUNNABLE};107108private Map<Thread.State, Integer> threadsCount = new HashMap<Thread.State, Integer>();109private Map<Thread.State, List<BaseThread>> threadsClusters = new HashMap<Thread.State, List<BaseThread>>();110111private ThreadsGroupLocks threadsGroupLocks;112113114int maxDepth;115static int invocationType;116117static {118try {119System.loadLibrary("ThreadController");120} catch (UnsatisfiedLinkError e) {121System.err.println("Could not load \"ThreadController\" "122+ "library");123System.err.println("java.library.path:"124+ System.getProperty("java.library.path"));125throw e;126}127}128129/**130* Creates a new <code>ThreadController</code> object with defined131* arguments..132*133* @param log <code>Log</code> object to print info to.134* @param threadCount number of threads to start.135* @param maxDepth depth of recursion.136* @param invocationType type of threads to start (java, native, or mixed).137*/138public ThreadController(Log log, int threadCount, int maxDepth,139String invocationType) {140logPrefix = LOG_PREFIX;141setLog(log);142setThreadCount(threadCount);143setDepth(maxDepth);144setInvocationType(invocationType);145}146147148// Calculate how many threads of each kind to start149private void setThreadCount(int threadCount) {150int total = 0;151int kinds = THREAD_KIND_COUNT;152int tmp = threadCount / kinds;153int rest = threadCount % kinds;154int increased = kinds - rest;155for (int i = 0; i < kinds; i++) {156if (i >= increased) {157threadsCount.put(THREAD_KINDS[i], tmp + 1);158} else {159threadsCount.put(THREAD_KINDS[i], tmp);160}161}162display("number of created threads:\t" + threadCount);163}164165// Print thread count166private void printThreadCount() {167for (Thread.State state : THREAD_KINDS) {168display("\t" + state + " threads ("169+ threadsCount.get(state) + ")");170}171}172173// Set recursion depth174private void setDepth(int depth) {175maxDepth = depth;176display("depth for all threads:\t" + maxDepth);177}178179// Set invocation type180private void setInvocationType(String value) {181display("invocation type:\t" + value);182if (value.equals(ArgumentHandler.JAVA_TYPE)) {183invocationType = JAVA_TYPE;184} else if (value.equals(ArgumentHandler.NATIVE_TYPE)) {185invocationType = NATIVE_TYPE;186} else if (value.equals(ArgumentHandler.MIXED_TYPE)) {187invocationType = MIXED_TYPE;188} else {189throw new Failure("UNKNOWN invocation type");190}191}192193/**194* Returns invocation type.195*196* @return invocation type.197*/198public int getInvocationType() {199return invocationType;200}201202/**203* Returns thread count.204*205* @param state kind of thread state206* @return thread count.207*/208public int getThreadCount(Thread.State state) {209return threadsCount.get(state);210}211212/**213* Returns thread count.214*215* @param kindIndex of thread state216* @return thread count.217*/218public int getThreadCount(int kindIndex) {219return threadsCount.get(THREAD_KINDS[kindIndex]);220}221222public int getThreadKindCount() {223return THREAD_KINDS.length;224}225226227/**228* Brings out VM into defined state.229* <p/>230* The method starts all threads.231*/232public void run() {233long startTime = System.currentTimeMillis() / 1000;234startThreads();235display("locking threads");236waitForThreads();237}238239/**240* Tries to return VM into initial state241* <p/>242* The method interrupts all threads.243*/244public void reset() {245for (Thread.State state : THREAD_KINDS) {246threadsGroupLocks.releaseGroup(state);247}248}249250// Get thread state via JVMTI251private native Thread.State getThreadState(Thread thread);252253// Start all threads254private void startThreads() throws Failure {255256String tmp_name;257BaseThread thread = null;258259threadsGroupLocks = new ThreadsGroupLocks(threadsCount, logger);260for (Thread.State state : THREAD_KINDS) {261threadsClusters.put(state, new ArrayList<BaseThread>());262for (int j = 0; j < threadsCount.get(state); j++) {263tmp_name = state + THREAD_SUFFIX + int2Str(j);264switch (state) {265case BLOCKED:266thread = new BlockedThread(this, tmp_name, logger.getLog(), threadsGroupLocks);267break;268case WAITING:269thread = new WaitingThread(this, tmp_name, logger.getLog(), threadsGroupLocks);270break;271case TIMED_WAITING:272thread = new SleepingThread(this, tmp_name, logger.getLog(), threadsGroupLocks);273break;274case RUNNABLE:275thread = new RunningThread(this, tmp_name, logger.getLog(), threadsGroupLocks);276break;277default:278throw new TestBug("Unknow thread kind");279}280threadsClusters.get(state).add(thread);281thread.start();282}283}284waitForThreads();285}286287private boolean checkState(Thread.State expectedState) {288for (Thread thread : threadsClusters.get(expectedState)) {289if (getThreadState(thread) != expectedState) {290291return false;292}293}294return true;295}296297private void waitForThreads() {298for (Thread.State state : THREAD_KINDS) {299threadsGroupLocks.waitForGroup(state);300while (!checkState(state)) {301Thread.yield();302}303}304}305306307/**308* Finds a thread with defined id.309*310* @param id ID of the thread.311* @return a thread with defined id.312*/313public BaseThread findThread(long id) {314for(Thread.State state:THREAD_KINDS){315for(BaseThread thread:threadsClusters.get(state)){316if (id==thread.getId()) {317return thread;318}319}320}321return null;322}323324/**325* Finds a thread by name.326*327* @param name name of the thread.328* @return a thread with defined name.329*/330public BaseThread findThread(String name) {331for(Thread.State state:THREAD_KINDS){332for(BaseThread thread:threadsClusters.get(state)){333if (name.equals(thread.getName())) {334return thread;335}336}337}338return null;339}340341/**342* Checks the thread's <code>ThreadInfo</code>.343*344* @param info <code>ThreadInfo</code> object to test.345* @return result code.346* @see #NO_ERROR347* @see #ERR_THREAD_NOTFOUND348* @see #ERR_STATE349* @see #ERR_STACKTRACE350*/351public int checkThreadInfo(ThreadInfo info) {352String name = info.getThreadName();353354if (name.indexOf(THREAD_SUFFIX) == -1) {355return NO_ERROR;356}357358long id = info.getThreadId();359Thread.State state = info.getThreadState();360StackTraceElement[] stackTrace = info.getStackTrace();361362BaseThread thrd = findThread(id);363if (thrd == null) {364return ERR_THREAD_NOTFOUND;365}366367if (!thrd.checkState(state))368return ERR_STATE;369370if (!thrd.checkStackTrace(stackTrace))371return ERR_STACKTRACE;372373return NO_ERROR;374}375}376377abstract class BaseThread extends Thread {378379private int currentDepth = 0;380private String logPrefix;381protected Log.Logger logger;382383protected ThreadController controller;384385protected List<String> expectedMethods = new ArrayList<String>();386protected int expectedLength;387388protected ThreadsGroupLocks threadsGroupLocks;389390static {391if (ThreadController.invocationType == ThreadController.NATIVE_TYPE ||392ThreadController.invocationType == ThreadController.MIXED_TYPE) {393try {394System.loadLibrary("ThreadController");395} catch (UnsatisfiedLinkError e) {396System.err.println("Could not load \"ThreadController\" "397+ "library");398System.err.println("java.library.path:"399+ System.getProperty("java.library.path"));400throw e;401}402}403}404405public BaseThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks) {406super(name);407this.controller = controller;408int pos = controller.LOG_PREFIX.indexOf('>');409logPrefix = controller.LOG_PREFIX.substring(0, pos) + "::"410+ name + "> ";411setLog(log);412this.threadsGroupLocks = threadsGroupLocks;413414expectedLength = 1 + controller.maxDepth + 1;415if(controller.invocationType == ThreadController.MIXED_TYPE) {416//nativeRecursiveMethod417expectedLength ++;418}419420expectedMethods.add(BaseThread.class.getName() + ".run");421422switch (controller.invocationType) {423case ThreadController.JAVA_TYPE:424expectedMethods.add(BaseThread.class.getName() + ".recursiveMethod");425break;426case ThreadController.NATIVE_TYPE:427expectedMethods.add(BaseThread.class.getName() + ".nativeRecursiveMethod");428break;429case ThreadController.MIXED_TYPE:430expectedMethods.add(BaseThread.class.getName() + ".recursiveMethod");431expectedMethods.add(BaseThread.class.getName() + ".nativeRecursiveMethod");432}433434expectedMethods.add(ThreadsGroupLocks.PlainCountDownLatch.class.getName() + ".countDown");435}436437public void run() {438try {439switch (controller.invocationType) {440case ThreadController.JAVA_TYPE:441case ThreadController.MIXED_TYPE:442recursiveMethod();443break;444case ThreadController.NATIVE_TYPE:445nativeRecursiveMethod();446break;447default:448throw new Failure("unknown invocationType:"449+ controller.invocationType);450}451} catch (StackOverflowError e) {452logger.complain(e.toString());453throw new RuntimeException(e);454}455logger.trace(controller.THREAD_TRACE_LEVEL, "thread finished");456}457458protected abstract void bringState();459460public abstract State getState();461462protected abstract void nativeBringState();463464public abstract boolean checkState(Thread.State state);465466public boolean checkStackTrace(StackTraceElement[] elements) {467boolean res = true;468469logger.trace(controller.THREAD_TRACE_LEVEL, "trace elements: "470+ elements.length);471472if (elements.length > expectedLength) {473res = false;474logger.complain("Contains " + elements.length + ", more then "475+ expectedLength + " elements");476}477478for (int j = 0; j < elements.length; j++) {479if (!checkElement(elements[j])) {480logger.complain("Unexpected method name: "481+ elements[j].getMethodName()482+ " at " + j + " position");483if (elements[j].isNativeMethod()) {484logger.complain("\tline number: (native method)");485logger.complain("\tclass name: " + elements[j].getClassName());486} else {487logger.complain("\tline number: " + elements[j].getLineNumber());488logger.complain("\tclass name: " + elements[j].getClassName());489logger.complain("\tfile name: " + elements[j].getFileName());490}491res = false;492}493}494return res;495}496497protected boolean checkElement(StackTraceElement element) {498String name = element.getClassName() + "." + element.getMethodName();499if (expectedMethods.contains(name)) {500return true;501}502503logger.trace(controller.THREAD_TRACE_LEVEL, "\"" + name + "\""504+ " is not expected method name");505return false;506}507508protected void recursiveMethod() {509currentDepth++;510511if (controller.maxDepth - currentDepth > 0) {512513Thread.yield();514try {515if (ThreadController.invocationType516== ThreadController.MIXED_TYPE) {517nativeRecursiveMethod();518} else {519recursiveMethod();520}521522} catch (StackOverflowError e) {523logger.display(getName() + "> " + e);524}525526} else if (controller.maxDepth == currentDepth) {527logger.trace(controller.THREAD_TRACE_LEVEL, "state has been "528+ "reached");529bringState();530}531currentDepth--;532}533534protected native void nativeRecursiveMethod();535536/**537* Defines <code>Log.Logger</code> object538*/539public void setLog(Log log) {540logger = new Log.Logger(log, logPrefix);541}542}543544class BlockedThread extends BaseThread {545546private static final Thread.State STATE = Thread.State.BLOCKED;547548public State getState() {549return STATE;550}551552public BlockedThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks) {553super(controller, name, log, threadsGroupLocks);554555this.threadsGroupLocks = threadsGroupLocks;556557expectedLength += 2;558559expectedMethods.add(ThreadsGroupLocks.Blocker.class.getName() + ".block");560561switch (controller.invocationType) {562case ThreadController.JAVA_TYPE:563expectedMethods.add(BlockedThread.class.getName() + ".bringState");564break;565case ThreadController.NATIVE_TYPE:566expectedMethods.add(BlockedThread.class.getName() + ".nativeBringState");567break;568case ThreadController.MIXED_TYPE:569expectedMethods.add(BlockedThread.class.getName() + ".bringState");570571}572}573574protected void bringState() {575logger.trace(controller.THREAD_TRACE_LEVEL, "entering to monitor");576threadsGroupLocks.getBarrier(getState()).countDown();577threadsGroupLocks.blocker.block();578logger.trace(controller.THREAD_TRACE_LEVEL, "exiting from monitor");579}580581protected native void nativeBringState();582583public boolean checkState(Thread.State state) {584return state == Thread.State.BLOCKED;585}586}587588class WaitingThread extends BaseThread {589590private static final Thread.State STATE = Thread.State.WAITING;591public State getState() {592return STATE;593}594595private ThreadsGroupLocks threadsGroupLocks;596597public WaitingThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks) {598super(controller, name, log, threadsGroupLocks);599600this.threadsGroupLocks = threadsGroupLocks;601602expectedLength += 4;603604expectedMethods.add(ThreadsGroupLocks.PlainCountDownLatch.class.getName() + ".await");605expectedMethods.add(Object.class.getName() + ".wait");606607switch (controller.invocationType) {608case ThreadController.JAVA_TYPE:609expectedMethods.add(WaitingThread.class.getName() + ".bringState");610break;611case ThreadController.NATIVE_TYPE:612expectedMethods.add(WaitingThread.class.getName() + ".nativeBringState");613break;614case ThreadController.MIXED_TYPE:615expectedMethods.add(WaitingThread.class.getName() + ".bringState");616617}618}619620621protected void bringState() {622ThreadsGroupLocks.PlainCountDownLatch barrier = threadsGroupLocks.getBarrier(STATE);623try {624logger.trace(controller.THREAD_TRACE_LEVEL, "waiting on a monitor");625threadsGroupLocks.getBarrier(getState()).countDown();626barrier.await();627} catch (InterruptedException e) {628logger.display(e.toString());629}630}631632protected native void nativeBringState();633634public boolean checkState(Thread.State state) {635return state == STATE;636}637638}639640class SleepingThread extends BaseThread {641private static final Thread.State STATE = State.TIMED_WAITING;642643public State getState() {644return STATE;645}646647private ThreadsGroupLocks threadsGroupLocks;648649public SleepingThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks) {650super(controller, name, log, threadsGroupLocks);651652this.threadsGroupLocks = threadsGroupLocks;653654expectedLength += 3;655656expectedMethods.add(Thread.class.getName() + ".sleep");657expectedMethods.add(SleepingThread.class.getName() + ".run");658659switch (controller.invocationType) {660case ThreadController.JAVA_TYPE:661expectedMethods.add(SleepingThread.class.getName() + ".bringState");662break;663case ThreadController.NATIVE_TYPE:664expectedMethods.add(SleepingThread.class.getName() + ".nativeBringState");665break;666case ThreadController.MIXED_TYPE:667expectedMethods.add(SleepingThread.class.getName() + ".bringState");668}669670}671672protected void bringState() {673try {674threadsGroupLocks.getBarrier(getState()).countDown();675Thread.sleep(3600 * 1000);676} catch (InterruptedException e) {677logger.display(e.toString());678}679}680681protected native void nativeBringState();682683public boolean checkState(Thread.State state) {684return state == Thread.State.TIMED_WAITING;685}686687public void run() {688try {689switch (controller.invocationType) {690case ThreadController.JAVA_TYPE:691case ThreadController.MIXED_TYPE:692recursiveMethod();693break;694case ThreadController.NATIVE_TYPE:695nativeRecursiveMethod();696break;697default:698throw new Failure("unknown invocationType:"699+ controller.invocationType);700}701logger.trace(controller.THREAD_TRACE_LEVEL, "thread finished");702} catch (StackOverflowError e) {703logger.complain(e.toString());704throw new RuntimeException(e);705}706}707}708709class RunningThread extends BaseThread {710public State getState() {711return STATE;712}713714private static final Thread.State STATE = Thread.State.RUNNABLE;715private ThreadsGroupLocks threadsGroupLocks;716717public RunningThread(ThreadController controller, String name, Log log, ThreadsGroupLocks threadsGroupLocks) {718super(controller, name, log, threadsGroupLocks);719this.threadsGroupLocks = threadsGroupLocks;720721expectedLength += 2;722723expectedMethods.add(Thread.class.getName() + ".yield");724725switch (controller.invocationType) {726case ThreadController.JAVA_TYPE:727expectedMethods.add(RunningThread.class.getName() + ".bringState");728break;729case ThreadController.NATIVE_TYPE:730expectedMethods.add(RunningThread.class.getName() + ".nativeBringState");731break;732case ThreadController.MIXED_TYPE:733expectedMethods.add(RunningThread.class.getName() + ".bringState");734}735}736737protected void bringState() {738logger.trace(controller.THREAD_TRACE_LEVEL, "running loop");739threadsGroupLocks.getBarrier(getState()).countDown();740while (!threadsGroupLocks.runnableCanExit) {741Thread.yield();742}743}744745protected native void nativeBringState();746747public boolean checkState(Thread.State state) {748return state == Thread.State.RUNNABLE;749}750}751752753class ThreadsGroupLocks {754755private Log.Logger logger;756757//for all758private Map<Thread.State, PlainCountDownLatch> barriers = new HashMap<Thread.State, PlainCountDownLatch>();759760//for Blocked761public final Blocker blocker = new Blocker();762763//for Runnable764public volatile boolean runnableCanExit = false;765766public ThreadsGroupLocks(Map<Thread.State, Integer> threadsCount, Log.Logger logger) {767this.logger = logger;768for (Thread.State state : threadsCount.keySet()) {769if (state == Thread.State.WAITING) {770barriers.put(state, new PlainCountDownLatch(threadsCount.get(state) + 1));771} else {772barriers.put(state, new PlainCountDownLatch(threadsCount.get(state)));773}774}775blocker.startBlocker();776}777778public PlainCountDownLatch getBarrier(Thread.State state) {779return barriers.get(state);780}781782public void waitForGroup(Thread.State stateGroup) {783switch (stateGroup) {784case BLOCKED:785case RUNNABLE:786case TIMED_WAITING:787try {788barriers.get(stateGroup).await();789} catch (InterruptedException e) {790logger.display(e.toString());791}792break;793794case WAITING:795while (barriers.get(stateGroup).getCount() != 1) {796Thread.yield();797}798break;799}800}801802public void releaseGroup(Thread.State stateGroup) {803switch (stateGroup) {804case BLOCKED:805blocker.unBlock();806break;807case RUNNABLE:808runnableCanExit = true;809break;810case TIMED_WAITING:811case WAITING:812barriers.get(stateGroup).countDown();813break;814}815}816817public class Blocker {818819private Object monitor = new Object();820private PlainCountDownLatch blockerCanExit = new PlainCountDownLatch(1);821private PlainCountDownLatch blockerStart = new PlainCountDownLatch(1);822823private Runnable blockerThread = new Runnable() {824public void run() {825synchronized (monitor) {826blockerStart.countDown();827try {828blockerCanExit.await();829} catch (InterruptedException e) {830logger.display(e.toString());831}832833}834}835};836837public void startBlocker() {838new Thread(blockerThread, "Blocker").start();839}840841public void block() {842try {843blockerStart.await();844} catch (InterruptedException e) {845logger.complain(e.toString());846}847synchronized (monitor) {848}849}850851public void unBlock() {852blockerCanExit.countDown();853}854}855856public static class PlainCountDownLatch {857private volatile int counter;858private Object counterMonitor = new Object();859860public PlainCountDownLatch(int counter){861this.counter = counter;862}863864public void countDown(){865synchronized (counterMonitor) {866counter--;867if(counter==0) {868counterMonitor.notifyAll();869}870}871}872873public void await() throws InterruptedException{874synchronized (counterMonitor){875while(counter != 0){876counterMonitor.wait();877}878}879}880881public int getCount(){882synchronized (counterMonitor) {883return counter;884}885}886}887888}889890891