/*******************************************************************************
* Copyright (c) 2012-2015 INRIA.
* 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
*
* Contributors:
* Generoso Pagano - initial API and implementation
******************************************************************************/
package fr.inria.soctrace.framesoc.ui.gantt.loaders;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.HashMap;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import org.eclipse.core.runtime.Assert;
import org.eclipse.core.runtime.IProgressMonitor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import fr.inria.soctrace.framesoc.ui.gantt.model.IEventLoader;
import fr.inria.soctrace.framesoc.ui.gantt.model.ReducedEvent;
import fr.inria.soctrace.framesoc.ui.loaders.LoaderUtils;
import fr.inria.soctrace.framesoc.ui.model.LoaderQueue;
import fr.inria.soctrace.framesoc.ui.model.TimeInterval;
import fr.inria.soctrace.lib.model.EventProducer;
import fr.inria.soctrace.lib.model.EventType;
import fr.inria.soctrace.lib.model.Trace;
import fr.inria.soctrace.lib.model.utils.SoCTraceException;
import fr.inria.soctrace.lib.query.EventProducerQuery;
import fr.inria.soctrace.lib.query.EventTypeQuery;
import fr.inria.soctrace.lib.query.conditions.ConditionsConstants.ComparisonOperation;
import fr.inria.soctrace.lib.storage.DBObject;
import fr.inria.soctrace.lib.storage.TraceDBObject;
import fr.inria.soctrace.lib.storage.utils.SQLConstants.FramesocTable;
import fr.inria.soctrace.lib.utils.DeltaManager;
/**
* Default event loader for the Gantt Chart.
*
* @author "Generoso Pagano <generoso.pagano@inria.fr>"
*/
public class EventLoader implements IEventLoader {
// logger
private static final Logger logger = LoggerFactory.getLogger(EventLoader.class);
// constants
private final int EVENTS_PER_QUERY = 100000;
// set by the user
private Trace fTrace = null;
private LoaderQueue<ReducedEvent> fQueue = null;
// current visualized trace data
private TraceDBObject fTraceDB = null;
private Map<Integer, EventProducer> fProducers = null;
private boolean fProducersLoaded = false;
private Map<Integer, EventType> fTypes = null;
private boolean fTypesLoaded = false;
private TimeInterval fTimeInterval;
private long fLatestStart;
@Override
public Map<Integer, EventProducer> getProducers() {
if (fProducersLoaded)
return fProducers;
fProducers = new HashMap<Integer, EventProducer>();
try {
EventProducerQuery epq = new EventProducerQuery(getTraceDB());
List<EventProducer> epl = epq.getList();
for (EventProducer ep : epl) {
fProducers.put(ep.getId(), ep);
}
fProducersLoaded = true;
} catch (SoCTraceException e) {
e.printStackTrace();
}
return fProducers;
}
@Override
public Map<Integer, EventType> getTypes() {
if (fTypesLoaded)
return fTypes;
fTypes = new HashMap<Integer, EventType>();
try {
EventTypeQuery etq = new EventTypeQuery(getTraceDB());
List<EventType> etl = etq.getList();
for (EventType et : etl) {
fTypes.put(et.getId(), et);
}
fTypesLoaded = true;
} catch (SoCTraceException e) {
e.printStackTrace();
}
return fTypes;
}
@Override
public void setTrace(Trace trace) {
if (fTrace != trace) {
clean();
fTrace = trace;
}
}
@Override
public void setQueue(LoaderQueue<ReducedEvent> queue) {
fQueue = queue;
}
@Override
public void release() {
fTrace = null;
fQueue = null;
clean();
}
public boolean checkCancel(IProgressMonitor monitor) {
if (monitor.isCanceled()) {
fQueue.setStop();
return true;
}
return false;
}
@Override
public void loadWindow(long start, long end, IProgressMonitor monitor) {
try {
Assert.isNotNull(fTrace, "Null trace in event loader");
Assert.isNotNull(fQueue, "Null queue in event loader");
start = Math.max(fTrace.getMinTimestamp(), start);
end = Math.min(fTrace.getMaxTimestamp(), end);
fTimeInterval = new TimeInterval(Long.MAX_VALUE, Long.MIN_VALUE);
// compute interval duration
long traceDuration = fTrace.getMaxTimestamp() - fTrace.getMinTimestamp();
long intervalDuration = LoaderUtils.getIntervalDuration(fTrace, EVENTS_PER_QUERY);
int totalWork = (int) ((double) traceDuration / intervalDuration);
// read the time window, interval by interval
monitor.beginTask("Loading Gantt Chart", totalWork);
int oldWorked = 0;
int totalEvents = 0;
TimeInterval firstInterval = null;
boolean first = true;
long t0 = start;
while (t0 < end) {
// check if cancelled
if (checkCancel(monitor)) {
return;
}
// load interval
long t1 = Math.min(end, t0 + intervalDuration);
if (first) {
// store the first time interval for later loading
firstInterval = new TimeInterval(t0, t1);
first = false;
}
boolean last = (t1 >= end);
List<ReducedEvent> events = loadInterval(false, last, t0, t1, monitor);
totalEvents = debug(events, totalEvents);
if (checkCancel(monitor)) {
return;
}
// check for empty regions
if (events.size() == 0 && !last) {
t0 = getNextTimestampStartingFrom(t1);
logger.debug("saved " + ((t0 - t1) / intervalDuration) + " queries.");
continue;
}
// update progress monitor
int worked = (int) ((double) (fLatestStart - start) / intervalDuration);
monitor.worked(Math.max(0, worked - oldWorked));
oldWorked = worked;
t0 = t1;
fQueue.push(events, new TimeInterval(fTimeInterval));
}
// load states and links intersecting the start of the first interval
if (firstInterval != null && firstInterval.startTimestamp != fTrace.getMinTimestamp()) {
List<ReducedEvent> events = loadInterval(true, (firstInterval.endTimestamp >= end),
firstInterval.startTimestamp, firstInterval.endTimestamp, monitor);
totalEvents = debug(events, totalEvents);
if (checkCancel(monitor)) {
return;
}
fQueue.push(events, new TimeInterval(fTimeInterval));
}
fQueue.setComplete();
} finally {
if (!fQueue.isStop() && !fQueue.isComplete()) {
// something went wrong, respect the queue contract anyway
fQueue.setStop();
}
monitor.done();
}
}
private List<ReducedEvent> loadInterval(boolean first, boolean last, long t0, long t1,
IProgressMonitor monitor) {
List<ReducedEvent> events = new LinkedList<>();
try {
Statement stm = getTraceDB().getConnection().createStatement();
DeltaManager dm = new DeltaManager();
dm.start();
ResultSet rs = stm.executeQuery(getQuery(t0, t1, first, last));
logger.debug(dm.endMessage("exec query"));
dm.start();
while (rs.next()) {
ReducedEvent ev = new ReducedEvent(rs);
events.add(ev);
if (ev.timestamp > fLatestStart)
fLatestStart = ev.timestamp;
if (fTimeInterval.startTimestamp > ev.timestamp)
fTimeInterval.startTimestamp = ev.timestamp;
long end = ((ev.category == 0) ? ev.timestamp : ev.endTimestamp);
if (fTimeInterval.endTimestamp < end)
fTimeInterval.endTimestamp = end;
if (monitor.isCanceled()) {
fQueue.setStop();
break;
}
}
logger.debug(dm.endMessage("reduced event creation"));
rs.close();
stm.close();
} catch (SQLException e) {
e.printStackTrace();
fQueue.setStop();
} catch (SoCTraceException e) {
e.printStackTrace();
fQueue.setStop();
}
return events;
}
private long getNextTimestampStartingFrom(long end) {
long next = end + 1;
try {
Statement stm = getTraceDB().getConnection().createStatement();
DeltaManager dm = new DeltaManager();
dm.start();
ResultSet rs = stm.executeQuery("SELECT MIN(TIMESTAMP) FROM EVENT WHERE TIMESTAMP >= "
+ end);
logger.debug(dm.endMessage("exec query"));
while (rs.next()) {
next = rs.getLong(1);
}
rs.close();
stm.close();
} catch (SQLException e) {
e.printStackTrace();
} catch (SoCTraceException e) {
e.printStackTrace();
}
return Math.max(next, end + 1);
}
private String getQuery(long t0, long t1, boolean first, boolean last) {
StringBuilder sb = new StringBuilder();
sb.append("SELECT " + ReducedEvent.SELECT_COLUMNS + " FROM " + FramesocTable.EVENT
+ " WHERE ");
ComparisonOperation endComp = (last) ? ComparisonOperation.LE : ComparisonOperation.LT;
if (first) {
// states and links: start < t0 and end >= t0
sb.append(" (CATEGORY IN (1,2) AND (TIMESTAMP < " + t0 + " AND LPAR >= " + t0 + ")) ");
} else {
// all events: start >= t0 and start < t1 (last interval start >= t0 and start <= t1)
sb.append(" (TIMESTAMP >= " + t0 + " AND TIMESTAMP " + endComp + t1 + ") ");
}
logger.debug("Query: " + sb.toString());
return sb.toString();
}
private void clean() {
fProducersLoaded = false;
fTypesLoaded = false;
fProducers = new HashMap<Integer, EventProducer>();
fTypes = new HashMap<Integer, EventType>();
fLatestStart = Long.MIN_VALUE;
DBObject.finalClose(fTraceDB);
}
private TraceDBObject getTraceDB() throws SoCTraceException {
if (fTraceDB == null) {
Assert.isNotNull(fTrace, "Null trace in event loader");
fTraceDB = TraceDBObject.openNewInstance(fTrace.getDbName());
}
return fTraceDB;
}
private int debug(List<ReducedEvent> events, int totalEvents) {
totalEvents += events.size();
logger.debug("events read : {}", events.size());
logger.debug("total events: {}", totalEvents);
for (ReducedEvent event : events) {
logger.trace(event.toString());
}
return totalEvents;
}
}