1 /* 2 * Copyright (C) 2017 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 package android.car.storagemonitoring; 17 18 import android.annotation.SystemApi; 19 import android.os.Parcel; 20 import android.os.Parcelable; 21 import android.util.JsonWriter; 22 23 import org.json.JSONException; 24 import org.json.JSONObject; 25 26 import java.io.IOException; 27 import java.util.Objects; 28 29 /** 30 * uid_io stats about one user ID. 31 * 32 * Contains information about I/O activity that can be attributed to processes running on 33 * behalf of one user of the system, as collected by the kernel. 34 * 35 * @hide 36 */ 37 @SystemApi 38 public final class IoStatsEntry implements Parcelable { 39 40 public static final Parcelable.Creator<IoStatsEntry> CREATOR = 41 new Parcelable.Creator<IoStatsEntry>() { 42 public IoStatsEntry createFromParcel(Parcel in) { 43 return new IoStatsEntry(in); 44 } 45 46 public IoStatsEntry[] newArray(int size) { 47 return new IoStatsEntry[size]; 48 } 49 }; 50 51 /** 52 * The user id that this object contains metrics for. 53 * 54 * In many cases this can be converted to a list of Java app packages installed on the device. 55 * In other cases, the user id can refer to either the kernel itself (uid 0), or low-level 56 * system services that are running entirely natively. 57 */ 58 public final int uid; 59 60 /** 61 * How long any process running on behalf of this user id running for, in milliseconds. 62 * 63 * This field is allowed to be an approximation and it does not provide any way to 64 * relate uptime to specific processes. 65 */ 66 public final long runtimeMillis; 67 68 /** 69 * Statistics for apps running in foreground. 70 */ 71 public final IoStatsEntry.Metrics foreground; 72 73 /** 74 * Statistics for apps running in background. 75 */ 76 public final IoStatsEntry.Metrics background; 77 IoStatsEntry(int uid, long runtimeMillis, IoStatsEntry.Metrics foreground, IoStatsEntry.Metrics background)78 public IoStatsEntry(int uid, 79 long runtimeMillis, IoStatsEntry.Metrics foreground, IoStatsEntry.Metrics background) { 80 this.uid = uid; 81 this.runtimeMillis = runtimeMillis; 82 this.foreground = Objects.requireNonNull(foreground); 83 this.background = Objects.requireNonNull(background); 84 } 85 IoStatsEntry(Parcel in)86 public IoStatsEntry(Parcel in) { 87 uid = in.readInt(); 88 runtimeMillis = in.readLong(); 89 foreground = in.readParcelable(IoStatsEntry.Metrics.class.getClassLoader()); 90 background = in.readParcelable(IoStatsEntry.Metrics.class.getClassLoader()); 91 } 92 IoStatsEntry(UidIoRecord record, long runtimeMillis)93 public IoStatsEntry(UidIoRecord record, long runtimeMillis) { 94 uid = record.uid; 95 this.runtimeMillis = runtimeMillis; 96 foreground = new IoStatsEntry.Metrics(record.foreground_rchar, 97 record.foreground_wchar, 98 record.foreground_read_bytes, 99 record.foreground_write_bytes, 100 record.foreground_fsync); 101 background = new IoStatsEntry.Metrics(record.background_rchar, 102 record.background_wchar, 103 record.background_read_bytes, 104 record.background_write_bytes, 105 record.background_fsync); 106 } 107 108 @Override describeContents()109 public int describeContents() { 110 return 0; 111 } 112 113 @Override writeToParcel(Parcel dest, int flags)114 public void writeToParcel(Parcel dest, int flags) { 115 dest.writeInt(uid); 116 dest.writeLong(runtimeMillis); 117 dest.writeParcelable(foreground, flags); 118 dest.writeParcelable(background, flags); 119 } 120 121 /** 122 * @hide 123 */ writeToJson(JsonWriter jsonWriter)124 public void writeToJson(JsonWriter jsonWriter) throws IOException { 125 jsonWriter.beginObject(); 126 jsonWriter.name("uid").value(uid); 127 jsonWriter.name("runtimeMillis").value(runtimeMillis); 128 jsonWriter.name("foreground"); foreground.writeToJson(jsonWriter); 129 jsonWriter.name("background"); background.writeToJson(jsonWriter); 130 jsonWriter.endObject(); 131 } 132 133 /** 134 * @hide 135 */ IoStatsEntry(JSONObject in)136 public IoStatsEntry(JSONObject in) throws JSONException { 137 uid = in.getInt("uid"); 138 runtimeMillis = in.getLong("runtimeMillis"); 139 foreground = new IoStatsEntry.Metrics(in.getJSONObject("foreground")); 140 background = new IoStatsEntry.Metrics(in.getJSONObject("background")); 141 } 142 143 /** 144 * Returns the difference between the values stored in this object vs. those 145 * stored in other. 146 * 147 * It is the same as doing a delta() on foreground and background, plus verifying that 148 * both objects refer to the same uid. 149 * 150 * @hide 151 */ delta(IoStatsEntry other)152 public IoStatsEntry delta(IoStatsEntry other) { 153 if (uid != other.uid) { 154 throw new IllegalArgumentException("cannot calculate delta between different user IDs"); 155 } 156 return new IoStatsEntry(uid, 157 runtimeMillis - other.runtimeMillis, 158 foreground.delta(other.foreground), background.delta(other.background)); 159 } 160 161 @Override equals(Object other)162 public boolean equals(Object other) { 163 if (other instanceof IoStatsEntry) { 164 IoStatsEntry uidIoStatEntry = (IoStatsEntry)other; 165 166 return uid == uidIoStatEntry.uid && 167 runtimeMillis == uidIoStatEntry.runtimeMillis && 168 foreground.equals(uidIoStatEntry.foreground) && 169 background.equals(uidIoStatEntry.background); 170 } 171 172 return false; 173 } 174 175 @Override hashCode()176 public int hashCode() { 177 return Objects.hash(uid, runtimeMillis, foreground, background); 178 } 179 180 @Override toString()181 public String toString() { 182 return String.format("uid = %d, runtime = %d, foreground = %s, background = %s", 183 uid, runtimeMillis, foreground, background); 184 } 185 186 /** 187 * Validates that this object contains the same I/O metrics as a UidIoStatsRecord. 188 * 189 * It matches UID, and I/O activity values, but ignores runtime. 190 * @hide 191 */ representsSameMetrics(UidIoRecord record)192 public boolean representsSameMetrics(UidIoRecord record) { 193 return record.uid == uid && 194 record.foreground_rchar == foreground.bytesRead && 195 record.foreground_wchar == foreground.bytesWritten && 196 record.foreground_read_bytes == foreground.bytesReadFromStorage && 197 record.foreground_write_bytes == foreground.bytesWrittenToStorage && 198 record.foreground_fsync == foreground.fsyncCalls && 199 record.background_rchar == background.bytesRead && 200 record.background_wchar == background.bytesWritten && 201 record.background_read_bytes == background.bytesReadFromStorage && 202 record.background_write_bytes == background.bytesWrittenToStorage && 203 record.background_fsync == background.fsyncCalls; 204 } 205 206 /** 207 * I/O activity metrics that pertain to either the foreground or the background state. 208 */ 209 public static final class Metrics implements Parcelable { 210 211 public static final Parcelable.Creator<IoStatsEntry.Metrics> CREATOR = 212 new Parcelable.Creator<IoStatsEntry.Metrics>() { 213 public IoStatsEntry.Metrics createFromParcel(Parcel in) { 214 return new IoStatsEntry.Metrics(in); 215 } 216 217 public IoStatsEntry.Metrics[] newArray(int size) { 218 return new IoStatsEntry.Metrics[size]; 219 } 220 }; 221 222 /** 223 * Total bytes that processes running on behalf of this user obtained 224 * via read() system calls. 225 */ 226 public final long bytesRead; 227 228 /** 229 * Total bytes that processes running on behalf of this user transferred 230 * via write() system calls. 231 */ 232 public final long bytesWritten; 233 234 /** 235 * Total bytes that processes running on behalf of this user obtained 236 * via read() system calls that actually were served by physical storage. 237 */ 238 public final long bytesReadFromStorage; 239 240 /** 241 * Total bytes that processes running on behalf of this user transferred 242 * via write() system calls that were actually sent to physical storage. 243 */ 244 public final long bytesWrittenToStorage; 245 246 /** 247 * Total number of fsync() system calls that processes running on behalf of this user made. 248 */ 249 public final long fsyncCalls; 250 Metrics(long bytesRead, long bytesWritten, long bytesReadFromStorage, long bytesWrittenToStorage, long fsyncCalls)251 public Metrics(long bytesRead, long bytesWritten, long bytesReadFromStorage, 252 long bytesWrittenToStorage, long fsyncCalls) { 253 this.bytesRead = bytesRead; 254 this.bytesWritten = bytesWritten; 255 this.bytesReadFromStorage = bytesReadFromStorage; 256 this.bytesWrittenToStorage = bytesWrittenToStorage; 257 this.fsyncCalls = fsyncCalls; 258 } 259 260 @Override describeContents()261 public int describeContents() { 262 return 0; 263 } 264 265 @Override writeToParcel(Parcel dest, int flags)266 public void writeToParcel(Parcel dest, int flags) { 267 dest.writeLong(bytesRead); 268 dest.writeLong(bytesWritten); 269 dest.writeLong(bytesReadFromStorage); 270 dest.writeLong(bytesWrittenToStorage); 271 dest.writeLong(fsyncCalls); 272 } 273 274 /** 275 * @hide 276 */ writeToJson(JsonWriter jsonWriter)277 public void writeToJson(JsonWriter jsonWriter) throws IOException { 278 jsonWriter.beginObject(); 279 jsonWriter.name("bytesRead").value(bytesRead); 280 jsonWriter.name("bytesWritten").value(bytesWritten); 281 jsonWriter.name("bytesReadFromStorage").value(bytesReadFromStorage); 282 jsonWriter.name("bytesWrittenToStorage").value(bytesWrittenToStorage); 283 jsonWriter.name("fsyncCalls").value(fsyncCalls); 284 jsonWriter.endObject(); 285 } 286 Metrics(Parcel in)287 public Metrics(Parcel in) { 288 bytesRead = in.readLong(); 289 bytesWritten = in.readLong(); 290 bytesReadFromStorage = in.readLong(); 291 bytesWrittenToStorage = in.readLong(); 292 fsyncCalls = in.readLong(); 293 } 294 295 /** 296 * @hide 297 */ Metrics(JSONObject in)298 public Metrics(JSONObject in) throws JSONException { 299 bytesRead = in.getLong("bytesRead"); 300 bytesWritten = in.getLong("bytesWritten"); 301 bytesReadFromStorage = in.getLong("bytesReadFromStorage"); 302 bytesWrittenToStorage = in.getLong("bytesWrittenToStorage"); 303 fsyncCalls = in.getLong("fsyncCalls"); 304 } 305 306 /** 307 * Computes the difference between the values stored in this object 308 * vs. those stored in other 309 * 310 * It is the same as doing 311 * new PerStateMetrics(bytesRead-other.bytesRead,bytesWritten-other.bytesWritten, ...) 312 * 313 * @hide 314 */ delta(Metrics other)315 public Metrics delta(Metrics other) { 316 return new Metrics(bytesRead-other.bytesRead, 317 bytesWritten-other.bytesWritten, 318 bytesReadFromStorage-other.bytesReadFromStorage, 319 bytesWrittenToStorage-other.bytesWrittenToStorage, 320 fsyncCalls-other.fsyncCalls); 321 } 322 323 @Override equals(Object other)324 public boolean equals(Object other) { 325 if (other instanceof Metrics) { 326 Metrics metrics = (Metrics)other; 327 328 return (bytesRead == metrics.bytesRead) && 329 (bytesWritten == metrics.bytesWritten) && 330 (bytesReadFromStorage == metrics.bytesReadFromStorage) && 331 (bytesWrittenToStorage == metrics.bytesWrittenToStorage) && 332 (fsyncCalls == metrics.fsyncCalls); 333 } 334 return false; 335 } 336 337 @Override hashCode()338 public int hashCode() { 339 return Objects.hash(bytesRead, bytesWritten, bytesReadFromStorage, 340 bytesWrittenToStorage, fsyncCalls); 341 } 342 343 @Override toString()344 public String toString() { 345 return String.format("bytesRead=%d, bytesWritten=%d, bytesReadFromStorage=%d, bytesWrittenToStorage=%d, fsyncCalls=%d", 346 bytesRead, bytesWritten, bytesReadFromStorage, bytesWrittenToStorage, fsyncCalls); 347 } 348 } 349 } 350