/******************************************************************************* * Copyright (c) 2016 École Polytechnique de Montréal * * All rights reserved. This program and the accompanying materials are * made available under the terms of the Eclipse Public License v1.0 which * accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html *******************************************************************************/ package org.eclipse.tracecompass.analysis.os.linux.core.inputoutput; import java.util.List; import org.eclipse.jdt.annotation.Nullable; import org.eclipse.tracecompass.common.core.NonNullUtils; import org.eclipse.tracecompass.internal.analysis.os.linux.core.Activator; import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem; import org.eclipse.tracecompass.statesystem.core.StateSystemUtils; import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException; import org.eclipse.tracecompass.statesystem.core.exceptions.StateSystemDisposedException; import org.eclipse.tracecompass.statesystem.core.interval.ITmfStateInterval; import com.google.common.hash.HashFunction; import com.google.common.hash.Hashing; /** * This class represents a storage device in the system that behaves like a disk * from the operating system point of view. Concretely, it can be an HDD, an * SSD, a USB key, etc. * * @author Geneviève Bastien */ public class Disk { private static final HashFunction HF = NonNullUtils.checkNotNull(Hashing.goodFastHash(32)); private static final Integer MINORBITS = 20; private static final Integer MINORMASK = ((1 << MINORBITS) - 1); private final Integer fDev; private final int fDiskQuark; private final ITmfStateSystem fSs; private @Nullable String fDiskName = null; /** * Constructor * * @param dev * The device number of the disk * @param ss * The state system this disk will be saved to * @param diskQuark * The quark of this disk in the state system */ public Disk(Integer dev, ITmfStateSystem ss, int diskQuark) { fDev = dev; fSs = ss; fDiskQuark = diskQuark; ITmfStateInterval diskNameInterval = StateSystemUtils.queryUntilNonNullValue(ss, diskQuark, ss.getStartTime(), ss.getCurrentEndTime()); if (diskNameInterval != null) { fDiskName = diskNameInterval.getStateValue().unboxStr(); } } /** * Get the device ID of this device * * @return The devide ID of this disk */ public Integer getDevideId() { return fDev; } /** * Get the disk name if available. If the disk name is not set, this method * will return the string corresponding to the major, minor value of the * disk's ID, ie the return value of {@link #getDeviceIdString()}. * * @return The disk name or the value returned by * {@link #getDeviceIdString()} */ public String getDiskName() { String diskName = fDiskName; if (diskName == null) { return getDeviceIdString(); } return diskName; } /** * Get the quark * * @return The quark of this disk in the state system */ public int getQuark() { return fDiskQuark; } /** * Set the human readable disk name of this device * * @param diskname * The human readable name of the disk */ public void setDiskName(String diskname) { fDiskName = diskname; } /** * Return the disk's device ID as a major,minor string. Those major,minor * numbers correspond to the number of the disk found when listing disk with * ls -al /dev, or using lsblk in Linux. * * @return The device ID string as major,minor */ public String getDeviceIdString() { Integer major = fDev >> MINORBITS; Integer minor = fDev & MINORMASK; return major.toString() + ',' + minor.toString(); } /** * Get the total number of sectors either read or written at the end of a * time range. This method will interpolate the requests that are in * progress. * * @param ts * The start of the time range to query * @param type * The type of IO operation to query * @return The number of sectors affected by operation at the end of the * range */ public long getSectorsAt(long ts, IoOperationType type) { ITmfStateSystem ss = fSs; long currentCount = 0; /* Get the quark for the number of sector for the requested operation */ int rwSectorQuark = ITmfStateSystem.INVALID_ATTRIBUTE; if (type == IoOperationType.READ) { rwSectorQuark = ss.optQuarkRelative(fDiskQuark, Attributes.SECTORS_READ); } else if (type == IoOperationType.WRITE) { rwSectorQuark = ss.optQuarkRelative(fDiskQuark, Attributes.SECTORS_WRITTEN); } if (rwSectorQuark == ITmfStateSystem.INVALID_ATTRIBUTE) { return currentCount; } int rw = type == IoOperationType.READ ? StateValues.READING_REQUEST : StateValues.WRITING_REQUEST; long time = Math.max(ts, ss.getStartTime()); time = Math.min(time, ss.getCurrentEndTime()); try { List<ITmfStateInterval> states = ss.queryFullState(time); long count = states.get(rwSectorQuark).getStateValue().unboxLong(); if (count == -1) { count = 0; } Integer driverQ = ss.getQuarkRelative(fDiskQuark, Attributes.DRIVER_QUEUE); /* * Interpolate the part of the requests in progress at requested * time */ for (Integer driverSlotQuark : ss.getSubAttributes(driverQ, false)) { int sizeQuark = ss.getQuarkRelative(driverSlotQuark, Attributes.REQUEST_SIZE); ITmfStateInterval interval = states.get(sizeQuark); if (!interval.getStateValue().isNull()) { if (states.get(driverSlotQuark).getStateValue().unboxInt() == rw) { /* * The request is fully completed (and included in the * r/w sectors) at interval end time + 1, so at interval * end time, we do not expect the size to be total size */ long runningTime = interval.getEndTime() - interval.getStartTime() + 1; long runningEnd = interval.getEndTime() + 1; long startsize = interval.getStateValue().unboxLong(); count = interpolateCount(count, time, runningEnd, runningTime, startsize); } } } currentCount = count; } catch (StateSystemDisposedException | AttributeNotFoundException e) { Activator.getDefault().logError("Error getting disk IO Activity", e); //$NON-NLS-1$ } return currentCount; } private static long interpolateCount(long count, long ts, long runningEnd, long runningTime, long size) { long newCount = count; if (runningTime > 0) { long runningStart = runningEnd - runningTime; if (ts < runningStart) { return newCount; } double interpolation = (double) (ts - runningStart) * (double) size / (runningTime); /* Will truncate the decimal part */ newCount += (long) interpolation; } return newCount; } /** * Return whether requests were made on this disk during the trace or not * * @return {@code true} if there was requests on this disk, {@code false} * otherwise */ public boolean hasActivity() { try { int wqQuark = fSs.getQuarkRelative(fDiskQuark, Attributes.WAITING_QUEUE); if (fSs.getSubAttributes(wqQuark, false).size() > 0) { return true; } int dqQuark = fSs.getQuarkRelative(fDiskQuark, Attributes.DRIVER_QUEUE); if (fSs.getSubAttributes(dqQuark, false).size() > 0) { return true; } } catch (AttributeNotFoundException e) { } return false; } // ---------------------------------------------------- // Object methods // ---------------------------------------------------- @Override public String toString() { return "Disk: [" + getDeviceIdString() + ',' + fDiskName + ']'; //$NON-NLS-1$ } @Override public int hashCode() { return HF.newHasher().putInt(fDev).hash().asInt(); } @Override public boolean equals(@Nullable Object o) { if (o instanceof Disk) { Disk disk = (Disk) o; if (fDev.equals(disk.fDev)) { return true; } } return false; } }