1 /* 2 * Copyright (C) 2014 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.app; 18 19 import android.annotation.Nullable; 20 import android.os.Parcel; 21 import android.os.ParcelFileDescriptor; 22 import android.os.Parcelable; 23 import android.util.Slog; 24 import android.util.proto.ProtoOutputStream; 25 26 import java.io.IOException; 27 import java.util.Objects; 28 29 /** 30 * System private API for passing profiler settings. 31 * 32 * {@hide} 33 */ 34 public class ProfilerInfo implements Parcelable { 35 // Regular profiling which provides different modes of profiling at some performance cost. 36 public static final int PROFILE_TYPE_REGULAR = 0; 37 38 // Low overhead profiling that captures a simple sliding window of past events. 39 public static final int PROFILE_TYPE_LOW_OVERHEAD = 1; 40 41 // Version of the profiler output 42 public static final int OUTPUT_VERSION_DEFAULT = 1; 43 // CLOCK_TYPE_DEFAULT chooses the default used by ART. ART uses CLOCK_TYPE_DUAL by default (see 44 // kDefaultTraceClockSource in art/runtime/runtime_globals.h). 45 public static final int CLOCK_TYPE_DEFAULT = 0x000; 46 // The values of these constants are chosen such that they correspond to the flags passed to 47 // VMDebug.startMethodTracing to choose the corresponding clock type (see 48 // core/java/android/app/ActivityThread.java). 49 // The flag values are defined in ART (see TraceFlag in art/runtime/trace.h). 50 public static final int CLOCK_TYPE_WALL = 0x010; 51 public static final int CLOCK_TYPE_THREAD_CPU = 0x100; 52 public static final int CLOCK_TYPE_DUAL = 0x110; 53 // The second and third bits of the flags field specify the trace format version. This should 54 // match with kTraceFormatVersionShift defined in art/runtime/trace.h. 55 public static final int TRACE_FORMAT_VERSION_SHIFT = 1; 56 57 private static final String TAG = "ProfilerInfo"; 58 59 /* Name of profile output file. */ 60 public final String profileFile; 61 62 /* File descriptor for profile output file, can be null. */ 63 public ParcelFileDescriptor profileFd; 64 65 /* Indicates sample profiling when nonzero, interval in microseconds. */ 66 public final int samplingInterval; 67 68 /* Automatically stop the profiler when the app goes idle. */ 69 public final boolean autoStopProfiler; 70 71 /* 72 * Indicates whether to stream the profiling info to the out file continuously. 73 */ 74 public final boolean streamingOutput; 75 76 /** 77 * Denotes an agent (and its parameters) to attach for profiling. 78 */ 79 public final String agent; 80 81 /** 82 * Whether the {@link agent} should be attached early (before bind-application) or during 83 * bind-application. Agents attached prior to binding cannot be loaded from the app's APK 84 * directly and must be given as an absolute path (or available in the default LD_LIBRARY_PATH). 85 * Agents attached during bind-application will miss early setup (e.g., resource initialization 86 * and classloader generation), but are searched in the app's library search path. 87 */ 88 public final boolean attachAgentDuringBind; 89 90 /** 91 * Indicates the clock source to be used for profiling. The source could be wallclock, thread 92 * cpu or both 93 */ 94 public final int clockType; 95 96 /** 97 * Indicates the version of profiler output. 98 */ 99 public final int profilerOutputVersion; 100 ProfilerInfo(String filename, ParcelFileDescriptor fd, int interval, boolean autoStop, boolean streaming, String agent, boolean attachAgentDuringBind, int clockType, int profilerOutputVersion)101 public ProfilerInfo(String filename, ParcelFileDescriptor fd, int interval, boolean autoStop, 102 boolean streaming, String agent, boolean attachAgentDuringBind, int clockType, 103 int profilerOutputVersion) { 104 profileFile = filename; 105 profileFd = fd; 106 samplingInterval = interval; 107 autoStopProfiler = autoStop; 108 streamingOutput = streaming; 109 this.clockType = clockType; 110 this.agent = agent; 111 this.attachAgentDuringBind = attachAgentDuringBind; 112 this.profilerOutputVersion = profilerOutputVersion; 113 } 114 ProfilerInfo(ProfilerInfo in)115 public ProfilerInfo(ProfilerInfo in) { 116 profileFile = in.profileFile; 117 profileFd = in.profileFd; 118 samplingInterval = in.samplingInterval; 119 autoStopProfiler = in.autoStopProfiler; 120 streamingOutput = in.streamingOutput; 121 agent = in.agent; 122 attachAgentDuringBind = in.attachAgentDuringBind; 123 clockType = in.clockType; 124 profilerOutputVersion = in.profilerOutputVersion; 125 } 126 127 /** 128 * Get the value for the clock type corresponding to the option string passed to the activity 129 * manager. am profile start / am start-activity start-profiler commands accept clock-type 130 * option to choose the source of timestamps when profiling. This function maps the option 131 * string to the value of flags that is used when calling VMDebug.startMethodTracing 132 */ getClockTypeFromString(String type)133 public static int getClockTypeFromString(String type) { 134 if ("thread-cpu".equals(type)) { 135 return CLOCK_TYPE_THREAD_CPU; 136 } else if ("wall".equals(type)) { 137 return CLOCK_TYPE_WALL; 138 } else if ("dual".equals(type)) { 139 return CLOCK_TYPE_DUAL; 140 } else { 141 return CLOCK_TYPE_DEFAULT; 142 } 143 } 144 145 /** 146 * Get the flags that need to be passed to VMDebug.startMethodTracing to specify the desired 147 * output format. 148 */ getFlagsForOutputVersion(int version)149 public static int getFlagsForOutputVersion(int version) { 150 // Only two version 1 and version 2 are supported. Just use the default if we see an unknown 151 // version. 152 if (version != 1 || version != 2) { 153 version = OUTPUT_VERSION_DEFAULT; 154 } 155 156 // The encoded version in the flags starts from 0, where as the version that we read from 157 // user starts from 1. So, subtract one before encoding it in the flags. 158 return (version - 1) << TRACE_FORMAT_VERSION_SHIFT; 159 } 160 161 /** 162 * Return a new ProfilerInfo instance, with fields populated from this object, 163 * and {@link agent} and {@link attachAgentDuringBind} as given. 164 */ setAgent(String agent, boolean attachAgentDuringBind)165 public ProfilerInfo setAgent(String agent, boolean attachAgentDuringBind) { 166 return new ProfilerInfo(this.profileFile, this.profileFd, this.samplingInterval, 167 this.autoStopProfiler, this.streamingOutput, agent, attachAgentDuringBind, 168 this.clockType, this.profilerOutputVersion); 169 } 170 171 /** 172 * Close profileFd, if it is open. The field will be null after a call to this function. 173 */ closeFd()174 public void closeFd() { 175 if (profileFd != null) { 176 try { 177 profileFd.close(); 178 } catch (IOException e) { 179 Slog.w(TAG, "Failure closing profile fd", e); 180 } 181 profileFd = null; 182 } 183 } 184 185 @Override describeContents()186 public int describeContents() { 187 if (profileFd != null) { 188 return profileFd.describeContents(); 189 } else { 190 return 0; 191 } 192 } 193 194 @Override writeToParcel(Parcel out, int flags)195 public void writeToParcel(Parcel out, int flags) { 196 out.writeString(profileFile); 197 if (profileFd != null) { 198 out.writeInt(1); 199 profileFd.writeToParcel(out, flags); 200 } else { 201 out.writeInt(0); 202 } 203 out.writeInt(samplingInterval); 204 out.writeInt(autoStopProfiler ? 1 : 0); 205 out.writeInt(streamingOutput ? 1 : 0); 206 out.writeString(agent); 207 out.writeBoolean(attachAgentDuringBind); 208 out.writeInt(clockType); 209 out.writeInt(profilerOutputVersion); 210 } 211 212 /** @hide */ dumpDebug(ProtoOutputStream proto, long fieldId)213 public void dumpDebug(ProtoOutputStream proto, long fieldId) { 214 final long token = proto.start(fieldId); 215 proto.write(ProfilerInfoProto.PROFILE_FILE, profileFile); 216 if (profileFd != null) { 217 proto.write(ProfilerInfoProto.PROFILE_FD, profileFd.getFd()); 218 } 219 proto.write(ProfilerInfoProto.SAMPLING_INTERVAL, samplingInterval); 220 proto.write(ProfilerInfoProto.AUTO_STOP_PROFILER, autoStopProfiler); 221 proto.write(ProfilerInfoProto.STREAMING_OUTPUT, streamingOutput); 222 proto.write(ProfilerInfoProto.AGENT, agent); 223 proto.write(ProfilerInfoProto.CLOCK_TYPE, clockType); 224 proto.write(ProfilerInfoProto.PROFILER_OUTPUT_VERSION, profilerOutputVersion); 225 proto.end(token); 226 } 227 228 public static final @android.annotation.NonNull Parcelable.Creator<ProfilerInfo> CREATOR = 229 new Parcelable.Creator<ProfilerInfo>() { 230 @Override 231 public ProfilerInfo createFromParcel(Parcel in) { 232 return new ProfilerInfo(in); 233 } 234 235 @Override 236 public ProfilerInfo[] newArray(int size) { 237 return new ProfilerInfo[size]; 238 } 239 }; 240 ProfilerInfo(Parcel in)241 private ProfilerInfo(Parcel in) { 242 profileFile = in.readString(); 243 profileFd = in.readInt() != 0 ? ParcelFileDescriptor.CREATOR.createFromParcel(in) : null; 244 samplingInterval = in.readInt(); 245 autoStopProfiler = in.readInt() != 0; 246 streamingOutput = in.readInt() != 0; 247 agent = in.readString(); 248 attachAgentDuringBind = in.readBoolean(); 249 clockType = in.readInt(); 250 profilerOutputVersion = in.readInt(); 251 } 252 253 @Override equals(@ullable Object o)254 public boolean equals(@Nullable Object o) { 255 if (this == o) { 256 return true; 257 } 258 if (o == null || getClass() != o.getClass()) { 259 return false; 260 } 261 final ProfilerInfo other = (ProfilerInfo) o; 262 // TODO: Also check #profileFd for equality. 263 return Objects.equals(profileFile, other.profileFile) 264 && autoStopProfiler == other.autoStopProfiler 265 && samplingInterval == other.samplingInterval 266 && streamingOutput == other.streamingOutput && Objects.equals(agent, other.agent) 267 && clockType == other.clockType 268 && profilerOutputVersion == other.profilerOutputVersion; 269 } 270 271 @Override hashCode()272 public int hashCode() { 273 int result = 17; 274 result = 31 * result + Objects.hashCode(profileFile); 275 result = 31 * result + samplingInterval; 276 result = 31 * result + (autoStopProfiler ? 1 : 0); 277 result = 31 * result + (streamingOutput ? 1 : 0); 278 result = 31 * result + Objects.hashCode(agent); 279 result = 31 * result + clockType; 280 result = 31 * result + profilerOutputVersion; 281 return result; 282 } 283 } 284