blob: 505c01e633e7b33a051b733997f22b712bafb14d [file] [log] [blame]
/*******************************************************************************
* 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 2.0 which
* accompanies this distribution, and is available at
* https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*******************************************************************************/
package org.eclipse.tracecompass.internal.analysis.os.linux.core.inputoutput;
import static org.eclipse.tracecompass.common.core.NonNullUtils.checkNotNull;
import java.util.HashMap;
import java.util.Map;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.analysis.os.linux.core.inputoutput.Attributes;
import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelAnalysisEventLayout;
import org.eclipse.tracecompass.common.core.NonNullUtils;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.Activator;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.inputoutput.handlers.BlockFrontMergeHandler;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.inputoutput.handlers.BlockRqComplete;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.inputoutput.handlers.BlockRqInsertHandler;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.inputoutput.handlers.BlockRqIssueHandler;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.inputoutput.handlers.MergeRequestsHandler;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.inputoutput.handlers.StateDumpHandler;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.inputoutput.handlers.SysEntryHandler;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.inputoutput.handlers.SysExitHandler;
import org.eclipse.tracecompass.internal.analysis.os.linux.core.kernel.handlers.KernelEventHandler;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
import org.eclipse.tracecompass.statesystem.core.exceptions.TimeRangeException;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.statesystem.AbstractTmfStateProvider;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
import com.google.common.collect.ImmutableMap;
/**
* State provider for the I/O analysis
*
* Attribute tree:
*
* <pre>
* |- SYSTEM_CALLS
* | |- <TID> -> System Call Name
* |- THREADS
* | |- <TID number>
* | | |- BYTES_READ
* | | |- BYTES_WRITTEN
* |- Disks
* | |- <Disk number> -> Disk Name
* | | |- SECTORS_READ
* | | |- SECTORS_WRITTEN
* | | |- WAITING_QUEUE -> Root for the Attribute pool for waiting queue
* | | | |- <slot #1> -> Status
* | | | | |- CURRENT_REQUEST
* | | | | |- REQUEST_SIZE
* | | | | |- MERGED_IN
* | | | |- <slot #2>
* | | |- WAITING_QUEUE_LENGTH
* | | |- DRIVER_QUEUE -> Root for the Attribute pool for driver queue
* | | | |- <slot #1> -> Status
* | | | | |- CURRENT_REQUEST
* | | | | |- REQUEST_SIZE
* | | | | |- ISSUED_FROM
* | | | |- <slot #2>
* | | |- DRIVER_QUEUE_LENGTH
* </pre>
*
* @author Houssem Daoud
* @since 2.0
*/
public class InputOutputStateProvider extends AbstractTmfStateProvider {
private static final int VERSION = 1;
private final Map<Integer, DiskWriteModel> fDisks = new HashMap<>();
private final Map<String, KernelEventHandler> fEventNames;
private final IKernelAnalysisEventLayout fLayout;
private final KernelEventHandler fSysEntryHandler;
private final KernelEventHandler fSysExitHandler;
/**
* Instantiate a new state provider plugin.
*
* @param trace
* The kernel trace to apply this state provider to
* @param layout
* The event layout to use for this state provider.
*/
public InputOutputStateProvider(ITmfTrace trace, IKernelAnalysisEventLayout layout) {
super(trace, "Input Output Analysis");//$NON-NLS-1$
fLayout = layout;
fEventNames = buildEventNames(layout);
fSysEntryHandler = new SysEntryHandler(layout);
fSysExitHandler = new SysExitHandler(layout);
}
private Map<String, KernelEventHandler> buildEventNames(IKernelAnalysisEventLayout layout) {
ImmutableMap.Builder<String, KernelEventHandler> builder = ImmutableMap.builder();
builder.put(layout.eventBlockRqInsert(), new BlockRqInsertHandler(layout, this));
builder.put(layout.eventBlockRqIssue(), new BlockRqIssueHandler(layout, this));
builder.put(layout.eventBlockRqComplete(), new BlockRqComplete(layout, this));
builder.put(layout.eventBlockBioFrontmerge(), new BlockFrontMergeHandler(layout, this));
builder.put(layout.eventBlockRqMerge(), new MergeRequestsHandler(layout, this));
final String eventStatedumpBlockDevice = layout.eventStatedumpBlockDevice();
if (eventStatedumpBlockDevice != null) {
builder.put(eventStatedumpBlockDevice, new StateDumpHandler(layout, this));
}
return builder.build();
}
@Override
public int getVersion() {
return VERSION;
}
@Override
public InputOutputStateProvider getNewInstance() {
return new InputOutputStateProvider(this.getTrace(), this.fLayout);
}
@Override
protected void eventHandle(@Nullable ITmfEvent event) {
if (event == null) {
return;
}
final String eventName = event.getName();
try {
final ITmfStateSystemBuilder ss = NonNullUtils.checkNotNull(getStateSystemBuilder());
/*
* Feed event to the history system if it's known to cause a state
* transition.
*/
KernelEventHandler handler = fEventNames.get(eventName);
if (handler == null) {
if (isSyscallExit(eventName)) {
handler = fSysExitHandler;
} else if (isSyscallEntry(eventName)) {
handler = fSysEntryHandler;
}
}
if (handler != null) {
handler.handleEvent(ss, event);
}
} catch (TimeRangeException | StateValueTypeException | AttributeNotFoundException e) {
Activator.getDefault().logError("Exception while building the IO state system", e); //$NON-NLS-1$
}
}
/**
* Get a disk identified by a device ID
*
* @param deviceId
* The device ID of the block device
* @return The disk corresponding to the device ID
*/
public DiskWriteModel getDisk(int deviceId) {
return fDisks.computeIfAbsent(deviceId, diskId -> new DiskWriteModel(diskId, checkNotNull(getStateSystemBuilder())));
}
private boolean isSyscallEntry(String eventName) {
return (eventName.startsWith(fLayout.eventSyscallEntryPrefix())
|| eventName.startsWith(fLayout.eventCompatSyscallEntryPrefix()));
}
private boolean isSyscallExit(String eventName) {
return (eventName.startsWith(fLayout.eventSyscallExitPrefix()) ||
eventName.startsWith(fLayout.eventCompatSyscallExitPrefix()));
}
/**
* Return the quark corresponding to the threads attributes
*
* @param ssb
* the state system builder
* @return The quark of the {@link Attributes#THREADS} node
*/
public static int getNodeThreads(ITmfStateSystemBuilder ssb) {
return ssb.getQuarkAbsoluteAndAdd(Attributes.THREADS);
}
/**
* Return the quark corresponding to the system call root attributes
*
* @param ssb
* the state system builder
* @return The quark of the {@link Attributes#SYSTEM_CALLS_ROOT} node
*/
public static int getNodeSyscalls(ITmfStateSystemBuilder ssb) {
return ssb.getQuarkAbsoluteAndAdd(Attributes.SYSTEM_CALLS_ROOT);
}
}