1 /* 2 * Copyright (C) 2023 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 android.server.wm; 18 19 import static androidx.test.InstrumentationRegistry.getInstrumentation; 20 21 import android.os.ParcelFileDescriptor; 22 23 import java.io.File; 24 import java.io.FileInputStream; 25 import java.io.FileOutputStream; 26 import java.io.IOException; 27 import java.nio.charset.StandardCharsets; 28 29 /* Captures WM dump through Perfetto. */ 30 class WindowManagerTraceMonitor { 31 private static final File PERFETTO_TRACES_DIR = new File("/data/misc/perfetto-traces"); 32 private static final String PERFETTO_CONFIG = """ 33 unique_session_name: "cts-windowmanager-dump" 34 buffers: { 35 size_kb: 65536 36 fill_policy: RING_BUFFER 37 } 38 data_sources: { 39 config { 40 name: "android.windowmanager" 41 windowmanager_config: { 42 log_level: LOG_LEVEL_VERBOSE 43 log_frequency: LOG_FREQUENCY_SINGLE_DUMP 44 } 45 } 46 } 47 duration_ms: 0 48 """; 49 captureDump()50 byte[] captureDump() { 51 try { 52 String fileName = 53 File.createTempFile("cts-windowmanager-dump-", ".perfetto-trace") 54 .getName(); 55 File traceFile = PERFETTO_TRACES_DIR.toPath().resolve(fileName).toFile(); 56 57 String command = "perfetto --background-wait" + " --config - --txt --out " 58 + traceFile.getAbsolutePath(); 59 String stdout = new String(executeShellCommand(command, PERFETTO_CONFIG.getBytes()), 60 StandardCharsets.UTF_8); 61 int pid = Integer.parseInt(stdout.trim()); 62 63 killPerfettoProcess(pid); 64 waitPerfettoProcessExits(pid); 65 66 byte[] dump = executeShellCommand("cat " + traceFile.getAbsolutePath()); 67 return dump; 68 } catch (IOException e) { 69 throw new RuntimeException(e); 70 } 71 } 72 killPerfettoProcess(int pid)73 private void killPerfettoProcess(int pid) { 74 if (isPerfettoProcessUp(pid)) { 75 executeShellCommand("kill " + pid); 76 } 77 } 78 waitPerfettoProcessExits(int pid)79 private void waitPerfettoProcessExits(int pid) { 80 while (true) { 81 if (!isPerfettoProcessUp(pid)) { 82 break; 83 } 84 try { 85 Thread.sleep(50); 86 } catch (InterruptedException e) { 87 throw new RuntimeException(e); 88 } 89 } 90 } 91 isPerfettoProcessUp(int pid)92 private boolean isPerfettoProcessUp(int pid) { 93 String out = 94 new String(executeShellCommand("ps -p " + pid + " -o PID"), StandardCharsets.UTF_8); 95 return out.contains(Integer.toString(pid)); 96 } 97 executeShellCommand(String command)98 private byte[] executeShellCommand(String command) { 99 StateLogger.log("Executing command: " + command); 100 try { 101 ParcelFileDescriptor fdStdout = 102 getInstrumentation().getUiAutomation().executeShellCommand(command); 103 try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(fdStdout)) { 104 return fis.readAllBytes(); 105 } 106 } catch (IOException e) { 107 throw new RuntimeException(e); 108 } 109 } 110 executeShellCommand(String command, byte[] stdin)111 private byte[] executeShellCommand(String command, byte[] stdin) { 112 StateLogger.log("Executing command: " + command); 113 try { 114 ParcelFileDescriptor[] fds = getInstrumentation().getUiAutomation() 115 .executeShellCommandRw(command); 116 117 ParcelFileDescriptor fdStdout = fds[0]; 118 ParcelFileDescriptor fdStdin = fds[1]; 119 120 try (FileOutputStream fos = new ParcelFileDescriptor.AutoCloseOutputStream(fdStdin)) { 121 fos.write(stdin); 122 } 123 124 try (FileInputStream fis = new ParcelFileDescriptor.AutoCloseInputStream(fdStdout)) { 125 byte[] stdout = fis.readAllBytes(); 126 return stdout; 127 } 128 } catch (IOException e) { 129 throw new RuntimeException(e); 130 } 131 } 132 } 133