blob: 55d386f30a2db264f326390935bab6350066c677 [file] [log] [blame]
/**
********************************************************************************
* Copyright (c) 2020 Eclipse APP4MC contributors.
*
* This program and the accompanying materials are made
* available under the terms of the Eclipse Public License 2.0
* which is available at https://www.eclipse.org/legal/epl-2.0/
*
* SPDX-License-Identifier: EPL-2.0
*
********************************************************************************
*/
package org.eclipse.app4mc.atdb._import.btf;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.lang.reflect.InvocationTargetException;
import java.sql.PreparedStatement;
import java.sql.SQLException;
import java.sql.Statement;
import java.sql.Types;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Optional;
import java.util.Scanner;
import java.util.Set;
import org.eclipse.app4mc.atdb.ATDBConnection;
import org.eclipse.app4mc.atdb._import.btf.model.BTFEntityType;
import org.eclipse.core.runtime.IProgressMonitor;
import org.eclipse.core.runtime.SubMonitor;
import org.eclipse.jface.operation.IRunnableWithProgress;
import com.google.common.collect.BiMap;
import com.google.common.collect.HashBiMap;
public class BTFImporter implements IRunnableWithProgress {
private final String btfFile;
private final ATDBConnection con;
public BTFImporter(final ATDBConnection con, final String btfFile) {
this.con = con;
this.btfFile = btfFile;
}
@Override
public void run(final IProgressMonitor progressMonitor) throws InvocationTargetException {
final SubMonitor subMon = SubMonitor.convert(progressMonitor, "Importing BTF file...", 6);
final SubMonitor readingBTFMonitor = subMon.split(5);
readingBTFMonitor.beginTask("Reading BTF file...", 10_000);
try (final FileInputStream fis = new FileInputStream(this.btfFile);
final Scanner sc = new Scanner(fis, "UTF-8")) {
final Statement propStmt = this.con.createStatement();
final PreparedStatement metaStmt = this.con.getPreparedStatementFor("INSERT INTO metaInformation VALUES(?, ?);");
final PreparedStatement entTStmt = this.con.getPreparedStatementFor("INSERT INTO entityType(name) VALUES(?);");
final PreparedStatement entStmt = this.con.getPreparedStatementFor("INSERT INTO entity(name, entityTypeId) VALUES(?,"
+ "(SELECT id FROM entityType WHERE name = ?));");
final PreparedStatement instStmt = this.con.getPreparedStatementFor("INSERT INTO entityInstance VALUES("
+ "(SELECT id FROM entity WHERE name = ?), ?);");
final PreparedStatement evTStmt = this.con.getPreparedStatementFor("INSERT INTO eventType(name) VALUES(?);");
final PreparedStatement evStmt = this.con.getPreparedStatementFor("INSERT INTO traceEvent VALUES(?, ?,"
+ "(SELECT id FROM entity WHERE name = ?), ?,"//
+ "(SELECT id FROM entity WHERE name = ?), ?,"//
+ "(SELECT id FROM eventType WHERE name = ?), ?)");
final long fileSize = new File(this.btfFile).length();
long currentTimestamp = Long.MIN_VALUE;
int currentSQCNR = 0;
String currentECU = "";
String currentProcessor = "";
long currentLineNumber = 0;
long lineLengthSum = 0;
long avgLineLength = 64;
final BiMap<String, String> actCore2Process = HashBiMap.create();
final BiMap<String, String> actProcess2Runnable = HashBiMap.create();
while (sc.hasNextLine()) {
currentLineNumber++;
final String line = sc.nextLine();
if (!line.startsWith("#")) {
final String[] fields = line.split(",");
if (fields.length > 6) {
final long newTimestamp = Long.parseLong(fields[0]);
final String entityType = fields[3];
final String entityName = fields[4];
final int entityInstance = Integer.parseInt(fields[5]);
final String sourceName = fields[1];
final int sourceInstance = Integer.parseInt(fields[2]);
final String eventType = fields[6];
final Optional<String> value = fields.length > 7 ? Optional.of(fields[7]) : Optional.empty();
insertEntity(entTStmt, entityName, entityType);
if (eventType.equalsIgnoreCase("tag") && value.isPresent()) {
String sourceType = "";
switch (value.get()) {
case "ECU_INIT":
sourceType = "ECU";
currentECU = sourceName;
break;
case "PROCESSOR_INIT":
sourceType = "Processor";
currentProcessor = sourceName;
appendToProperty(currentECU, "processors", "entityIdRef", sourceName);
break;
case "CORE_INIT":
sourceType = "C";
appendToProperty(currentProcessor, "cores", "entityIdRef", sourceName);
break;
case "SIG_INIT_VALUE":
sourceType = "SIG";
if (fields.length > 8) {
setProperty(sourceName, "initialValue", "object", fields[8]);
}
break;
default:
setProperty(sourceName, "tag", "object", value.get());
}
if (!sourceType.isEmpty()) {
insertEntity(entTStmt, sourceName, sourceType);
}
} else if (eventType.equalsIgnoreCase("set_frequence") && value.isPresent()) {
setProperty(entityName, "frequencyInHz", "long", value.get());
continue;
} else if ((eventType.equalsIgnoreCase("start") || eventType.equalsIgnoreCase("stop"))
&& entityType.equals("SYS")) {
setProperty(entityName, "system" + eventType.substring(0, 1).toUpperCase() + eventType.substring(1) + "Time",
"time", "" + newTimestamp);
continue;
}
if (BTFEntityType.PROCESS.isTraceAlias(entityType)//
&& "C".equals(entity2Type.getOrDefault(sourceName, ""))//
&& (!entity2property2Value.containsKey(entityName)//
|| !entity2property2Value.get(entityName).containsKey("executingCore"))) {
setProperty(entityName, "executingCore", "entityIdRef", sourceName);
}
if (BTFEntityType.RUNNABLE.isTraceAlias(entityType)//
&& BTFEntityType.PROCESS.isTraceAlias(entity2Type.getOrDefault(sourceName, ""))//
&& (!entity2property2Value.containsKey(sourceName) //
|| !entity2property2Value.get(sourceName).containsKey("runnables") //
|| !entity2property2Value.get(sourceName).get("runnables").contains(entityName))) {
appendToProperty(sourceName, "runnables", "entityIdRef", entityName);
}
if (eventType.equalsIgnoreCase("activate") && BTFEntityType.PROCESS.isTraceAlias(entityType)) {
insertEntity(entTStmt, sourceName, "STI");
if (!entity2property2Value.containsKey(entityName)
|| !entity2property2Value.get(entityName).containsKey("stimuli")
|| !entity2property2Value.get(entityName).get("stimuli").contains(sourceName)) {
appendToProperty(entityName, "stimuli", "entityIdRef", sourceName);
}
} else {
insertEntity(entTStmt, sourceName, "");
}
insertEntityInstance(instStmt, sourceName, sourceInstance);
insertEntityInstance(instStmt, entityName, entityInstance);
if (eventType.equalsIgnoreCase("tag")) {
continue;
}
if (newTimestamp > currentTimestamp) {
currentSQCNR = 0;
currentTimestamp = newTimestamp;
} else {
currentSQCNR++;
}
insertEventType(evTStmt, eventType);
// keep track of current active process/runnable per core for signal accesses
if (BTFEntityType.PROCESS.isTraceAlias(entityType) && "C".equals(entity2Type.getOrDefault(sourceName, ""))) {
if (eventType.equalsIgnoreCase("start")) {
actCore2Process.put(sourceName, entityName);
} else if (eventType.equalsIgnoreCase("terminate")) {
actCore2Process.remove(sourceName);
}
}
if (BTFEntityType.RUNNABLE.isTraceAlias(entityType)
&& BTFEntityType.PROCESS.isTraceAlias(entity2Type.getOrDefault(sourceName, ""))
&& actCore2Process.containsValue(sourceName)) {
if (eventType.equalsIgnoreCase("start")) {
actProcess2Runnable.put(sourceName, entityName);
} else if (eventType.equalsIgnoreCase("terminate")) {
actProcess2Runnable.remove(sourceName);
}
}
if (entityType.equals("SIG")
&& BTFEntityType.PROCESS.isTraceAlias(entity2Type.getOrDefault(sourceName, ""))
&& actProcess2Runnable.containsKey(sourceName)) {
final String rName = actProcess2Runnable.get(sourceName);
if (eventType.equalsIgnoreCase("read")
&& (!entity2property2Value.containsKey(rName)
|| !entity2property2Value.get(rName).containsKey("readSignals")
|| !entity2property2Value.get(rName).get("readSignals").contains(entityName))) {
appendToProperty(rName, "readSignals", "entityIdRef", entityName);
} else if (eventType.equalsIgnoreCase("write")
&& (!entity2property2Value.containsKey(rName)
|| !entity2property2Value.get(rName).containsKey("writtenSignals")
|| !entity2property2Value.get(rName).get("writtenSignals").contains(entityName))) {
appendToProperty(rName, "writtenSignals", "entityIdRef", entityName);
}
}
evStmt.setLong(1, currentTimestamp);
evStmt.setInt(2, currentSQCNR);
evStmt.setString(3, entityName);
evStmt.setInt(4, entityInstance);
evStmt.setString(5, sourceName);
evStmt.setInt(6, sourceInstance);
evStmt.setString(7, eventType);
if (value.isPresent()) {
evStmt.setString(8, value.get());
} else {
evStmt.setNull(8, Types.VARCHAR);
}
evStmt.addBatch();
}
} else {
if (line.toLowerCase().startsWith("#timescale")) {
metaStmt.setString(1, "timeBase");
metaStmt.setString(2, line.substring(11));
metaStmt.addBatch();
} else if (line.startsWith("#simulation_duration")) {
setProperty("SIM", "simulationDuration", "time", line.substring(21, line.lastIndexOf(' ')));
}
}
if ((currentLineNumber % (1 << 8)) == 0) {
lineLengthSum += line.length();
if ((currentLineNumber % (1 << 16)) == 0) {
avgLineLength = ((avgLineLength + (lineLengthSum >> 8)) >> 1);
lineLengthSum = 0;
final long numberOfLines = fileSize / avgLineLength;
readingBTFMonitor.setWorkRemaining(10_000 - (int)Math.min(10_000, ((currentLineNumber * 10_000) / numberOfLines)));
}
}
}
executeEntityInsertStatements(entStmt);
executePropertyInsertStatements(propStmt);
readingBTFMonitor.done();
final SubMonitor writeEventsMonitor = subMon.split(1);
writeEventsMonitor.beginTask("Writing events to data base...", 1);
this.con.executeBatchStatements(metaStmt, entTStmt, entStmt, instStmt, evTStmt, evStmt, propStmt);
writeEventsMonitor.done();
} catch (final IOException | SQLException e) {
throw new InvocationTargetException(e);
}
}
private final Map<String, String> entity2Type = new LinkedHashMap<>();
private final Set<String> entityTypes = new HashSet<>();
private void insertEntity(final PreparedStatement tStmt, final String name, final String entityType) throws SQLException {
if ((!entityType.isEmpty()) && this.entityTypes.add(entityType)) {
tStmt.setString(1, entityType);
tStmt.addBatch();
}
this.entity2Type.compute(name, (k, v) -> ((v == null) || v.isEmpty()) ? entityType : v);
}
private void executeEntityInsertStatements(final PreparedStatement eStmt) throws SQLException {
for (final Entry<String, String> entry : this.entity2Type.entrySet()) {
final String entityName = entry.getKey();
final String entityType = entry.getValue();
eStmt.setString(1, entityName);
eStmt.setString(2, entityType);
eStmt.addBatch();
}
}
private final Set<String> instNames = new HashSet<>();
private void insertEntityInstance(final PreparedStatement stmt, final String name, final int inst) throws SQLException {
final String srcInstName = name + "#" + inst;
if (this.instNames.add(srcInstName)) {
stmt.setString(1, name);
stmt.setInt(2, inst);
stmt.addBatch();
}
}
private final Set<String> eventTypes = new HashSet<>();
private void insertEventType(final PreparedStatement stmt, final String name) throws SQLException {
if (this.eventTypes.add(name)) {
stmt.setString(1, name);
stmt.addBatch();
}
}
private void appendToProperty(final String entityName, final String propertyName, final String propertyType,
final String propertyValue) {
setProperty(entityName, propertyName, propertyType, propertyValue, true);
}
private void setProperty(final String entityName, final String propertyName, final String propertyType,
final String propertyValue) {
setProperty(entityName, propertyName, propertyType, propertyValue, false);
}
private final Map<String, Map<String, List<String>>> entity2property2Value = new LinkedHashMap<>();
private final Map<String, String> properties = new LinkedHashMap<>();
private void setProperty(final String entityName, final String propertyName, final String propertyType,
final String propertyValue, final boolean append) {
this.properties.putIfAbsent(propertyName, propertyType);
final Map<String, List<String>> property2Value = this.entity2property2Value.computeIfAbsent(entityName, k -> new LinkedHashMap<>());
final List<String> values = property2Value.computeIfAbsent(propertyName, k -> new ArrayList<>());
if (!append) {
values.clear();
}
values.add(propertyValue);
}
private void executePropertyInsertStatements(final Statement stmt) throws SQLException {
final List<String> statements = new ArrayList<>();
this.properties.forEach((propertyName, propertyType) -> statements.add("INSERT INTO property(name, type) VALUES('"//
+ propertyName + "', '" + propertyType + "');"));
this.entity2property2Value.forEach((entityName, property2Value) -> property2Value.forEach((propertyName, propertyValue) -> {
final List<String> propertyValueStrings = new ArrayList<>();
final String propertyType = this.properties.get(propertyName);
if (propertyType.endsWith("IdRef")) {
final String tableName = propertyType.substring(0, propertyType.lastIndexOf("IdRef"));
propertyValue.forEach(pv -> propertyValueStrings.add("(SELECT id FROM " + tableName + " WHERE name = '" + pv + "')"));
} else {
propertyValueStrings.add("'" + String.join(", ", propertyValue) + "'");
}
for (int i = 0; i < propertyValueStrings.size(); i++) {
statements.add("INSERT INTO propertyValue(entityId, propertyId, sqcnr, value) VALUES("//
+ "(SELECT id FROM entity WHERE name = '" + entityName + "'), "//
+ "(SELECT id FROM property WHERE name = '" + propertyName + "'), "//
+ i + ","//
+ propertyValueStrings.get(i) + ");");
}
}));
for (final String st : statements) {
stmt.addBatch(st);
}
}
}