1 /* 2 * Copyright (C) 2019 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.device.cloud; 17 18 import com.android.tradefed.device.TestDeviceOptions; 19 import com.android.tradefed.device.TestDeviceOptions.InstanceType; 20 import com.android.tradefed.log.ITestLogger; 21 import com.android.tradefed.log.LogUtil.CLog; 22 import com.android.tradefed.result.FileInputStreamSource; 23 import com.android.tradefed.result.InputStreamSource; 24 import com.android.tradefed.result.LogDataType; 25 import com.android.tradefed.util.CommandResult; 26 import com.android.tradefed.util.CommandStatus; 27 import com.android.tradefed.util.IRunUtil; 28 import com.android.tradefed.util.MultiMap; 29 import com.android.tradefed.util.ZipUtil; 30 31 import java.io.File; 32 import java.io.IOException; 33 import java.util.List; 34 35 /** 36 * This utility allows to avoid code duplication across the different remote device representation 37 * for the remote log fetching logic of common files. 38 */ 39 public class CommonLogRemoteFileUtil { 40 41 /** The directory where to find debug logs for a nested remote instance. */ 42 public static final String NESTED_REMOTE_LOG_DIR = "/home/%s/cuttlefish_runtime/"; 43 /** The directory where to find debug logs for an emulator instance. */ 44 public static final String EMULATOR_REMOTE_LOG_DIR = "/home/%s/log/"; 45 public static final String TOMBSTONES_ZIP_NAME = "tombstones-zip"; 46 47 public static final MultiMap<InstanceType, KnownLogFileEntry> KNOWN_FILES_TO_FETCH = 48 new MultiMap<>(); 49 50 static { 51 // Cuttlefish known files to collect KNOWN_FILES_TO_FETCH.put( InstanceType.CUTTLEFISH, new KnownLogFileEntry( NESTED_REMOTE_LOG_DIR + "kernel.log", null, LogDataType.TEXT))52 KNOWN_FILES_TO_FETCH.put( 53 InstanceType.CUTTLEFISH, 54 new KnownLogFileEntry( 55 NESTED_REMOTE_LOG_DIR + "kernel.log", null, LogDataType.TEXT)); KNOWN_FILES_TO_FETCH.put( InstanceType.CUTTLEFISH, new KnownLogFileEntry( NESTED_REMOTE_LOG_DIR + "logcat", "full_gce_logcat", LogDataType.LOGCAT))56 KNOWN_FILES_TO_FETCH.put( 57 InstanceType.CUTTLEFISH, 58 new KnownLogFileEntry( 59 NESTED_REMOTE_LOG_DIR + "logcat", "full_gce_logcat", LogDataType.LOGCAT)); KNOWN_FILES_TO_FETCH.put( InstanceType.CUTTLEFISH, new KnownLogFileEntry( NESTED_REMOTE_LOG_DIR + "cuttlefish_config.json", null, LogDataType.TEXT))60 KNOWN_FILES_TO_FETCH.put( 61 InstanceType.CUTTLEFISH, 62 new KnownLogFileEntry( 63 NESTED_REMOTE_LOG_DIR + "cuttlefish_config.json", null, LogDataType.TEXT)); KNOWN_FILES_TO_FETCH.put( InstanceType.CUTTLEFISH, new KnownLogFileEntry( NESTED_REMOTE_LOG_DIR + "launcher.log", "cuttlefish_launcher.log", LogDataType.TEXT))64 KNOWN_FILES_TO_FETCH.put( 65 InstanceType.CUTTLEFISH, 66 new KnownLogFileEntry( 67 NESTED_REMOTE_LOG_DIR + "launcher.log", 68 "cuttlefish_launcher.log", 69 LogDataType.TEXT)); 70 // Emulator known files to collect KNOWN_FILES_TO_FETCH.put( InstanceType.EMULATOR, new KnownLogFileEntry( EMULATOR_REMOTE_LOG_DIR + "logcat.log", "full_gce_emulator_logcat", LogDataType.LOGCAT))71 KNOWN_FILES_TO_FETCH.put( 72 InstanceType.EMULATOR, 73 new KnownLogFileEntry( 74 EMULATOR_REMOTE_LOG_DIR + "logcat.log", 75 "full_gce_emulator_logcat", 76 LogDataType.LOGCAT)); KNOWN_FILES_TO_FETCH.put( InstanceType.EMULATOR, new KnownLogFileEntry(EMULATOR_REMOTE_LOG_DIR + "adb.log", null, LogDataType.TEXT))77 KNOWN_FILES_TO_FETCH.put( 78 InstanceType.EMULATOR, 79 new KnownLogFileEntry(EMULATOR_REMOTE_LOG_DIR + "adb.log", null, LogDataType.TEXT)); KNOWN_FILES_TO_FETCH.put( InstanceType.EMULATOR, new KnownLogFileEntry( EMULATOR_REMOTE_LOG_DIR + "kernel.log", null, LogDataType.TEXT))80 KNOWN_FILES_TO_FETCH.put( 81 InstanceType.EMULATOR, 82 new KnownLogFileEntry( 83 EMULATOR_REMOTE_LOG_DIR + "kernel.log", null, LogDataType.TEXT)); KNOWN_FILES_TO_FETCH.put( InstanceType.EMULATOR, new KnownLogFileEntry("/var/log/daemon.log", null, LogDataType.TEXT))84 KNOWN_FILES_TO_FETCH.put( 85 InstanceType.EMULATOR, 86 new KnownLogFileEntry("/var/log/daemon.log", null, LogDataType.TEXT)); 87 } 88 89 /** A representation of a known log entry for remote devices. */ 90 public static class KnownLogFileEntry { 91 public String path; 92 public String logName; 93 public LogDataType type; 94 KnownLogFileEntry(String path, String logName, LogDataType type)95 KnownLogFileEntry(String path, String logName, LogDataType type) { 96 this.path = path; 97 this.logName = logName; 98 this.type = type; 99 } 100 } 101 102 /** 103 * Fetch and log the commonly known files from remote instances. 104 * 105 * @param testLogger The {@link ITestLogger} where to log the files. 106 * @param gceAvd The descriptor of the remote instance. 107 * @param options The {@link TestDeviceOptions} describing the device options 108 * @param runUtil A {@link IRunUtil} to execute commands. 109 */ fetchCommonFiles( ITestLogger testLogger, GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil)110 public static void fetchCommonFiles( 111 ITestLogger testLogger, 112 GceAvdInfo gceAvd, 113 TestDeviceOptions options, 114 IRunUtil runUtil) { 115 if (gceAvd == null) { 116 CLog.e("GceAvdInfo was null, cannot collect remote files."); 117 return; 118 } 119 // Capture known extra files 120 List<KnownLogFileEntry> toFetch = KNOWN_FILES_TO_FETCH.get(options.getInstanceType()); 121 if (toFetch != null) { 122 for (KnownLogFileEntry entry : toFetch) { 123 LogRemoteFile( 124 testLogger, 125 gceAvd, 126 options, 127 runUtil, 128 // Default fetch rely on main user 129 String.format(entry.path, options.getInstanceUser()), 130 entry.type, 131 entry.logName); 132 } 133 } 134 135 if (options.getRemoteFetchFilePattern().isEmpty()) { 136 return; 137 } 138 for (String file : options.getRemoteFetchFilePattern()) { 139 // TODO: Improve type of files. 140 LogRemoteFile(testLogger, gceAvd, options, runUtil, file, LogDataType.TEXT, null); 141 } 142 } 143 144 /** 145 * Fetch and log the tombstones from the remote instance. 146 * 147 * @param testLogger The {@link ITestLogger} where to log the files. 148 * @param gceAvd The descriptor of the remote instance. 149 * @param options The {@link TestDeviceOptions} describing the device options 150 * @param runUtil A {@link IRunUtil} to execute commands. 151 */ fetchTombstones( ITestLogger testLogger, GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil)152 public static void fetchTombstones( 153 ITestLogger testLogger, 154 GceAvdInfo gceAvd, 155 TestDeviceOptions options, 156 IRunUtil runUtil) { 157 if (gceAvd == null) { 158 CLog.e("GceAvdInfo was null, cannot collect remote files."); 159 return; 160 } 161 InstanceType type = options.getInstanceType(); 162 if (!InstanceType.CUTTLEFISH.equals(type) && !InstanceType.REMOTE_AVD.equals(type)) { 163 return; 164 } 165 String pattern = 166 String.format( 167 "/home/%s/cuttlefish_runtime/tombstones/*", options.getInstanceUser()); 168 CommandResult resultList = 169 GceManager.remoteSshCommandExecution( 170 gceAvd, options, runUtil, 60000, "ls", "-A1", pattern); 171 if (!CommandStatus.SUCCESS.equals(resultList.getStatus())) { 172 CLog.e("Failed to list the tombstones: %s", resultList.getStderr()); 173 return; 174 } 175 if (resultList.getStdout().split("\n").length <= 0) { 176 return; 177 } 178 File tombstonesDir = 179 RemoteFileUtil.fetchRemoteDir( 180 gceAvd, 181 options, 182 runUtil, 183 120000, 184 String.format( 185 "/home/%s/cuttlefish_runtime/tombstones", 186 options.getInstanceUser())); 187 if (tombstonesDir == null) { 188 CLog.w("No tombstones directory was pulled."); 189 return; 190 } 191 try { 192 File zipTombstones = ZipUtil.createZip(tombstonesDir); 193 try (InputStreamSource source = new FileInputStreamSource(zipTombstones, true)) { 194 testLogger.testLog(TOMBSTONES_ZIP_NAME, LogDataType.ZIP, source); 195 } 196 } catch (IOException e) { 197 CLog.e("Failed to zip the tombstones:"); 198 CLog.e(e); 199 } 200 } 201 202 /** 203 * Captures a log from the remote destination. 204 * 205 * @param testLogger The {@link ITestLogger} where to log the files. 206 * @param gceAvd The descriptor of the remote instance. 207 * @param options The {@link TestDeviceOptions} describing the device options 208 * @param runUtil A {@link IRunUtil} to execute commands. 209 * @param fileToRetrieve The remote path to the file to pull. 210 * @param logType The expected type of the pulled log. 211 * @param baseName The base name that will be used to log the file, if null the actually file 212 * name will be used. 213 */ LogRemoteFile( ITestLogger testLogger, GceAvdInfo gceAvd, TestDeviceOptions options, IRunUtil runUtil, String fileToRetrieve, LogDataType logType, String baseName)214 private static void LogRemoteFile( 215 ITestLogger testLogger, 216 GceAvdInfo gceAvd, 217 TestDeviceOptions options, 218 IRunUtil runUtil, 219 String fileToRetrieve, 220 LogDataType logType, 221 String baseName) { 222 GceManager.logNestedRemoteFile( 223 testLogger, gceAvd, options, runUtil, fileToRetrieve, logType, baseName); 224 } 225 } 226