Home | History | Annotate | Download | only in src
      1 /*
      2  * Copyright (C) 2017 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 import java.lang.reflect.Method;
     18 import java.util.Enumeration;
     19 
     20 import java.nio.file.Files;
     21 import java.nio.file.Paths;
     22 
     23 /**
     24  * DexFile tests (Dalvik-specific).
     25  */
     26 public class Main {
     27     private static final String CLASS_PATH =
     28         System.getenv("DEX_LOCATION") + "/071-dexfile-map-clean-ex.jar";
     29 
     30     /**
     31      * Prep the environment then run the test.
     32      */
     33     public static void main(String[] args) throws Exception {
     34         // Load the dex file, this is a pre-requisite to mmap-ing it in.
     35         Class<?> AnotherClass = testDexFile();
     36         // Check that the memory maps are clean.
     37         testDexMemoryMaps();
     38 
     39         // Prevent garbage collector from collecting our DexFile
     40         // (and unmapping too early) by using it after we finish
     41         // our verification.
     42         AnotherClass.newInstance();
     43     }
     44 
     45     private static boolean checkSmapsEntry(String[] smapsLines, int offset) {
     46       String nameDescription = smapsLines[offset];
     47       String[] split = nameDescription.split(" ");
     48 
     49       String permissions = split[1];
     50       // Mapped as read-only + anonymous.
     51       if (!permissions.startsWith("r--p")) {
     52         return false;
     53       }
     54 
     55       boolean validated = false;
     56 
     57       // We have the right entry, now make sure it's valid.
     58       for (int i = offset; i < smapsLines.length; ++i) {
     59         String line = smapsLines[i];
     60 
     61         if (line.startsWith("Shared_Dirty") || line.startsWith("Private_Dirty")) {
     62           String lineTrimmed = line.trim();
     63           String[] lineSplit = lineTrimmed.split(" +");
     64 
     65           String sizeUsuallyInKb = lineSplit[lineSplit.length - 2];
     66 
     67           sizeUsuallyInKb = sizeUsuallyInKb.trim();
     68 
     69           if (!sizeUsuallyInKb.equals("0")) {
     70             System.out.println(
     71                 "ERROR: Memory mapping for " + CLASS_PATH + " is unexpectedly dirty");
     72             System.out.println(line);
     73           } else {
     74             validated = true;
     75           }
     76         }
     77 
     78         // VmFlags marks the "end" of an smaps entry.
     79         if (line.startsWith("VmFlags")) {
     80           break;
     81         }
     82       }
     83 
     84       if (validated) {
     85         System.out.println("Secondary dexfile mmap is clean");
     86       } else {
     87         System.out.println("ERROR: Memory mapping is missing Shared_Dirty/Private_Dirty entries");
     88       }
     89 
     90       return true;
     91     }
     92 
     93     // This test takes relies on dex2oat being skipped.
     94     // (enforced in 'run' file by using '--no-dex2oat'
     95     //
     96     // This could happen in a non-test situation
     97     // if a secondary dex file is loaded (but not yet maintenance-mode compiled)
     98     // with JIT.
     99     //
    100     // Or it could also happen if a secondary dex file is loaded and forced
    101     // into running into the interpreter (e.g. duplicate classes).
    102     //
    103     // Rather than relying on those weird fallbacks,
    104     // we force the runtime not to dex2oat the dex file to ensure
    105     // this test is repeatable and less brittle.
    106     private static void testDexMemoryMaps() throws Exception {
    107         // Ensure that the secondary dex file is mapped clean (directly from JAR file).
    108         String smaps = new String(Files.readAllBytes(Paths.get("/proc/self/smaps")));
    109 
    110         String[] smapsLines = smaps.split("\n");
    111         boolean found = true;
    112         for (int i = 0; i < smapsLines.length; ++i) {
    113           if (smapsLines[i].contains(CLASS_PATH)) {
    114             if (checkSmapsEntry(smapsLines, i)) {
    115               return;
    116             } // else we found the wrong one, keep going.
    117           }
    118         }
    119 
    120         // Error case:
    121         System.out.println("Could not find " + CLASS_PATH + " RO-anonymous smaps entry");
    122         System.out.println(smaps);
    123     }
    124 
    125     private static Class<?> testDexFile() throws Exception {
    126         ClassLoader classLoader = Main.class.getClassLoader();
    127         Class<?> DexFile = classLoader.loadClass("dalvik.system.DexFile");
    128         Method DexFile_loadDex = DexFile.getMethod("loadDex",
    129                                                    String.class,
    130                                                    String.class,
    131                                                    Integer.TYPE);
    132         Method DexFile_entries = DexFile.getMethod("entries");
    133         Object dexFile = DexFile_loadDex.invoke(null, CLASS_PATH, null, 0);
    134         Enumeration<String> e = (Enumeration<String>) DexFile_entries.invoke(dexFile);
    135         while (e.hasMoreElements()) {
    136             String className = e.nextElement();
    137             System.out.println(className);
    138         }
    139 
    140         Method DexFile_loadClass = DexFile.getMethod("loadClass",
    141                                                      String.class,
    142                                                      ClassLoader.class);
    143         Class<?> AnotherClass = (Class<?>)DexFile_loadClass.invoke(dexFile,
    144             "Another", Main.class.getClassLoader());
    145         return AnotherClass;
    146     }
    147 }
    148