/* * Copyright (C) 2014 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.ddmlib.allocations; import com.android.ddmlib.AllocationInfo; import com.android.ddmlib.AllocationsParser; import com.google.common.base.Charsets; import junit.framework.TestCase; import java.io.IOException; import java.net.URL; import java.nio.ByteBuffer; public class AllocationsParserTest extends TestCase { public void testParsingOnNoAllocations() throws IOException { ByteBuffer data = putAllocationInfo(new String[0], new String[0], new String[0], new int[0][], new short[0][][]); assertEquals(0, AllocationsParser.parse(data).length); } public void testParsingOnOneAllocationWithoutStackTrace() throws IOException { ByteBuffer data = putAllocationInfo(new String[]{"path.Foo"}, new String[0], new String[0], new int[][]{{32, 4, 0, 0}}, new short[][][]{{}}); AllocationInfo[] info = AllocationsParser.parse(data); assertEquals(1, info.length); AllocationInfo alloc = info[0]; checkEntry(1, "path.Foo", 32, 4, alloc); checkFirstTrace(null, null, alloc); assertEquals(0, alloc.getStackTrace().length); } public void testParsingOnOneAllocationWithStackTrace() throws IOException { ByteBuffer data = putAllocationInfo(new String[]{"path.Foo", "path.Bar", "path.Baz"}, new String[]{"foo", "bar", "baz"}, new String[]{"Foo.java", "Bar.java"}, new int[][]{{64, 0, 1, 3}}, new short[][][]{{{1, 1, 1, -1}, {2, 0, 1, 2000}, {0, 2, 0, 10}}}); AllocationInfo[] info = AllocationsParser.parse(data); assertEquals(1, info.length); AllocationInfo alloc = info[0]; checkEntry(1, "path.Bar", 64, 0, alloc); checkFirstTrace("path.Bar", "bar", alloc); StackTraceElement[] elems = alloc.getStackTrace(); assertEquals(3, elems.length); checkStackFrame("path.Bar", "bar", "Bar.java", -1, elems[0]); checkStackFrame("path.Baz", "foo", "Bar.java", 2000, elems[1]); checkStackFrame("path.Foo", "baz", "Foo.java", 10, elems[2]); } public void testParsing() throws IOException { ByteBuffer data = putAllocationInfo(new String[]{"path.Red", "path.Green", "path.Blue", "path.LightCanaryishGrey"}, new String[]{"eatTiramisu", "failUnitTest", "watchCatVideos", "passGo", "collectFakeMoney", "findWaldo"}, new String[]{"Red.java", "SomewhatBlue.java", "LightCanaryishGrey.java"}, new int[][]{{128, 8, 0, 2}, {16, 8, 2, 1}, {42, 2, 1, 3}}, new short[][][]{{{1, 0, 1, 100}, {2, 5, 1, -2}}, {{0, 1, 0, -1}}, {{3, 4, 2, 10001}, {0, 3, 0, 0}, {2, 2, 1, 16}}}); AllocationInfo[] info = AllocationsParser.parse(data); assertEquals(3, info.length); AllocationInfo alloc1 = info[0]; checkEntry(3, "path.Red", 128, 8, alloc1); checkFirstTrace("path.Green", "eatTiramisu", alloc1); StackTraceElement[] elems1 = alloc1.getStackTrace(); assertEquals(2, elems1.length); checkStackFrame("path.Green", "eatTiramisu", "SomewhatBlue.java", 100, elems1[0]); checkStackFrame("path.Blue", "findWaldo", "SomewhatBlue.java", -2, elems1[1]); AllocationInfo alloc2 = info[1]; checkEntry(2, "path.Blue", 16, 8, alloc2); checkFirstTrace("path.Red", "failUnitTest", alloc2); StackTraceElement[] elems2 = alloc2.getStackTrace(); assertEquals(1, elems2.length); checkStackFrame("path.Red", "failUnitTest", "Red.java", -1, elems2[0]); AllocationInfo alloc3 = info[2]; checkEntry(1, "path.Green", 42, 2, alloc3); checkFirstTrace("path.LightCanaryishGrey", "collectFakeMoney", alloc3); StackTraceElement[] elems3 = alloc3.getStackTrace(); assertEquals(3, elems3.length); checkStackFrame("path.LightCanaryishGrey", "collectFakeMoney", "LightCanaryishGrey.java", 10001, elems3[0]); checkStackFrame("path.Red", "passGo", "Red.java", 0, elems3[1]); checkStackFrame("path.Blue", "watchCatVideos", "SomewhatBlue.java", 16, elems3[2]); } private static void checkEntry(int order, String className, int size, int thread, AllocationInfo alloc) { assertEquals(order, alloc.getAllocNumber()); assertEquals(className, alloc.getAllocatedClass()); assertEquals(size, alloc.getSize()); assertEquals(thread, alloc.getThreadId()); } private static void checkFirstTrace(String className, String methodName, AllocationInfo alloc) { assertEquals(className, alloc.getFirstTraceClassName()); assertEquals(methodName, alloc.getFirstTraceMethodName()); } private static void checkStackFrame(String className, String methodName, String fileName, int lineNumber, StackTraceElement elem) { assertEquals(className, elem.getClassName()); assertEquals(methodName, elem.getMethodName()); assertEquals(fileName, elem.getFileName()); assertEquals(lineNumber, elem.getLineNumber()); } public static ByteBuffer putAllocationInfo(String[] classNames, String[] methodNames, String[] fileNames, int[][] entries, short[][][] stackFrames) throws IOException { byte msgHdrLen = 15, entryHdrLen = 9, stackFrameLen = 8; // Number of bytes from start of message to string tables int offset = msgHdrLen; for (int[] entry : entries) { offset += entryHdrLen + (stackFrameLen * entry[3]); } // Number of bytes in string tables int strNamesLen = 0; for (String name : classNames) { strNamesLen += 4 + (2 * name.length()); } for (String name : methodNames) { strNamesLen += 4 + (2 * name.length()); } for (String name : fileNames) { strNamesLen += 4 + (2 * name.length()); } ByteBuffer data = ByteBuffer.allocate(offset + strNamesLen); data.put(new byte[]{msgHdrLen, entryHdrLen, stackFrameLen}); data.putShort((short) entries.length); data.putInt(offset); data.putShort((short) classNames.length); data.putShort((short) methodNames.length); data.putShort((short) fileNames.length); for (short i = 0; i < entries.length; ++i) { data.putInt(entries[i][0]); // total alloc size data.putShort((short) entries[i][1]); // thread id data.putShort((short) entries[i][2]); // allocated class index data.put((byte) entries[i][3]); // stack depth short[][] frames = stackFrames[i]; for (short[] frame : frames) { data.putShort(frame[0]); // class name data.putShort(frame[1]); // method name data.putShort(frame[2]); // source file data.putShort(frame[3]); // line number } } for (String name : classNames) { data.putInt(name.length()); data.put(strToBytes(name)); } for (String name : methodNames) { data.putInt(name.length()); data.put(strToBytes(name)); } for (String name : fileNames) { data.putInt(name.length()); data.put(strToBytes(name)); } data.rewind(); return data; } private static byte[] strToBytes(String str) { return str.getBytes(Charsets.UTF_16BE); } }