/* * Copyright (c) 2022, RPTU Kaiserslautern-Landau * All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions are * met: * * 1. Redistributions of source code must retain the above copyright notice, * this list of conditions and the following disclaimer. * * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the distribution. * * 3. Neither the name of the copyright holder nor the names of its * contributors may be used to endorse or promote products derived from * this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER * OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. * * Authors: * Iron Prando da Silva */ #include "phasedependenciestracker.h" #include #include void PhaseDependenciesTracker::calculateDependencies(TraceDB& tdb, std::vector& commands) { using std::chrono::high_resolution_clock; using std::chrono::duration_cast; using std::chrono::duration; 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; auto numberOfEntries = entries.size(); 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 + "' "; }