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