| /** |
| ******************************************************************************** |
| * 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); |
| } |
| } |
| |
| } |