diff --git a/src/init.cpp b/src/init.cpp index dd75e77..ea6a553 100644 --- a/src/init.cpp +++ b/src/init.cpp @@ -51,6 +51,21 @@ enum Checkpoints::CPMode CheckpointsMode; static CCriticalSection cs_DeferredStartup; static bool fDeferredStartupRunning = false; +static void StartupPerfLog(const char* phase, int64_t elapsedMs) +{ + printf("STARTUP-PERF: %s %" PRId64 "ms\n", phase, elapsedMs); +} + +static void StartupPerfLog(const char* phase, int64_t elapsedMs, const std::string& detail) +{ + if (detail.empty()) + { + StartupPerfLog(phase, elapsedMs); + return; + } + printf("STARTUP-PERF: %s %" PRId64 "ms %s\n", phase, elapsedMs, detail.c_str()); +} + ////////////////////////////////////////////////////////////////////////////// // // Shutdown @@ -96,6 +111,7 @@ void ThreadDeferredStartup(void* parg) int64_t nStart = GetTimeMillis(); SecureMsgStart(fNoSmsg, GetBoolArg("-smsgscanchain")); printf(" securemsg %15" PRId64 "ms\n", GetTimeMillis() - nStart); + StartupPerfLog("deferred.securemsg", GetTimeMillis() - nStart); } if (!fShutdown && pwalletMain) @@ -103,9 +119,11 @@ void ThreadDeferredStartup(void* parg) int64_t nStart = GetTimeMillis(); pwalletMain->ReacceptWalletTransactions(); printf(" reaccept %15" PRId64 "ms\n", GetTimeMillis() - nStart); + StartupPerfLog("deferred.reaccept_wallet_transactions", GetTimeMillis() - nStart); } printf("Deferred startup tasks finished %" PRId64 "ms\n", GetTimeMillis() - nTotalStart); + StartupPerfLog("deferred.total", GetTimeMillis() - nTotalStart); } catch (std::exception& e) { @@ -449,6 +467,7 @@ bool InitSanityCheck(void) */ bool AppInit2() { + const int64_t nAppInitStart = GetTimeMillis(); // ********************************************************* Step 1: setup #ifdef _MSC_VER // Turn off Microsoft heap dump noise @@ -672,6 +691,7 @@ bool AppInit2() // ********************************************************* Step 5: verify database integrity uiInterface.InitMessage(_("Verifying database integrity...")); + nStart = GetTimeMillis(); if (!bitdb.Open(GetDataDir())) { @@ -702,8 +722,10 @@ bool AppInit2() if (r == CDBEnv::RECOVER_FAIL) return InitError(_("wallet.dat corrupt, salvage failed")); } + StartupPerfLog("verify_db", GetTimeMillis() - nStart, strprintf("wallet=%s", strWalletFileName.c_str())); // ********************************************************* Step 6: network initialization + nStart = GetTimeMillis(); //int nSocksVersion = GetArg("-socks", 5); // @@ -797,11 +819,13 @@ bool AppInit2() for (string strDest : mapMultiArgs["-seednode"]) AddOneShot(strDest); + StartupPerfLog("network_init", GetTimeMillis() - nStart, strprintf("listen=%d seednodes=%" PRIszu, !fNoListen, mapMultiArgs["-seednode"].size())); // ********************************************************* Step 6b: bootstrap download (daemon) #ifndef QT_GUI if (GetBoolArg("-bootstrap", false)) { + int64_t nBootstrapStart = GetTimeMillis(); fs::path dataPath = GetDataDir(); std::string host = Bootstrap::DEFAULT_HOST; std::string strError; @@ -832,6 +856,8 @@ bool AppInit2() } else { printf("\nBootstrap: done.\n"); } + StartupPerfLog("bootstrap_download", GetTimeMillis() - nBootstrapStart, + strprintf("host=%s success=%d", host.c_str(), success)); } #endif @@ -867,7 +893,9 @@ bool AppInit2() { uiInterface.InitMessage(_("Importing bootstrap blocks...")); printf("Block index empty but blk0001.dat exists - running fast import...\n"); + int64_t nFastImportStart = GetTimeMillis(); FastImportBlockFile(); + StartupPerfLog("bootstrap_fast_import", GetTimeMillis() - nFastImportStart, strprintf("bestheight=%d", nBestHeight)); } // as LoadBlockIndex can take several minutes, it's possible the user @@ -879,6 +907,7 @@ bool AppInit2() return false; } printf(" block index %15" PRId64 "ms\n", GetTimeMillis() - nStart); + StartupPerfLog("block_index", GetTimeMillis() - nStart, strprintf("bestheight=%d indexsize=%" PRIszu, nBestHeight, mapBlockIndex.size())); // Diagnostic: check for blocks in mapBlockIndex above pindexBest { @@ -989,6 +1018,7 @@ bool AppInit2() printf("%s", strErrors.str().c_str()); printf(" wallet %15" PRId64 "ms\n", GetTimeMillis() - nStart); + StartupPerfLog("wallet_load", GetTimeMillis() - nStart, strprintf("firstrun=%d", fFirstRun)); RegisterWallet(pwalletMain); @@ -997,10 +1027,12 @@ bool AppInit2() pindexRescan = pindexGenesisBlock; else { + int64_t nWalletLocatorStart = GetTimeMillis(); CWalletDB walletdb(strWalletFileName); CBlockLocator locator; if (walletdb.ReadBestBlock(locator)) pindexRescan = locator.GetBlockIndex(); + StartupPerfLog("wallet_bestblock_locator", GetTimeMillis() - nWalletLocatorStart); } if (pindexBest != pindexRescan && pindexBest && pindexRescan && pindexBest->nHeight > pindexRescan->nHeight) { @@ -1033,6 +1065,12 @@ bool AppInit2() pwalletMain->ScanForWalletTransactions(pindexRescan, true); printf(" rescan %15" PRId64 "ms\n", GetTimeMillis() - nStart); + StartupPerfLog("wallet_rescan", GetTimeMillis() - nStart, + strprintf("from=%d to=%d indexed=%d", pindexRescan->nHeight, pindexBest->nHeight, fScannedWithIndex)); + } + else + { + StartupPerfLog("wallet_rescan", 0, "skipped"); } // ********************************************************* Step 8.5: start Tor and initialize V3 identity @@ -1040,7 +1078,9 @@ bool AppInit2() uiInterface.InitMessage(_("Starting Tor...")); printf("Starting Tor process...\n"); + int64_t nTorStart = GetTimeMillis(); bool torStarted = StartEmbeddedTor(); + StartupPerfLog("tor_start", GetTimeMillis() - nTorStart, strprintf("started=%d", torStarted)); std::string torDataPath = CTorEmbedded::GetInstance()->GetDataDir(); if (torDataPath.empty()) torDataPath = (GetDataDir() / "tor_data").string(); @@ -1057,6 +1097,7 @@ bool AppInit2() uiInterface.InitMessage(_("Initializing Tor V3 identity...")); printf("Initializing Tor V3 onion identity...\n"); + int64_t nTorIdentityStart = GetTimeMillis(); LoadTorV3Config(); TorV3Config& torConfig = GetTorV3Config(); torConfig.enableTor = true; @@ -1085,6 +1126,7 @@ bool AppInit2() } else { printf("WARNING: Failed to initialize Tor V3 identity\n"); } + StartupPerfLog("tor_v3_identity", GetTimeMillis() - nTorIdentityStart); // Also check if Tor gave us a hidden service hostname if (torStarted) { @@ -1103,6 +1145,7 @@ bool AppInit2() } } } + StartupPerfLog("tor_setup_total", GetTimeMillis() - nTorStart); } // ********************************************************* Step 9: import blocks @@ -1113,9 +1156,11 @@ bool AppInit2() for (string strFile : mapMultiArgs["-loadblock"]) { + int64_t nLoadBlockStart = GetTimeMillis(); FILE *file = fopen(strFile.c_str(), "rb"); if (file) LoadExternalBlockFile(file); + StartupPerfLog("loadblock_import", GetTimeMillis() - nLoadBlockStart, strprintf("file=%s", strFile.c_str())); } exit(0); } @@ -1124,12 +1169,14 @@ bool AppInit2() if (fs::exists(pathBootstrap)) { uiInterface.InitMessage(_("Importing bootstrap blockchain data file.")); + int64_t nBootstrapImportStart = GetTimeMillis(); FILE *file = fopen(pathBootstrap.string().c_str(), "rb"); if (file) { fs::path pathBootstrapOld = GetDataDir() / "bootstrap.dat.old"; LoadExternalBlockFile(file); RenameOver(pathBootstrap, pathBootstrapOld); } + StartupPerfLog("bootstrap_dat_import", GetTimeMillis() - nBootstrapImportStart, strprintf("file=%s", pathBootstrap.string().c_str())); } // ********************************************************* Step 10: load peers @@ -1146,9 +1193,11 @@ bool AppInit2() printf("Loaded %i addresses from peers.dat %" PRId64 "ms\n", addrman.size(), GetTimeMillis() - nStart); + StartupPerfLog("peers_load", GetTimeMillis() - nStart, strprintf("count=%d", addrman.size())); // ********************************************************* Step 11: start node + nStart = GetTimeMillis(); if (!CheckDiskSpace()) return false; @@ -1177,6 +1226,7 @@ bool AppInit2() printf("Warning: deferred startup thread could not be started, running inline\n"); ThreadDeferredStartup(NULL); } + StartupPerfLog("start_services", GetTimeMillis() - nStart); // ********************************************************* Step 11.5: ZMQ notifications #ifdef ENABLE_ZMQ @@ -1215,6 +1265,7 @@ bool AppInit2() uiInterface.InitMessage(_("Done loading")); printf("Done loading\n"); + StartupPerfLog("appinit_total", GetTimeMillis() - nAppInitStart); if (!strErrors.str().empty()) return InitError(strErrors.str()); diff --git a/src/qt/trianglesgui.cpp b/src/qt/trianglesgui.cpp index ed7b6a2..7dd594a 100644 --- a/src/qt/trianglesgui.cpp +++ b/src/qt/trianglesgui.cpp @@ -107,7 +107,8 @@ TrianglesGUI::TrianglesGUI(bool fIsTestnet, QWidget *parent): notificator(0), rpcConsole(0), prevBlocks(0), - walletTransactionSyncing(false) + walletTransactionSyncing(false), + walletTransactionSyncPending(0) { ui->setupUi(this); @@ -594,6 +595,7 @@ void TrianglesGUI::setWalletModel(WalletModel *walletModel) setEncryptionStatus(walletModel->getEncryptionStatus()); connect(walletModel, SIGNAL(encryptionStatusChanged(int)), this, SLOT(setEncryptionStatus(int))); connect(walletModel, SIGNAL(transactionSyncStateChanged(bool)), this, SLOT(setWalletTransactionSyncState(bool))); + connect(walletModel, SIGNAL(transactionSyncProgressChanged(bool,int)), this, SLOT(setWalletTransactionSyncProgress(bool,int))); setWalletTransactionSyncState(walletModel->isTransactionSyncing()); // Balloon pop-up for new transaction @@ -769,6 +771,32 @@ void TrianglesGUI::restoreWindowGeometry() move(pos); } +void TrianglesGUI::refreshSyncStatusDisplay() +{ + if (clientModel) + { + setNumBlocks(clientModel->getNumBlocks(), clientModel->getNumBlocksOfPeers()); + return; + } + + if (!walletTransactionSyncing) + { + progressBarLabel->setVisible(false); + progressBar->setVisible(false); + ui->label_blocks->setVisible(false); + return; + } + + progressBarLabel->setText(walletTransactionSyncPending > 0 + ? tr("Updating wallet history... %n change(s) queued", "", walletTransactionSyncPending) + : tr("Updating wallet history...")); + progressBarLabel->setVisible(true); + progressBar->setRange(0, 0); + progressBar->setValue(0); + progressBar->setVisible(true); + ui->label_blocks->setVisible(false); +} + void TrianglesGUI::optionsClicked() { if(!clientModel || !clientModel->getOptionsModel()) @@ -806,19 +834,10 @@ void TrianglesGUI::setNumBlocks(int count, int nTotalBlocks) return; int nConnections = clientModel->getNumConnections(); - - // Hide progress bar when disconnected, but don't return early - - // we still need to update sync state and the out-of-sync warning - if (nConnections == 0) - { - progressBarLabel->setVisible(false); - progressBar->setVisible(false); - ui->label_blocks->setVisible(false); - } - + const bool blockSyncActive = nConnections > 0 && count < nTotalBlocks; QString tooltip; - if(nConnections > 0 && count < nTotalBlocks) + if(blockSyncActive) { // Calculate blocks/sec - only update rate when new blocks arrive static int lastCount = 0; @@ -870,10 +889,25 @@ void TrianglesGUI::setNumBlocks(int count, int nTotalBlocks) tooltip = tr("Downloaded %1 of %2 blocks of transaction history (%3% done).").arg(count).arg(nTotalBlocks).arg(nPercentageDone, 0, 'f', 2); } + else if (walletTransactionSyncing) + { + progressBarLabel->setText(walletTransactionSyncPending > 0 + ? tr("Updating wallet history... %n change(s) queued", "", walletTransactionSyncPending) + : tr("Updating wallet history...")); + progressBarLabel->setVisible(true); + progressBar->setRange(0, 0); + progressBar->setValue(0); + progressBar->setVisible(true); + ui->label_blocks->setVisible(false); + + tooltip = tr("Wallet history is catching up to recent transactions and stakes."); + tooltip += QString("
") + (walletTransactionSyncPending > 0 + ? tr("%n wallet update(s) are queued for the UI.", "", walletTransactionSyncPending) + : tr("Finalizing the latest wallet updates.")); + } else { progressBarLabel->setVisible(false); - progressBar->setVisible(false); ui->label_blocks->setVisible(false); tooltip = tr("Processed %1 blocks of transaction history.").arg(count); @@ -1078,6 +1112,16 @@ void TrianglesGUI::incomingTransaction(const QModelIndex & parent, int start, in void TrianglesGUI::setWalletTransactionSyncState(bool syncing) { walletTransactionSyncing = syncing; + if (!syncing) + walletTransactionSyncPending = 0; + refreshSyncStatusDisplay(); +} + +void TrianglesGUI::setWalletTransactionSyncProgress(bool syncing, int pendingNotifications) +{ + walletTransactionSyncing = syncing; + walletTransactionSyncPending = pendingNotifications; + refreshSyncStatusDisplay(); } void TrianglesGUI::incomingMessage(const QModelIndex & parent, int start, int end) diff --git a/src/qt/trianglesgui.h b/src/qt/trianglesgui.h index dcdedea..3e674d7 100644 --- a/src/qt/trianglesgui.h +++ b/src/qt/trianglesgui.h @@ -144,6 +144,7 @@ private: /** Keep track of previous number of blocks, to detect progress */ int prevBlocks; bool walletTransactionSyncing; + int walletTransactionSyncPending; /** Create the main UI actions. */ void createActions(bool fIsTestnet); @@ -159,6 +160,7 @@ private: void saveWindowGeometry(); /** Restore window size and position */ void restoreWindowGeometry(); + void refreshSyncStatusDisplay(); public slots: /** Set number of connections shown in the UI */ @@ -171,6 +173,7 @@ public slots: */ void setEncryptionStatus(int status); void setWalletTransactionSyncState(bool syncing); + void setWalletTransactionSyncProgress(bool syncing, int pendingNotifications); /** Notify the user of an error in the network or transaction handling code. */ void error(const QString &title, const QString &message, bool modal); diff --git a/src/qt/walletmodel.cpp b/src/qt/walletmodel.cpp index 636c04e..11ae465 100644 --- a/src/qt/walletmodel.cpp +++ b/src/qt/walletmodel.cpp @@ -232,23 +232,29 @@ void WalletModel::flushTransactionNotifications() transactionNotificationTimer->start(MODEL_FULL_REFRESH_MIN_INTERVAL_MS); bool stillPending = false; + int queuedNotificationCount = 0; { QMutexLocker locker(&transactionNotificationMutex); stillPending = transactionNotificationFlushQueued || !queuedTransactionNotifications.isEmpty(); + queuedNotificationCount = queuedTransactionNotifications.size(); } - if (!transactionSyncing && pendingNotifications.size() >= MODEL_UPDATE_BATCH_THRESHOLD) { transactionSyncing = true; emit transactionSyncStateChanged(true); } - if (transactionSyncing && !IsInitialBlockDownload() && !stillPending && pendingNotifications.size() < MODEL_UPDATE_BATCH_THRESHOLD) + if (transactionSyncing && !IsInitialBlockDownload() && !stillPending) { transactionSyncing = false; emit transactionSyncStateChanged(false); } + const int pendingNotificationCount = transactionSyncing + ? pendingNotifications.size() + queuedNotificationCount + : queuedNotificationCount; + emit transactionSyncProgressChanged(transactionSyncing, pendingNotificationCount); + // Don't call checkBalanceChanged() here - it does LOCK(cs_wallet) + iterates // all wallet transactions, blocking the UI thread. The pollBalanceChanged() // timer already handles balance updates every 2.5 seconds with TRY_LOCK. diff --git a/src/qt/walletmodel.h b/src/qt/walletmodel.h index 17d233a..a606cba 100644 --- a/src/qt/walletmodel.h +++ b/src/qt/walletmodel.h @@ -192,6 +192,7 @@ signals: // Asynchronous error notification void error(const QString &title, const QString &message, bool modal); void transactionSyncStateChanged(bool syncing); + void transactionSyncProgressChanged(bool syncing, int pendingNotifications); private: QMutex transactionNotificationMutex;