1 /* 2 * Copyright (C) 2024 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.internal.inputmethod; 18 19 import static android.tracing.perfetto.DataSourceParams.PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT; 20 21 import android.annotation.NonNull; 22 import android.annotation.Nullable; 23 import android.internal.perfetto.protos.Inputmethodeditor.InputMethodClientsTraceProto; 24 import android.internal.perfetto.protos.Inputmethodeditor.InputMethodManagerServiceTraceProto; 25 import android.internal.perfetto.protos.Inputmethodeditor.InputMethodServiceTraceProto; 26 import android.internal.perfetto.protos.TracePacketOuterClass.TracePacket; 27 import android.internal.perfetto.protos.WinscopeExtensionsImplOuterClass.WinscopeExtensionsImpl; 28 import android.os.SystemClock; 29 import android.os.Trace; 30 import android.tracing.inputmethod.InputMethodDataSource; 31 import android.tracing.perfetto.DataSourceParams; 32 import android.tracing.perfetto.InitArguments; 33 import android.tracing.perfetto.Producer; 34 import android.util.proto.ProtoOutputStream; 35 import android.view.inputmethod.InputMethodManager; 36 37 import java.io.PrintWriter; 38 import java.util.concurrent.atomic.AtomicBoolean; 39 import java.util.concurrent.atomic.AtomicInteger; 40 41 /** 42 * An implementation of {@link ImeTracing} for perfetto tracing. 43 */ 44 final class ImeTracingPerfettoImpl extends ImeTracing { 45 private final AtomicInteger mTracingSessionsCount = new AtomicInteger(0); 46 private final AtomicBoolean mIsClientDumpInProgress = new AtomicBoolean(false); 47 private final AtomicBoolean mIsServiceDumpInProgress = new AtomicBoolean(false); 48 private final AtomicBoolean mIsManagerServiceDumpInProgress = new AtomicBoolean(false); 49 private final InputMethodDataSource mDataSource = new InputMethodDataSource( 50 mTracingSessionsCount::incrementAndGet, 51 mTracingSessionsCount::decrementAndGet); 52 ImeTracingPerfettoImpl()53 ImeTracingPerfettoImpl() { 54 Producer.init(InitArguments.DEFAULTS); 55 DataSourceParams params = 56 new DataSourceParams.Builder() 57 .setBufferExhaustedPolicy( 58 PERFETTO_DS_BUFFER_EXHAUSTED_POLICY_STALL_AND_ABORT) 59 .setNoFlush(true) 60 .setWillNotifyOnStop(false) 61 .build(); 62 mDataSource.register(params); 63 } 64 65 66 @Override triggerClientDump(String where, InputMethodManager immInstance, @Nullable byte[] icProto)67 public void triggerClientDump(String where, InputMethodManager immInstance, 68 @Nullable byte[] icProto) { 69 if (!isEnabled() || !isAvailable()) { 70 return; 71 } 72 73 if (!mIsClientDumpInProgress.compareAndSet(false, true)) { 74 return; 75 } 76 77 if (immInstance == null) { 78 return; 79 } 80 81 try { 82 Trace.beginSection("inputmethod_client_dump"); 83 mDataSource.trace((ctx) -> { 84 final ProtoOutputStream os = ctx.newTracePacket(); 85 os.write(TracePacket.TIMESTAMP, SystemClock.elapsedRealtimeNanos()); 86 final long tokenWinscopeExtensions = 87 os.start(TracePacket.WINSCOPE_EXTENSIONS); 88 final long tokenExtensionsField = 89 os.start(WinscopeExtensionsImpl.INPUTMETHOD_CLIENTS); 90 os.write(InputMethodClientsTraceProto.WHERE, where); 91 final long tokenClient = 92 os.start(InputMethodClientsTraceProto.CLIENT); 93 immInstance.dumpDebug(os, icProto); 94 os.end(tokenClient); 95 os.end(tokenExtensionsField); 96 os.end(tokenWinscopeExtensions); 97 }); 98 } finally { 99 mIsClientDumpInProgress.set(false); 100 Trace.endSection(); 101 } 102 } 103 104 @Override triggerServiceDump(String where, @NonNull ServiceDumper dumper, @Nullable byte[] icProto)105 public void triggerServiceDump(String where, 106 @NonNull ServiceDumper dumper, @Nullable byte[] icProto) { 107 if (!isEnabled() || !isAvailable()) { 108 return; 109 } 110 111 if (!mIsServiceDumpInProgress.compareAndSet(false, true)) { 112 return; 113 } 114 115 try { 116 Trace.beginSection("inputmethod_service_dump"); 117 mDataSource.trace((ctx) -> { 118 final ProtoOutputStream os = ctx.newTracePacket(); 119 os.write(TracePacket.TIMESTAMP, SystemClock.elapsedRealtimeNanos()); 120 final long tokenWinscopeExtensions = 121 os.start(TracePacket.WINSCOPE_EXTENSIONS); 122 final long tokenExtensionsField = 123 os.start(WinscopeExtensionsImpl.INPUTMETHOD_SERVICE); 124 os.write(InputMethodServiceTraceProto.WHERE, where); 125 dumper.dumpToProto(os, icProto); 126 os.end(tokenExtensionsField); 127 os.end(tokenWinscopeExtensions); 128 }); 129 } finally { 130 mIsServiceDumpInProgress.set(false); 131 Trace.endSection(); 132 } 133 } 134 135 @Override triggerManagerServiceDump(@onNull String where, @NonNull ServiceDumper dumper)136 public void triggerManagerServiceDump(@NonNull String where, @NonNull ServiceDumper dumper) { 137 if (!isEnabled() || !isAvailable()) { 138 return; 139 } 140 141 if (!mIsManagerServiceDumpInProgress.compareAndSet(false, true)) { 142 return; 143 } 144 145 try { 146 Trace.beginSection("inputmethod_manager_service_dump"); 147 mDataSource.trace((ctx) -> { 148 final ProtoOutputStream os = ctx.newTracePacket(); 149 os.write(TracePacket.TIMESTAMP, SystemClock.elapsedRealtimeNanos()); 150 final long tokenWinscopeExtensions = 151 os.start(TracePacket.WINSCOPE_EXTENSIONS); 152 final long tokenExtensionsField = 153 os.start(WinscopeExtensionsImpl.INPUTMETHOD_MANAGER_SERVICE); 154 os.write(InputMethodManagerServiceTraceProto.WHERE, where); 155 dumper.dumpToProto(os, null); 156 os.end(tokenExtensionsField); 157 os.end(tokenWinscopeExtensions); 158 }); 159 } finally { 160 mIsManagerServiceDumpInProgress.set(false); 161 Trace.endSection(); 162 } 163 } 164 165 @Override isEnabled()166 public boolean isEnabled() { 167 return mTracingSessionsCount.get() > 0; 168 } 169 170 @Override startTrace(@ullable PrintWriter pw)171 public void startTrace(@Nullable PrintWriter pw) { 172 // Intentionally left empty. Tracing start/stop is managed through Perfetto. 173 } 174 175 @Override stopTrace(@ullable PrintWriter pw)176 public void stopTrace(@Nullable PrintWriter pw) { 177 // Intentionally left empty. Tracing start/stop is managed through Perfetto. 178 } 179 180 @Override addToBuffer(ProtoOutputStream proto, int source)181 public void addToBuffer(ProtoOutputStream proto, int source) { 182 // Intentionally left empty. Only used for legacy tracing. 183 } 184 } 185