/* * Copyright (c) 2025 Fraunhofer IESE. All rights reserved. * * Authors: * Iron Prando da Silva */ #include "phasedependenciestracker.h" #include #include void PhaseDependenciesTracker::calculateDependencies(TraceDB& tdb, std::vector& commands) { using std::chrono::duration; using std::chrono::duration_cast; using std::chrono::high_resolution_clock; using std::chrono::microseconds; auto deviceInstantiationTimeStart = high_resolution_clock::now(); auto deviceConfig = ConfigurationFactory::make(tdb); auto deviceInstantiationTimeEnd = high_resolution_clock::now(); auto deviceInstantiationTimeDuration = duration_cast(deviceInstantiationTimeEnd - deviceInstantiationTimeStart); mBeginTransaction(tdb); mDropTable(tdb); if (commands.size() > 0) { auto phasesLoadingTimeStart = high_resolution_clock::now(); auto& phases = mGetFilteredPhases(deviceConfig, tdb, commands); auto phasesLoadingTimeEnd = high_resolution_clock::now(); auto phasesLoadingTimeDuration = duration_cast(phasesLoadingTimeEnd - phasesLoadingTimeStart); if (phases.size() != 0) { auto dependenciesCalcTimeStart = high_resolution_clock::now(); auto& entries = mCalculateDependencies(deviceConfig, phases, commands); auto dependenciesCalcTimeEnd = high_resolution_clock::now(); auto dependenciesCalcTimeDuration = duration_cast(dependenciesCalcTimeEnd - dependenciesCalcTimeStart); if (entries.size() > 0) { mCreateTable(tdb); } auto tableInsertionTimeStart = high_resolution_clock::now(); mInsertIntoTable(tdb, entries); auto tableInsertionTimeEnd = high_resolution_clock::now(); auto tableInsertionTimeDuration = duration_cast(tableInsertionTimeEnd - tableInsertionTimeStart); auto totalTime = deviceInstantiationTimeDuration + phasesLoadingTimeDuration + dependenciesCalcTimeDuration + tableInsertionTimeDuration; std::cout << "PhaseDependenciesTracker times (us):" << std::endl << "\tDevice instantiation: " << deviceInstantiationTimeDuration.count() << std::endl << "\tPhase loading: " << phasesLoadingTimeDuration.count() << std::endl << "\tDependencies calculation: " << dependenciesCalcTimeDuration.count() << std::endl << "\tDB table population: " << tableInsertionTimeDuration.count() << std::endl << " - Total time: " << totalTime.count() << std::endl; } else { // TODO - not sure if necessary. Still, a possibility // mRollbackChanges(tdb); // return; } } mCommitTransaction(tdb); } void PhaseDependenciesTracker::mDropTable(TraceDB& tdb) { QString command = "DROP TABLE IF EXISTS DirectDependencies; "; auto query = mExecuteQuery(tdb, command); query.finish(); } void PhaseDependenciesTracker::mCreateTable(TraceDB& tdb) { QString command = "CREATE TABLE DirectDependencies( DelayedPhaseID INT, DelayedPhaseName, DependencyType, " "TimeDependency, DependencyPhaseID INT, DependencyPhaseName ); "; auto query = mExecuteQuery(tdb, command); query.finish(); } void PhaseDependenciesTracker::mInsertIntoTable(TraceDB& tdb, const std::vector& entries) { static const size_t bulkInsertionSize = 30; QString command; size_t counter = 0; for (const auto& entry : entries) { if (counter == 0) { // Reset command string and add first entry command = "INSERT INTO 'DirectDependencies' ('DelayedPhaseID', 'DelayedPhaseName', " "'DependencyType', 'TimeDependency', 'DependencyPhaseID', 'DependencyPhaseName') "; mAddFirstEntryCommandString(command, entry); counter++; } else if (counter == bulkInsertionSize - 1) { // Write last entry and submit mAddEntryCommandString(command, entry); auto query = mExecuteQuery(tdb, command); query.finish(); counter = 0; } else { // Write entry mAddEntryCommandString(command, entry); counter++; } } if (counter != 0) { auto query = mExecuteQuery(tdb, command); query.finish(); } } const std::vector> PhaseDependenciesTracker::mGetFilteredPhases(const std::shared_ptr deviceConfig, TraceDB& tdb, const std::vector& commands) { std::vector> phases; QString queryStr = deviceConfig->getQueryStr(commands); auto query = mExecuteQuery(tdb, queryStr); if (!query.next()) return phases; if (!query.last()) { throw sqlException(("Query:\n " + tdb.queryToString(query) + "\n failed. Error: \n" "Could not retrieve number of rows\n") .toStdString(), tdb.pathToDB.toStdString()); } size_t nrows = query.at() + 1; if (!query.first()) { throw sqlException(("Query:\n " + tdb.queryToString(query) + "\n failed. Error: \n" "Could not retrieve number of rows safely\n") .toStdString(), tdb.pathToDB.toStdString()); } phases.resize(nrows); size_t rowIt = 0; do { phases[rowIt] = deviceConfig->makePhaseEntry(query); ++rowIt; } while (query.next()); if (rowIt != nrows) { throw std::runtime_error("An error occurred while fetching phases in " "'PhaseDependenciesTracker::mGetFilteredPhases': expected " + std::to_string(nrows) + " rows, but found " + std::to_string(rowIt) + "\n"); } query.finish(); return phases; } const std::vector PhaseDependenciesTracker::mCalculateDependencies( const std::shared_ptr deviceConfig, const std::vector>& phases, std::vector& commands) { std::vector entries; entries.reserve((size_t)(0.4 * phases.size())); // Get dependencies for device const DependencyMap deviceDependencies = deviceConfig->getDependencies(commands); // Tries to find all timing dependencies for each phase on the trace PoolControllerMap poolController = deviceConfig->getPools(); for (size_t i = 1; i < phases.size(); i++) { // Pool dependencies variables reset poolController.clear(); // Auxiliary variables const auto phase = phases[i]; if (phase == nullptr) continue; // Get time dependency descriptions for the current phase const auto& deps = deviceDependencies.at(phase->phaseName); // Loop all previous phases until there cannot be any more time dependencies for (int j = i - 1; j >= 0; j--) { // Get next phase to analyse const auto& otherPhase = phases[j]; // Calculates the time difference in nanoseconds const auto timeDiff = phase->phaseBegin - otherPhase->phaseBegin; // Time difference begin greater than the maximum possible dependency time ends the // internal loop if (timeDiff > deps.maxTime) break; // For each possible dependency for the current phase, // checks if otherPhase would match as a dependency for (const auto& dep : deps.dependencies) { bool isPoolDep = dep.phaseDep.isPool(); if (!isPoolDep && dep.phaseDep != otherPhase->phaseName) continue; if (!phase->potentialDependency(dep, otherPhase)) { continue; } if (isPoolDep) { // Captures activate window and command bus dependencies auto busyTime = poolController.getBusyTime(dep.phaseDep, otherPhase->phaseName); if (busyTime > 0 && timeDiff <= busyTime) { if (timeDiff == busyTime) { // Captures only the first (exactly matching time) phase in // the pool window as a dependency poolController.push( dep.phaseDep, DBDependencyEntry{phase->id, phase->phaseName.getIDStr(), PhaseDependency::dependencyTypeName(dep.depType), dep.timeDepName, otherPhase->id, otherPhase->phaseName.getIDStr()}); } if (timeDiff < busyTime) { poolController.increment(dep.phaseDep); } } continue; } if (timeDiff == dep.timeValue) { entries.emplace_back( DBDependencyEntry{phase->id, phase->phaseName.getIDStr(), PhaseDependency::dependencyTypeName(dep.depType), dep.timeDepName, otherPhase->id, otherPhase->phaseName.getIDStr()}); } } } poolController.merge(entries); } return entries; } QSqlQuery PhaseDependenciesTracker::mExecuteQuery(TraceDB& tdb, const QString queryStr) { QSqlQuery query(tdb.database); query.prepare(queryStr); tdb.executeQuery(query); return query; } void PhaseDependenciesTracker::mBeginTransaction(TraceDB& tdb) { const QString queryStr = "BEGIN TRANSACTION;"; auto query = mExecuteQuery(tdb, queryStr); query.finish(); } void PhaseDependenciesTracker::mRollbackChanges(TraceDB& tdb) { const QString queryStr = "ROLLBACK;"; auto query = mExecuteQuery(tdb, queryStr); query.finish(); } void PhaseDependenciesTracker::mCommitTransaction(TraceDB& tdb) { const QString queryStr = "COMMIT;"; auto query = mExecuteQuery(tdb, queryStr); query.finish(); } void PhaseDependenciesTracker::mAddFirstEntryCommandString(QString& command, const DBDependencyEntry& entry) { command = command + " SELECT '" + QString::number(entry.delayedPhaseID) + "' AS 'DelayedPhaseID', '" + entry.delayedPhaseName + "' AS 'DelayedPhaseName', '" + entry.dependencyType + "' AS 'DependencyType', '" + entry.timeDependency + "' AS 'TimeDependency', '" + QString::number(entry.dependencyPhaseID) + "' AS 'DependencyPhaseID', '" + entry.dependencyPhaseName + "' AS 'DependencyPhaseName' "; } void PhaseDependenciesTracker::mAddEntryCommandString(QString& command, const DBDependencyEntry& entry) { command = command + " UNION ALL SELECT '" + QString::number(entry.delayedPhaseID) + "', '" + entry.delayedPhaseName + "', '" + entry.dependencyType + "', '" + entry.timeDependency + "', '" + QString::number(entry.dependencyPhaseID) + "', '" + entry.dependencyPhaseName + "' "; }