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