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 */ 018 019package org.apache.hadoop.hbase; 020 021import java.util.ArrayList; 022import java.util.HashSet; 023import java.util.List; 024import java.util.Map; 025import java.util.Set; 026import java.util.concurrent.ConcurrentHashMap; 027import org.apache.hadoop.hbase.ResourceChecker.Phase; 028import org.apache.hadoop.hbase.util.JVM; 029import org.junit.runner.notification.RunListener; 030 031/** 032 * Listen to the test progress and check the usage of: 033 * <ul> 034 * <li>threads</li> 035 * <li>open file descriptor</li> 036 * <li>max open file descriptor</li> 037 * </ul> 038 * <p> 039 * When surefire forkMode=once/always/perthread, this code is executed on the forked process. 040 */ 041public class ResourceCheckerJUnitListener extends RunListener { 042 private Map<String, ResourceChecker> rcs = new ConcurrentHashMap<>(); 043 044 static class ThreadResourceAnalyzer extends ResourceChecker.ResourceAnalyzer { 045 private static Set<String> initialThreadNames = new HashSet<>(); 046 private static List<String> stringsToLog = null; 047 048 @Override 049 public int getVal(Phase phase) { 050 Map<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces(); 051 if (phase == Phase.INITIAL) { 052 stringsToLog = null; 053 for (Thread t : stackTraces.keySet()) { 054 initialThreadNames.add(t.getName()); 055 } 056 } else if (phase == Phase.END) { 057 if (stackTraces.size() > initialThreadNames.size()) { 058 stringsToLog = new ArrayList<>(); 059 for (Thread t : stackTraces.keySet()) { 060 if (!initialThreadNames.contains(t.getName())) { 061 stringsToLog.add("\nPotentially hanging thread: " + t.getName() + "\n"); 062 StackTraceElement[] stackElements = stackTraces.get(t); 063 for (StackTraceElement ele : stackElements) { 064 stringsToLog.add("\t" + ele + "\n"); 065 } 066 } 067 } 068 } 069 } 070 return stackTraces.size(); 071 } 072 073 @Override 074 public int getMax() { 075 return 500; 076 } 077 078 @Override 079 public List<String> getStringsToLog() { 080 return stringsToLog; 081 } 082 } 083 084 085 static class OpenFileDescriptorResourceAnalyzer extends ResourceChecker.ResourceAnalyzer { 086 @Override 087 public int getVal(Phase phase) { 088 if (!JVM.isUnix()) { 089 return 0; 090 } 091 JVM jvm = new JVM(); 092 return (int) jvm.getOpenFileDescriptorCount(); 093 } 094 095 @Override 096 public int getMax() { 097 return 1024; 098 } 099 } 100 101 static class MaxFileDescriptorResourceAnalyzer extends ResourceChecker.ResourceAnalyzer { 102 @Override 103 public int getVal(Phase phase) { 104 if (!JVM.isUnix()) { 105 return 0; 106 } 107 JVM jvm = new JVM(); 108 return (int) jvm.getMaxFileDescriptorCount(); 109 } 110 } 111 112 static class SystemLoadAverageResourceAnalyzer extends ResourceChecker.ResourceAnalyzer { 113 @Override 114 public int getVal(Phase phase) { 115 if (!JVM.isUnix()) { 116 return 0; 117 } 118 return (int) (new JVM().getSystemLoadAverage() * 100); 119 } 120 } 121 122 static class ProcessCountResourceAnalyzer extends ResourceChecker.ResourceAnalyzer { 123 @Override 124 public int getVal(Phase phase) { 125 if (!JVM.isUnix()) { 126 return 0; 127 } 128 return new JVM().getNumberOfRunningProcess(); 129 } 130 } 131 132 static class AvailableMemoryMBResourceAnalyzer extends ResourceChecker.ResourceAnalyzer { 133 @Override 134 public int getVal(Phase phase) { 135 if (!JVM.isUnix()) { 136 return 0; 137 } 138 return (int) (new JVM().getFreeMemory() / (1024L * 1024L)); 139 } 140 } 141 142 143 /** 144 * To be implemented by sub classes if they want to add specific ResourceAnalyzer. 145 */ 146 protected void addResourceAnalyzer(ResourceChecker rc) { 147 } 148 149 150 private void start(String testName) { 151 ResourceChecker rc = new ResourceChecker(testName); 152 rc.addResourceAnalyzer(new ThreadResourceAnalyzer()); 153 rc.addResourceAnalyzer(new OpenFileDescriptorResourceAnalyzer()); 154 rc.addResourceAnalyzer(new MaxFileDescriptorResourceAnalyzer()); 155 rc.addResourceAnalyzer(new SystemLoadAverageResourceAnalyzer()); 156 rc.addResourceAnalyzer(new ProcessCountResourceAnalyzer()); 157 rc.addResourceAnalyzer(new AvailableMemoryMBResourceAnalyzer()); 158 159 addResourceAnalyzer(rc); 160 161 rcs.put(testName, rc); 162 163 rc.start(); 164 } 165 166 private void end(String testName) { 167 ResourceChecker rc = rcs.remove(testName); 168 assert rc != null; 169 rc.end(); 170 } 171 172 /** 173 * Get the test name from the JUnit Description 174 * 175 * @return the string for the short test name 176 */ 177 private String descriptionToShortTestName( 178 org.junit.runner.Description description) { 179 final int toRemove = "org.apache.hadoop.hbase.".length(); 180 return description.getTestClass().getName().substring(toRemove) + 181 "#" + description.getMethodName(); 182 } 183 184 @Override 185 public void testStarted(org.junit.runner.Description description) throws java.lang.Exception { 186 start(descriptionToShortTestName(description)); 187 } 188 189 @Override 190 public void testFinished(org.junit.runner.Description description) throws java.lang.Exception { 191 end(descriptionToShortTestName(description)); 192 } 193}