blob: 4df25a82a427452d8b9810958fa13454be0ffce6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2018 Ericsson
*
* 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.incubator.internal.kernel.core.filedescriptor;
import java.util.HashMap;
import java.util.Map;
import java.util.function.Consumer;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.jdt.annotation.Nullable;
import org.eclipse.tracecompass.analysis.os.linux.core.event.aspect.LinuxTidAspect;
import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelAnalysisEventLayout;
import org.eclipse.tracecompass.analysis.os.linux.core.trace.IKernelTrace;
import org.eclipse.tracecompass.incubator.internal.kernel.core.Activator;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystem;
import org.eclipse.tracecompass.statesystem.core.ITmfStateSystemBuilder;
import org.eclipse.tracecompass.statesystem.core.StateSystemBuilderUtils;
import org.eclipse.tracecompass.statesystem.core.exceptions.AttributeNotFoundException;
import org.eclipse.tracecompass.statesystem.core.exceptions.StateValueTypeException;
import org.eclipse.tracecompass.tmf.core.event.ITmfEvent;
import org.eclipse.tracecompass.tmf.core.statesystem.AbstractTmfStateProvider;
import org.eclipse.tracecompass.tmf.core.trace.TmfTraceUtils;
/**
* <p>
* File Descriptor State Provider
* </p>
* <p>
* This allows handling of generic file descriptors with common operations
* </p>
* <p>
* Common elements
* </p>
* <ul>
* <li>read</li>
* <li>write</li>
* <li>close</li>
* </ul>
*
* @author Matthew Khouzam
*/
public abstract class FileDescriptorStateProvider extends AbstractTmfStateProvider {
/**
* TID field in state system
*/
public static final String TID = "TID"; //$NON-NLS-1$
/**
* Resources field in state system
*/
public static final String RESOURCES = "RES"; //$NON-NLS-1$
/**
* File descriptor name
*/
protected static final String DESCRIPTOR = "fd"; //$NON-NLS-1$
/**
* Read entry
*/
public static final String READ = "read"; //$NON-NLS-1$
/**
* Write entry
*/
public static final String WRITE = "write"; //$NON-NLS-1$
private static final String COUNT = "count"; //$NON-NLS-1$
private static final String PREAD = "pread"; //$NON-NLS-1$
private static final String READ64 = "read64"; //$NON-NLS-1$
private static final String PREAD64 = "pread64"; //$NON-NLS-1$
private static final String PWRITE = "pwrite"; //$NON-NLS-1$
private static final String WRITE64 = "write64"; //$NON-NLS-1$
private static final String PWRITE64 = "pwrite64"; //$NON-NLS-1$
private static final String CLOSE = "close"; //$NON-NLS-1$
private final Map<String, Consumer<HandlerParameter>> fHandlers = new HashMap<>();
private final IKernelAnalysisEventLayout fLayout;
/*
* Nullable to make the jdt be quiet
*/
private final Map<Integer, @Nullable Long> fToRead = new HashMap<>();
private final Map<Integer, @Nullable Long> fToWrite = new HashMap<>();
private final Map<Integer, Long> fToClose = new HashMap<>();
/**
* Constructor
*
* @param trace
* The trace
* @param id
* Name given to this state change input. Only used internally.
*/
public FileDescriptorStateProvider(IKernelTrace trace, String id) {
super(trace, id);
fLayout = trace.getKernelEventLayout();
addEventHandler(getLayout().eventSyscallEntryPrefix() + CLOSE, this::closeBegin);
addEventHandler(getLayout().eventSyscallExitPrefix() + CLOSE, this::closeEnd);
addEventHandler(getLayout().eventSyscallEntryPrefix() + READ, this::readBegin);
addEventHandler(getLayout().eventSyscallEntryPrefix() + PREAD, this::readBegin);
addEventHandler(getLayout().eventSyscallEntryPrefix() + READ64, this::readBegin);
addEventHandler(getLayout().eventSyscallEntryPrefix() + PREAD64, this::readBegin);
addEventHandler(getLayout().eventSyscallExitPrefix() + READ, this::readEnd);
addEventHandler(getLayout().eventSyscallExitPrefix() + PREAD, this::readEnd);
addEventHandler(getLayout().eventSyscallExitPrefix() + READ64, this::readEnd);
addEventHandler(getLayout().eventSyscallExitPrefix() + PREAD64, this::readEnd);
addEventHandler(getLayout().eventSyscallEntryPrefix() + WRITE, this::writeBegin);
addEventHandler(getLayout().eventSyscallEntryPrefix() + PWRITE, this::writeBegin);
addEventHandler(getLayout().eventSyscallEntryPrefix() + WRITE64, this::writeBegin);
addEventHandler(getLayout().eventSyscallEntryPrefix() + PWRITE64, this::writeBegin);
addEventHandler(getLayout().eventSyscallExitPrefix() + WRITE, this::writeEnd);
addEventHandler(getLayout().eventSyscallExitPrefix() + PWRITE, this::writeEnd);
addEventHandler(getLayout().eventSyscallExitPrefix() + WRITE64, this::writeEnd);
addEventHandler(getLayout().eventSyscallExitPrefix() + PWRITE64, this::writeEnd);
}
/**
* Add a handler for entries
*
* @param eventName
* the name to handle
* @param handler
* handler
*/
protected final void addEventHandler(String eventName, Consumer<HandlerParameter> handler) {
fHandlers.put(eventName, handler);
}
@Override
protected final void eventHandle(@NonNull ITmfEvent event) {
ITmfStateSystemBuilder ssb = getStateSystemBuilder();
if (ssb == null) {
return;
}
Integer tid = TmfTraceUtils.resolveIntEventAspectOfClassForEvent(event.getTrace(), LinuxTidAspect.class, event);
if (tid == null) {
return;
}
String name = event.getName();
Consumer<HandlerParameter> eventHandler = fHandlers.get(name);
if (eventHandler != null) {
eventHandler.accept(new HandlerParameter(ssb, event, tid));
}
}
/**
* Get the event layout of this trace. Many known concepts from the Linux
* kernel may be exported under different names, depending on the tracer.
*
* @return The event layout
*/
protected final IKernelAnalysisEventLayout getLayout() {
return fLayout;
}
/**
* Check if a file descriptor is valid. Has it been opened? if not, let's
* ignore it for now.
*
* @param ssb
* State system
* @param tid
* the TID that owns the file descriptor
* @param fd
* the file descriptor
* @return the file descriptor or null if invalid
*/
protected static final @Nullable Long isValidFileDescriptor(ITmfStateSystem ssb, Integer tid, @Nullable Long fd) {
if (fd == null) {
return null;
}
int tidFileQuark = ssb.optQuarkAbsolute(TID, String.valueOf(tid), String.valueOf(fd));
if (tidFileQuark == ITmfStateSystem.INVALID_ATTRIBUTE) {
return null;
}
return ssb.queryOngoing(tidFileQuark) != null ? fd : null;
}
private void readBegin(HandlerParameter params) {
ITmfEvent event = params.getEvent();
Integer tid = params.getTid();
ITmfStateSystemBuilder ssb = params.getSsb();
Long fd = (event.getContent().getFieldValue(Long.class, DESCRIPTOR));
fd = isValidFileDescriptor(ssb, tid, fd);
if (fd == null) {
return;
}
Long read = event.getContent().getFieldValue(Long.class, COUNT);
if (read == null) {
return;
}
int tidQuark = ssb.optQuarkAbsolute(TID, String.valueOf(tid));
if (tidQuark == ITmfStateSystem.INVALID_ATTRIBUTE) {
return;
}
fToRead.put(tid, fd);
}
private void readEnd(HandlerParameter params) {
ITmfEvent event = params.getEvent();
Integer tid = params.getTid();
ITmfStateSystemBuilder ssb = params.getSsb();
long time = params.getTime();
Long count = (event.getContent().getFieldValue(Long.class, getLayout().fieldSyscallRet()));
Long fd = fToRead.remove(tid);
fd = isValidFileDescriptor(ssb, tid, fd);
if (fd != null && count != null) {
try {
int fileNameQuark = ssb.getQuarkAbsoluteAndAdd(TID, String.valueOf(tid), String.valueOf(fd));
int readTid = ssb.getQuarkAbsoluteAndAdd(TID, String.valueOf(tid), String.valueOf(fd), READ);
StateSystemBuilderUtils.incrementAttributeLong(ssb, time, readTid, count);
Object fileNameObj = ssb.queryOngoing(fileNameQuark);
if (fileNameObj instanceof String) {
int readFile = ssb.getQuarkAbsoluteAndAdd(RESOURCES, String.valueOf(fileNameObj), String.valueOf(tid), READ);
StateSystemBuilderUtils.incrementAttributeLong(ssb, time, readFile, count);
}
} catch (StateValueTypeException | AttributeNotFoundException e) {
Activator.getInstance().logError(e.getMessage(), e);
}
}
}
private void writeBegin(HandlerParameter params) {
ITmfEvent event = params.getEvent();
Integer tid = params.getTid();
ITmfStateSystemBuilder ssb = params.getSsb();
Long fd = (event.getContent().getFieldValue(Long.class, DESCRIPTOR));
fd = isValidFileDescriptor(ssb, tid, fd);
if (fd == null) {
return;
}
fToWrite.put(tid, fd);
}
private void writeEnd(HandlerParameter params) {
ITmfEvent event = params.getEvent();
Integer tid = params.getTid();
ITmfStateSystemBuilder ssb = params.getSsb();
long time = params.getTime();
Long count = (event.getContent().getFieldValue(Long.class, getLayout().fieldSyscallRet()));
Long fd = fToWrite.remove(tid);
fd = isValidFileDescriptor(ssb, tid, fd);
if (fd != null && count != null) {
try {
int fileNameQuark = ssb.getQuarkAbsoluteAndAdd(TID, String.valueOf(tid), String.valueOf(fd));
int writeTid = ssb.getQuarkAbsoluteAndAdd(TID, String.valueOf(tid), String.valueOf(fd), WRITE);
StateSystemBuilderUtils.incrementAttributeLong(ssb, time, writeTid, count);
Object fileNameObj = ssb.queryOngoing(fileNameQuark);
if (fileNameObj instanceof String) {
int writeFile = ssb.getQuarkAbsoluteAndAdd(RESOURCES, String.valueOf(fileNameObj), String.valueOf(tid), WRITE);
StateSystemBuilderUtils.incrementAttributeLong(ssb, time, writeFile, count);
}
} catch (StateValueTypeException | AttributeNotFoundException e) {
Activator.getInstance().logError(e.getMessage(), e);
}
}
}
private void closeBegin(HandlerParameter params) {
ITmfEvent event = params.getEvent();
ITmfStateSystemBuilder ssb = params.getSsb();
Integer tid = params.getTid();
Long fd = (event.getContent().getFieldValue(Long.class, DESCRIPTOR));
fd = isValidFileDescriptor(ssb, tid, fd);
if (fd == null) {
return;
}
fToClose.put(tid, fd);
}
private void closeEnd(HandlerParameter params) {
ITmfEvent event = params.getEvent();
Integer tid = params.getTid();
ITmfStateSystemBuilder ssb = params.getSsb();
long time = params.getTime();
try {
Long ret = (event.getContent().getFieldValue(Long.class, getLayout().fieldSyscallRet()));
Long fd = fToClose.remove(tid);
if (ret == null || fd == null || ret < 0) {
return;
}
int tidQuark = ssb.getQuarkAbsoluteAndAdd(TID, String.valueOf(tid));
Object current = ssb.queryOngoing(tidQuark);
if (!(current instanceof Integer)) {
return;
}
if (Integer.valueOf(1).equals(current)) {
ssb.modifyAttribute(time, (Object) null, tidQuark);
} else {
StateSystemBuilderUtils.incrementAttributeInt(ssb, time, tidQuark, -1);
}
int tidFileQuark = ssb.getQuarkAbsoluteAndAdd(TID, String.valueOf(tid), String.valueOf(fd));
Object fileName = ssb.queryOngoing(tidFileQuark);
ssb.modifyAttribute(time, (Object) null, tidFileQuark);
if (ssb.optQuarkAbsolute(RESOURCES) != ITmfStateSystem.INVALID_ATTRIBUTE && (fileName instanceof String)) {
int fileQuark = ssb.getQuarkAbsoluteAndAdd(RESOURCES, String.valueOf(fileName));
int fileTidQuark = ssb.getQuarkRelativeAndAdd(fileQuark, String.valueOf(tid));
current = ssb.queryOngoing(fileQuark);
if (current instanceof Integer) {
if (Integer.valueOf(1).equals(current)) {
ssb.modifyAttribute(time, (Object) null, fileQuark);
} else {
StateSystemBuilderUtils.incrementAttributeInt(ssb, time, fileQuark, -1);
}
}
ssb.modifyAttribute(time, (Object) null, fileTidQuark);
}
} catch (StateValueTypeException | AttributeNotFoundException e) {
Activator.getInstance().logError(e.getMessage(), e);
}
}
}