src/jdk.internal.vm.ci/share/classes/jdk.vm.ci.hotspot/src/jdk/vm/ci/hotspot/TranslatedException.java
changeset 54669 ad45b3802d4e
child 54732 2d012a75d35c
equal deleted inserted replaced
54668:0bda2308eded 54669:ad45b3802d4e
       
     1 /*
       
     2  * Copyright (c) 2018, 2019, 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 package jdk.vm.ci.hotspot;
       
    24 
       
    25 import java.util.Arrays;
       
    26 import java.util.Formatter;
       
    27 import java.util.Objects;
       
    28 
       
    29 /**
       
    30  * Support for translating exceptions between different runtime heaps.
       
    31  */
       
    32 @SuppressWarnings("serial")
       
    33 final class TranslatedException extends Exception {
       
    34 
       
    35     private TranslatedException(String message) {
       
    36         super(message);
       
    37     }
       
    38 
       
    39     private TranslatedException(String message, Throwable cause) {
       
    40         super(message, cause);
       
    41     }
       
    42 
       
    43     /**
       
    44      * No need to record an initial stack trace since it will be manually overwritten.
       
    45      */
       
    46     @SuppressWarnings("sync-override")
       
    47     @Override
       
    48     public Throwable fillInStackTrace() {
       
    49         return this;
       
    50     }
       
    51 
       
    52     @Override
       
    53     public String toString() {
       
    54         return getMessage();
       
    55     }
       
    56 
       
    57     private static TranslatedException create(String className, String message) {
       
    58         if (className.equals(TranslatedException.class.getName())) {
       
    59             // Chop the class name when boxing another TranslatedException
       
    60             return new TranslatedException(message);
       
    61         }
       
    62         if (message == null) {
       
    63             return new TranslatedException(className);
       
    64         }
       
    65         return new TranslatedException(className + ": " + message);
       
    66     }
       
    67 
       
    68     private static String encodedString(String value) {
       
    69         return Objects.toString(value, "").replace('|', '_');
       
    70     }
       
    71 
       
    72     /**
       
    73      * Encodes {@code throwable} including its stack and causes as a string. The encoding format of
       
    74      * a single exception with its cause is:
       
    75      *
       
    76      * <pre>
       
    77      * <exception class name> '|' <exception message> '|' <stack size> '|' [<class> '|' <method> '|' <file> '|' <line> '|' ]*
       
    78      * </pre>
       
    79      *
       
    80      * Each cause is appended after the exception is it the cause of.
       
    81      */
       
    82     @VMEntryPoint
       
    83     static String encodeThrowable(Throwable throwable) throws Throwable {
       
    84         try {
       
    85             Formatter enc = new Formatter();
       
    86             Throwable current = throwable;
       
    87             do {
       
    88                 enc.format("%s|%s|", current.getClass().getName(), encodedString(current.getMessage()));
       
    89                 StackTraceElement[] stackTrace = current.getStackTrace();
       
    90                 if (stackTrace == null) {
       
    91                     stackTrace = new StackTraceElement[0];
       
    92                 }
       
    93                 enc.format("%d|", stackTrace.length);
       
    94                 for (int i = 0; i < stackTrace.length; i++) {
       
    95                     StackTraceElement frame = stackTrace[i];
       
    96                     if (frame != null) {
       
    97                         enc.format("%s|%s|%s|%d|", frame.getClassName(), frame.getMethodName(),
       
    98                                         encodedString(frame.getFileName()), frame.getLineNumber());
       
    99                     }
       
   100                 }
       
   101                 current = current.getCause();
       
   102             } while (current != null);
       
   103             return enc.toString();
       
   104         } catch (Throwable e) {
       
   105             try {
       
   106                 return e.getClass().getName() + "|" + encodedString(e.getMessage()) + "|0|";
       
   107             } catch (Throwable e2) {
       
   108                 return "java.lang.Throwable|too many errors during encoding|0|";
       
   109             }
       
   110         }
       
   111     }
       
   112 
       
   113     /**
       
   114      * Gets the stack of the current thread without the frames between this call and the one just
       
   115      * below the frame of the first method in {@link CompilerToVM}. The chopped frames are specific
       
   116      * to the implementation of {@link HotSpotJVMCIRuntime#decodeThrowable(String)}.
       
   117      */
       
   118     private static StackTraceElement[] getStackTraceSuffix() {
       
   119         StackTraceElement[] stack = new Exception().getStackTrace();
       
   120         for (int i = 0; i < stack.length; i++) {
       
   121             StackTraceElement e = stack[i];
       
   122             if (e.getClassName().equals(CompilerToVM.class.getName())) {
       
   123                 return Arrays.copyOfRange(stack, i, stack.length);
       
   124             }
       
   125         }
       
   126         // This should never happen but since we're in exception handling
       
   127         // code, just return a safe value instead raising a nested exception.
       
   128         return new StackTraceElement[0];
       
   129     }
       
   130 
       
   131     /**
       
   132      * Decodes {@code encodedThrowable} into a {@link TranslatedException}.
       
   133      *
       
   134      * @param encodedThrowable an encoded exception in the format specified by
       
   135      *            {@link #encodeThrowable}
       
   136      */
       
   137     @VMEntryPoint
       
   138     static Throwable decodeThrowable(String encodedThrowable) {
       
   139         try {
       
   140             int i = 0;
       
   141             String[] parts = encodedThrowable.split("\\|");
       
   142             Throwable parent = null;
       
   143             Throwable result = null;
       
   144             while (i != parts.length) {
       
   145                 String exceptionClassName = parts[i++];
       
   146                 String exceptionMessage = parts[i++];
       
   147                 Throwable throwable = create(exceptionClassName, exceptionMessage);
       
   148                 int stackTraceDepth = Integer.parseInt(parts[i++]);
       
   149 
       
   150                 StackTraceElement[] suffix = parent == null ? new StackTraceElement[0] : getStackTraceSuffix();
       
   151                 StackTraceElement[] stackTrace = new StackTraceElement[stackTraceDepth + suffix.length];
       
   152                 for (int j = 0; j < stackTraceDepth; j++) {
       
   153                     String className = parts[i++];
       
   154                     String methodName = parts[i++];
       
   155                     String fileName = parts[i++];
       
   156                     int lineNumber = Integer.parseInt(parts[i++]);
       
   157                     if (fileName.isEmpty()) {
       
   158                         fileName = null;
       
   159                     }
       
   160                     stackTrace[j] = new StackTraceElement(className, methodName, fileName, lineNumber);
       
   161                 }
       
   162                 System.arraycopy(suffix, 0, stackTrace, stackTraceDepth, suffix.length);
       
   163                 throwable.setStackTrace(stackTrace);
       
   164                 if (parent != null) {
       
   165                     parent.initCause(throwable);
       
   166                 } else {
       
   167                     result = throwable;
       
   168                 }
       
   169                 parent = throwable;
       
   170             }
       
   171             return result;
       
   172         } catch (Throwable t) {
       
   173             return new TranslatedException("Error decoding exception: " + encodedThrowable, t);
       
   174         }
       
   175     }
       
   176 }