/* * Copyright (c) 2015, Technische Universität Kaiserslautern * 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: * Janik Schlemminger * Robert Gernhardt * Matthias Jung * Derek Christ * Iron Prando da Silva */ #include #include #include #include #include #include #include #include #include "data/tracedb.h" #include "businessObjects/phases/phasefactory.h" //define symbol printqueries if all queries should be printed to the console //#define printqueries using namespace std; TraceDB::TraceDB(QString path, bool openExisting) { this->pathToDB = path; database = QSqlDatabase::database(path); if (database.isValid() && database.isOpen()) { // Close the database connection if it exists and was not closed yet. database.close(); QSqlDatabase::removeDatabase(path); } database = QSqlDatabase::addDatabase("QSQLITE", path); database.setDatabaseName(path); if (!database.open()) { qDebug() << database.lastError().text(); } if (!openExisting) dropAndCreateTables(); prepareQueries(); generalInfo = getGeneralInfoFromDB(); commandLengths = getCommandLengthsFromDB(); } void TraceDB::prepareQueries() { selectTransactionsByTimespan = QSqlQuery(database); if (!selectTransactionsByTimespan.prepare(queryTexts.selectTransactionsByTimespan)) qDebug() << database.lastError().text(); selectTransactionById = QSqlQuery(database); if (!selectTransactionById.prepare(queryTexts.selectTransactionById)) qDebug() << database.lastError().text(); selectDebugMessagesByTimespan = QSqlQuery(database); if (!selectDebugMessagesByTimespan.prepare("SELECT time, Message FROM DebugMessages WHERE :begin <= time AND time <= :end ")) qDebug() << database.lastError().text(); selectDebugMessagesByTimespanWithLimit = QSqlQuery(database); if (!selectDebugMessagesByTimespanWithLimit.prepare("SELECT time, Message FROM DebugMessages WHERE :begin <= time AND time <= :end LIMIT :limit")) qDebug() << database.lastError().text(); checkDependenciesExist = QSqlQuery(database); if (!checkDependenciesExist.prepare(queryTexts.checkDependenciesExist)) qDebug() << database.lastError().text(); selectDependenciesByTimespan = QSqlQuery(database); if (!selectDependenciesByTimespan.prepare(queryTexts.selectDependenciesByTimespan)) qDebug() << database.lastError().text(); selectDependencyTypePercentages = QSqlQuery(database); if (!selectDependencyTypePercentages.prepare(queryTexts.selectDependencyTypePercentages)) qDebug() << database.lastError().text(); selectTimeDependencyPercentages = QSqlQuery(database); if (!selectTimeDependencyPercentages.prepare(queryTexts.selectTimeDependencyPercentages)) qDebug() << database.lastError().text(); selectDelayedPhasePercentages = QSqlQuery(database); if (!selectDelayedPhasePercentages.prepare(queryTexts.selectDelayedPhasePercentages)) qDebug() << database.lastError().text(); selectDependencyPhasePercentages = QSqlQuery(database); if (!selectDependencyPhasePercentages.prepare(queryTexts.selectDependencyPhasePercentages)) qDebug() << database.lastError().text(); } void TraceDB::updateComments(const std::vector &comments) { QSqlQuery query(database); query.prepare("DELETE FROM Comments"); executeQuery(query); query.prepare("insert into Comments values(:time,:text)"); for (const auto &comment : comments) { query.bindValue(":time", comment.time); query.bindValue(":text", comment.text); executeQuery(query); } } void TraceDB::updateFileDescription(const QString &description) { QSqlQuery query(database); query.prepare("UPDATE GeneralInfo SET Description=:description"); query.bindValue(":description", description); executeQuery(query); } void TraceDB::refreshData() { generalInfo = getGeneralInfoFromDB(); } //QueryText must select the fields //TransactionID, Ranges.begin, Ranges.end, Address, TThread, TChannel, TBank, TRow, TColumn, Phases.ID AS PhaseID, PhaseName, PhaseBegin, PhaseEnd vector> TraceDB::getTransactionsWithCustomQuery( QString queryText) { QSqlQuery query(database); query.prepare(queryText); executeQuery(query); return parseTransactionsFromQuery(query); } vector> TraceDB::getTransactionsInTimespan(const Timespan &span, bool updateVisiblePhases) { selectTransactionsByTimespan.bindValue(":begin", span.Begin()); selectTransactionsByTimespan.bindValue(":end", span.End()); executeQuery(selectTransactionsByTimespan); return parseTransactionsFromQuery(selectTransactionsByTimespan, updateVisiblePhases); } bool TraceDB::checkDependencyTableExists() { executeQuery(checkDependenciesExist); bool exists = checkDependenciesExist.next() && checkDependenciesExist.value(0).toInt() == 1; checkDependenciesExist.finish(); return exists; } void TraceDB::updateDependenciesInTimespan(const Timespan &span) { if (checkDependencyTableExists()) { selectDependenciesByTimespan.bindValue(":begin", span.Begin()); selectDependenciesByTimespan.bindValue(":end", span.End()); executeQuery(selectDependenciesByTimespan); mUpdateDependenciesFromQuery(selectDependenciesByTimespan); } } //TODO Remove exception shared_ptr TraceDB::getTransactionByID(ID id) { selectTransactionById.bindValue(":id", id); executeQuery(selectTransactionById); auto result = parseTransactionsFromQuery(selectTransactionById); if (!result.empty()) return result[0]; else throw sqlException(("Transaction with ID " + QString::number( id) + " not in DB").toStdString(), this->pathToDB.toStdString()); } shared_ptr TraceDB::getNextActivate(traceTime time) { QSqlQuery query(database); QString queryText = queryTexts.queryHead + "WHERE PhaseBegin > :traceTime AND PhaseName " "IN ('ACT','ACTB') ORDER BY PhaseBegin ASC LIMIT 1"; query.prepare(queryText); query.bindValue(":traceTime", time); executeQuery(query); return parseTransactionFromQuery(query); } shared_ptr TraceDB::getNextPrecharge(traceTime time) { QSqlQuery query(database); QString queryText = queryTexts.queryHead + "WHERE PhaseBegin > :traceTime AND PhaseName " "IN ('PRE','PREA','PREB','PRESB') ORDER BY PhaseBegin ASC LIMIT 1"; query.prepare(queryText); query.bindValue(":traceTime", time); executeQuery(query); return parseTransactionFromQuery(query); } // shared_ptr TraceDB::getNextActb(ID currentTransactionId) // { // QSqlQuery query(database); // QString queryText = queryTexts.queryHead + // "WHERE TransactionID > :currentID AND PhaseName = 'ACTB' LIMIT 1"; // // query.prepare(queryText); // query.bindValue(":currentID", currentTransactionId); // executeQuery(query); // return parseTransactionFromQuery(query); // } // shared_ptr TraceDB::getNextPreb(ID currentTransactionId) // { // QSqlQuery query(database); // QString queryText = queryTexts.queryHead + // "WHERE TransactionID > :currentID AND PhaseName = 'PREB' LIMIT 1"; // // query.prepare(queryText); // query.bindValue(":currentID", currentTransactionId); // executeQuery(query); // return parseTransactionFromQuery(query); // } shared_ptr TraceDB::getNextRefresh(traceTime time) { QSqlQuery query(database); QString queryText = queryTexts.queryHead + "WHERE PhaseBegin > :traceTime AND PhaseName " "IN ('REFA','REFB','REFSB','SREF','SREFB') ORDER BY PhaseBegin ASC LIMIT 1"; query.prepare(queryText); query.bindValue(":traceTime", time); executeQuery(query); return parseTransactionFromQuery(query); } std::shared_ptr TraceDB::getNextCommand(traceTime time) { QSqlQuery query(database); QString queryText = queryTexts.queryHead + "WHERE PhaseBegin > :traceTime ORDER BY PhaseBegin ASC LIMIT 1"; query.prepare(queryText); query.bindValue(":traceTime", time); executeQuery(query); return parseTransactionFromQuery(query); } // shared_ptr TraceDB::getNextRefb(ID currentTransactionId) // { // QSqlQuery query(database); // QString queryText = queryTexts.queryHead + // "WHERE TransactionID > :currentID AND PhaseName = 'REFB' LIMIT 1"; // // query.prepare(queryText); // query.bindValue(":currentID", currentTransactionId); // executeQuery(query); // return parseTransactionFromQuery(query); // } ID TraceDB::getTransactionIDFromPhaseID(ID phaseID) { QSqlQuery query(database); query.prepare("SELECT Transact FROM Phases WHERE ID=:id"); query.bindValue(":id", phaseID); executeQuery(query); if (query.next()) { return query.value(0).toInt(); } else { throw sqlException("Phase with ID " + to_string(phaseID) + " not in db", this->pathToDB.toStdString()); } } GeneralInfo *TraceDB::getGeneralInfoFromDB() { QSqlQuery query(database); query.prepare("SELECT NumberOfTransactions, TraceEnd, NumberOfRanks, NumberOfBankgroups, NumberOfBanks, Clk, " "UnitOfTime, Traces, Memspec, MCconfig, WindowSize, ControllerThread FROM GeneralInfo"); executeQuery(query); if (query.next()) { unsigned int numberOfTransactions = query.value(0).toInt(); traceTime traceEnd = query.value(1).toLongLong(); unsigned int numberOfRanks = query.value(2).toInt(); unsigned int numberOfBankgroups = query.value(3).toInt(); unsigned int numberOfBanks = query.value(4).toInt(); unsigned int clkPeriod = query.value(5).toInt(); QString unitOfTime = query.value(6).toString(); unsigned int numberOfPhases = getNumberOfPhases(); QString traces = "Traces: " + query.value(7).toString(); QString memspec = "Memspec: " + query.value(8).toString(); QString mcconfig = "MCconfig: " + query.value(9).toString(); unsigned int windowSize = query.value(10).toInt(); unsigned int controllerThread = query.value(11).toUInt(); return new GeneralInfo(numberOfTransactions, numberOfPhases, Timespan(0, traceEnd), numberOfRanks, numberOfBankgroups, numberOfBanks, unitOfTime, clkPeriod, windowSize, controllerThread); } else { throw sqlException("Tracefile corrupted. No general info table", this->pathToDB.toStdString()); } } CommandLengths TraceDB::getCommandLengthsFromDB() { unsigned NOP = getLengthOfCommandFromDB("NOP"); unsigned RD = getLengthOfCommandFromDB("RD"); unsigned WR = getLengthOfCommandFromDB("WR"); unsigned RDA = getLengthOfCommandFromDB("RDA"); unsigned WRA = getLengthOfCommandFromDB("WRA"); unsigned ACT = getLengthOfCommandFromDB("ACT"); unsigned PRE = getLengthOfCommandFromDB("PRE"); unsigned REFB = getLengthOfCommandFromDB("REFB"); unsigned PRESB = getLengthOfCommandFromDB("PRESB"); unsigned REFSB = getLengthOfCommandFromDB("REFSB"); unsigned RFMSB = getLengthOfCommandFromDB("RFMSB"); unsigned PREA = getLengthOfCommandFromDB("PREA"); unsigned REFA = getLengthOfCommandFromDB("REFA"); unsigned RFMAB = getLengthOfCommandFromDB("RFMAB"); unsigned PDEA = getLengthOfCommandFromDB("PDEA"); unsigned PDXA = getLengthOfCommandFromDB("PDXA"); unsigned PDEP = getLengthOfCommandFromDB("PDEP"); unsigned PDXP = getLengthOfCommandFromDB("PDXP"); unsigned SREFEN = getLengthOfCommandFromDB("SREFEN"); unsigned SREFEX = getLengthOfCommandFromDB("SREFEX"); return {NOP, RD, WR, RDA, WRA, ACT, PRE, REFB, PRESB, REFSB, RFMSB, PREA, REFA, RFMAB, PDEA, PDXA, PDEP, PDXP, SREFEN, SREFEX}; } unsigned int TraceDB::getLengthOfCommandFromDB(const std::string& command) { QSqlQuery query(("SELECT " + command + " FROM CommandLengths").c_str(), database); if (query.first()) return query.value(0).toUInt(); else { qDebug() << "Warning: Length of command " << command.c_str() << " not present in DB, setting 1 as default."; return 1; } } unsigned int TraceDB::getNumberOfPhases() { QSqlQuery query(database); query.prepare("SELECT COUNT(ID) FROM Phases"); executeQuery(query); query.next(); return query.value(0).toInt(); } vector TraceDB::getComments() { QSqlQuery query(database); query.prepare("SELECT Time,Text From Comments"); executeQuery(query); return parseCommentsFromQuery(query); } vector TraceDB::getDebugMessagesInTimespan(const Timespan &span) { selectDebugMessagesByTimespan.bindValue(":begin", span.Begin()); selectDebugMessagesByTimespan.bindValue(":end", span.End()); executeQuery(selectDebugMessagesByTimespan); return parseCommentsFromQuery(selectDebugMessagesByTimespan); } vector TraceDB::getDebugMessagesInTimespan(const Timespan &span, unsigned int limit = 50) { selectDebugMessagesByTimespanWithLimit.bindValue(":begin", span.Begin()); selectDebugMessagesByTimespanWithLimit.bindValue(":end", span.End()); selectDebugMessagesByTimespanWithLimit.bindValue(":limit", limit); executeQuery(selectDebugMessagesByTimespanWithLimit); return parseCommentsFromQuery(selectDebugMessagesByTimespanWithLimit); } DependencyInfos TraceDB::getDependencyInfos(DependencyInfos::Type infoType) { DependencyInfos dummy; executeQuery(checkDependenciesExist); if (!checkDependenciesExist.next() || checkDependenciesExist.value(0).toInt() != 1) { checkDependenciesExist.finish(); return dummy; } switch (infoType) { case DependencyInfos::Type::DependencyType: executeQuery(selectDependencyTypePercentages); return parseDependencyInfos(selectDependencyTypePercentages, infoType); case DependencyInfos::Type::TimeDependency: executeQuery(selectTimeDependencyPercentages); return parseDependencyInfos(selectTimeDependencyPercentages, infoType); case DependencyInfos::Type::DelayedPhase: executeQuery(selectDelayedPhasePercentages); return parseDependencyInfos(selectDelayedPhasePercentages, infoType); case DependencyInfos::Type::DependencyPhase: executeQuery(selectDependencyPhasePercentages); return parseDependencyInfos(selectDependencyPhasePercentages, infoType); } return dummy; } QSqlDatabase TraceDB::getDatabase() const { return database; } /* Helpers * * * */ shared_ptr TraceDB::parseTransactionFromQuery(QSqlQuery &query) { auto result = parseTransactionsFromQuery(query); if (!result.empty()) return result[0]; else return shared_ptr(); } vector> TraceDB::parseTransactionsFromQuery(QSqlQuery &query, bool updateVisiblePhases) { if (updateVisiblePhases) { _visiblePhases.clear(); } vector> result; bool firstIteration = true; ID currentID = 0; int i = -1; while (query.next()) { ID id = query.value(0).toInt(); if (currentID != id || firstIteration) { ++i; firstIteration = false; currentID = id; Timespan span(query.value(1).toLongLong(), query.value(2).toLongLong()); Timespan spanOnStrobe(query.value(3).toLongLong(), query.value(4).toLongLong()); unsigned int address = query.value(5).toInt(); unsigned int burstlength = query.value(6).toInt(); unsigned int thread = query.value(7).toInt(); unsigned int channel = query.value(8).toInt(); unsigned int rank = query.value(9).toInt(); unsigned int bankgroup = query.value(10).toInt(); unsigned int bank = query.value(11).toInt(); unsigned int row = query.value(12).toInt(); unsigned int column = query.value(13).toInt(); result.push_back(shared_ptr(new Transaction(id, address, burstlength, thread, channel, rank, bankgroup, bank, row, column, span, spanOnStrobe, generalInfo->clkPeriod))); } unsigned int phaseID = query.value(14).toInt(); QString phaseName = query.value(15).toString(); Timespan span(query.value(16).toLongLong(), query.value(17).toLongLong()); auto phase = PhaseFactory::CreatePhase(phaseID, phaseName, span, result.at(result.size() - 1), *this); result.at(result.size() - 1)->addPhase(phase); if (updateVisiblePhases) { _visiblePhases[phaseID] = phase; } } return result; } void TraceDB::mUpdateDependenciesFromQuery(QSqlQuery &query) { DependencyType type; while (query.next()) { ID delayedID = query.value(0).toInt(); ID dependencyID = query.value(4).toInt(); QString dependencyTypeStr = query.value(2).toString(); if (dependencyTypeStr == "bank") { type = DependencyType::IntraBank; } else if (dependencyTypeStr == "rank") { type = DependencyType::IntraRank; } else if (dependencyTypeStr == "interRank") { type = DependencyType::InterRank; } QString timeDependencyStr = query.value(3).toString(); if (_visiblePhases.count(delayedID) > 0) { if (_visiblePhases.count(dependencyID) > 0) { _visiblePhases[delayedID]->addDependency(std::shared_ptr( new PhaseDependency(type, timeDependencyStr, _visiblePhases[dependencyID]))); } else { _visiblePhases[delayedID]->addDependency( std::shared_ptr(new PhaseDependency(type, timeDependencyStr))); } } else { // TODO delayed phase not visible? } } } vector TraceDB::parseCommentsFromQuery(QSqlQuery &query) { vector result; while (query.next()) { result.push_back(CommentModel::Comment{query.value(0).toLongLong(), query.value(1).toString()}); } return result; } DependencyInfos TraceDB::parseDependencyInfos(QSqlQuery &query, const DependencyInfos::Type infoType) { DependencyInfos infos(infoType); while (query.next()) { infos.addInfo({query.value(0).toString(), query.value(1).toFloat()}); } query.finish(); return infos; } void TraceDB::executeQuery(QSqlQuery query) { //query.exec returns bool indicating if the query was sucessfull if (query.exec()) { #ifdef printqueries cout << queryToString(query).toStdString() << endl; #endif } else { query.finish(); throw sqlException( ("Query:\n " + queryToString(query) + "\n failed. Error: \n" + query.lastError().text()).toStdString(), this->pathToDB.toStdString()); } } QString TraceDB::queryToString(QSqlQuery query) { QString str = query.lastQuery(); QMapIterator it(query.boundValues()); while (it.hasNext()) { it.next(); str.replace(it.key(), it.value().toString()); } return str; } void TraceDB::dropAndCreateTables() { executeScriptFile("common/static/createTraceDB.sql"); } void TraceDB::executeScriptFile(QString fileName) { QSqlQuery query(database); QFile scriptFile(fileName); if (scriptFile.open(QIODevice::ReadOnly)) { // The SQLite driver executes only a single (the first) query in the QSqlQuery // if the script contains more queries, it needs to be splitted. QStringList scriptQueries = QTextStream(&scriptFile).readAll().split(';'); for (QString &queryTxt : scriptQueries) { if (queryTxt.trimmed().isEmpty()) { continue; } if (!query.exec(queryTxt)) { throw sqlException("Querry failed:" + query.lastError().text().toStdString(), this->pathToDB.toStdString()); } query.finish(); } } }