| /* |
| * Copyright (C) 2010 The Android Open Source Project |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package dalvik.system.profiler; |
| |
| import dalvik.system.profiler.AsciiHprofWriter; |
| import dalvik.system.profiler.BinaryHprofReader; |
| import dalvik.system.profiler.BinaryHprofWriter; |
| import dalvik.system.profiler.HprofData.Sample; |
| import dalvik.system.profiler.HprofData.StackTrace; |
| import dalvik.system.profiler.HprofData.ThreadEvent; |
| import dalvik.system.profiler.HprofData; |
| import dalvik.system.profiler.SamplingProfiler.ThreadSet; |
| import java.io.ByteArrayInputStream; |
| import java.io.ByteArrayOutputStream; |
| import java.io.File; |
| import java.io.FileOutputStream; |
| import java.io.InputStream; |
| import java.io.OutputStream; |
| import java.math.BigInteger; |
| import java.security.KeyPairGenerator; |
| import java.security.SecureRandom; |
| import java.util.Arrays; |
| import java.util.HashMap; |
| import java.util.HashSet; |
| import java.util.List; |
| import java.util.Map; |
| import java.util.Set; |
| import javax.crypto.spec.DHParameterSpec; |
| import junit.framework.TestCase; |
| |
| public class SamplingProfilerTest extends TestCase { |
| |
| /** |
| * Run the SamplingProfiler to gather some data on an actual |
| * computation, then assert that it looks correct with test_HprofData. |
| */ |
| public void test_SamplingProfiler() throws Exception { |
| ThreadSet threadSet = SamplingProfiler.newArrayThreadSet(Thread.currentThread()); |
| SamplingProfiler profiler = new SamplingProfiler(12, threadSet); |
| profiler.start(100); |
| toBeMeasured(); |
| profiler.stop(); |
| profiler.shutdown(); |
| test_HprofData(profiler.getHprofData(), true); |
| } |
| |
| private static final String P_STR = |
| "9494fec095f3b85ee286542b3836fc81a5dd0a0349b4c239dd38744d488cf8e3" |
| + "1db8bcb7d33b41abb9e5a33cca9144b1cef332c94bf0573bf047a3aca98cdf3b"; |
| private static final String G_STR = |
| "98ab7c5c431479d8645e33aa09758e0907c78747798d0968576f9877421a9089" |
| + "756f7876e76590b76765645c987976d764dd4564698a87585e64554984bb4445" |
| + "76e5764786f875b4456c"; |
| |
| private static final byte[] P = new BigInteger(P_STR,16).toByteArray(); |
| private static final byte[] G = new BigInteger(G_STR,16).toByteArray(); |
| |
| private static void toBeMeasured () throws Exception { |
| long start = System.currentTimeMillis(); |
| for (int i = 0; i < 10000; i++) { |
| BigInteger p = new BigInteger(P); |
| BigInteger g = new BigInteger(G); |
| KeyPairGenerator gen = KeyPairGenerator.getInstance("DH"); |
| gen.initialize(new DHParameterSpec(p, g), new SecureRandom()); |
| } |
| long end = System.currentTimeMillis(); |
| } |
| |
| public void test_HprofData_null() throws Exception { |
| try { |
| new HprofData(null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| } |
| |
| public void test_HprofData_empty() throws Exception { |
| Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>(); |
| HprofData hprofData = new HprofData(stackTraces); |
| test_HprofData(hprofData, true); |
| } |
| |
| public void test_HprofData_timeMillis() throws Exception { |
| Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>(); |
| HprofData hprofData = new HprofData(stackTraces); |
| long now = System.currentTimeMillis(); |
| hprofData.setStartMillis(now); |
| assertEquals(now, hprofData.getStartMillis()); |
| test_HprofData(hprofData, true); |
| } |
| |
| public void test_HprofData_addThreadEvent_null() throws Exception { |
| Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>(); |
| HprofData hprofData = new HprofData(stackTraces); |
| try { |
| hprofData.addThreadEvent(null); |
| fail(); |
| } catch (NullPointerException expected) { |
| } |
| test_HprofData(hprofData, true); |
| } |
| |
| public void test_HprofData_addThreadEvent() throws Exception { |
| Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>(); |
| HprofData hprofData = new HprofData(stackTraces); |
| |
| // should have nothing in the thread history to start |
| assertEquals(0, hprofData.getThreadHistory().size()); |
| |
| // add thread 1 |
| final int threadId = 1; |
| final int objectId = 2; |
| ThreadEvent start1 = ThreadEvent.start(objectId, threadId, |
| "thread-name", "thread-group", "parent-group"); |
| hprofData.addThreadEvent(start1); |
| assertEquals(Arrays.asList(start1), hprofData.getThreadHistory()); |
| test_HprofData(hprofData, true); |
| |
| // remove thread 2, which should not exist (but that's okay on the RI) |
| ThreadEvent end2 = ThreadEvent.end(threadId+1); |
| hprofData.addThreadEvent(end2); |
| assertEquals(Arrays.asList(start1, end2), hprofData.getThreadHistory()); |
| test_HprofData(hprofData, false); // non-strict from here down because of this RI data |
| |
| // remove thread 1, which should exist |
| ThreadEvent end1 = ThreadEvent.end(threadId); |
| hprofData.addThreadEvent(end1); |
| assertEquals(Arrays.asList(start1, end2, end1), hprofData.getThreadHistory()); |
| test_HprofData(hprofData, false); |
| |
| // remove thread 1 again, which should not exist (its not okay to have end followed by end) |
| try { |
| hprofData.addThreadEvent(ThreadEvent.end(threadId)); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| assertEquals(Arrays.asList(start1, end2, end1), hprofData.getThreadHistory()); |
| test_HprofData(hprofData, false); |
| } |
| |
| public void test_HprofData_addStackTrace() throws Exception { |
| Map<StackTrace, int[]> stackTraces = new HashMap<StackTrace, int[]>(); |
| HprofData hprofData = new HprofData(stackTraces); |
| |
| // should have no samples to start |
| assertEquals(0, hprofData.getSamples().size()); |
| |
| // attempt to add a stack for a non-existent thread, should fail |
| final int stackTraceId = 1; |
| final int threadId = 2; |
| final int objectId = 3; |
| final int sampleCount = 4; |
| StackTraceElement[] stackFrames = new Throwable().getStackTrace(); |
| final int[] countCell = new int[] { 4 }; |
| StackTrace stackTrace = new StackTrace(stackTraceId, threadId, stackFrames); |
| try { |
| hprofData.addStackTrace(stackTrace, countCell); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| |
| // add the thread and add the event |
| ThreadEvent start = ThreadEvent.start(objectId, threadId, |
| "thread-name", "thread-group", "parent-group"); |
| hprofData.addThreadEvent(start); |
| hprofData.addStackTrace(stackTrace, countCell); |
| Set<Sample> samples = hprofData.getSamples(); |
| assertNotNull(samples); |
| assertNotSame(samples, hprofData.getSamples()); |
| assertEquals(1, samples.size()); |
| Sample sample = samples.iterator().next(); |
| assertNotNull(sample); |
| assertEquals(stackTrace, sample.stackTrace); |
| assertEquals(sampleCount, sample.count); |
| test_HprofData(hprofData, true); |
| |
| // confirm we can mutate the sample count, but that its not |
| // visible in the current sample, but it will be visible in a |
| // new one. |
| countCell[0] += 42; |
| assertEquals(sampleCount, sample.count); |
| Sample sample2 = hprofData.getSamples().iterator().next(); |
| assertEquals(sampleCount + 42, sample2.count); |
| test_HprofData(hprofData, true); |
| |
| // try to reuse the stackTraceId, should fail |
| try { |
| hprofData.addStackTrace(stackTrace, countCell); |
| fail(); |
| } catch (IllegalArgumentException expected) { |
| } |
| assertEquals(1, hprofData.getSamples().size()); |
| test_HprofData(hprofData, true); |
| |
| } |
| |
| private void test_HprofData(HprofData hprofData, boolean strict) throws Exception { |
| assertHprofData(hprofData, strict); |
| test_HprofData_ascii(hprofData); |
| test_HprofData_binary(hprofData, strict); |
| } |
| |
| /** |
| * Assert general properities of HprofData hold true. |
| */ |
| private void assertHprofData(HprofData hprofData, boolean strict) throws Exception { |
| List<ThreadEvent> threadHistory = hprofData.getThreadHistory(); |
| assertNotNull(threadHistory); |
| Set<Integer> threadsSeen = new HashSet<Integer>(); |
| Set<Integer> threadsActive = new HashSet<Integer>(); |
| for (ThreadEvent event : threadHistory) { |
| assertNotNull(event); |
| assertNotNull(event.type); |
| switch (event.type) { |
| case START: |
| assertNotNull(event.threadName); |
| assertTrue(threadsActive.add(event.threadId)); |
| assertTrue(threadsSeen.add(event.threadId)); |
| break; |
| case END: |
| assertEquals(-1, event.objectId); |
| assertNull(event.threadName); |
| assertNull(event.groupName); |
| assertNull(event.parentGroupName); |
| if (strict) { |
| assertTrue(threadsActive.remove(event.threadId)); |
| } |
| break; |
| } |
| } |
| |
| Set<Sample> samples = hprofData.getSamples(); |
| assertNotNull(samples); |
| for (Sample sample : samples) { |
| assertNotNull(sample); |
| assertTrue(sample.count > 0); |
| assertNotNull(sample.stackTrace); |
| assertTrue(sample.stackTrace.stackTraceId != -1); |
| assertTrue(threadsSeen.contains(sample.stackTrace.getThreadId())); |
| assertNotNull(sample.stackTrace.getStackFrames()); |
| } |
| } |
| |
| /** |
| * Convert to HprofData to ASCII to see if it triggers any exceptions |
| */ |
| private void test_HprofData_ascii(HprofData hprofData) throws Exception { |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| AsciiHprofWriter.write(hprofData, out); |
| assertFalse(out.toByteArray().length == 0); |
| } |
| |
| /** |
| * Convert to HprofData to binary and then reparse as to |
| * HprofData. Make sure the accessible data is equivalent. |
| */ |
| private void test_HprofData_binary(HprofData hprofData, boolean strict) throws Exception { |
| |
| ByteArrayOutputStream out = new ByteArrayOutputStream(); |
| BinaryHprofWriter.write(hprofData, out); |
| out.close(); |
| |
| byte[] bytes = out.toByteArray(); |
| assertFalse(bytes.length == 0); |
| if (false) { |
| File file = new File("/sdcard/java.hprof"); |
| OutputStream debug = new FileOutputStream(file); |
| debug.write(bytes); |
| debug.close(); |
| System.out.println("Wrote binary hprof data to " + file); |
| } |
| |
| InputStream in = new ByteArrayInputStream(bytes); |
| BinaryHprofReader reader = new BinaryHprofReader(in); |
| assertTrue(reader.getStrict()); |
| reader.read(); |
| in.close(); |
| assertEquals("JAVA PROFILE 1.0.2", reader.getVersion()); |
| assertNotNull(reader.getHprofData()); |
| |
| HprofData parsed = reader.getHprofData(); |
| assertHprofData(hprofData, strict); |
| |
| assertEquals(Long.toHexString(hprofData.getStartMillis()), |
| Long.toHexString(parsed.getStartMillis())); |
| assertEquals(Long.toHexString(hprofData.getFlags()), |
| Long.toHexString(parsed.getFlags())); |
| assertEquals(Long.toHexString(hprofData.getDepth()), |
| Long.toHexString(parsed.getDepth())); |
| assertEquals(hprofData.getThreadHistory(), |
| parsed.getThreadHistory()); |
| assertEquals(hprofData.getSamples(), |
| parsed.getSamples()); |
| } |
| } |