View Javadoc

1   /*
2    *
3    * Licensed to the Apache Software Foundation (ASF) under one
4    * or more contributor license agreements.  See the NOTICE file
5    * distributed with this work for additional information
6    * regarding copyright ownership.  The ASF licenses this file
7    * to you under the Apache License, Version 2.0 (the
8    * "License"); you may not use this file except in compliance
9    * with the License.  You may obtain a copy of the License at
10   *
11   *     http://www.apache.org/licenses/LICENSE-2.0
12   *
13   * Unless required by applicable law or agreed to in writing, software
14   * distributed under the License is distributed on an "AS IS" BASIS,
15   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16   * See the License for the specific language governing permissions and
17   * limitations under the License.
18   */
19  
20  package org.apache.hadoop.hbase;
21  
22  
23  
24  import java.util.ArrayList;
25  import java.util.HashSet;
26  import java.util.List;
27  import java.util.Map;
28  import java.util.Set;
29  import java.util.concurrent.ConcurrentHashMap;
30  
31  import org.apache.hadoop.hbase.ResourceChecker.Phase;
32  import org.junit.runner.notification.RunListener;
33  
34  import org.apache.hadoop.hbase.util.JVM;
35  
36  /**
37   * Listen to the test progress and check the usage of:
38   * - threads
39   * - open file descriptor
40   * - max open file descriptor
41   * <p/>
42   * When surefire forkMode=once/always/perthread, this code is executed on the forked process.
43   */
44  public class ResourceCheckerJUnitListener extends RunListener {
45    private Map<String, ResourceChecker> rcs = new ConcurrentHashMap<String, ResourceChecker>();
46  
47    static class ThreadResourceAnalyzer extends ResourceChecker.ResourceAnalyzer {
48      private static Set<String> initialThreadNames = new HashSet<String>();
49      private static List<String> stringsToLog = null;
50  
51      @Override
52      public int getVal(Phase phase) {
53        Map<Thread, StackTraceElement[]> stackTraces = Thread.getAllStackTraces();
54        if (phase == Phase.INITIAL) {
55          stringsToLog = null;
56          for (Thread t : stackTraces.keySet()) {
57            initialThreadNames.add(t.getName());
58          }
59        } else if (phase == Phase.END) {
60          if (stackTraces.size() > initialThreadNames.size()) {
61            stringsToLog = new ArrayList<String>();
62            for (Thread t : stackTraces.keySet()) {
63              if (!initialThreadNames.contains(t.getName())) {
64                stringsToLog.add("\nPotentially hanging thread: " + t.getName() + "\n");
65                StackTraceElement[] stackElements = stackTraces.get(t);
66                for (StackTraceElement ele : stackElements) {
67                  stringsToLog.add("\t" + ele + "\n");
68                }
69              }
70            }
71          }
72        }
73        return stackTraces.size();
74      }
75  
76      @Override
77      public int getMax() {
78        return 500;
79      }
80      
81      @Override
82      public List<String> getStringsToLog() {
83        return stringsToLog;
84      }
85    }
86  
87  
88    static class OpenFileDescriptorResourceAnalyzer extends ResourceChecker.ResourceAnalyzer {
89      @Override
90      public int getVal(Phase phase) {
91        if (!JVM.isUnix()) return 0;
92        JVM jvm = new JVM();
93        return (int)jvm.getOpenFileDescriptorCount();
94      }
95  
96      @Override
97      public int getMax() {
98        return 1024;
99      }
100   }
101 
102   static class MaxFileDescriptorResourceAnalyzer extends ResourceChecker.ResourceAnalyzer {
103     @Override
104     public int getVal(Phase phase) {
105       if (!JVM.isUnix()) return 0;
106       JVM jvm = new JVM();
107       return (int)jvm.getMaxFileDescriptorCount();
108      } 
109    }
110 
111   static class SystemLoadAverageResourceAnalyzer extends ResourceChecker.ResourceAnalyzer {
112     @Override
113     public int getVal(Phase phase) {
114       if (!JVM.isUnix()) return 0;
115       return (int)(new JVM().getSystemLoadAverage()*100);
116     }
117   }
118 
119   static class ProcessCountResourceAnalyzer extends ResourceChecker.ResourceAnalyzer {
120     @Override
121     public int getVal(Phase phase) {
122       if (!JVM.isUnix()) return 0;
123       return new JVM().getNumberOfRunningProcess();
124     }
125   }
126 
127   static class AvailableMemoryMBResourceAnalyzer extends ResourceChecker.ResourceAnalyzer {
128     @Override
129     public int getVal(Phase phase) {
130       if (!JVM.isUnix()) return 0;
131       return (int) (new JVM().getFreeMemory() / (1024L * 1024L));
132     }
133   }
134 
135 
136   /**
137    * To be implemented by sub classes if they want to add specific ResourceAnalyzer.
138    */
139   protected void addResourceAnalyzer(ResourceChecker rc) {
140   }
141 
142 
143   private void start(String testName) {
144     ResourceChecker rc = new ResourceChecker(testName);
145     rc.addResourceAnalyzer(new ThreadResourceAnalyzer());
146     rc.addResourceAnalyzer(new OpenFileDescriptorResourceAnalyzer());
147     rc.addResourceAnalyzer(new MaxFileDescriptorResourceAnalyzer());
148     rc.addResourceAnalyzer(new SystemLoadAverageResourceAnalyzer());
149     rc.addResourceAnalyzer(new ProcessCountResourceAnalyzer());
150     rc.addResourceAnalyzer(new AvailableMemoryMBResourceAnalyzer());
151 
152     addResourceAnalyzer(rc);
153 
154     rcs.put(testName, rc);
155 
156     rc.start();
157   }
158 
159   private void end(String testName) {
160     ResourceChecker rc = rcs.remove(testName);
161     assert rc != null;
162     rc.end();
163   }
164 
165   /**
166    * Get the test name from the JUnit Description
167    *
168    * @return the string for the short test name
169    */
170   private String descriptionToShortTestName(
171       org.junit.runner.Description description) {
172     final int toRemove = "org.apache.hadoop.hbase.".length();
173     return description.getTestClass().getName().substring(toRemove) +
174         "#" + description.getMethodName();
175   }
176 
177   @Override
178   public void testStarted(org.junit.runner.Description description) throws java.lang.Exception {
179     start(descriptionToShortTestName(description));
180   }
181 
182   @Override
183   public void testFinished(org.junit.runner.Description description) throws java.lang.Exception {
184     end(descriptionToShortTestName(description));
185   }
186 }
187