• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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 package com.android.tradefed.util;
17 
18 import com.android.tradefed.device.DeviceNotAvailableException;
19 import com.android.tradefed.error.HarnessRuntimeException;
20 import com.android.tradefed.error.IHarnessException;
21 import com.android.tradefed.log.LogUtil.CLog;
22 import com.android.tradefed.result.error.ErrorIdentifier;
23 import com.android.tradefed.result.error.InfraErrorIdentifier;
24 import com.android.tradefed.result.error.TestErrorIdentifier;
25 import com.android.tradefed.sandbox.TradefedSandboxRunner;
26 
27 import org.json.JSONException;
28 import org.json.JSONObject;
29 
30 import java.io.File;
31 import java.io.IOException;
32 import java.util.regex.Matcher;
33 import java.util.regex.Pattern;
34 
35 /** Helper to handle the exception output from standard Tradefed command runners. */
36 public class SubprocessExceptionParser {
37 
38     public static final String EVENT_THREAD_JOIN = "Event receiver thread did not complete.";
39 
40     /** Extract the file path of the serialized exception. */
getPathFromStderr(String stderr)41     public static String getPathFromStderr(String stderr) {
42         String patternString = String.format(".*%s.*", TradefedSandboxRunner.EXCEPTION_KEY);
43         Pattern pattern = Pattern.compile(patternString);
44         for (String line : stderr.split("\n")) {
45             Matcher m = pattern.matcher(line);
46             if (m.matches()) {
47                 try {
48                     JSONObject json = new JSONObject(line);
49                     String filePath = json.getString(TradefedSandboxRunner.EXCEPTION_KEY);
50                     return filePath;
51                 } catch (JSONException e) {
52                     // Ignore
53                     CLog.w("Could not parse the stderr as a particular exception.");
54                 }
55             } else {
56                 CLog.w("'%s' doesn't match pattern '%s'", line, patternString);
57             }
58         }
59         return null;
60     }
61 
62     /** Attempt to extract a proper exception from stderr, if not stick to RuntimeException. */
handleStderrException(CommandResult result)63     public static void handleStderrException(CommandResult result)
64             throws DeviceNotAvailableException {
65         String stderr = result.getStderr();
66         String filePath = getPathFromStderr(stderr);
67         Integer exitCode = result.getExitCode();
68         String message =
69                 String.format(
70                         "Subprocess finished with error exit code: %s.\nStderr: %s",
71                         exitCode, stderr);
72         if (filePath != null) {
73             try {
74                 File exception = new File(filePath);
75                 Throwable obj = (Throwable) SerializationUtil.deserialize(exception, true);
76                 if (obj instanceof DeviceNotAvailableException) {
77                     throw (DeviceNotAvailableException) obj;
78                 }
79                 if (obj instanceof IHarnessException) {
80                     throw new HarnessRuntimeException(
81                             message,
82                             TestErrorIdentifier.SUBPROCESS_UNCATEGORIZED_EXCEPTION,
83                             (IHarnessException) obj);
84                 }
85                 throw new HarnessRuntimeException(message, obj, InfraErrorIdentifier.UNDETERMINED);
86             } catch (IOException e) {
87                 // Ignore
88                 CLog.w(
89                         "Could not parse the stderr as a particular exception. "
90                                 + "Using HarnessRuntimeException instead.");
91             }
92         }
93         ErrorIdentifier id = InfraErrorIdentifier.UNDETERMINED;
94         if (CommandStatus.TIMED_OUT.equals(result.getStatus())) {
95             id = InfraErrorIdentifier.INVOCATION_TIMEOUT;
96         }
97         // If we reach here and it's an event issue, set the id
98         if (stderr.startsWith(EVENT_THREAD_JOIN)) {
99             id = InfraErrorIdentifier.EVENT_PROCESSING_TIMEOUT;
100         }
101         throw new HarnessRuntimeException(message, id);
102     }
103 }
104