1/* 2 * Copyright (C) 2010 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17package dalvik.system.profiler; 18 19import dalvik.system.profiler.AsciiHprofWriter; 20import dalvik.system.profiler.BinaryHprofReader; 21import dalvik.system.profiler.BinaryHprofWriter; 22import dalvik.system.profiler.HprofData.Sample; 23import dalvik.system.profiler.HprofData.StackTrace; 24import dalvik.system.profiler.HprofData.ThreadEvent; 25import dalvik.system.profiler.HprofData; 26import dalvik.system.profiler.SamplingProfiler.ThreadSet; 27import java.io.ByteArrayInputStream; 28import java.io.ByteArrayOutputStream; 29import java.io.File; 30import java.io.FileOutputStream; 31import java.io.InputStream; 32import java.io.OutputStream; 33import java.math.BigInteger; 34import java.security.KeyPairGenerator; 35import java.security.SecureRandom; 36import java.util.Arrays; 37import java.util.HashMap; 38import java.util.HashSet; 39import java.util.List; 40import java.util.Map; 41import java.util.Set; 42import javax.crypto.spec.DHParameterSpec; 43import junit.framework.TestCase; 44 45public class SamplingProfilerTest extends TestCase { 46 47 /** 48 * Run the SamplingProfiler to gather some data on an actual 49 * computation, then assert that it looks correct with test_HprofData. 50 */ 51 public void test_SamplingProfiler() throws Exception { 52 ThreadSet threadSet = SamplingProfiler.newArrayThreadSet(Thread.currentThread()); 53 SamplingProfiler profiler = new SamplingProfiler(12, threadSet); 54 profiler.start(100); 55 toBeMeasured(); 56 profiler.stop(); 57 profiler.shutdown(); 58 test_HprofData(profiler.getHprofData(), true); 59 } 60 61 private static final String P_STR = 62 "9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e3" 63 + "1db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b"; 64 private static final String G_STR = 65 "98ab7c5c431479d8645e33aa09758e0907c78747798d0968576f9877421a9089" 66 + "756f7876e76590b76765645c987976d764dd4564698a87585e64554984bb4445" 67 + "76e5764786f875b4456c"; 68 69 private static final byte[] P = new BigInteger(P_STR,16).toByteArray(); 70 private static final byte[] G = new BigInteger(G_STR,16).toByteArray(); 71 72 private static void toBeMeasured () throws Exception { 73 long start = System.currentTimeMillis(); 74 for (int i = 0; i < 10000; i++) { 75 BigInteger p = new BigInteger(P); 76 BigInteger g = new BigInteger(G); 77 KeyPairGenerator gen = KeyPairGenerator.getInstance("DH"); 78 gen.initialize(new DHParameterSpec(p, g), new SecureRandom()); 79 } 80 long end = System.currentTimeMillis(); 81 } 82 83 public void test_HprofData_null() throws Exception { 84 try { 85 new HprofData(null); 86 fail(); 87 } catch (NullPointerException expected) { 88 } 89 } 90 91 public void test_HprofData_empty() throws Exception { 92 Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>(); 93 HprofData hprofData = new HprofData(stackTraces); 94 test_HprofData(hprofData, true); 95 } 96 97 public void test_HprofData_timeMillis() throws Exception { 98 Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>(); 99 HprofData hprofData = new HprofData(stackTraces); 100 long now = System.currentTimeMillis(); 101 hprofData.setStartMillis(now); 102 assertEquals(now, hprofData.getStartMillis()); 103 test_HprofData(hprofData, true); 104 } 105 106 public void test_HprofData_addThreadEvent_null() throws Exception { 107 Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>(); 108 HprofData hprofData = new HprofData(stackTraces); 109 try { 110 hprofData.addThreadEvent(null); 111 fail(); 112 } catch (NullPointerException expected) { 113 } 114 test_HprofData(hprofData, true); 115 } 116 117 public void test_HprofData_addThreadEvent() throws Exception { 118 Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>(); 119 HprofData hprofData = new HprofData(stackTraces); 120 121 // should have nothing in the thread history to start 122 assertEquals(0, hprofData.getThreadHistory().size()); 123 124 // add thread 1 125 final int threadId = 1; 126 final int objectId = 2; 127 ThreadEvent start1 = ThreadEvent.start(objectId, threadId, 128 "thread-name", "thread-group", "parent-group"); 129 hprofData.addThreadEvent(start1); 130 assertEquals(Arrays.asList(start1), hprofData.getThreadHistory()); 131 test_HprofData(hprofData, true); 132 133 // remove thread 2, which should not exist (but that's okay on the RI) 134 ThreadEvent end2 = ThreadEvent.end(threadId+1); 135 hprofData.addThreadEvent(end2); 136 assertEquals(Arrays.asList(start1, end2), hprofData.getThreadHistory()); 137 test_HprofData(hprofData, false); // non-strict from here down because of this RI data 138 139 // remove thread 1, which should exist 140 ThreadEvent end1 = ThreadEvent.end(threadId); 141 hprofData.addThreadEvent(end1); 142 assertEquals(Arrays.asList(start1, end2, end1), hprofData.getThreadHistory()); 143 test_HprofData(hprofData, false); 144 145 // remove thread 1 again, which should not exist (its not okay to have end followed by end) 146 try { 147 hprofData.addThreadEvent(ThreadEvent.end(threadId)); 148 fail(); 149 } catch (IllegalArgumentException expected) { 150 } 151 assertEquals(Arrays.asList(start1, end2, end1), hprofData.getThreadHistory()); 152 test_HprofData(hprofData, false); 153 } 154 155 public void test_HprofData_addStackTrace() throws Exception { 156 Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>(); 157 HprofData hprofData = new HprofData(stackTraces); 158 159 // should have no samples to start 160 assertEquals(0, hprofData.getSamples().size()); 161 162 // attempt to add a stack for a non-existent thread, should fail 163 final int stackTraceId = 1; 164 final int threadId = 2; 165 final int objectId = 3; 166 final int sampleCount = 4; 167 StackTraceElement[] stackFrames = new Throwable().getStackTrace(); 168 final int[] countCell = new int[] { 4 }; 169 StackTrace stackTrace = new StackTrace(stackTraceId, threadId, stackFrames); 170 try { 171 hprofData.addStackTrace(stackTrace, countCell); 172 fail(); 173 } catch (IllegalArgumentException expected) { 174 } 175 176 // add the thread and add the event 177 ThreadEvent start = ThreadEvent.start(objectId, threadId, 178 "thread-name", "thread-group", "parent-group"); 179 hprofData.addThreadEvent(start); 180 hprofData.addStackTrace(stackTrace, countCell); 181 Set<Sample> samples = hprofData.getSamples(); 182 assertNotNull(samples); 183 assertNotSame(samples, hprofData.getSamples()); 184 assertEquals(1, samples.size()); 185 Sample sample = samples.iterator().next(); 186 assertNotNull(sample); 187 assertEquals(stackTrace, sample.stackTrace); 188 assertEquals(sampleCount, sample.count); 189 test_HprofData(hprofData, true); 190 191 // confirm we can mutate the sample count, but that its not 192 // visible in the current sample, but it will be visible in a 193 // new one. 194 countCell[0] += 42; 195 assertEquals(sampleCount, sample.count); 196 Sample sample2 = hprofData.getSamples().iterator().next(); 197 assertEquals(sampleCount + 42, sample2.count); 198 test_HprofData(hprofData, true); 199 200 // try to reuse the stackTraceId, should fail 201 try { 202 hprofData.addStackTrace(stackTrace, countCell); 203 fail(); 204 } catch (IllegalArgumentException expected) { 205 } 206 assertEquals(1, hprofData.getSamples().size()); 207 test_HprofData(hprofData, true); 208 209 } 210 211 private void test_HprofData(HprofData hprofData, boolean strict) throws Exception { 212 assertHprofData(hprofData, strict); 213 test_HprofData_ascii(hprofData); 214 test_HprofData_binary(hprofData, strict); 215 } 216 217 /** 218 * Assert general properities of HprofData hold true. 219 */ 220 private void assertHprofData(HprofData hprofData, boolean strict) throws Exception { 221 List<ThreadEvent> threadHistory = hprofData.getThreadHistory(); 222 assertNotNull(threadHistory); 223 Set<Integer> threadsSeen = new HashSet<Integer>(); 224 Set<Integer> threadsActive = new HashSet<Integer>(); 225 for (ThreadEvent event : threadHistory) { 226 assertNotNull(event); 227 assertNotNull(event.type); 228 switch (event.type) { 229 case START: 230 assertNotNull(event.threadName); 231 assertTrue(threadsActive.add(event.threadId)); 232 assertTrue(threadsSeen.add(event.threadId)); 233 break; 234 case END: 235 assertEquals(-1, event.objectId); 236 assertNull(event.threadName); 237 assertNull(event.groupName); 238 assertNull(event.parentGroupName); 239 if (strict) { 240 assertTrue(threadsActive.remove(event.threadId)); 241 } 242 break; 243 } 244 } 245 246 Set<Sample> samples = hprofData.getSamples(); 247 assertNotNull(samples); 248 for (Sample sample : samples) { 249 assertNotNull(sample); 250 assertTrue(sample.count > 0); 251 assertNotNull(sample.stackTrace); 252 assertTrue(sample.stackTrace.stackTraceId != -1); 253 assertTrue(threadsSeen.contains(sample.stackTrace.getThreadId())); 254 assertNotNull(sample.stackTrace.getStackFrames()); 255 } 256 } 257 258 /** 259 * Convert to HprofData to ASCII to see if it triggers any exceptions 260 */ 261 private void test_HprofData_ascii(HprofData hprofData) throws Exception { 262 ByteArrayOutputStream out = new ByteArrayOutputStream(); 263 AsciiHprofWriter.write(hprofData, out); 264 assertFalse(out.toByteArray().length == 0); 265 } 266 267 /** 268 * Convert to HprofData to binary and then reparse as to 269 * HprofData. Make sure the accessible data is equivalent. 270 */ 271 private void test_HprofData_binary(HprofData hprofData, boolean strict) throws Exception { 272 273 ByteArrayOutputStream out = new ByteArrayOutputStream(); 274 BinaryHprofWriter.write(hprofData, out); 275 out.close(); 276 277 byte[] bytes = out.toByteArray(); 278 assertFalse(bytes.length == 0); 279 if (false) { 280 File file = new File("/sdcard/java.hprof"); 281 OutputStream debug = new FileOutputStream(file); 282 debug.write(bytes); 283 debug.close(); 284 System.out.println("Wrote binary hprof data to " + file); 285 } 286 287 InputStream in = new ByteArrayInputStream(bytes); 288 BinaryHprofReader reader = new BinaryHprofReader(in); 289 assertTrue(reader.getStrict()); 290 reader.read(); 291 in.close(); 292 assertEquals("JAVA PROFILE 1.0.2", reader.getVersion()); 293 assertNotNull(reader.getHprofData()); 294 295 HprofData parsed = reader.getHprofData(); 296 assertHprofData(hprofData, strict); 297 298 assertEquals(Long.toHexString(hprofData.getStartMillis()), 299 Long.toHexString(parsed.getStartMillis())); 300 assertEquals(Long.toHexString(hprofData.getFlags()), 301 Long.toHexString(parsed.getFlags())); 302 assertEquals(Long.toHexString(hprofData.getDepth()), 303 Long.toHexString(parsed.getDepth())); 304 assertEquals(hprofData.getThreadHistory(), 305 parsed.getThreadHistory()); 306 assertEquals(hprofData.getSamples(), 307 parsed.getSamples()); 308 } 309} 310