From 7ea18c7202104839541c937c39b9f17b53981d31 Mon Sep 17 00:00:00 2001 From: Matthew Khouzam Date: Thu, 3 Mar 2016 18:17:24 -0500 Subject: [PATCH] tmf/ctf: Show trace read progress in the progress bar This patch adds an interface : ITmfTraceKnownSize. This interface has two methods: - size() - progress() If a tracetype implements these methods, the progress will show a scrolling bar instead of the night-rider/battlestar galactica style slider of an unknown progress bar. Change-Id: If1b61957f26c2dec26543f64cfb42e12b127391c Signed-off-by: Matthew Khouzam Reviewed-on: https://git.eclipse.org/r/67779 Reviewed-by: Genevieve Bastien Tested-by: Genevieve Bastien Reviewed-by: Hudson CI --- .../META-INF/MANIFEST.MF | 2 +- .../tmf/ctf/core/trace/CtfTmfTrace.java | 81 +++++++++++++++---- .../tmf/core/trace/ITmfTraceKnownSize.java | 38 +++++++++ .../checkpoint/TmfCheckpointIndexer.java | 21 ++++- 4 files changed, 122 insertions(+), 20 deletions(-) create mode 100644 tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/ITmfTraceKnownSize.java diff --git a/ctf/org.eclipse.tracecompass.tmf.ctf.core/META-INF/MANIFEST.MF b/ctf/org.eclipse.tracecompass.tmf.ctf.core/META-INF/MANIFEST.MF index 6bbeda1c6b..179db87af8 100644 --- a/ctf/org.eclipse.tracecompass.tmf.ctf.core/META-INF/MANIFEST.MF +++ b/ctf/org.eclipse.tracecompass.tmf.ctf.core/META-INF/MANIFEST.MF @@ -2,7 +2,7 @@ Manifest-Version: 1.0 Bundle-ManifestVersion: 2 Bundle-Name: %Bundle-Name Bundle-Vendor: %Bundle-Vendor -Bundle-Version: 2.0.0.qualifier +Bundle-Version: 2.1.0.qualifier Bundle-Localization: plugin Bundle-SymbolicName: org.eclipse.tracecompass.tmf.ctf.core;singleton:=true Bundle-Activator: org.eclipse.tracecompass.internal.tmf.ctf.core.Activator diff --git a/ctf/org.eclipse.tracecompass.tmf.ctf.core/src/org/eclipse/tracecompass/tmf/ctf/core/trace/CtfTmfTrace.java b/ctf/org.eclipse.tracecompass.tmf.ctf.core/src/org/eclipse/tracecompass/tmf/ctf/core/trace/CtfTmfTrace.java index 8bbf4a8cde..d06abe13d2 100644 --- a/ctf/org.eclipse.tracecompass.tmf.ctf.core/src/org/eclipse/tracecompass/tmf/ctf/core/trace/CtfTmfTrace.java +++ b/ctf/org.eclipse.tracecompass.tmf.ctf.core/src/org/eclipse/tracecompass/tmf/ctf/core/trace/CtfTmfTrace.java @@ -37,8 +37,10 @@ import org.eclipse.tracecompass.ctf.core.CTFException; import org.eclipse.tracecompass.ctf.core.event.CTFClock; import org.eclipse.tracecompass.ctf.core.event.IEventDeclaration; import org.eclipse.tracecompass.ctf.core.event.types.StructDeclaration; +import org.eclipse.tracecompass.ctf.core.trace.CTFStreamInput; import org.eclipse.tracecompass.ctf.core.trace.CTFTrace; import org.eclipse.tracecompass.ctf.core.trace.CTFTraceReader; +import org.eclipse.tracecompass.ctf.core.trace.ICTFStream; import org.eclipse.tracecompass.ctf.core.trace.Metadata; import org.eclipse.tracecompass.internal.tmf.ctf.core.Activator; import org.eclipse.tracecompass.internal.tmf.ctf.core.trace.iterator.CtfIterator; @@ -53,6 +55,7 @@ import org.eclipse.tracecompass.tmf.core.project.model.ITmfPropertiesProvider; import org.eclipse.tracecompass.tmf.core.timestamp.ITmfTimestamp; import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; import org.eclipse.tracecompass.tmf.core.trace.ITmfContext; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTraceKnownSize; import org.eclipse.tracecompass.tmf.core.trace.ITmfTraceWithPreDefinedEvents; import org.eclipse.tracecompass.tmf.core.trace.TmfTrace; import org.eclipse.tracecompass.tmf.core.trace.TraceValidationStatus; @@ -83,7 +86,7 @@ import com.google.common.collect.ImmutableSet; */ public class CtfTmfTrace extends TmfTrace implements ITmfPropertiesProvider, ITmfPersistentlyIndexable, - ITmfTraceWithPreDefinedEvents { + ITmfTraceWithPreDefinedEvents, ITmfTraceKnownSize { // ------------------------------------------- // Constants @@ -91,6 +94,7 @@ public class CtfTmfTrace extends TmfTrace /** * Clock offset property + * * @since 1.2 */ public static final String CLOCK_OFFSET = "clock_offset"; //$NON-NLS-1$ @@ -102,16 +106,15 @@ public class CtfTmfTrace extends TmfTrace /** * Event aspects available for all CTF traces + * * @since 1.0 */ - protected static final @NonNull Collection<@NonNull ITmfEventAspect> CTF_ASPECTS = - ImmutableList.of( - TmfBaseAspects.getTimestampAspect(), - new CtfChannelAspect(), - new CtfCpuAspect(), - TmfBaseAspects.getEventTypeAspect(), - TmfBaseAspects.getContentsAspect() - ); + protected static final @NonNull Collection<@NonNull ITmfEventAspect> CTF_ASPECTS = ImmutableList.of( + TmfBaseAspects.getTimestampAspect(), + new CtfChannelAspect(), + new CtfCpuAspect(), + TmfBaseAspects.getEventTypeAspect(), + TmfBaseAspects.getContentsAspect()); /** * The Ctf clock unique identifier field @@ -120,12 +123,26 @@ public class CtfTmfTrace extends TmfTrace private static final int CONFIDENCE = 10; private static final int MIN_CONFIDENCE = 1; + /** + * This is a reduction factor to avoid overflows. + */ + private static final long REDUCTION_FACTOR = 4096; + + /** + * Average CTF event size, used to estimate the trace size. (Inspired by + * empirical observations with LTTng kernel traces, to avoid hanging at 100% + * for too long) + * + * TODO: Find a more suitable approximation, perhaps per concrete trace type + * or per trace directly with the metadata + */ + private static final int CTF_AVG_EVENT_SIZE = 16; + // ------------------------------------------- // Fields // ------------------------------------------- - private final Map<@NonNull String, @NonNull CtfTmfEventType> fContainedEventTypes = - Collections.synchronizedMap(new HashMap<>()); + private final Map<@NonNull String, @NonNull CtfTmfEventType> fContainedEventTypes = Collections.synchronizedMap(new HashMap<>()); private final CtfIteratorManager fIteratorManager = new CtfIteratorManager(this); @@ -270,11 +287,11 @@ public class CtfTmfTrace extends TmfTrace * Firstly a weak validation of the metadata is done to determine if the * path is actually for a CTF trace. After that a full validation is done. * - * If the weak and full validation are successful the confidence is set - * to 10. + * If the weak and full validation are successful the confidence is set to + * 10. * - * If the weak validation was successful, but the full validation fails - * a TraceValidationStatus with severity warning and confidence of 1 is + * If the weak validation was successful, but the full validation fails a + * TraceValidationStatus with severity warning and confidence of 1 is * returned. * * If both weak and full validation fails an error status is returned. @@ -300,12 +317,14 @@ public class CtfTmfTrace extends TmfTrace } // Validate using reader initialization - try (CTFTraceReader ctfTraceReader = new CTFTraceReader(trace)) {} + try (CTFTraceReader ctfTraceReader = new CTFTraceReader(trace)) { + // do nothing + } // Trace is validated, return with confidence return new CtfTraceValidationStatus(CONFIDENCE, Activator.PLUGIN_ID, trace.getEnvironment()); - } catch (final CTFException | BufferOverflowException e ) { + } catch (final CTFException | BufferOverflowException e) { // return warning since it's a CTF trace but with errors in it return new TraceValidationStatus(MIN_CONFIDENCE, IStatus.WARNING, Activator.PLUGIN_ID, Messages.CtfTmfTrace_ReadingError + ": " + e.toString(), e); //$NON-NLS-1$ } @@ -690,4 +709,32 @@ public class CtfTmfTrace extends TmfTrace Activator.getDefault().logError(e.getMessage(), e); } } + + /** + * @return the number of estimated chunks of events read. This reads the + * file size of the trace and divides it by a factor and the average + * event size, this is not accurate but can give a ball park figure + * of how much is done. + * @since 2.1 + */ + @Override + public int size() { + long size = 0; + Iterable streams = fTrace.getStreams(); + for (ICTFStream stream : streams) { + for (CTFStreamInput si : stream.getStreamInputs()) { + size += si.getFile().length(); + } + } + return (int) (size / REDUCTION_FACTOR / CTF_AVG_EVENT_SIZE); + } + + /** + * @return the number of events divided a reduction factor. Is monotonic. + * @since 2.1 + */ + @Override + public int progress() { + return (int) (getNbEvents() / REDUCTION_FACTOR); + } } diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/ITmfTraceKnownSize.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/ITmfTraceKnownSize.java new file mode 100644 index 0000000000..8a671c348a --- /dev/null +++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/ITmfTraceKnownSize.java @@ -0,0 +1,38 @@ +/********************************************************************** + * Copyright (c) 2016 Ericsson + * + * 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.tmf.core.trace; + +/** + * An interface that trace classes can implement if they have a known size, so + * that reading progress can be shown. + * + * @author Matthew Khouzam + * @since 2.1 + */ +public interface ITmfTraceKnownSize { + /** + * Get the size of the trace. The units of this value are not important, but + * they should always be the same as {@link #progress()} + * + * @return the size of the trace. This can change from one call to the + * other, but a later call to this method should not see a decrease + * in size. + */ + int size(); + + /** + * How much of the trace is read. The units of this value are not important, + * but they should always be the same as {@link #size()} + * + * @return how much of the trace is read. This should not exceed + * {@link #size()} + */ + int progress(); +} diff --git a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/indexer/checkpoint/TmfCheckpointIndexer.java b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/indexer/checkpoint/TmfCheckpointIndexer.java index 355f67e041..01f9098389 100644 --- a/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/indexer/checkpoint/TmfCheckpointIndexer.java +++ b/tmf/org.eclipse.tracecompass.tmf.core/src/org/eclipse/tracecompass/tmf/core/trace/indexer/checkpoint/TmfCheckpointIndexer.java @@ -16,6 +16,7 @@ package org.eclipse.tracecompass.tmf.core.trace.indexer.checkpoint; 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.internal.tmf.core.Activator; @@ -32,6 +33,7 @@ import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimeRange; import org.eclipse.tracecompass.tmf.core.timestamp.TmfTimestamp; import org.eclipse.tracecompass.tmf.core.trace.ITmfContext; import org.eclipse.tracecompass.tmf.core.trace.ITmfTrace; +import org.eclipse.tracecompass.tmf.core.trace.ITmfTraceKnownSize; import org.eclipse.tracecompass.tmf.core.trace.indexer.ITmfTraceIndexer; import org.eclipse.tracecompass.tmf.core.trace.location.ITmfLocation; @@ -347,27 +349,42 @@ public class TmfCheckpointIndexer implements ITmfTraceIndexer { private final class TmfIndexingJob extends Job { private Exception fException = null; + private final ITmfTraceKnownSize fTraceWithSize; private TmfIndexingJob(String name) { super(name); + fTraceWithSize = (fTrace instanceof ITmfTraceKnownSize) ? (ITmfTraceKnownSize) fTrace : null; } @Override protected IStatus run(final IProgressMonitor monitor) { - monitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$ + int alreadyDone = 0; + SubMonitor subMonitor = SubMonitor.convert(monitor); + if (fTraceWithSize != null) { + subMonitor.beginTask("", fTraceWithSize.size()); //$NON-NLS-1$ + } else { + subMonitor.beginTask("", IProgressMonitor.UNKNOWN); //$NON-NLS-1$ + } while (!monitor.isCanceled()) { try { long prevNbEvents = fTrace.getNbEvents(); Thread.sleep(250); long nbEvents = fTrace.getNbEvents(); + if (fTraceWithSize != null) { + final int done = fTraceWithSize.progress(); + subMonitor.setWorkRemaining(fTraceWithSize.size() - done); + subMonitor.worked(done - alreadyDone); + alreadyDone = done; + } setName(Messages.TmfCheckpointIndexer_Indexing + ' ' + fTrace.getName() + " (" + String.format("%,d", nbEvents) + ")"); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ // setName doesn't refresh the UI, setTaskName does long rate = (nbEvents - prevNbEvents) * 4; - monitor.setTaskName(String.format("%,d", rate) + " " + Messages.TmfCheckpointIndexer_EventsPerSecond); //$NON-NLS-1$ //$NON-NLS-2$ + subMonitor.setTaskName(String.format("%,d", rate) + " " + Messages.TmfCheckpointIndexer_EventsPerSecond); //$NON-NLS-1$ //$NON-NLS-2$ } catch (final InterruptedException e) { return Status.OK_STATUS; } } + subMonitor.done(); monitor.done(); return fException != null ? new Status(IStatus.ERROR, Activator.PLUGIN_ID, fException.getMessage(), fException) : Status.OK_STATUS; } -- 2.34.1