1 /**
2  * Licensed to the Apache Software Foundation (ASF) under one
3  * or more contributor license agreements.  See the NOTICE file
4  * distributed with this work for additional information
5  * regarding copyright ownership.  The ASF licenses this file
6  * to you under the Apache License, Version 2.0 (the
7  * "License"); you may not use this file except in compliance
8  * with the License.  You may obtain a copy of the License at
9  *
10  *     http://www.apache.org/licenses/LICENSE-2.0
11  *
12  * Unless required by applicable law or agreed to in writing, software
13  * distributed under the License is distributed on an "AS IS" BASIS,
14  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15  * See the License for the specific language governing permissions and
16  * limitations under the License.
17  */
18 
19 package org.apache.hadoop.util;
20 
21 import java.io.File;
22 import java.io.FileWriter;
23 import java.io.IOException;
24 import java.util.Random;
25 
26 import junit.framework.TestCase;
27 
28 import org.apache.hadoop.fs.Path;
29 import org.apache.hadoop.mapred.TaskTrackerStatus;
30 import org.junit.Test;
31 
32 /**
33  * A JUnit test to test {@link LinuxResourceCalculatorPlugin}
34  * Create the fake /proc/ information and verify the parsing and calculation
35  */
36 public class TestLinuxResourceCalculatorPlugin extends TestCase {
37   /**
38    * LinuxResourceCalculatorPlugin with a fake timer
39    */
40   static class FakeLinuxResourceCalculatorPlugin extends
41       LinuxResourceCalculatorPlugin {
42 
43 	  long currentTime = 0;
FakeLinuxResourceCalculatorPlugin(String procfsMemFile, String procfsCpuFile, String procfsStatFile, long jiffyLengthInMillis)44 	  public FakeLinuxResourceCalculatorPlugin(String procfsMemFile,
45 			                                       String procfsCpuFile,
46 			                                       String procfsStatFile,
47 			                                       long jiffyLengthInMillis) {
48 	    super(procfsMemFile, procfsCpuFile, procfsStatFile, jiffyLengthInMillis);
49 	  }
50 	  @Override
getCurrentTime()51 	  long getCurrentTime() {
52 	    return currentTime;
53 	  }
advanceTime(long adv)54 	  public void advanceTime(long adv) {
55 	    currentTime += adv * jiffyLengthInMillis;
56 	  }
57   }
58   private static final FakeLinuxResourceCalculatorPlugin plugin;
59   private static String TEST_ROOT_DIR = new Path(System.getProperty(
60          "test.build.data", "/tmp")).toString().replace(' ', '+');
61   private static final String FAKE_MEMFILE;
62   private static final String FAKE_CPUFILE;
63   private static final String FAKE_STATFILE;
64   private static final long FAKE_JIFFY_LENGTH = 10L;
65   static {
66     int randomNum = (new Random()).nextInt(1000000000);
67     FAKE_MEMFILE = TEST_ROOT_DIR + File.separator + "MEMINFO_" + randomNum;
68     FAKE_CPUFILE = TEST_ROOT_DIR + File.separator + "CPUINFO_" + randomNum;
69     FAKE_STATFILE = TEST_ROOT_DIR + File.separator + "STATINFO_" + randomNum;
70     plugin = new FakeLinuxResourceCalculatorPlugin(FAKE_MEMFILE, FAKE_CPUFILE,
71                                                    FAKE_STATFILE,
72                                                    FAKE_JIFFY_LENGTH);
73   }
74   static final String MEMINFO_FORMAT =
75 	  "MemTotal:      %d kB\n" +
76 	  "MemFree:         %d kB\n" +
77 	  "Buffers:        138244 kB\n" +
78 	  "Cached:         947780 kB\n" +
79 	  "SwapCached:     142880 kB\n" +
80 	  "Active:        3229888 kB\n" +
81 	  "Inactive:       %d kB\n" +
82 	  "SwapTotal:     %d kB\n" +
83 	  "SwapFree:      %d kB\n" +
84 	  "Dirty:          122012 kB\n" +
85 	  "Writeback:           0 kB\n" +
86 	  "AnonPages:     2710792 kB\n" +
87 	  "Mapped:          24740 kB\n" +
88 	  "Slab:           132528 kB\n" +
89 	  "SReclaimable:   105096 kB\n" +
90 	  "SUnreclaim:      27432 kB\n" +
91 	  "PageTables:      11448 kB\n" +
92 	  "NFS_Unstable:        0 kB\n" +
93 	  "Bounce:              0 kB\n" +
94 	  "CommitLimit:   4125904 kB\n" +
95 	  "Committed_AS:  4143556 kB\n" +
96 	  "VmallocTotal: 34359738367 kB\n" +
97 	  "VmallocUsed:      1632 kB\n" +
98 	  "VmallocChunk: 34359736375 kB\n" +
99 	  "HugePages_Total:     0\n" +
100 	  "HugePages_Free:      0\n" +
101 	  "HugePages_Rsvd:      0\n" +
102 	  "Hugepagesize:     2048 kB";
103 
104   static final String CPUINFO_FORMAT =
105     "processor : %s\n" +
106     "vendor_id : AuthenticAMD\n" +
107     "cpu family  : 15\n" +
108     "model   : 33\n" +
109     "model name  : Dual Core AMD Opteron(tm) Processor 280\n" +
110     "stepping  : 2\n" +
111     "cpu MHz   : %f\n" +
112     "cache size  : 1024 KB\n" +
113     "physical id : 0\n" +
114     "siblings  : 2\n" +
115     "core id   : 0\n" +
116     "cpu cores : 2\n" +
117     "fpu   : yes\n" +
118     "fpu_exception : yes\n" +
119     "cpuid level : 1\n" +
120     "wp    : yes\n" +
121     "flags   : fpu vme de pse tsc msr pae mce cx8 apic sep mtrr pge mca cmov " +
122     "pat pse36 clflush mmx fxsr sse sse2 ht syscall nx mmxext fxsr_opt lm " +
123     "3dnowext 3dnow pni lahf_lm cmp_legacy\n" +
124     "bogomips  : 4792.41\n" +
125     "TLB size  : 1024 4K pages\n" +
126     "clflush size  : 64\n" +
127     "cache_alignment : 64\n" +
128     "address sizes : 40 bits physical, 48 bits virtual\n" +
129     "power management: ts fid vid ttp";
130 
131   static final String STAT_FILE_FORMAT =
132     "cpu  %d %d %d 1646495089 831319 48713 164346 0\n" +
133     "cpu0 15096055 30805 3823005 411456015 206027 13 14269 0\n" +
134     "cpu1 14760561 89890 6432036 408707910 456857 48074 130857 0\n" +
135     "cpu2 12761169 20842 3758639 413976772 98028 411 10288 0\n" +
136     "cpu3 12355207 47322 5789691 412354390 70406 213 8931 0\n" +
137     "intr 114648668 20010764 2 0 945665 2 0 0 0 0 0 0 0 4 0 0 0 0 0 0\n" +
138     "ctxt 242017731764\n" +
139     "btime 1257808753\n" +
140     "processes 26414943\n" +
141     "procs_running 1\n" +
142     "procs_blocked 0\n";
143 
144   /**
145    * Test parsing /proc/stat and /proc/cpuinfo
146    * @throws IOException
147    */
testParsingProcStatAndCpuFile()148   public void testParsingProcStatAndCpuFile() throws IOException {
149     // Write fake /proc/cpuinfo file.
150     long numProcessors = 8;
151     long cpuFrequencyKHz = 2392781;
152     String fileContent = "";
153     for (int i = 0; i < numProcessors; i++) {
154       fileContent += String.format(CPUINFO_FORMAT, i, cpuFrequencyKHz / 1000D) +
155                      "\n";
156     }
157     File tempFile = new File(FAKE_CPUFILE);
158     tempFile.deleteOnExit();
159     FileWriter fWriter = new FileWriter(FAKE_CPUFILE);
160     fWriter.write(fileContent);
161     fWriter.close();
162     assertEquals(plugin.getNumProcessors(), numProcessors);
163     assertEquals(plugin.getCpuFrequency(), cpuFrequencyKHz);
164 
165     // Write fake /proc/stat file.
166     long uTime = 54972994;
167     long nTime = 188860;
168     long sTime = 19803373;
169     tempFile = new File(FAKE_STATFILE);
170     tempFile.deleteOnExit();
171     updateStatFile(uTime, nTime, sTime);
172     assertEquals(plugin.getCumulativeCpuTime(),
173                  FAKE_JIFFY_LENGTH * (uTime + nTime + sTime));
174     assertEquals(plugin.getCpuUsage(), (float)(TaskTrackerStatus.UNAVAILABLE));
175 
176     // Advance the time and sample again to test the CPU usage calculation
177     uTime += 100L;
178     plugin.advanceTime(200L);
179     updateStatFile(uTime, nTime, sTime);
180     assertEquals(plugin.getCumulativeCpuTime(),
181                  FAKE_JIFFY_LENGTH * (uTime + nTime + sTime));
182     assertEquals(plugin.getCpuUsage(), 6.25F);
183 
184     // Advance the time and sample again. This time, we call getCpuUsage() only.
185     uTime += 600L;
186     plugin.advanceTime(300L);
187     updateStatFile(uTime, nTime, sTime);
188     assertEquals(plugin.getCpuUsage(), 25F);
189 
190     // Advance very short period of time (one jiffy length).
191     // In this case, CPU usage should not be updated.
192     uTime += 1L;
193     plugin.advanceTime(1L);
194     updateStatFile(uTime, nTime, sTime);
195     assertEquals(plugin.getCumulativeCpuTime(),
196                  FAKE_JIFFY_LENGTH * (uTime + nTime + sTime));
197     assertEquals(plugin.getCpuUsage(), 25F); // CPU usage is not updated.
198   }
199 
200   /**
201    * Write information to fake /proc/stat file
202    */
updateStatFile(long uTime, long nTime, long sTime)203   private void updateStatFile(long uTime, long nTime, long sTime)
204     throws IOException {
205     FileWriter fWriter = new FileWriter(FAKE_STATFILE);
206     fWriter.write(String.format(STAT_FILE_FORMAT, uTime, nTime, sTime));
207     fWriter.close();
208   }
209 
210   /**
211    * Test parsing /proc/meminfo
212    * @throws IOException
213    */
testParsingProcMemFile()214   public void testParsingProcMemFile() throws IOException {
215     long memTotal = 4058864L;
216     long memFree = 99632L;
217     long inactive = 567732L;
218     long swapTotal = 2096472L;
219     long swapFree = 1818480L;
220     File tempFile = new File(FAKE_MEMFILE);
221     tempFile.deleteOnExit();
222     FileWriter fWriter = new FileWriter(FAKE_MEMFILE);
223     fWriter.write(String.format(MEMINFO_FORMAT,
224       memTotal, memFree, inactive, swapTotal, swapFree));
225 
226     fWriter.close();
227     assertEquals(plugin.getAvailablePhysicalMemorySize(),
228                  1024L * (memFree + inactive));
229     assertEquals(plugin.getAvailableVirtualMemorySize(),
230                  1024L * (memFree + inactive + swapFree));
231     assertEquals(plugin.getPhysicalMemorySize(), 1024L * memTotal);
232     assertEquals(plugin.getVirtualMemorySize(), 1024L * (memTotal + swapTotal));
233   }
234 }
235