348 lines
12 KiB
C++
348 lines
12 KiB
C++
/*
|
|
* Copyright (c) 2025 Fraunhofer IESE. All rights reserved.
|
|
*
|
|
* Authors:
|
|
* Iron Prando da Silva
|
|
*/
|
|
|
|
#include "phasedependenciestracker.h"
|
|
|
|
#include <chrono>
|
|
#include <iostream>
|
|
|
|
void PhaseDependenciesTracker::calculateDependencies(TraceDB& tdb, std::vector<QString>& 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<microseconds>(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<microseconds>(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<microseconds>(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<microseconds>(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<DBDependencyEntry>& 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<std::shared_ptr<DBPhaseEntryBase>>
|
|
PhaseDependenciesTracker::mGetFilteredPhases(const std::shared_ptr<ConfigurationBase> deviceConfig,
|
|
TraceDB& tdb,
|
|
const std::vector<QString>& commands)
|
|
{
|
|
std::vector<std::shared_ptr<DBPhaseEntryBase>> 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<DBDependencyEntry> PhaseDependenciesTracker::mCalculateDependencies(
|
|
const std::shared_ptr<ConfigurationBase> deviceConfig,
|
|
const std::vector<std::shared_ptr<DBPhaseEntryBase>>& phases,
|
|
std::vector<QString>& commands)
|
|
{
|
|
std::vector<DBDependencyEntry> 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 + "' ";
|
|
}
|