Home | History | Annotate | Download | only in src
      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 import java.io.BufferedReader;
     18 import java.io.FileReader;
     19 import java.io.InputStreamReader;
     20 import java.util.Arrays;
     21 import java.util.Comparator;
     22 
     23 public class Main extends Base implements Comparator<Main> {
     24   // Whether to test local unwinding.
     25   private boolean testLocal;
     26 
     27   // Unwinding another process, modelling debuggerd.
     28   private boolean testRemote;
     29 
     30   // We fork ourself to create the secondary process for remote unwinding.
     31   private boolean secondary;
     32 
     33   // Expect the symbols to contain full method signatures including parameters.
     34   private boolean fullSignatures;
     35 
     36   private boolean passed;
     37 
     38   public Main(String[] args) throws Exception {
     39       System.loadLibrary(args[0]);
     40       for (String arg : args) {
     41           if (arg.equals("--test-local")) {
     42               testLocal = true;
     43           }
     44           if (arg.equals("--test-remote")) {
     45               testRemote = true;
     46           }
     47           if (arg.equals("--secondary")) {
     48               secondary = true;
     49           }
     50           if (arg.equals("--full-signatures")) {
     51               fullSignatures = true;
     52           }
     53       }
     54       if (!testLocal && !testRemote) {
     55           System.out.println("No test selected.");
     56       }
     57   }
     58 
     59   public static void main(String[] args) throws Exception {
     60       new Main(args).runBase();
     61   }
     62 
     63   public void runImpl() {
     64       if (secondary) {
     65           if (!testRemote) {
     66               throw new RuntimeException("Should not be running secondary!");
     67           }
     68           runSecondary();
     69       } else {
     70           runPrimary();
     71       }
     72   }
     73 
     74   private void runSecondary() {
     75       foo();
     76       throw new RuntimeException("Didn't expect to get back...");
     77   }
     78 
     79   private void runPrimary() {
     80       // First do the in-process unwinding.
     81       if (testLocal && !foo()) {
     82           System.out.println("Unwinding self failed.");
     83       }
     84 
     85       if (!testRemote) {
     86           // Skip the remote step.
     87           return;
     88       }
     89 
     90       // Fork the secondary.
     91       String[] cmdline = getCmdLine();
     92       String[] secCmdLine = new String[cmdline.length + 1];
     93       System.arraycopy(cmdline, 0, secCmdLine, 0, cmdline.length);
     94       secCmdLine[secCmdLine.length - 1] = "--secondary";
     95       Process p = exec(secCmdLine);
     96 
     97       try {
     98           int pid = getPid(p);
     99           if (pid <= 0) {
    100               throw new RuntimeException("Couldn't parse process");
    101           }
    102 
    103           // Wait until the forked process had time to run until its sleep phase.
    104           BufferedReader lineReader;
    105           try {
    106               InputStreamReader stdout = new InputStreamReader(p.getInputStream(), "UTF-8");
    107               lineReader = new BufferedReader(stdout);
    108               while (!lineReader.readLine().contains("Going to sleep")) {
    109               }
    110           } catch (Exception e) {
    111               throw new RuntimeException(e);
    112           }
    113 
    114           if (!unwindOtherProcess(fullSignatures, pid)) {
    115               System.out.println("Unwinding other process failed.");
    116 
    117               // In this case, log all the output.
    118               // Note: this is potentially non-terminating code, if the secondary is totally stuck.
    119               //       We rely on the run-test timeout infrastructure to terminate the primary in
    120               //       such a case.
    121               try {
    122                   String tmp;
    123                   System.out.println("Output from the secondary:");
    124                   while ((tmp = lineReader.readLine()) != null) {
    125                       System.out.println("Secondary: " + tmp);
    126                   }
    127               } catch (Exception e) {
    128                   e.printStackTrace(System.out);
    129               }
    130           }
    131 
    132           try {
    133               lineReader.close();
    134           } catch (Exception e) {
    135               e.printStackTrace(System.out);
    136           }
    137       } finally {
    138           // Kill the forked process if it is not already dead.
    139           p.destroy();
    140       }
    141   }
    142 
    143   private static Process exec(String[] args) {
    144       try {
    145           return Runtime.getRuntime().exec(args);
    146       } catch (Exception exc) {
    147           throw new RuntimeException(exc);
    148       }
    149   }
    150 
    151   private static int getPid(Process p) {
    152       // Could do reflection for the private pid field, but String parsing is easier.
    153       String s = p.toString();
    154       if (s.startsWith("Process[pid=")) {
    155           return Integer.parseInt(s.substring("Process[pid=".length(), s.indexOf(",")));
    156       } else {
    157           return -1;
    158       }
    159   }
    160 
    161   // Read /proc/self/cmdline to find the invocation command line (so we can fork another runtime).
    162   private static String[] getCmdLine() {
    163       try {
    164           BufferedReader in = new BufferedReader(new FileReader("/proc/self/cmdline"));
    165           String s = in.readLine();
    166           in.close();
    167           return s.split("\0");
    168       } catch (Exception exc) {
    169           throw new RuntimeException(exc);
    170       }
    171   }
    172 
    173   public boolean foo() {
    174       // Call bar via Arrays.binarySearch.
    175       // This tests that we can unwind from framework code.
    176       Main[] array = { this, this, this };
    177       Arrays.binarySearch(array, 0, 3, this /* value */, this /* comparator */);
    178       return passed;
    179   }
    180 
    181   public int compare(Main lhs, Main rhs) {
    182       passed = bar(secondary);
    183       // Returning "equal" ensures that we terminate search
    184       // after first item and thus call bar() only once.
    185       return 0;
    186   }
    187 
    188   public boolean bar(boolean b) {
    189       if (b) {
    190           return sleep(2, b, 1.0);
    191       } else {
    192           return unwindInProcess(fullSignatures, 1, b);
    193       }
    194   }
    195 
    196   // Native functions. Note: to avoid deduping, they must all have different signatures.
    197 
    198   public native boolean sleep(int i, boolean b, double dummy);
    199 
    200   public native boolean unwindInProcess(boolean fullSignatures, int i, boolean b);
    201   public native boolean unwindOtherProcess(boolean fullSignatures, int pid);
    202 }
    203