001/* 002 * Licensed to the Apache Software Foundation (ASF) under one 003 * or more contributor license agreements. See the NOTICE file 004 * distributed with this work for additional information 005 * regarding copyright ownership. The ASF licenses this file 006 * to you under the Apache License, Version 2.0 (the 007 * "License"); you may not use this file except in compliance 008 * with the License. You may obtain a copy of the License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software 013 * distributed under the License is distributed on an "AS IS" BASIS, 014 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 015 * See the License for the specific language governing permissions and 016 * limitations under the License. 017 */ 018package org.apache.hadoop.hbase; 019 020import java.util.ArrayList; 021import java.util.HashSet; 022import java.util.List; 023import java.util.Map; 024import java.util.Set; 025import java.util.concurrent.ConcurrentHashMap; 026import org.apache.hadoop.hbase.ResourceChecker.Phase; 027import org.apache.hadoop.hbase.util.JVM; 028import org.junit.runner.notification.RunListener; 029 030/** 031 * Listen to the test progress and check the usage of: 032 * <ul> 033 * <li>threads</li> 034 * <li>open file descriptor</li> 035 * <li>max open file descriptor</li> 036 * </ul> 037 * <p> 038 * When surefire forkMode=once/always/perthread, this code is executed on the forked process. 039 */ 040public class ResourceCheckerJUnitListener extends RunListener { 041 private Map<String, ResourceChecker> rcs = new ConcurrentHashMap<>(); 042 043 static class ThreadResourceAnalyzer extends ResourceChecker.ResourceAnalyzer { 044 private static Set<String> initialThreadNames = new HashSet<>(); 045 private static List<String> stringsToLog = null; 046 047 @Override 048 public int getVal(Phase phase) { 049 Map<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces(); 050 if (phase == Phase.INITIAL) { 051 stringsToLog = null; 052 for (Thread t : stackTraces.keySet()) { 053 initialThreadNames.add(t.getName()); 054 } 055 } else if (phase == Phase.END) { 056 if (stackTraces.size() > initialThreadNames.size()) { 057 stringsToLog = new ArrayList<>(); 058 for (Thread t : stackTraces.keySet()) { 059 if (!initialThreadNames.contains(t.getName())) { 060 stringsToLog.add("\nPotentially hanging thread: " + t.getName() + "\n"); 061 StackTraceElement[] stackElements = stackTraces.get(t); 062 for (StackTraceElement ele : stackElements) { 063 stringsToLog.add("\t" + ele + "\n"); 064 } 065 } 066 } 067 } 068 } 069 return stackTraces.size(); 070 } 071 072 @Override 073 public int getMax() { 074 return 500; 075 } 076 077 @Override 078 public List<String> getStringsToLog() { 079 return stringsToLog; 080 } 081 } 082 083 static class OpenFileDescriptorResourceAnalyzer extends ResourceChecker.ResourceAnalyzer { 084 @Override 085 public int getVal(Phase phase) { 086 if (!JVM.isUnix()) { 087 return 0; 088 } 089 JVM jvm = new JVM(); 090 return (int) jvm.getOpenFileDescriptorCount(); 091 } 092 093 @Override 094 public int getMax() { 095 return 1024; 096 } 097 } 098 099 static class MaxFileDescriptorResourceAnalyzer extends ResourceChecker.ResourceAnalyzer { 100 @Override 101 public int getVal(Phase phase) { 102 if (!JVM.isUnix()) { 103 return 0; 104 } 105 JVM jvm = new JVM(); 106 return (int) jvm.getMaxFileDescriptorCount(); 107 } 108 } 109 110 static class SystemLoadAverageResourceAnalyzer extends ResourceChecker.ResourceAnalyzer { 111 @Override 112 public int getVal(Phase phase) { 113 if (!JVM.isUnix()) { 114 return 0; 115 } 116 return (int) (new JVM().getSystemLoadAverage() * 100); 117 } 118 } 119 120 static class ProcessCountResourceAnalyzer extends ResourceChecker.ResourceAnalyzer { 121 @Override 122 public int getVal(Phase phase) { 123 if (!JVM.isUnix()) { 124 return 0; 125 } 126 return new JVM().getNumberOfRunningProcess(); 127 } 128 } 129 130 static class AvailableMemoryMBResourceAnalyzer extends ResourceChecker.ResourceAnalyzer { 131 @Override 132 public int getVal(Phase phase) { 133 if (!JVM.isUnix()) { 134 return 0; 135 } 136 return (int) (new JVM().getFreeMemory() / (1024L * 1024L)); 137 } 138 } 139 140 /** 141 * To be implemented by sub classes if they want to add specific ResourceAnalyzer. 142 */ 143 protected void addResourceAnalyzer(ResourceChecker rc) { 144 } 145 146 private void start(String testName) { 147 ResourceChecker rc = new ResourceChecker(testName); 148 rc.addResourceAnalyzer(new ThreadResourceAnalyzer()); 149 rc.addResourceAnalyzer(new OpenFileDescriptorResourceAnalyzer()); 150 rc.addResourceAnalyzer(new MaxFileDescriptorResourceAnalyzer()); 151 rc.addResourceAnalyzer(new SystemLoadAverageResourceAnalyzer()); 152 rc.addResourceAnalyzer(new ProcessCountResourceAnalyzer()); 153 rc.addResourceAnalyzer(new AvailableMemoryMBResourceAnalyzer()); 154 155 addResourceAnalyzer(rc); 156 157 rcs.put(testName, rc); 158 159 rc.start(); 160 } 161 162 private void end(String testName) { 163 ResourceChecker rc = rcs.remove(testName); 164 assert rc != null; 165 rc.end(); 166 } 167 168 /** 169 * Get the test name from the JUnit Description 170 * @return the string for the short test name 171 */ 172 private String descriptionToShortTestName(org.junit.runner.Description description) { 173 final int toRemove = "org.apache.hadoop.hbase.".length(); 174 return description.getTestClass().getName().substring(toRemove) + "#" 175 + description.getMethodName(); 176 } 177 178 @Override 179 public void testStarted(org.junit.runner.Description description) throws java.lang.Exception { 180 start(descriptionToShortTestName(description)); 181 } 182 183 @Override 184 public void testFinished(org.junit.runner.Description description) throws java.lang.Exception { 185 end(descriptionToShortTestName(description)); 186 } 187}