1 /* 2 * Copyright (C) 2015 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 17 package com.android.ahat; 18 19 import com.android.ahat.heapdump.AhatClassObj; 20 import com.android.ahat.heapdump.AhatInstance; 21 import com.android.ahat.heapdump.AhatSnapshot; 22 import com.android.ahat.heapdump.Diff; 23 import com.android.ahat.heapdump.FieldValue; 24 import com.android.ahat.heapdump.Value; 25 import com.android.tools.perflib.heap.ProguardMap; 26 import java.io.File; 27 import java.io.IOException; 28 import java.text.ParseException; 29 30 /** 31 * The TestDump class is used to get an AhatSnapshot for the test-dump 32 * program. 33 */ 34 public class TestDump { 35 // It can take on the order of a second to parse and process the test-dump 36 // hprof. To avoid repeating this overhead for each test case, we cache the 37 // loaded instance of TestDump and reuse it when possible. In theory the 38 // test cases should not be able to modify the cached snapshot in a way that 39 // is visible to other test cases. 40 private static TestDump mCachedTestDump = null; 41 42 // If the test dump fails to load the first time, it will likely fail every 43 // other test we try. Rather than having to wait a potentially very long 44 // time for test dump loading to fail over and over again, record when it 45 // fails and don't try to load it again. 46 private static boolean mTestDumpFailed = false; 47 48 private AhatSnapshot mSnapshot = null; 49 private AhatSnapshot mBaseline = null; 50 51 /** 52 * Load the test-dump.hprof and test-dump-base.hprof files. 53 * The location of the files are read from the system properties 54 * "ahat.test.dump.hprof" and "ahat.test.dump.base.hprof", which is expected 55 * to be set on the command line. 56 * The location of the proguard map for both hprof files is read from the 57 * system property "ahat.test.dump.map". For example: 58 * java -Dahat.test.dump.hprof=test-dump.hprof \ 59 * -Dahat.test.dump.base.hprof=test-dump-base.hprof \ 60 * -Dahat.test.dump.map=proguard.map \ 61 * -jar ahat-tests.jar 62 * 63 * An IOException is thrown if there is a failure reading the hprof files or 64 * the proguard map. 65 */ 66 private TestDump() throws IOException { 67 // TODO: Make use of the baseline hprof for tests. 68 String hprof = System.getProperty("ahat.test.dump.hprof"); 69 String hprofBase = System.getProperty("ahat.test.dump.base.hprof"); 70 71 String mapfile = System.getProperty("ahat.test.dump.map"); 72 ProguardMap map = new ProguardMap(); 73 try { 74 map.readFromFile(new File(mapfile)); 75 } catch (ParseException e) { 76 throw new IOException("Unable to load proguard map", e); 77 } 78 79 mSnapshot = AhatSnapshot.fromHprof(new File(hprof), map); 80 mBaseline = AhatSnapshot.fromHprof(new File(hprofBase), map); 81 Diff.snapshots(mSnapshot, mBaseline); 82 } 83 84 /** 85 * Get the AhatSnapshot for the test dump program. 86 */ 87 public AhatSnapshot getAhatSnapshot() { 88 return mSnapshot; 89 } 90 91 /** 92 * Get the baseline AhatSnapshot for the test dump program. 93 */ 94 public AhatSnapshot getBaselineAhatSnapshot() { 95 return mBaseline; 96 } 97 98 /** 99 * Returns the value of a field in the DumpedStuff instance in the 100 * snapshot for the test-dump program. 101 */ 102 public Value getDumpedValue(String name) { 103 return getDumpedValue(name, mSnapshot); 104 } 105 106 /** 107 * Returns the value of a field in the DumpedStuff instance in the 108 * baseline snapshot for the test-dump program. 109 */ 110 public Value getBaselineDumpedValue(String name) { 111 return getDumpedValue(name, mBaseline); 112 } 113 114 /** 115 * Returns the value of a field in the DumpedStuff instance in the 116 * given snapshot for the test-dump program. 117 */ 118 private Value getDumpedValue(String name, AhatSnapshot snapshot) { 119 AhatClassObj main = snapshot.findClass("Main"); 120 AhatInstance stuff = null; 121 for (FieldValue fields : main.getStaticFieldValues()) { 122 if ("stuff".equals(fields.getName())) { 123 stuff = fields.getValue().asAhatInstance(); 124 } 125 } 126 return stuff.getField(name); 127 } 128 129 /** 130 * Returns the value of a non-primitive field in the DumpedStuff instance in 131 * the snapshot for the test-dump program. 132 */ 133 public AhatInstance getDumpedAhatInstance(String name) { 134 Value value = getDumpedValue(name); 135 return value == null ? null : value.asAhatInstance(); 136 } 137 138 /** 139 * Returns the value of a non-primitive field in the DumpedStuff instance in 140 * the baseline snapshot for the test-dump program. 141 */ 142 public AhatInstance getBaselineDumpedAhatInstance(String name) { 143 Value value = getBaselineDumpedValue(name); 144 return value == null ? null : value.asAhatInstance(); 145 } 146 147 /** 148 * Get the test dump. 149 * An IOException is thrown if there is an error reading the test dump hprof 150 * file. 151 * To improve performance, this returns a cached instance of the TestDump 152 * when possible. 153 */ 154 public static synchronized TestDump getTestDump() throws IOException { 155 if (mTestDumpFailed) { 156 throw new RuntimeException("Test dump failed before, assuming it will again"); 157 } 158 159 if (mCachedTestDump == null) { 160 mTestDumpFailed = true; 161 mCachedTestDump = new TestDump(); 162 mTestDumpFailed = false; 163 } 164 return mCachedTestDump; 165 } 166 } 167