blob: a58eafc5d1b7a30438bd2e80eb2ebdbbe639d9d6 [file] [log] [blame]
/*******************************************************************************
* Copyright (c) 2017 Ericsson and others
*
* 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
*
* Contributors:
* Loïc Prieur-Drevon - Initial API and implementation
* Simon Delisle - Move this job in its own class
*******************************************************************************/
package org.eclipse.tracecompass.tmf.ui.project.model;
import java.io.File;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.nio.file.Files;
import java.nio.file.StandardOpenOption;
import java.util.Queue;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.eclipse.core.resources.IFolder;
import org.eclipse.core.resources.IResource;
import org.eclipse.core.runtime.CoreException;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.IStatus;
import org.eclipse.core.runtime.Status;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.core.runtime.jobs.Job;
import org.eclipse.jdt.annotation.NonNull;
import org.eclipse.tracecompass.common.core.log.TraceCompassLog;
import org.eclipse.tracecompass.common.core.log.TraceCompassLogUtils;
import org.eclipse.tracecompass.internal.tmf.ui.Activator;
import org.eclipse.tracecompass.tmf.core.exceptions.TmfTraceException;
import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp;
import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp;
import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace;
/**
* Update traces bounds, by using supplementary files if they exist or by
* reading the trace to determine start and end time of the traces.
*
* @author Loïc Prieur-Drevon
* @since 3.1
*
*/
public class UpdateTraceBoundsJob extends Job {
private static final @NonNull Logger LOGGER = TraceCompassLog.getLogger(UpdateTraceBoundsJob.class);
private static final String BOUNDS_FILE_NAME = "bounds"; //$NON-NLS-1$
private final Queue<TmfTraceElement> fTraceBoundsToUpdate;
/**
* Constructor.
*
* @param name
* The name of the job
* @param traceBoundsToUpdate
* Queue of TmfTraceElement to update
*/
public UpdateTraceBoundsJob(String name, Queue<TmfTraceElement> traceBoundsToUpdate) {
super(name);
fTraceBoundsToUpdate = traceBoundsToUpdate;
}
@Override
public IStatus run(IProgressMonitor monitor) {
SubMonitor subMonitor = SubMonitor.convert(monitor, fTraceBoundsToUpdate.size());
while (!fTraceBoundsToUpdate.isEmpty()) {
subMonitor.setTaskName(getName());
if (subMonitor.isCanceled()) {
return Status.CANCEL_STATUS;
}
TmfTraceElement tElement = fTraceBoundsToUpdate.remove();
ITmfTimestamp start = tElement.getStartTime();
ITmfTimestamp end = tElement.getEndTime();
if (start != null && end != null) {
/*
* The start and end times are already known, no need to go any
* further
*/
subMonitor.worked(1);
continue;
}
/*
* Try to get the element bounds from the supplementary files.
*/
IFolder folder = tElement.getTraceSupplementaryFolder(tElement.getSupplementaryFolderPath());
tElement.refreshSupplementaryFolder();
File f = folder.getFile(BOUNDS_FILE_NAME).getLocation().toFile();
tryReadBoundsFile(tElement, f);
start = tElement.getStartTime();
end = tElement.getEndTime();
if (start == null || end == null) {
/*
* We are missing a bound, we must go and read them from the
* trace.
*/
extractBoundsFromTrace(tElement);
tryWriteBoundsFile(subMonitor.newChild(1), tElement, folder, f);
} else {
subMonitor.worked(1);
}
}
return Status.OK_STATUS;
}
/**
* Extract the bounds from a trace and refresh its trace elements
*
* @param traceElement
* the trace element to refresh
*/
private static void extractBoundsFromTrace(TmfTraceElement traceElement) {
ITmfTimestamp start;
ITmfTimestamp end;
ITmfTrace trace = traceElement.getTrace();
boolean wasInitBefore = (trace != null);
if (!wasInitBefore) {
trace = traceElement.instantiateTrace();
}
if (trace == null) {
/*
* We could not instantiate the trace because its type is unknown,
* abandon.
*/
traceElement.setStartTime(TmfTimestamp.BIG_BANG);
traceElement.setEndTime(TmfTimestamp.BIG_BANG);
} else {
try {
if (!wasInitBefore) {
trace.initTrace(traceElement.getResource(), traceElement.getResource().getLocation().toOSString(),
traceElement.instantiateEvent().getClass(), traceElement.getElementPath(), traceElement.getTraceType());
}
start = trace.readStart();
if (start != null) {
traceElement.setStartTime(start);
/*
* Intermediate refresh when we get the start time, will not
* re-trigger a job.
*/
traceElement.refreshViewer();
end = trace.readEnd();
traceElement.setEndTime((end != null) ? end : TmfTimestamp.BIG_BANG);
} else {
traceElement.setStartTime(TmfTimestamp.BIG_BANG);
traceElement.setEndTime(TmfTimestamp.BIG_BANG);
}
} catch (TmfTraceException e1) {
/*
* Set the bounds to BIG_BANG to avoid trying to reread the
* trace.
*/
traceElement.setStartTime(TmfTimestamp.BIG_BANG);
traceElement.setEndTime(TmfTimestamp.BIG_BANG);
TraceCompassLogUtils.traceInstant(LOGGER, Level.CONFIG, "Failed to read time bounds", "trace", traceElement.getName()); //$NON-NLS-1$ //$NON-NLS-2$
} finally {
/*
* Leave the trace at the same initialization status as
* previously.
*/
if (!wasInitBefore) {
trace.dispose();
}
}
}
}
/**
* Save the bounds for a trace to supplementary file and refresh elements
*
* @param monitor
* a progress monitor
* @param traceElement
* the traceElement which we are refreshing
* @param folder
* the IFolder for this trace elements supplementary files
* @param bounds
* File file in which the trace's bounds will be persisted
*/
private static void tryWriteBoundsFile(IProgressMonitor monitor, TmfTraceElement traceElement, IFolder folder, File boundsFile) {
try {
SubMonitor subMonitor = SubMonitor.convert(monitor, 1);
/*
* Now that we know the bounds we can persist them to disk
*/
boundsFile.createNewFile();
ByteBuffer writeBuffer = ByteBuffer.allocate(2 * Long.BYTES);
long writeStart = traceElement.getStartTime() != null ? traceElement.getStartTime().toNanos() : Long.MIN_VALUE;
long writeEnd = traceElement.getEndTime() != null ? traceElement.getEndTime().toNanos() : Long.MIN_VALUE;
writeBuffer.putLong(writeStart);
writeBuffer.putLong(writeEnd);
Files.write(boundsFile.toPath(), writeBuffer.array(), StandardOpenOption.WRITE);
SubMonitor newChild = subMonitor.newChild(1);
folder.refreshLocal(IResource.DEPTH_INFINITE, newChild);
/*
* It seems that the subTask name is never cleared. The current solution is to
* set the name to null.
*/
newChild.subTask(""); //$NON-NLS-1$
} catch (IOException e) {
TraceCompassLogUtils.traceInstant(LOGGER, Level.CONFIG, "Failed to write time bounds to supplementary file", "trace", traceElement.getName(), "supplementary file", folder.getName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
} catch (CoreException e) {
TraceCompassLogUtils.traceInstant(LOGGER, Level.CONFIG, "Failed to refresh supplementary file", "trace", traceElement.getName(), "supplementary file", folder.getName()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
}
}
/**
* Try and read the bounds from a trace element's supplementary data and
* refresh the elements bounds.
*
* @param traceElement
* the trace element whose trace we must read and update
* @param boundsFile
* Supplementary file with persisted trace bounds
*/
private static void tryReadBoundsFile(TmfTraceElement traceElement, File boundsFile) {
if (boundsFile.exists()) {
/*
* We have already read the start and end times for this trace, we
* can get them from the supplementary file.
*/
try {
byte[] bytes = Files.readAllBytes(boundsFile.toPath());
ByteBuffer readBuffer = ByteBuffer.allocate(2 * Long.BYTES);
readBuffer.put(bytes);
readBuffer.flip();
/*
* If MIN_VALUE was written, then the bounds could not be read.
*/
long tmp = readBuffer.getLong();
if (tmp == Long.MIN_VALUE) {
traceElement.setStartTime(TmfTimestamp.BIG_BANG);
} else {
traceElement.setStartTime(TmfTimestamp.fromNanos(tmp));
}
tmp = readBuffer.getLong();
if (tmp == Long.MIN_VALUE) {
traceElement.setEndTime(TmfTimestamp.BIG_BANG);
} else {
traceElement.setEndTime(TmfTimestamp.fromNanos(tmp));
}
traceElement.refreshViewer();
} catch (IOException e) {
TraceCompassLogUtils.traceInstant(LOGGER, Level.CONFIG, "Failed to read time bounds", "trace", traceElement.getName(), "bounds file", boundsFile.getAbsoluteFile()); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$
if (!boundsFile.delete()) {
Activator.getDefault().logError("Failed to delete " + boundsFile); //$NON-NLS-1$
}
}
}
}
}