Move alltests plugin to the Trace Compass namespace
[deliverable/tracecompass.git] / org.eclipse.tracecompass.tmf.core / src / org / eclipse / linuxtools / tmf / core / trace / TmfExperiment.java
1 /*******************************************************************************
2 * Copyright (c) 2009, 2014 Ericsson, École Polytechnique de Montréal
3 *
4 * All rights reserved. This program and the accompanying materials are
5 * made available under the terms of the Eclipse Public License v1.0 which
6 * accompanies this distribution, and is available at
7 * http://www.eclipse.org/legal/epl-v10.html
8 *
9 * Contributors:
10 * Francois Chouinard - Initial API and implementation
11 * Francois Chouinard - Updated as per TMF Trace Model 1.0
12 * Patrick Tasse - Updated for removal of context clone
13 * Patrick Tasse - Updated for ranks in experiment location
14 * Geneviève Bastien - Added support of experiment synchronization
15 * Added the initExperiment method and default constructor
16 *******************************************************************************/
17
18 package org.eclipse.linuxtools.tmf.core.trace;
19
20 import java.io.File;
21 import java.nio.ByteBuffer;
22 import java.util.Arrays;
23
24 import org.eclipse.core.resources.IProject;
25 import org.eclipse.core.resources.IResource;
26 import org.eclipse.core.runtime.CoreException;
27 import org.eclipse.core.runtime.IStatus;
28 import org.eclipse.core.runtime.MultiStatus;
29 import org.eclipse.core.runtime.Status;
30 import org.eclipse.linuxtools.internal.tmf.core.Activator;
31 import org.eclipse.linuxtools.internal.tmf.core.trace.TmfExperimentContext;
32 import org.eclipse.linuxtools.internal.tmf.core.trace.TmfExperimentLocation;
33 import org.eclipse.linuxtools.internal.tmf.core.trace.TmfLocationArray;
34 import org.eclipse.linuxtools.tmf.core.TmfCommonConstants;
35 import org.eclipse.linuxtools.tmf.core.event.ITmfEvent;
36 import org.eclipse.linuxtools.tmf.core.exceptions.TmfTraceException;
37 import org.eclipse.linuxtools.tmf.core.request.ITmfEventRequest;
38 import org.eclipse.linuxtools.tmf.core.signal.TmfSignalHandler;
39 import org.eclipse.linuxtools.tmf.core.signal.TmfTraceOpenedSignal;
40 import org.eclipse.linuxtools.tmf.core.signal.TmfTraceRangeUpdatedSignal;
41 import org.eclipse.linuxtools.tmf.core.signal.TmfTraceSynchronizedSignal;
42 import org.eclipse.linuxtools.tmf.core.synchronization.SynchronizationAlgorithm;
43 import org.eclipse.linuxtools.tmf.core.synchronization.SynchronizationManager;
44 import org.eclipse.linuxtools.tmf.core.timestamp.ITmfTimestamp;
45 import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimeRange;
46 import org.eclipse.linuxtools.tmf.core.timestamp.TmfTimestamp;
47 import org.eclipse.linuxtools.tmf.core.trace.indexer.ITmfPersistentlyIndexable;
48 import org.eclipse.linuxtools.tmf.core.trace.indexer.ITmfTraceIndexer;
49 import org.eclipse.linuxtools.tmf.core.trace.indexer.TmfBTreeTraceIndexer;
50 import org.eclipse.linuxtools.tmf.core.trace.location.ITmfLocation;
51
52 /**
53 * TmfExperiment presents a time-ordered, unified view of a set of ITmfTrace:s
54 * that are part of a tracing experiment.
55 *
56 * @version 1.0
57 * @author Francois Chouinard
58 */
59 public class TmfExperiment extends TmfTrace implements ITmfEventParser, ITmfPersistentlyIndexable {
60
61 // ------------------------------------------------------------------------
62 // Constants
63 // ------------------------------------------------------------------------
64
65 /**
66 * The file name of the Synchronization
67 *
68 * @since 3.0
69 * @deprecated This file name shouldn't be used directly anymore. All
70 * synchronization files have been moved to a folder and you
71 * should use the {@link #getSynchronizationFolder(boolean)}
72 * method to return the path to this folder.
73 */
74 @Deprecated
75 public static final String SYNCHRONIZATION_FILE_NAME = "synchronization.bin"; //$NON-NLS-1$
76
77 /**
78 * The name of the directory containing trace synchronization data. This
79 * directory typically will be preserved when traces are synchronized.
80 * Analysis involved in synchronization can put their supplementary files in
81 * there so they are not deleted when synchronized traces are copied.
82 */
83 private static final String SYNCHRONIZATION_DIRECTORY = "sync_data"; //$NON-NLS-1$
84
85 /**
86 * The default index page size
87 */
88 public static final int DEFAULT_INDEX_PAGE_SIZE = 5000;
89
90 // ------------------------------------------------------------------------
91 // Attributes
92 // ------------------------------------------------------------------------
93
94 /**
95 * The set of traces that constitute the experiment
96 */
97 protected ITmfTrace[] fTraces;
98
99 /**
100 * The set of traces that constitute the experiment
101 */
102 private boolean fInitialized = false;
103
104 // ------------------------------------------------------------------------
105 // Construction
106 // ------------------------------------------------------------------------
107
108 /**
109 * Default constructor
110 *
111 * @since 3.0
112 */
113 public TmfExperiment() {
114 super();
115 }
116
117 /**
118 * Constructor with parameters
119 *
120 * @param type
121 * the event type
122 * @param id
123 * the experiment id
124 * @param traces
125 * the experiment set of traces
126 */
127 public TmfExperiment(final Class<? extends ITmfEvent> type, final String id, final ITmfTrace[] traces) {
128 this(type, id, traces, DEFAULT_INDEX_PAGE_SIZE, null);
129 }
130
131 /**
132 * Constructor of experiment taking type, path, traces and resource
133 *
134 * @param type
135 * the event type
136 * @param id
137 * the experiment id
138 * @param traces
139 * the experiment set of traces
140 * @param resource
141 * the resource associated to the experiment
142 */
143 public TmfExperiment(final Class<? extends ITmfEvent> type, final String id, final ITmfTrace[] traces, IResource resource) {
144 this(type, id, traces, DEFAULT_INDEX_PAGE_SIZE, resource);
145 }
146
147 /**
148 * @param type
149 * the event type
150 * @param path
151 * the experiment path
152 * @param traces
153 * the experiment set of traces
154 * @param indexPageSize
155 * the experiment index page size
156 */
157 public TmfExperiment(final Class<? extends ITmfEvent> type, final String path, final ITmfTrace[] traces, final int indexPageSize) {
158 this(type, path, traces, indexPageSize, null);
159 }
160
161 /**
162 * Full constructor of an experiment, taking the type, path, traces,
163 * indexPageSize and resource
164 *
165 * @param type
166 * the event type
167 * @param path
168 * the experiment path
169 * @param traces
170 * the experiment set of traces
171 * @param indexPageSize
172 * the experiment index page size
173 * @param resource
174 * the resource associated to the experiment
175 */
176 public TmfExperiment(final Class<? extends ITmfEvent> type, final String path, final ITmfTrace[] traces, final int indexPageSize, IResource resource) {
177 initExperiment(type, path, traces, indexPageSize, resource);
178 }
179
180 @Override
181 protected ITmfTraceIndexer createIndexer(int interval) {
182 if (getCheckpointSize() > 0) {
183 return new TmfBTreeTraceIndexer(this, interval);
184 }
185 return super.createIndexer(interval);
186 }
187
188 /**
189 * Clears the experiment
190 */
191 @Override
192 public synchronized void dispose() {
193
194 // Clean up the index if applicable
195 if (getIndexer() != null) {
196 getIndexer().dispose();
197 }
198
199 if (fTraces != null) {
200 for (final ITmfTrace trace : fTraces) {
201 trace.dispose();
202 }
203 fTraces = null;
204 }
205 super.dispose();
206 }
207
208 // ------------------------------------------------------------------------
209 // ITmfTrace - Initializers
210 // ------------------------------------------------------------------------
211
212 @Override
213 public void initTrace(final IResource resource, final String path, final Class<? extends ITmfEvent> type) {
214 }
215
216 /**
217 * Initialization of an experiment, taking the type, path, traces,
218 * indexPageSize and resource
219 *
220 * @param type
221 * the event type
222 * @param path
223 * the experiment path
224 * @param traces
225 * the experiment set of traces
226 * @param indexPageSize
227 * the experiment index page size
228 * @param resource
229 * the resource associated to the experiment
230 * @since 3.0
231 */
232 public void initExperiment(final Class<? extends ITmfEvent> type, final String path, final ITmfTrace[] traces, final int indexPageSize, IResource resource) {
233 setCacheSize(indexPageSize);
234 setStreamingInterval(0);
235 setParser(this);
236 // traces have to be set before super.initialize()
237 fTraces = traces;
238 try {
239 super.initialize(resource, path, type);
240 } catch (TmfTraceException e) {
241 Activator.logError("Error initializing experiment", e); //$NON-NLS-1$
242 }
243
244 if (resource != null) {
245 this.synchronizeTraces();
246 }
247 }
248
249 /**
250 * @since 2.0
251 */
252 @Override
253 public IStatus validate(final IProject project, final String path) {
254 return Status.OK_STATUS;
255 }
256
257 // ------------------------------------------------------------------------
258 // Accessors
259 // ------------------------------------------------------------------------
260
261 /**
262 * Get the traces contained in this experiment.
263 *
264 * @return The array of contained traces
265 */
266 public ITmfTrace[] getTraces() {
267 return fTraces;
268 }
269
270 /**
271 * Returns the timestamp of the event at the requested index. If none,
272 * returns null.
273 *
274 * @param index
275 * the event index (rank)
276 * @return the corresponding event timestamp
277 * @since 2.0
278 */
279 public ITmfTimestamp getTimestamp(final int index) {
280 final ITmfContext context = seekEvent(index);
281 final ITmfEvent event = getNext(context);
282 context.dispose();
283 return (event != null) ? event.getTimestamp() : null;
284 }
285
286 // ------------------------------------------------------------------------
287 // Request management
288 // ------------------------------------------------------------------------
289
290 /**
291 * @since 2.0
292 */
293 @Override
294 public synchronized ITmfContext armRequest(final ITmfEventRequest request) {
295
296 // Make sure we have something to read from
297 if (fTraces == null) {
298 return null;
299 }
300
301 if (!TmfTimestamp.BIG_BANG.equals(request.getRange().getStartTime())
302 && request.getIndex() == 0) {
303 final ITmfContext context = seekEvent(request.getRange().getStartTime());
304 request.setStartIndex((int) context.getRank());
305 return context;
306
307 }
308
309 return seekEvent(request.getIndex());
310 }
311
312 // ------------------------------------------------------------------------
313 // ITmfTrace trace positioning
314 // ------------------------------------------------------------------------
315
316 /**
317 * @since 3.0
318 */
319 @Override
320 public synchronized ITmfContext seekEvent(final ITmfLocation location) {
321 // Validate the location
322 if (location != null && !(location instanceof TmfExperimentLocation)) {
323 return null; // Throw an exception?
324 }
325 // Make sure we have something to read from
326 if (fTraces == null) {
327 return null;
328 }
329
330 // Initialize the location array if necessary
331 TmfLocationArray locationArray = ((location == null) ?
332 new TmfLocationArray(fTraces.length) :
333 ((TmfExperimentLocation) location).getLocationInfo());
334
335 ITmfLocation[] locations = locationArray.getLocations();
336 long[] ranks = locationArray.getRanks();
337
338 // Create and populate the context's traces contexts
339 final TmfExperimentContext context = new TmfExperimentContext(fTraces.length);
340
341 // Position the traces
342 long rank = 0;
343 for (int i = 0; i < fTraces.length; i++) {
344 // Get the relevant trace attributes
345 final ITmfContext traceContext = fTraces[i].seekEvent(locations[i]);
346 context.setContext(i, traceContext);
347 traceContext.setRank(ranks[i]);
348 // update location after seek
349 locations[i] = traceContext.getLocation();
350 context.setEvent(i, fTraces[i].getNext(traceContext));
351 rank += ranks[i];
352 }
353
354 // Finalize context
355 context.setLocation(new TmfExperimentLocation(new TmfLocationArray(locations, ranks)));
356 context.setLastTrace(TmfExperimentContext.NO_TRACE);
357 context.setRank(rank);
358
359 return context;
360 }
361
362 // ------------------------------------------------------------------------
363 // ITmfTrace - SeekEvent operations (returning a trace context)
364 // ------------------------------------------------------------------------
365
366 @Override
367 public ITmfContext seekEvent(final double ratio) {
368 final ITmfContext context = seekEvent(Math.round(ratio * getNbEvents()));
369 return context;
370 }
371
372 /**
373 * @since 3.0
374 */
375 @Override
376 public double getLocationRatio(final ITmfLocation location) {
377 if (location instanceof TmfExperimentLocation) {
378 long rank = 0;
379 TmfLocationArray locationArray = ((TmfExperimentLocation) location).getLocationInfo();
380 for (int i = 0; i < locationArray.size(); i++) {
381 rank += locationArray.getRank(i);
382 }
383 return (double) rank / getNbEvents();
384 }
385 return 0.0;
386 }
387
388 /**
389 * @since 3.0
390 */
391 @Override
392 public ITmfLocation getCurrentLocation() {
393 // never used
394 return null;
395 }
396
397 // ------------------------------------------------------------------------
398 // ITmfTrace trace positioning
399 // ------------------------------------------------------------------------
400
401 @Override
402 public synchronized ITmfEvent parseEvent(final ITmfContext context) {
403 final ITmfContext tmpContext = seekEvent(context.getLocation());
404 final ITmfEvent event = getNext(tmpContext);
405 return event;
406 }
407
408 @Override
409 public synchronized ITmfEvent getNext(ITmfContext context) {
410
411 // Validate the context
412 if (!(context instanceof TmfExperimentContext)) {
413 return null; // Throw an exception?
414 }
415
416 // Make sure that we have something to read from
417 if (fTraces == null) {
418 return null;
419 }
420
421 TmfExperimentContext expContext = (TmfExperimentContext) context;
422
423 // If an event was consumed previously, first get the next one from that
424 // trace
425 final int lastTrace = expContext.getLastTrace();
426 if (lastTrace != TmfExperimentContext.NO_TRACE) {
427 final ITmfContext traceContext = expContext.getContext(lastTrace);
428 expContext.setEvent(lastTrace, fTraces[lastTrace].getNext(traceContext));
429 expContext.setLastTrace(TmfExperimentContext.NO_TRACE);
430 }
431
432 // Scan the candidate events and identify the "next" trace to read from
433 int trace = TmfExperimentContext.NO_TRACE;
434 ITmfTimestamp timestamp = TmfTimestamp.BIG_CRUNCH;
435 for (int i = 0; i < fTraces.length; i++) {
436 final ITmfEvent event = expContext.getEvent(i);
437 if (event != null && event.getTimestamp() != null) {
438 final ITmfTimestamp otherTS = event.getTimestamp();
439 if (otherTS.compareTo(timestamp, true) < 0) {
440 trace = i;
441 timestamp = otherTS;
442 }
443 }
444 }
445
446 ITmfEvent event = null;
447 if (trace != TmfExperimentContext.NO_TRACE) {
448 event = expContext.getEvent(trace);
449 if (event != null) {
450 updateAttributes(expContext, event.getTimestamp());
451 expContext.increaseRank();
452 expContext.setLastTrace(trace);
453 final ITmfContext traceContext = expContext.getContext(trace);
454 if (traceContext == null) {
455 throw new IllegalStateException();
456 }
457
458 // Update the experiment location
459 TmfLocationArray locationArray = new TmfLocationArray(
460 ((TmfExperimentLocation) expContext.getLocation()).getLocationInfo(),
461 trace, traceContext.getLocation(), traceContext.getRank());
462 expContext.setLocation(new TmfExperimentLocation(locationArray));
463
464 processEvent(event);
465 }
466 }
467
468 return event;
469 }
470
471 /**
472 * @since 2.0
473 */
474 @Override
475 public ITmfTimestamp getInitialRangeOffset() {
476 if ((fTraces == null) || (fTraces.length == 0)) {
477 return super.getInitialRangeOffset();
478 }
479
480 ITmfTimestamp initTs = TmfTimestamp.BIG_CRUNCH;
481 for (int i = 0; i < fTraces.length; i++) {
482 ITmfTimestamp ts = fTraces[i].getInitialRangeOffset();
483 if (ts.compareTo(initTs) < 0) {
484 initTs = ts;
485 }
486 }
487 return initTs;
488 }
489
490 /**
491 * Get the path to the folder in the supplementary file where
492 * synchronization-related data can be kept so they are not deleted when the
493 * experiment is synchronized. Analysis involved in synchronization can put
494 * their supplementary files in there so they are preserved after
495 * synchronization.
496 *
497 * If the directory does not exist, it will be created. A return value of
498 * <code>null</code> means either the trace resource does not exist or
499 * supplementary resources cannot be kept.
500 *
501 * @param absolute
502 * If <code>true</code>, it returns the absolute path in the file
503 * system, including the supplementary file path. Otherwise, it
504 * returns only the directory name.
505 * @return The path to the folder where synchronization-related
506 * supplementary files can be kept or <code>null</code> if not
507 * available.
508 * @since 3.2
509 */
510 public String getSynchronizationFolder(boolean absolute) {
511 /* Set up the path to the synchronization file we'll use */
512 IResource resource = this.getResource();
513 String syncDirectory = null;
514
515 try {
516 /* get the directory where the file will be stored. */
517 if (resource != null) {
518 String fullDirectory = resource.getPersistentProperty(TmfCommonConstants.TRACE_SUPPLEMENTARY_FOLDER);
519 /* Create the synchronization data directory if not present */
520 if (fullDirectory != null) {
521 fullDirectory = fullDirectory + File.separator + SYNCHRONIZATION_DIRECTORY;
522 File syncDir = new File(fullDirectory);
523 syncDir.mkdirs();
524 }
525 if (absolute) {
526 syncDirectory = fullDirectory;
527 } else {
528 syncDirectory = SYNCHRONIZATION_DIRECTORY;
529 }
530 }
531 } catch (CoreException e) {
532 return null;
533 }
534
535 return syncDirectory;
536 }
537
538 /**
539 * Synchronizes the traces of an experiment. By default it only tries to
540 * read a synchronization file if it exists
541 *
542 * @return The synchronization object
543 * @since 3.0
544 */
545 public synchronized SynchronizationAlgorithm synchronizeTraces() {
546 return synchronizeTraces(false);
547 }
548
549 /**
550 * Synchronizes the traces of an experiment.
551 *
552 * @param doSync
553 * Whether to actually synchronize or just try opening a sync
554 * file
555 * @return The synchronization object
556 * @since 3.0
557 */
558 public synchronized SynchronizationAlgorithm synchronizeTraces(boolean doSync) {
559
560 String syncDirectory = getSynchronizationFolder(true);
561
562 final File syncFile = (syncDirectory != null) ? new File(syncDirectory + File.separator + SYNCHRONIZATION_FILE_NAME) : null;
563
564 final SynchronizationAlgorithm syncAlgo = SynchronizationManager.synchronizeTraces(syncFile, Arrays.asList(fTraces), doSync);
565
566 final TmfTraceSynchronizedSignal signal = new TmfTraceSynchronizedSignal(this, syncAlgo);
567
568 /* Broadcast in separate thread to prevent deadlock */
569 new Thread() {
570 @Override
571 public void run() {
572 broadcast(signal);
573 }
574 }.start();
575
576 return syncAlgo;
577 }
578
579 @Override
580 @SuppressWarnings("nls")
581 public synchronized String toString() {
582 return "[TmfExperiment (" + getName() + ")]";
583 }
584
585 // ------------------------------------------------------------------------
586 // Streaming support
587 // ------------------------------------------------------------------------
588
589 private synchronized void initializeStreamingMonitor() {
590
591 if (fInitialized) {
592 return;
593 }
594 fInitialized = true;
595
596 if (getStreamingInterval() == 0) {
597 final ITmfContext context = seekEvent(0);
598 final ITmfEvent event = getNext(context);
599 context.dispose();
600 if (event == null) {
601 return;
602 }
603 final TmfTimeRange timeRange = new TmfTimeRange(event.getTimestamp(), TmfTimestamp.BIG_CRUNCH);
604 final TmfTraceRangeUpdatedSignal signal = new TmfTraceRangeUpdatedSignal(this, this, timeRange);
605
606 // Broadcast in separate thread to prevent deadlock
607 new Thread() {
608 @Override
609 public void run() {
610 broadcast(signal);
611 }
612 }.start();
613 return;
614 }
615
616 final Thread thread = new Thread("Streaming Monitor for experiment " + getName()) { //$NON-NLS-1$
617 private ITmfTimestamp safeTimestamp = null;
618 private ITmfTimestamp lastSafeTimestamp = null;
619 private TmfTimeRange timeRange = null;
620
621 @Override
622 public void run() {
623 while (!executorIsShutdown()) {
624 if (!getIndexer().isIndexing()) {
625 ITmfTimestamp startTimestamp = TmfTimestamp.BIG_CRUNCH;
626 ITmfTimestamp endTimestamp = TmfTimestamp.BIG_BANG;
627 for (final ITmfTrace trace : fTraces) {
628 if (trace.getStartTime().compareTo(startTimestamp) < 0) {
629 startTimestamp = trace.getStartTime();
630 }
631 if (trace.getStreamingInterval() != 0 && trace.getEndTime().compareTo(endTimestamp) > 0) {
632 endTimestamp = trace.getEndTime();
633 }
634 }
635 if (safeTimestamp != null && (lastSafeTimestamp == null || safeTimestamp.compareTo(lastSafeTimestamp, false) > 0)) {
636 timeRange = new TmfTimeRange(startTimestamp, safeTimestamp);
637 lastSafeTimestamp = safeTimestamp;
638 } else {
639 timeRange = null;
640 }
641 safeTimestamp = endTimestamp;
642 if (timeRange != null) {
643 final TmfTraceRangeUpdatedSignal signal =
644 new TmfTraceRangeUpdatedSignal(TmfExperiment.this, TmfExperiment.this, timeRange);
645 broadcast(signal);
646 }
647 }
648 try {
649 Thread.sleep(getStreamingInterval());
650 } catch (final InterruptedException e) {
651 e.printStackTrace();
652 }
653 }
654 }
655 };
656 thread.start();
657 }
658
659 @Override
660 public long getStreamingInterval() {
661 long interval = 0;
662 for (final ITmfTrace trace : fTraces) {
663 interval = Math.max(interval, trace.getStreamingInterval());
664 }
665 return interval;
666 }
667
668 // ------------------------------------------------------------------------
669 // Signal handlers
670 // ------------------------------------------------------------------------
671
672 @Override
673 @TmfSignalHandler
674 public void traceOpened(TmfTraceOpenedSignal signal) {
675 if (signal.getTrace() == this) {
676 initializeStreamingMonitor();
677
678 /* Initialize the analysis */
679 MultiStatus status = new MultiStatus(Activator.PLUGIN_ID, IStatus.OK, null, null);
680 status.add(executeAnalysis());
681 if (!status.isOK()) {
682 Activator.log(status);
683 }
684 TmfTraceManager.refreshSupplementaryFiles(this);
685 }
686 }
687
688 /**
689 * @since 3.0
690 */
691 @Override
692 public synchronized int getCheckpointSize() {
693 int totalCheckpointSize = 0;
694 try {
695 if (fTraces != null) {
696 for (final ITmfTrace trace : fTraces) {
697 if (!(trace instanceof ITmfPersistentlyIndexable)) {
698 return 0;
699 }
700
701 ITmfPersistentlyIndexable persistableIndexTrace = (ITmfPersistentlyIndexable) trace;
702 int currentTraceCheckpointSize = persistableIndexTrace.getCheckpointSize();
703 if (currentTraceCheckpointSize <= 0) {
704 return 0;
705 }
706 totalCheckpointSize += currentTraceCheckpointSize;
707 // each entry in the TmfLocationArray has a rank in addition
708 // of the location
709 totalCheckpointSize += 8;
710 }
711 }
712 } catch (UnsupportedOperationException e) {
713 return 0;
714 }
715
716 return totalCheckpointSize;
717 }
718
719 /**
720 * @since 3.0
721 */
722 @Override
723 public ITmfLocation restoreLocation(ByteBuffer bufferIn) {
724 ITmfLocation[] locations = new ITmfLocation[fTraces.length];
725 long[] ranks = new long[fTraces.length];
726 for (int i = 0; i < fTraces.length; ++i) {
727 final ITmfTrace trace = fTraces[i];
728 locations[i] = ((ITmfPersistentlyIndexable) trace).restoreLocation(bufferIn);
729 ranks[i] = bufferIn.getLong();
730 }
731 TmfLocationArray arr = new TmfLocationArray(locations, ranks);
732 TmfExperimentLocation l = new TmfExperimentLocation(arr);
733 return l;
734 }
735
736 }
This page took 0.074278 seconds and 5 git commands to generate.