1 /*
   2  * Copyright (c) 2015, Oracle and/or its affiliates. All rights reserved.
   3  * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
   4  *
   5  * This code is free software; you can redistribute it and/or modify it
   6  * under the terms of the GNU General Public License version 2 only, as
   7  * published by the Free Software Foundation.
   8  *
   9  * This code is distributed in the hope that it will be useful, but WITHOUT
  10  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  11  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
  12  * version 2 for more details (a copy is included in the LICENSE file that
  13  * accompanied this code).
  14  *
  15  * You should have received a copy of the GNU General Public License version
  16  * 2 along with this work; if not, write to the Free Software Foundation,
  17  * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
  18  *
  19  * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
  20  * or visit www.oracle.com if you need additional information or have any
  21  * questions.
  22  */
  23 
  24 package jdk.test.failurehandler.jtreg;
  25 
  26 import com.sun.javatest.regtest.OS;
  27 import com.sun.javatest.regtest.TimeoutHandler;
  28 import jdk.test.failurehandler.*;
  29 
  30 import java.io.File;
  31 import java.io.FileWriter;
  32 import java.io.IOException;
  33 import java.io.PrintWriter;
  34 import java.lang.reflect.Field;
  35 import java.nio.file.Path;
  36 
  37 /**
  38  * A timeout handler for jtreg, which gathers information about the timed out
  39  * process and its children.
  40  */
  41 public class GatherProcessInfoTimeoutHandler extends TimeoutHandler {
  42     static {
  43         try {
  44             System.loadLibrary("timeoutHandler");
  45         } catch (UnsatisfiedLinkError ignore) {
  46             // not all os need timeoutHandler native-library
  47         }
  48     }
  49     private static final String LOG_FILENAME = "processes.log";
  50     private static final String OUTPUT_FILENAME = "processes.html";
  51 
  52     public GatherProcessInfoTimeoutHandler(PrintWriter jtregLog, File outputDir,
  53                                            File testJdk) {
  54         super(jtregLog, outputDir, testJdk);
  55     }
  56 
  57     /**
  58      * Runs various actions for jtreg timeout handler.
  59      *
  60      * <p>Please see method code for the actions.
  61      */
  62     @Override
  63     protected void runActions(Process process, long pid)
  64             throws InterruptedException {
  65         Path workDir = outputDir.toPath();
  66 
  67         String name = getClass().getName();
  68         PrintWriter actionsLog;
  69         try {
  70             // try to open a separate file for aciton log
  71             actionsLog = new PrintWriter(new FileWriter(
  72                     workDir.resolve(LOG_FILENAME).toFile(), true));
  73         } catch (IOException e) {
  74             // use jtreg log as a fallback
  75             actionsLog = log;
  76             actionsLog.printf("ERROR: %s cannot open log file %s : %s", name,
  77                     LOG_FILENAME, e.getMessage());
  78         }
  79         try {
  80             actionsLog.printf("%s ---%n", name);
  81 
  82             File output = workDir.resolve(OUTPUT_FILENAME).toFile();
  83             try {
  84                 PrintWriter pw = new PrintWriter(new FileWriter(output, true));
  85                 runGatherer(name, workDir, actionsLog, pw, pid);
  86             } catch (IOException e) {
  87                 actionsLog.printf("IOException: cannot open output file[%s] : %s",
  88                         output, e.getMessage());
  89                 e.printStackTrace(actionsLog);
  90             }
  91         } finally {
  92             actionsLog.printf("--- %s%n", name);
  93             // don't close jtreg log
  94             if (actionsLog != log) {
  95                 actionsLog.close();
  96             } else {
  97                 log.flush();
  98             }
  99         }
 100     }
 101 
 102     @Override
 103     protected long getProcessId(Process process) {
 104         long result = super.getProcessId(process);
 105         if (result == 0L) {
 106             /* jtreg didn't find pid, most probably we are on JDK < 9
 107                there is no Process::getPid */
 108             if ("windows".equals(OS.current().family)) {
 109                 try {
 110                     Field field = process.getClass().getDeclaredField("handle");
 111                     boolean old = field.isAccessible();
 112                     try {
 113                         field.setAccessible(true);
 114                         long handle = field.getLong(process);
 115                         result = getWin32Pid(handle);
 116                     } finally {
 117                         field.setAccessible(old);
 118                     }
 119                 } catch (ReflectiveOperationException e) {
 120                     e.printStackTrace(log);
 121                 }
 122             }
 123         }
 124         return result;
 125     }
 126 
 127     private native long getWin32Pid(long handle);
 128 
 129     private void runGatherer(String name, Path workDir, PrintWriter log,
 130                              PrintWriter out, long pid) {
 131         try (HtmlPage html = new HtmlPage(out)) {
 132             ProcessInfoGatherer gatherer = new GathererFactory(
 133                     OS.current().family,
 134                     workDir, log, testJdk.toPath()).getProcessInfoGatherer();
 135             try (ElapsedTimePrinter timePrinter
 136                          = new ElapsedTimePrinter(new Stopwatch(), name, log)) {
 137                 gatherer.gatherProcessInfo(html.getRootSection(), pid);
 138             }
 139         } catch (Throwable e) {
 140             log.printf("ERROR: exception in timeout handler %s:", name);
 141             e.printStackTrace(log);
 142         }
 143     }
 144 }