| /******************************************************************************* |
| * 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$ |
| } |
| } |
| } |
| } |
| |
| } |