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 43 public IoStatsEntry createFromParcel(Parcel in) { 44 return new IoStatsEntry(in); 45 } 46 47 public IoStatsEntry[] newArray(int size) { 48 return new IoStatsEntry[size]; 49 } 50 }; 51 52 /** 53 * The user id that this object contains metrics for. 54 * 55 * In many cases this can be converted to a list of Java app packages installed on the device. 56 * In other cases, the user id can refer to either the kernel itself (uid 0), or low-level 57 * system services that are running entirely natively. 58 */ 59 public final int uid; 60 61 /** 62 * How long any process running on behalf of this user id running for, in milliseconds. 63 * 64 * This field is allowed to be an approximation and it does not provide any way to 65 * relate uptime to specific processes. 66 */ 67 public final long runtimeMillis; 68 69 /** 70 * Statistics for apps running in foreground. 71 */ 72 public final IoStatsEntry.Metrics foreground; 73 74 /** 75 * Statistics for apps running in background. 76 */ 77 public final IoStatsEntry.Metrics background; 78 IoStatsEntry(int uid, long runtimeMillis, IoStatsEntry.Metrics foreground, IoStatsEntry.Metrics background)79 public IoStatsEntry(int uid, 80 long runtimeMillis, IoStatsEntry.Metrics foreground, IoStatsEntry.Metrics background) { 81 this.uid = uid; 82 this.runtimeMillis = runtimeMillis; 83 this.foreground = Objects.requireNonNull(foreground); 84 this.background = Objects.requireNonNull(background); 85 } 86 IoStatsEntry(Parcel in)87 public IoStatsEntry(Parcel in) { 88 uid = in.readInt(); 89 runtimeMillis = in.readLong(); 90 foreground = in.readParcelable(IoStatsEntry.Metrics.class.getClassLoader()); 91 background = in.readParcelable(IoStatsEntry.Metrics.class.getClassLoader()); 92 } 93 IoStatsEntry(UidIoRecord record, long runtimeMillis)94 public IoStatsEntry(UidIoRecord record, long runtimeMillis) { 95 uid = record.uid; 96 this.runtimeMillis = runtimeMillis; 97 foreground = new IoStatsEntry.Metrics(record.foreground_rchar, 98 record.foreground_wchar, 99 record.foreground_read_bytes, 100 record.foreground_write_bytes, 101 record.foreground_fsync); 102 background = new IoStatsEntry.Metrics(record.background_rchar, 103 record.background_wchar, 104 record.background_read_bytes, 105 record.background_write_bytes, 106 record.background_fsync); 107 } 108 109 @Override describeContents()110 public int describeContents() { 111 return 0; 112 } 113 114 @Override writeToParcel(Parcel dest, int flags)115 public void writeToParcel(Parcel dest, int flags) { 116 dest.writeInt(uid); 117 dest.writeLong(runtimeMillis); 118 dest.writeParcelable(foreground, flags); 119 dest.writeParcelable(background, flags); 120 } 121 122 /** 123 * @hide 124 */ writeToJson(JsonWriter jsonWriter)125 public void writeToJson(JsonWriter jsonWriter) throws IOException { 126 jsonWriter.beginObject(); 127 jsonWriter.name("uid").value(uid); 128 jsonWriter.name("runtimeMillis").value(runtimeMillis); 129 jsonWriter.name("foreground"); foreground.writeToJson(jsonWriter); 130 jsonWriter.name("background"); background.writeToJson(jsonWriter); 131 jsonWriter.endObject(); 132 } 133 134 /** 135 * @hide 136 */ IoStatsEntry(JSONObject in)137 public IoStatsEntry(JSONObject in) throws JSONException { 138 uid = in.getInt("uid"); 139 runtimeMillis = in.getLong("runtimeMillis"); 140 foreground = new IoStatsEntry.Metrics(in.getJSONObject("foreground")); 141 background = new IoStatsEntry.Metrics(in.getJSONObject("background")); 142 } 143 144 /** 145 * Returns the difference between the values stored in this object vs. those 146 * stored in other. 147 * 148 * It is the same as doing a delta() on foreground and background, plus verifying that 149 * both objects refer to the same uid. 150 * 151 * @hide 152 */ delta(IoStatsEntry other)153 public IoStatsEntry delta(IoStatsEntry other) { 154 if (uid != other.uid) { 155 throw new IllegalArgumentException("cannot calculate delta between different user IDs"); 156 } 157 return new IoStatsEntry(uid, 158 runtimeMillis - other.runtimeMillis, 159 foreground.delta(other.foreground), background.delta(other.background)); 160 } 161 162 @Override equals(Object other)163 public boolean equals(Object other) { 164 if (other instanceof IoStatsEntry) { 165 IoStatsEntry uidIoStatEntry = (IoStatsEntry) other; 166 167 return uid == uidIoStatEntry.uid 168 && runtimeMillis == uidIoStatEntry.runtimeMillis 169 && foreground.equals(uidIoStatEntry.foreground) 170 && background.equals(uidIoStatEntry.background); 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, " 346 + "bytesWrittenToStorage=%d, fsyncCalls=%d", bytesRead, bytesWritten, 347 bytesReadFromStorage, bytesWrittenToStorage, fsyncCalls); 348 } 349 } 350 } 351