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    */
TestDump()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    */
getAhatSnapshot()87   public AhatSnapshot getAhatSnapshot() {
88     return mSnapshot;
89   }
90 
91   /**
92    * Get the baseline AhatSnapshot for the test dump program.
93    */
getBaselineAhatSnapshot()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    */
getDumpedValue(String name)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    */
getBaselineDumpedValue(String name)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    */
getDumpedValue(String name, AhatSnapshot snapshot)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    */
getDumpedAhatInstance(String name)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    */
getBaselineDumpedAhatInstance(String name)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    */
getTestDump()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