Home | History | Annotate | Download | only in test
      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