/* * Copyright (C) 2013 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 com.android.tools.perflib.vmtrace; import junit.framework.TestCase; import java.util.Arrays; import java.util.Iterator; import java.util.List; import java.util.concurrent.TimeUnit; public class CallStackReconstructorTest extends TestCase { public void testBasicCallStack() { CallStackReconstructor reconstructor = new CallStackReconstructor(0xff); reconstructor.addTraceAction(0x1, TraceAction.METHOD_ENTER, 10, 10); reconstructor.addTraceAction(0x1, TraceAction.METHOD_EXIT, 15, 15); Call topLevel = reconstructor.getTopLevel(); assertEquals(1, topLevel.getCallees().size()); assertEquals(0x1, topLevel.getCallees().get(0).getMethodId()); } private Call reconstructSampleCallStack() { CallStackReconstructor reconstructor = new CallStackReconstructor(0xff); reconstructor.addTraceAction(0x1, TraceAction.METHOD_ENTER, 10, 10); reconstructor.addTraceAction(0x2, TraceAction.METHOD_ENTER, 11, 11); reconstructor.addTraceAction(0x3, TraceAction.METHOD_ENTER, 12, 12); reconstructor.addTraceAction(0x3, TraceAction.METHOD_EXIT, 13, 13); reconstructor.addTraceAction(0x3, TraceAction.METHOD_ENTER, 14, 14); reconstructor.addTraceAction(0x3, TraceAction.METHOD_EXIT, 15, 15); reconstructor.addTraceAction(0x2, TraceAction.METHOD_EXIT, 16, 16); reconstructor.addTraceAction(0x5, TraceAction.METHOD_ENTER, 17, 17); reconstructor.addTraceAction(0x5, TraceAction.METHOD_EXIT, 18, 18); reconstructor.addTraceAction(0x1, TraceAction.METHOD_EXIT, 20, 20); reconstructor.addTraceAction(0x6, TraceAction.METHOD_ENTER, 21, 21); reconstructor.addTraceAction(0x6, TraceAction.METHOD_EXIT, 22, 22); return reconstructor.getTopLevel(); } public void testCallStack() { Call topLevel = reconstructSampleCallStack(); String callStack = topLevel.toString(); String expectedCallStack = " -> 255 -> 1 -> 2 -> 3\n" + " -> 3\n" + " -> 5\n" + " -> 6"; assertEquals(expectedCallStack, callStack); } public void testCallHierarchyIterator() { Call topLevel = reconstructSampleCallStack(); List<Integer> expectedSequence = Arrays.asList(255, 1, 2, 3, 3, 5, 6); int i = 0; Iterator<Call> it = topLevel.getCallHierarchyIterator(); while (it.hasNext()) { Call c = it.next(); long expectedMethodId = expectedSequence.get(i++); long actualMethodId = c.getMethodId(); assertEquals(expectedMethodId, actualMethodId); } } public void testInvalidTrace() { CallStackReconstructor reconstructor = new CallStackReconstructor(0xff); try { reconstructor.addTraceAction(0x1, TraceAction.METHOD_ENTER, 1, 1); reconstructor.addTraceAction(0x2, TraceAction.METHOD_EXIT, 1, 1); fail("Runtime Exception should've been thrown by the previous statement"); } catch (RuntimeException e) { // expected } } public void testMisMatchedCallStack() { CallStackReconstructor reconstructor = new CallStackReconstructor(0xff); reconstructor.addTraceAction(0x3, TraceAction.METHOD_EXIT, 1, 1); reconstructor.addTraceAction(0x2, TraceAction.METHOD_EXIT, 2, 2); reconstructor.addTraceAction(0x1, TraceAction.METHOD_EXIT, 3, 3); String callStack = reconstructor.getTopLevel().toString(); assertEquals(" -> 255 -> 1 -> 2 -> 3", callStack); } public void testCallStackDepths() { CallStackReconstructor reconstructor = new CallStackReconstructor(0xff); reconstructor.addTraceAction(0x1, TraceAction.METHOD_ENTER, 10, 10); reconstructor.addTraceAction(0x2, TraceAction.METHOD_ENTER, 11, 11); reconstructor.addTraceAction(0x3, TraceAction.METHOD_ENTER, 12, 12); reconstructor.addTraceAction(0x3, TraceAction.METHOD_EXIT, 13, 13); reconstructor.addTraceAction(0x3, TraceAction.METHOD_ENTER, 14, 14); reconstructor.addTraceAction(0x3, TraceAction.METHOD_EXIT, 15, 15); reconstructor.addTraceAction(0x2, TraceAction.METHOD_EXIT, 16, 16); reconstructor.addTraceAction(0x5, TraceAction.METHOD_ENTER, 17, 17); reconstructor.addTraceAction(0x5, TraceAction.METHOD_EXIT, 18, 18); reconstructor.addTraceAction(0x1, TraceAction.METHOD_EXIT, 20, 20); Call topLevel = reconstructor.getTopLevel(); assertEquals(1, topLevel.getCallees().size()); Call c = topLevel.getCallees().get(0); String actualDepths = c.format(new Call.Formatter() { @Override public String format(Call c) { return Integer.toString(c.getDepth()); } }); String expected = " -> 1 -> 2 -> 3\n" + " -> 3\n" + " -> 2"; assertEquals(expected, actualDepths); } public void testCallDurations() { CallStackReconstructor reconstructor = new CallStackReconstructor(0xff); reconstructor.addTraceAction(0x1, TraceAction.METHOD_ENTER, 10, 10); reconstructor.addTraceAction(0x2, TraceAction.METHOD_ENTER, 11, 11); reconstructor.addTraceAction(0x2, TraceAction.METHOD_EXIT, 14, 14); reconstructor.addTraceAction(0x1, TraceAction.METHOD_EXIT, 15, 15); List<Call> callees = reconstructor.getTopLevel().getCallees(); assertFalse(callees.isEmpty()); Call call = callees.get(0); assertEquals(15 - 10, call.getInclusiveTime(ClockType.THREAD, TimeUnit.MICROSECONDS)); assertEquals(15 - 10 - (14 - 11), call.getExclusiveTime(ClockType.THREAD, TimeUnit.MICROSECONDS)); } public void testMissingCallDurations() { CallStackReconstructor reconstructor = new CallStackReconstructor(0xff); // missing entry time for method 0x1, verify that it is computed as 1 less than // the entry time for its callee (method 0x2) reconstructor.addTraceAction(0x2, TraceAction.METHOD_ENTER, 11, 11); reconstructor.addTraceAction(0x2, TraceAction.METHOD_EXIT, 14, 14); reconstructor.addTraceAction(0x1, TraceAction.METHOD_EXIT, 15, 15); List<Call> callees = reconstructor.getTopLevel().getCallees(); assertFalse(callees.isEmpty()); Call call = callees.get(0); assertEquals(15 - (11 - 1), call.getInclusiveTime(ClockType.THREAD, TimeUnit.MICROSECONDS)); assertEquals(15 - (11 - 1) - (14 - 11), call.getExclusiveTime(ClockType.THREAD, TimeUnit.MICROSECONDS)); } /** * Verify that the model handles cases where the timings exceed {@link Integer#MAX_VALUE}, * but are still within the scope of an unsigned integer. */ public void testCallDurationOverflow() { CallStackReconstructor reconstructor = new CallStackReconstructor(0xff); reconstructor.addTraceAction(0x1, TraceAction.METHOD_ENTER, 0xfffffff0, 0xfffffff0); reconstructor.addTraceAction(0x2, TraceAction.METHOD_ENTER, 0xfffffff2, 0xfffffff2); reconstructor.addTraceAction(0x2, TraceAction.METHOD_EXIT, 0xfffffff4, 0xfffffff4); reconstructor.addTraceAction(0x1, TraceAction.METHOD_EXIT, 0xfffffff8, 0xfffffff8); List<Call> callees = reconstructor.getTopLevel().getCallees(); assertFalse(callees.isEmpty()); Call call = callees.get(0); assertEquals(8, call.getInclusiveTime(ClockType.THREAD, TimeUnit.MICROSECONDS)); assertEquals(6, call.getExclusiveTime(ClockType.THREAD, TimeUnit.MICROSECONDS)); } public void testRecursiveCalls() { CallStackReconstructor reconstructor = new CallStackReconstructor(0xff); reconstructor.addTraceAction(0x1, TraceAction.METHOD_ENTER, 1, 1); reconstructor.addTraceAction(0x2, TraceAction.METHOD_ENTER, 3, 3); reconstructor.addTraceAction(0x1, TraceAction.METHOD_ENTER, 4, 4); // recursive call, method id 1 reconstructor.addTraceAction(0x1, TraceAction.METHOD_EXIT, 5, 5); reconstructor.addTraceAction(0x2, TraceAction.METHOD_EXIT, 6, 6); reconstructor.addTraceAction(0x1, TraceAction.METHOD_EXIT, 8, 8); List<Call> callees = reconstructor.getTopLevel().getCallees(); assertEquals(1, callees.size()); Call call1 = callees.get(0); assertEquals(0x1, call1.getMethodId()); assertFalse(call1.isRecursive()); callees = call1.getCallees(); assertEquals(1, callees.size()); Call call2 = callees.get(0); assertEquals(0x2, call2.getMethodId()); assertFalse(call2.isRecursive()); callees = call2.getCallees(); assertEquals(1, callees.size()); Call call3 = callees.get(0); assertEquals(0x1, call3.getMethodId()); assertTrue(call3.isRecursive()); } }