1 /* 2 * Copyright (C) 2021 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 package com.android.server.pm.dex; 18 19 import static org.mockito.Mockito.inOrder; 20 21 import android.platform.test.annotations.Presubmit; 22 23 import com.android.internal.art.ArtStatsLog; 24 import com.android.server.pm.dex.ArtStatsLogUtils.ArtStatsLogger; 25 26 import org.junit.AfterClass; 27 import org.junit.Assert; 28 import org.junit.Before; 29 import org.junit.BeforeClass; 30 import org.junit.Test; 31 import org.junit.runner.RunWith; 32 import org.junit.runners.JUnit4; 33 import org.mockito.InOrder; 34 import org.mockito.Mock; 35 import org.mockito.MockitoAnnotations; 36 37 import java.io.IOException; 38 import java.io.OutputStream; 39 import java.nio.file.Files; 40 import java.nio.file.Path; 41 import java.util.zip.ZipEntry; 42 import java.util.zip.ZipOutputStream; 43 44 /** 45 * Unit tests for {@link com.android.server.pm.dex.ArtStatsLogUtils}. 46 * 47 * Run with "atest ArtStatsLogUtilsTest". 48 */ 49 @Presubmit 50 @RunWith(JUnit4.class) 51 public final class ArtStatsLogUtilsTest { 52 private static final String TAG = ArtStatsLogUtilsTest.class.getSimpleName(); 53 private static final String COMPILER_FILTER = "space-profile"; 54 private static final String PROFILE_DEX_METADATA = "primary.prof"; 55 private static final String VDEX_DEX_METADATA = "primary.vdex"; 56 private static final String INSTRUCTION_SET = "arm64"; 57 private static final String BASE_APK_PATH = "/tmp/base.apk"; 58 private static final String[] SPLIT_APK_PATHS = 59 new String[]{"/tmp/split1.apk", "/tmp/split2.apk"}; 60 private static final byte[] DEX_CONTENT = "dexData".getBytes(); 61 private static final int COMPILATION_REASON = 1; 62 private static final int RESULT_CODE = 222; 63 private static final int UID = 111; 64 private static final long COMPILE_TIME = 333L; 65 private static final long SESSION_ID = 444L; 66 67 @Mock 68 ArtStatsLogger mockLogger; 69 70 private static Path TEST_DIR; 71 private static Path DEX; 72 private static Path NON_DEX; 73 74 @BeforeClass setUpAll()75 public static void setUpAll() throws IOException { 76 TEST_DIR = Files.createTempDirectory(null); 77 DEX = Files.createFile(TEST_DIR.resolve("classes.dex")); 78 NON_DEX = Files.createFile(TEST_DIR.resolve("test.dex")); 79 Files.write(DEX, DEX_CONTENT); 80 Files.write(NON_DEX, "empty".getBytes()); 81 } 82 83 @AfterClass tearnDownAll()84 public static void tearnDownAll() { 85 deleteSliently(DEX); 86 deleteSliently(NON_DEX); 87 deleteSliently(TEST_DIR); 88 } 89 90 @Before setUp()91 public void setUp() { 92 MockitoAnnotations.initMocks(this); 93 } 94 95 @Test testProfileAndVdexDexMetadata()96 public void testProfileAndVdexDexMetadata() throws IOException { 97 // Setup 98 Path dexMetadataPath = null; 99 Path apk = null; 100 try { 101 dexMetadataPath = createDexMetadata(PROFILE_DEX_METADATA, VDEX_DEX_METADATA); 102 apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath); 103 104 // Act 105 ArtStatsLogUtils.writeStatsLog( 106 mockLogger, 107 SESSION_ID, 108 COMPILER_FILTER, 109 UID, 110 COMPILE_TIME, 111 dexMetadataPath.toString(), 112 COMPILATION_REASON, 113 RESULT_CODE, 114 ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, 115 INSTRUCTION_SET, 116 apk.toString()); 117 118 // Assert 119 verifyWrites(ArtStatsLog. 120 ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE_AND_VDEX); 121 } finally { 122 deleteSliently(dexMetadataPath); 123 deleteSliently(apk); 124 } 125 } 126 127 @Test testProfileOnlyDexMetadata()128 public void testProfileOnlyDexMetadata() throws IOException { 129 // Setup 130 Path dexMetadataPath = null; 131 Path apk = null; 132 try { 133 dexMetadataPath = createDexMetadata(PROFILE_DEX_METADATA); 134 apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath); 135 136 // Act 137 ArtStatsLogUtils.writeStatsLog( 138 mockLogger, 139 SESSION_ID, 140 COMPILER_FILTER, 141 UID, 142 COMPILE_TIME, 143 dexMetadataPath.toString(), 144 COMPILATION_REASON, 145 RESULT_CODE, 146 ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, 147 INSTRUCTION_SET, 148 apk.toString()); 149 150 // Assert 151 verifyWrites(ArtStatsLog. 152 ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_PROFILE); 153 } finally { 154 deleteSliently(dexMetadataPath); 155 deleteSliently(apk); 156 } 157 } 158 159 @Test testVdexOnlyDexMetadata()160 public void testVdexOnlyDexMetadata() throws IOException { 161 // Setup 162 Path dexMetadataPath = null; 163 Path apk = null; 164 try { 165 dexMetadataPath = createDexMetadata(VDEX_DEX_METADATA); 166 apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath); 167 168 // Act 169 ArtStatsLogUtils.writeStatsLog( 170 mockLogger, 171 SESSION_ID, 172 COMPILER_FILTER, 173 UID, 174 COMPILE_TIME, 175 dexMetadataPath.toString(), 176 COMPILATION_REASON, 177 RESULT_CODE, 178 ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, 179 INSTRUCTION_SET, 180 apk.toString()); 181 182 // Assert 183 verifyWrites(ArtStatsLog. 184 ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_VDEX); 185 } finally { 186 deleteSliently(dexMetadataPath); 187 deleteSliently(apk); 188 } 189 } 190 191 @Test testNoneDexMetadata()192 public void testNoneDexMetadata() throws IOException { 193 // Setup 194 Path apk = null; 195 try { 196 apk = zipFiles(".apk", DEX, NON_DEX); 197 198 // Act 199 ArtStatsLogUtils.writeStatsLog( 200 mockLogger, 201 SESSION_ID, 202 COMPILER_FILTER, 203 UID, 204 COMPILE_TIME, 205 /*dexMetadataPath=*/ null, 206 COMPILATION_REASON, 207 RESULT_CODE, 208 ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, 209 INSTRUCTION_SET, 210 apk.toString()); 211 212 // Assert 213 verifyWrites(ArtStatsLog. 214 ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_NONE); 215 } finally { 216 deleteSliently(apk); 217 } 218 } 219 220 @Test testUnKnownDexMetadata()221 public void testUnKnownDexMetadata() throws IOException { 222 // Setup 223 Path dexMetadataPath = null; 224 Path apk = null; 225 try { 226 dexMetadataPath = createDexMetadata("unknown"); 227 apk = zipFiles(".apk", DEX, NON_DEX, dexMetadataPath); 228 229 // Act 230 ArtStatsLogUtils.writeStatsLog( 231 mockLogger, 232 SESSION_ID, 233 COMPILER_FILTER, 234 UID, 235 COMPILE_TIME, 236 dexMetadataPath.toString(), 237 COMPILATION_REASON, 238 RESULT_CODE, 239 ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, 240 INSTRUCTION_SET, 241 apk.toString()); 242 243 // Assert 244 verifyWrites(ArtStatsLog. 245 ART_DATUM_REPORTED__DEX_METADATA_TYPE__ART_DEX_METADATA_TYPE_UNKNOWN); 246 } finally { 247 deleteSliently(dexMetadataPath); 248 deleteSliently(apk); 249 } 250 } 251 252 @Test testGetApkType()253 public void testGetApkType() { 254 // Act 255 int result1 = ArtStatsLogUtils.getApkType("/tmp/base.apk", BASE_APK_PATH, SPLIT_APK_PATHS); 256 int result2 = ArtStatsLogUtils.getApkType("/tmp/split1.apk", BASE_APK_PATH, 257 SPLIT_APK_PATHS); 258 int result3 = ArtStatsLogUtils.getApkType("/tmp/none.apk", BASE_APK_PATH, SPLIT_APK_PATHS); 259 260 // Assert 261 Assert.assertEquals(result1, ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE); 262 Assert.assertEquals(result2, ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_SPLIT); 263 Assert.assertEquals(result3, 264 ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_UNKNOWN); 265 } 266 verifyWrites(int dexMetadataType)267 private void verifyWrites(int dexMetadataType) { 268 InOrder inorder = inOrder(mockLogger); 269 inorder.verify(mockLogger).write( 270 SESSION_ID, UID, 271 COMPILATION_REASON, 272 COMPILER_FILTER, 273 ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_RESULT_CODE, 274 RESULT_CODE, 275 dexMetadataType, 276 ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, 277 INSTRUCTION_SET); 278 inorder.verify(mockLogger).write( 279 SESSION_ID, 280 UID, 281 COMPILATION_REASON, 282 COMPILER_FILTER, 283 ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_DEX_CODE_COUNTER_BYTES, 284 DEX_CONTENT.length, 285 dexMetadataType, 286 ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, 287 INSTRUCTION_SET); 288 inorder.verify(mockLogger).write( 289 SESSION_ID, 290 UID, 291 COMPILATION_REASON, 292 COMPILER_FILTER, 293 ArtStatsLog.ART_DATUM_REPORTED__KIND__ART_DATUM_DEX2OAT_TOTAL_TIME_COUNTER_MILLIS, 294 COMPILE_TIME, 295 dexMetadataType, 296 ArtStatsLog.ART_DATUM_REPORTED__APK_TYPE__ART_APK_TYPE_BASE, 297 INSTRUCTION_SET); 298 } 299 zipFiles(String suffix, Path... files)300 private Path zipFiles(String suffix, Path... files) throws IOException { 301 Path zipFile = Files.createTempFile(null, suffix); 302 try (final OutputStream os = Files.newOutputStream(zipFile)) { 303 try (final ZipOutputStream zos = new ZipOutputStream(os)) { 304 for (Path file : files) { 305 ZipEntry zipEntry = new ZipEntry(file.getFileName().toString()); 306 zos.putNextEntry(zipEntry); 307 zos.write(Files.readAllBytes(file)); 308 zos.closeEntry(); 309 } 310 } 311 } 312 return zipFile; 313 } 314 createDexMetadata(String... entryNames)315 private Path createDexMetadata(String... entryNames) throws IOException { 316 Path zipFile = Files.createTempFile(null, ".dm"); 317 try (final OutputStream os = Files.newOutputStream(zipFile)) { 318 try (final ZipOutputStream zos = new ZipOutputStream(os)) { 319 for (String entryName : entryNames) { 320 ZipEntry zipEntry = new ZipEntry(entryName); 321 zos.putNextEntry(zipEntry); 322 zos.write(entryName.getBytes()); 323 zos.closeEntry(); 324 } 325 } 326 } 327 return zipFile; 328 } 329 deleteSliently(Path file)330 private static void deleteSliently(Path file) { 331 if (file != null) { 332 try { 333 Files.deleteIfExists(file); 334 } catch (IOException e) { 335 // ignore 336 } 337 } 338 } 339 } 340