LCOV - code coverage report
Current view: top level - src - addrman.cpp (source / functions) Hit Total Coverage
Test: coverage.lcov Lines: 461 525 87.8 %
Date: 2021-06-29 14:35:33 Functions: 23 23 100.0 %
Legend: Modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed

Not modified by patch:
Lines: hit not hit | Branches: + taken - not taken # not executed
Branches: 211 296 71.3 %

           Branch data     Line data    Source code
#       1                 :            : // Copyright (c) 2012 Pieter Wuille
#       2                 :            : // Copyright (c) 2012-2020 The Bitcoin Core developers
#       3                 :            : // Distributed under the MIT software license, see the accompanying
#       4                 :            : // file COPYING or http://www.opensource.org/licenses/mit-license.php.
#       5                 :            : 
#       6                 :            : #include <addrman.h>
#       7                 :            : 
#       8                 :            : #include <hash.h>
#       9                 :            : #include <i2p.h>
#      10                 :            : #include <logging.h>
#      11                 :            : #include <netaddress.h>
#      12                 :            : #include <serialize.h>
#      13                 :            : 
#      14                 :            : #include <cmath>
#      15                 :            : #include <optional>
#      16                 :            : 
#      17                 :            : int CAddrInfo::GetTriedBucket(const uint256& nKey, const std::vector<bool> &asmap) const
#      18                 :    1181214 : {
#      19                 :    1181214 :     uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetKey()).GetCheapHash();
#      20                 :    1181214 :     uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << (hash1 % ADDRMAN_TRIED_BUCKETS_PER_GROUP)).GetCheapHash();
#      21                 :    1181214 :     int tried_bucket = hash2 % ADDRMAN_TRIED_BUCKET_COUNT;
#      22                 :    1181214 :     uint32_t mapped_as = GetMappedAS(asmap);
#      23         [ +  - ]:    1181214 :     LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to tried bucket %i\n", ToStringIP(), mapped_as, tried_bucket);
#      24                 :    1181214 :     return tried_bucket;
#      25                 :    1181214 : }
#      26                 :            : 
#      27                 :            : int CAddrInfo::GetNewBucket(const uint256& nKey, const CNetAddr& src, const std::vector<bool> &asmap) const
#      28                 :      30891 : {
#      29                 :      30891 :     std::vector<unsigned char> vchSourceGroupKey = src.GetGroup(asmap);
#      30                 :      30891 :     uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << GetGroup(asmap) << vchSourceGroupKey).GetCheapHash();
#      31                 :      30891 :     uint64_t hash2 = (CHashWriter(SER_GETHASH, 0) << nKey << vchSourceGroupKey << (hash1 % ADDRMAN_NEW_BUCKETS_PER_SOURCE_GROUP)).GetCheapHash();
#      32                 :      30891 :     int new_bucket = hash2 % ADDRMAN_NEW_BUCKET_COUNT;
#      33                 :      30891 :     uint32_t mapped_as = GetMappedAS(asmap);
#      34         [ +  - ]:      30891 :     LogPrint(BCLog::NET, "IP %s mapped to AS%i belongs to new bucket %i\n", ToStringIP(), mapped_as, new_bucket);
#      35                 :      30891 :     return new_bucket;
#      36                 :      30891 : }
#      37                 :            : 
#      38                 :            : int CAddrInfo::GetBucketPosition(const uint256 &nKey, bool fNew, int nBucket) const
#      39                 :  286835690 : {
#      40         [ +  + ]:  286835690 :     uint64_t hash1 = (CHashWriter(SER_GETHASH, 0) << nKey << (fNew ? 'N' : 'K') << nBucket << GetKey()).GetCheapHash();
#      41                 :  286835690 :     return hash1 % ADDRMAN_BUCKET_SIZE;
#      42                 :  286835690 : }
#      43                 :            : 
#      44                 :            : bool CAddrInfo::IsTerrible(int64_t nNow) const
#      45                 :      22180 : {
#      46 [ +  + ][ +  + ]:      22180 :     if (nLastTry && nLastTry >= nNow - 60) // never remove things tried in the last minute
#      47                 :         99 :         return false;
#      48                 :            : 
#      49         [ -  + ]:      22081 :     if (nTime > nNow + 10 * 60) // came in a flying DeLorean
#      50                 :          0 :         return true;
#      51                 :            : 
#      52 [ -  + ][ +  + ]:      22081 :     if (nTime == 0 || nNow - nTime > ADDRMAN_HORIZON_DAYS * 24 * 60 * 60) // not seen in recent history
#      53                 :          2 :         return true;
#      54                 :            : 
#      55 [ +  + ][ -  + ]:      22079 :     if (nLastSuccess == 0 && nAttempts >= ADDRMAN_RETRIES) // tried N times and never a success
#      56                 :      22079 :         return true;
#      57                 :            : 
#      58 [ +  + ][ -  + ]:      22079 :     if (nNow - nLastSuccess > ADDRMAN_MIN_FAIL_DAYS * 24 * 60 * 60 && nAttempts >= ADDRMAN_MAX_FAILURES) // N successive failures in the last week
#      59                 :      22079 :         return true;
#      60                 :            : 
#      61                 :      22079 :     return false;
#      62                 :      22079 : }
#      63                 :            : 
#      64                 :            : double CAddrInfo::GetChance(int64_t nNow) const
#      65                 :      13114 : {
#      66                 :      13114 :     double fChance = 1.0;
#      67                 :      13114 :     int64_t nSinceLastTry = std::max<int64_t>(nNow - nLastTry, 0);
#      68                 :            : 
#      69                 :            :     // deprioritize very recent attempts away
#      70         [ +  + ]:      13114 :     if (nSinceLastTry < 60 * 10)
#      71                 :        434 :         fChance *= 0.01;
#      72                 :            : 
#      73                 :            :     // deprioritize 66% after each failed attempt, but at most 1/28th to avoid the search taking forever or overly penalizing outages.
#      74                 :      13114 :     fChance *= pow(0.66, std::min(nAttempts, 8));
#      75                 :            : 
#      76                 :      13114 :     return fChance;
#      77                 :      13114 : }
#      78                 :            : 
#      79                 :            : CAddrInfo* CAddrMan::Find(const CNetAddr& addr, int* pnId)
#      80                 :      26740 : {
#      81                 :      26740 :     std::map<CNetAddr, int>::iterator it = mapAddr.find(addr);
#      82         [ +  + ]:      26740 :     if (it == mapAddr.end())
#      83                 :      25653 :         return nullptr;
#      84         [ +  + ]:       1087 :     if (pnId)
#      85                 :        895 :         *pnId = (*it).second;
#      86                 :       1087 :     std::map<int, CAddrInfo>::iterator it2 = mapInfo.find((*it).second);
#      87         [ +  - ]:       1087 :     if (it2 != mapInfo.end())
#      88                 :       1087 :         return &(*it2).second;
#      89                 :          0 :     return nullptr;
#      90                 :          0 : }
#      91                 :            : 
#      92                 :            : CAddrInfo* CAddrMan::Create(const CAddress& addr, const CNetAddr& addrSource, int* pnId)
#      93                 :      24231 : {
#      94                 :      24231 :     int nId = nIdCount++;
#      95                 :      24231 :     mapInfo[nId] = CAddrInfo(addr, addrSource);
#      96                 :      24231 :     mapAddr[addr] = nId;
#      97                 :      24231 :     mapInfo[nId].nRandomPos = vRandom.size();
#      98                 :      24231 :     vRandom.push_back(nId);
#      99         [ +  - ]:      24231 :     if (pnId)
#     100                 :      24231 :         *pnId = nId;
#     101                 :      24231 :     return &mapInfo[nId];
#     102                 :      24231 : }
#     103                 :            : 
#     104                 :            : void CAddrMan::SwapRandom(unsigned int nRndPos1, unsigned int nRndPos2)
#     105                 :      48620 : {
#     106         [ +  + ]:      48620 :     if (nRndPos1 == nRndPos2)
#     107                 :       1462 :         return;
#     108                 :            : 
#     109                 :      47158 :     assert(nRndPos1 < vRandom.size() && nRndPos2 < vRandom.size());
#     110                 :            : 
#     111                 :      47158 :     int nId1 = vRandom[nRndPos1];
#     112                 :      47158 :     int nId2 = vRandom[nRndPos2];
#     113                 :            : 
#     114                 :      47158 :     assert(mapInfo.count(nId1) == 1);
#     115                 :      47158 :     assert(mapInfo.count(nId2) == 1);
#     116                 :            : 
#     117                 :      47158 :     mapInfo[nId1].nRandomPos = nRndPos2;
#     118                 :      47158 :     mapInfo[nId2].nRandomPos = nRndPos1;
#     119                 :            : 
#     120                 :      47158 :     vRandom[nRndPos1] = nId2;
#     121                 :      47158 :     vRandom[nRndPos2] = nId1;
#     122                 :      47158 : }
#     123                 :            : 
#     124                 :            : void CAddrMan::Delete(int nId)
#     125                 :       1394 : {
#     126                 :       1394 :     assert(mapInfo.count(nId) != 0);
#     127                 :       1394 :     CAddrInfo& info = mapInfo[nId];
#     128                 :       1394 :     assert(!info.fInTried);
#     129                 :       1394 :     assert(info.nRefCount == 0);
#     130                 :            : 
#     131                 :       1394 :     SwapRandom(info.nRandomPos, vRandom.size() - 1);
#     132                 :       1394 :     vRandom.pop_back();
#     133                 :       1394 :     mapAddr.erase(info);
#     134                 :       1394 :     mapInfo.erase(nId);
#     135                 :       1394 :     nNew--;
#     136                 :       1394 : }
#     137                 :            : 
#     138                 :            : void CAddrMan::ClearNew(int nUBucket, int nUBucketPos)
#     139                 :      22843 : {
#     140                 :            :     // if there is an entry in the specified bucket, delete it.
#     141         [ +  + ]:      22843 :     if (vvNew[nUBucket][nUBucketPos] != -1) {
#     142                 :          4 :         int nIdDelete = vvNew[nUBucket][nUBucketPos];
#     143                 :          4 :         CAddrInfo& infoDelete = mapInfo[nIdDelete];
#     144                 :          4 :         assert(infoDelete.nRefCount > 0);
#     145                 :          4 :         infoDelete.nRefCount--;
#     146                 :          4 :         vvNew[nUBucket][nUBucketPos] = -1;
#     147         [ +  - ]:          4 :         if (infoDelete.nRefCount == 0) {
#     148                 :          4 :             Delete(nIdDelete);
#     149                 :          4 :         }
#     150                 :          4 :     }
#     151                 :      22843 : }
#     152                 :            : 
#     153                 :            : void CAddrMan::MakeTried(CAddrInfo& info, int nId)
#     154                 :        802 : {
#     155                 :            :     // remove the entry from all new buckets
#     156         [ +  + ]:     822050 :     for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; bucket++) {
#     157                 :     821248 :         int pos = info.GetBucketPosition(nKey, true, bucket);
#     158         [ +  + ]:     821248 :         if (vvNew[bucket][pos] == nId) {
#     159                 :        802 :             vvNew[bucket][pos] = -1;
#     160                 :        802 :             info.nRefCount--;
#     161                 :        802 :         }
#     162                 :     821248 :     }
#     163                 :        802 :     nNew--;
#     164                 :            : 
#     165                 :        802 :     assert(info.nRefCount == 0);
#     166                 :            : 
#     167                 :            :     // which tried bucket to move the entry to
#     168                 :        802 :     int nKBucket = info.GetTriedBucket(nKey, m_asmap);
#     169                 :        802 :     int nKBucketPos = info.GetBucketPosition(nKey, false, nKBucket);
#     170                 :            : 
#     171                 :            :     // first make space to add it (the existing tried entry there is moved to new, deleting whatever is there).
#     172         [ +  + ]:        802 :     if (vvTried[nKBucket][nKBucketPos] != -1) {
#     173                 :            :         // find an item to evict
#     174                 :          2 :         int nIdEvict = vvTried[nKBucket][nKBucketPos];
#     175                 :          2 :         assert(mapInfo.count(nIdEvict) == 1);
#     176                 :          2 :         CAddrInfo& infoOld = mapInfo[nIdEvict];
#     177                 :            : 
#     178                 :            :         // Remove the to-be-evicted item from the tried set.
#     179                 :          2 :         infoOld.fInTried = false;
#     180                 :          2 :         vvTried[nKBucket][nKBucketPos] = -1;
#     181                 :          2 :         nTried--;
#     182                 :            : 
#     183                 :            :         // find which new bucket it belongs to
#     184                 :          2 :         int nUBucket = infoOld.GetNewBucket(nKey, m_asmap);
#     185                 :          2 :         int nUBucketPos = infoOld.GetBucketPosition(nKey, true, nUBucket);
#     186                 :          2 :         ClearNew(nUBucket, nUBucketPos);
#     187                 :          2 :         assert(vvNew[nUBucket][nUBucketPos] == -1);
#     188                 :            : 
#     189                 :            :         // Enter it into the new set again.
#     190                 :          2 :         infoOld.nRefCount = 1;
#     191                 :          2 :         vvNew[nUBucket][nUBucketPos] = nIdEvict;
#     192                 :          2 :         nNew++;
#     193                 :          2 :     }
#     194                 :        802 :     assert(vvTried[nKBucket][nKBucketPos] == -1);
#     195                 :            : 
#     196                 :        802 :     vvTried[nKBucket][nKBucketPos] = nId;
#     197                 :        802 :     nTried++;
#     198                 :        802 :     info.fInTried = true;
#     199                 :        802 : }
#     200                 :            : 
#     201                 :            : void CAddrMan::Good_(const CService& addr, bool test_before_evict, int64_t nTime)
#     202                 :       1248 : {
#     203                 :       1248 :     int nId;
#     204                 :            : 
#     205                 :       1248 :     nLastGood = nTime;
#     206                 :            : 
#     207                 :       1248 :     CAddrInfo* pinfo = Find(addr, &nId);
#     208                 :            : 
#     209                 :            :     // if not found, bail out
#     210         [ +  + ]:       1248 :     if (!pinfo)
#     211                 :        366 :         return;
#     212                 :            : 
#     213                 :        882 :     CAddrInfo& info = *pinfo;
#     214                 :            : 
#     215                 :            :     // check whether we are talking about the exact same CService (including same port)
#     216         [ +  + ]:        882 :     if (info != addr)
#     217                 :          2 :         return;
#     218                 :            : 
#     219                 :            :     // update info
#     220                 :        880 :     info.nLastSuccess = nTime;
#     221                 :        880 :     info.nLastTry = nTime;
#     222                 :        880 :     info.nAttempts = 0;
#     223                 :            :     // nTime is not updated here, to avoid leaking information about
#     224                 :            :     // currently-connected peers.
#     225                 :            : 
#     226                 :            :     // if it is already in the tried set, don't do anything else
#     227         [ +  + ]:        880 :     if (info.fInTried)
#     228                 :         48 :         return;
#     229                 :            : 
#     230                 :            :     // find a bucket it is in now
#     231                 :        832 :     int nRnd = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
#     232                 :        832 :     int nUBucket = -1;
#     233         [ +  - ]:     434108 :     for (unsigned int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
#     234                 :     434108 :         int nB = (n + nRnd) % ADDRMAN_NEW_BUCKET_COUNT;
#     235                 :     434108 :         int nBpos = info.GetBucketPosition(nKey, true, nB);
#     236         [ +  + ]:     434108 :         if (vvNew[nB][nBpos] == nId) {
#     237                 :        832 :             nUBucket = nB;
#     238                 :        832 :             break;
#     239                 :        832 :         }
#     240                 :     434108 :     }
#     241                 :            : 
#     242                 :            :     // if no bucket is found, something bad happened;
#     243                 :            :     // TODO: maybe re-add the node, but for now, just bail out
#     244         [ -  + ]:        832 :     if (nUBucket == -1)
#     245                 :          0 :         return;
#     246                 :            : 
#     247                 :            :     // which tried bucket to move the entry to
#     248                 :        832 :     int tried_bucket = info.GetTriedBucket(nKey, m_asmap);
#     249                 :        832 :     int tried_bucket_pos = info.GetBucketPosition(nKey, false, tried_bucket);
#     250                 :            : 
#     251                 :            :     // Will moving this address into tried evict another entry?
#     252 [ +  + ][ +  + ]:        832 :     if (test_before_evict && (vvTried[tried_bucket][tried_bucket_pos] != -1)) {
#     253                 :            :         // Output the entry we'd be colliding with, for debugging purposes
#     254                 :         30 :         auto colliding_entry = mapInfo.find(vvTried[tried_bucket][tried_bucket_pos]);
#     255 [ +  - ][ +  - ]:         30 :         LogPrint(BCLog::ADDRMAN, "Collision inserting element into tried table (%s), moving %s to m_tried_collisions=%d\n", colliding_entry != mapInfo.end() ? colliding_entry->second.ToString() : "", addr.ToString(), m_tried_collisions.size());
#     256         [ +  - ]:         30 :         if (m_tried_collisions.size() < ADDRMAN_SET_TRIED_COLLISION_SIZE) {
#     257                 :         30 :             m_tried_collisions.insert(nId);
#     258                 :         30 :         }
#     259                 :        802 :     } else {
#     260         [ +  - ]:        802 :         LogPrint(BCLog::ADDRMAN, "Moving %s to tried\n", addr.ToString());
#     261                 :            : 
#     262                 :            :         // move nId to the tried tables
#     263                 :        802 :         MakeTried(info, nId);
#     264                 :        802 :     }
#     265                 :        832 : }
#     266                 :            : 
#     267                 :            : bool CAddrMan::Add_(const CAddress& addr, const CNetAddr& source, int64_t nTimePenalty)
#     268                 :      25569 : {
#     269         [ +  + ]:      25569 :     if (!addr.IsRoutable())
#     270                 :       1329 :         return false;
#     271                 :            : 
#     272                 :      24240 :     bool fNew = false;
#     273                 :      24240 :     int nId;
#     274                 :      24240 :     CAddrInfo* pinfo = Find(addr, &nId);
#     275                 :            : 
#     276                 :            :     // Do not set a penalty for a source's self-announcement
#     277         [ +  + ]:      24240 :     if (addr == source) {
#     278                 :      23775 :         nTimePenalty = 0;
#     279                 :      23775 :     }
#     280                 :            : 
#     281         [ +  + ]:      24240 :     if (pinfo) {
#     282                 :            :         // periodically update nTime
#     283                 :         13 :         bool fCurrentlyOnline = (GetAdjustedTime() - addr.nTime < 24 * 60 * 60);
#     284         [ +  + ]:         13 :         int64_t nUpdateInterval = (fCurrentlyOnline ? 60 * 60 : 24 * 60 * 60);
#     285 [ +  - ][ -  + ]:         13 :         if (addr.nTime && (!pinfo->nTime || pinfo->nTime < addr.nTime - nUpdateInterval - nTimePenalty))
#                 [ -  + ]
#     286                 :          0 :             pinfo->nTime = std::max((int64_t)0, addr.nTime - nTimePenalty);
#     287                 :            : 
#     288                 :            :         // add services
#     289                 :         13 :         pinfo->nServices = ServiceFlags(pinfo->nServices | addr.nServices);
#     290                 :            : 
#     291                 :            :         // do not update if no new information is present
#     292 [ -  + ][ +  - ]:         13 :         if (!addr.nTime || (pinfo->nTime && addr.nTime <= pinfo->nTime))
#                 [ +  - ]
#     293                 :         13 :             return false;
#     294                 :            : 
#     295                 :            :         // do not update if the entry was already in the "tried" table
#     296         [ #  # ]:          0 :         if (pinfo->fInTried)
#     297                 :          0 :             return false;
#     298                 :            : 
#     299                 :            :         // do not update if the max reference count is reached
#     300         [ #  # ]:          0 :         if (pinfo->nRefCount == ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
#     301                 :          0 :             return false;
#     302                 :            : 
#     303                 :            :         // stochastic test: previous nRefCount == N: 2^N times harder to increase it
#     304                 :          0 :         int nFactor = 1;
#     305         [ #  # ]:          0 :         for (int n = 0; n < pinfo->nRefCount; n++)
#     306                 :          0 :             nFactor *= 2;
#     307 [ #  # ][ #  # ]:          0 :         if (nFactor > 1 && (insecure_rand.randrange(nFactor) != 0))
#     308                 :          0 :             return false;
#     309                 :      24227 :     } else {
#     310                 :      24227 :         pinfo = Create(addr, source, &nId);
#     311                 :      24227 :         pinfo->nTime = std::max((int64_t)0, (int64_t)pinfo->nTime - nTimePenalty);
#     312                 :      24227 :         nNew++;
#     313                 :      24227 :         fNew = true;
#     314                 :      24227 :     }
#     315                 :            : 
#     316                 :      24240 :     int nUBucket = pinfo->GetNewBucket(nKey, source, m_asmap);
#     317                 :      24227 :     int nUBucketPos = pinfo->GetBucketPosition(nKey, true, nUBucket);
#     318         [ +  - ]:      24227 :     if (vvNew[nUBucket][nUBucketPos] != nId) {
#     319                 :      24227 :         bool fInsert = vvNew[nUBucket][nUBucketPos] == -1;
#     320         [ +  + ]:      24227 :         if (!fInsert) {
#     321                 :       1390 :             CAddrInfo& infoExisting = mapInfo[vvNew[nUBucket][nUBucketPos]];
#     322 [ +  + ][ -  + ]:       1390 :             if (infoExisting.IsTerrible() || (infoExisting.nRefCount > 1 && pinfo->nRefCount == 0)) {
#                 [ #  # ]
#     323                 :            :                 // Overwrite the existing new table entry.
#     324                 :          2 :                 fInsert = true;
#     325                 :          2 :             }
#     326                 :       1390 :         }
#     327         [ +  + ]:      24227 :         if (fInsert) {
#     328                 :      22839 :             ClearNew(nUBucket, nUBucketPos);
#     329                 :      22839 :             pinfo->nRefCount++;
#     330                 :      22839 :             vvNew[nUBucket][nUBucketPos] = nId;
#     331                 :      22839 :         } else {
#     332         [ +  - ]:       1388 :             if (pinfo->nRefCount == 0) {
#     333                 :       1388 :                 Delete(nId);
#     334                 :       1388 :             }
#     335                 :       1388 :         }
#     336                 :      24227 :     }
#     337                 :      24227 :     return fNew;
#     338                 :      24240 : }
#     339                 :            : 
#     340                 :            : void CAddrMan::Attempt_(const CService& addr, bool fCountFailure, int64_t nTime)
#     341                 :        546 : {
#     342                 :        546 :     CAddrInfo* pinfo = Find(addr);
#     343                 :            : 
#     344                 :            :     // if not found, bail out
#     345         [ +  + ]:        546 :     if (!pinfo)
#     346                 :        362 :         return;
#     347                 :            : 
#     348                 :        184 :     CAddrInfo& info = *pinfo;
#     349                 :            : 
#     350                 :            :     // check whether we are talking about the exact same CService (including same port)
#     351         [ -  + ]:        184 :     if (info != addr)
#     352                 :          0 :         return;
#     353                 :            : 
#     354                 :            :     // update info
#     355                 :        184 :     info.nLastTry = nTime;
#     356 [ -  + ][ #  # ]:        184 :     if (fCountFailure && info.nLastCountAttempt < nLastGood) {
#     357                 :          0 :         info.nLastCountAttempt = nTime;
#     358                 :          0 :         info.nAttempts++;
#     359                 :          0 :     }
#     360                 :        184 : }
#     361                 :            : 
#     362                 :            : CAddrInfo CAddrMan::Select_(bool newOnly)
#     363                 :      31338 : {
#     364         [ +  + ]:      31338 :     if (size() == 0)
#     365                 :      18632 :         return CAddrInfo();
#     366                 :            : 
#     367 [ +  + ][ +  + ]:      12706 :     if (newOnly && nNew == 0)
#     368                 :          2 :         return CAddrInfo();
#     369                 :            : 
#     370                 :            :     // Use a 50% chance for choosing between tried and new table entries.
#     371         [ +  + ]:      12704 :     if (!newOnly &&
#     372 [ +  + ][ +  + ]:      12704 :        (nTried > 0 && (nNew == 0 || insecure_rand.randbool() == 0))) {
#                 [ +  + ]
#     373                 :            :         // use a tried node
#     374                 :         24 :         double fChanceFactor = 1.0;
#     375                 :        378 :         while (1) {
#     376                 :        378 :             int nKBucket = insecure_rand.randrange(ADDRMAN_TRIED_BUCKET_COUNT);
#     377                 :        378 :             int nKBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
#     378         [ +  + ]:    1575728 :             while (vvTried[nKBucket][nKBucketPos] == -1) {
#     379                 :    1575350 :                 nKBucket = (nKBucket + insecure_rand.randbits(ADDRMAN_TRIED_BUCKET_COUNT_LOG2)) % ADDRMAN_TRIED_BUCKET_COUNT;
#     380                 :    1575350 :                 nKBucketPos = (nKBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
#     381                 :    1575350 :             }
#     382                 :        378 :             int nId = vvTried[nKBucket][nKBucketPos];
#     383                 :        378 :             assert(mapInfo.count(nId) == 1);
#     384                 :        378 :             CAddrInfo& info = mapInfo[nId];
#     385         [ +  + ]:        378 :             if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
#     386                 :         24 :                 return info;
#     387                 :        354 :             fChanceFactor *= 1.2;
#     388                 :        354 :         }
#     389                 :      12680 :     } else {
#     390                 :            :         // use a new node
#     391                 :      12680 :         double fChanceFactor = 1.0;
#     392                 :      12736 :         while (1) {
#     393                 :      12736 :             int nUBucket = insecure_rand.randrange(ADDRMAN_NEW_BUCKET_COUNT);
#     394                 :      12736 :             int nUBucketPos = insecure_rand.randrange(ADDRMAN_BUCKET_SIZE);
#     395         [ +  + ]:  229325778 :             while (vvNew[nUBucket][nUBucketPos] == -1) {
#     396                 :  229313042 :                 nUBucket = (nUBucket + insecure_rand.randbits(ADDRMAN_NEW_BUCKET_COUNT_LOG2)) % ADDRMAN_NEW_BUCKET_COUNT;
#     397                 :  229313042 :                 nUBucketPos = (nUBucketPos + insecure_rand.randbits(ADDRMAN_BUCKET_SIZE_LOG2)) % ADDRMAN_BUCKET_SIZE;
#     398                 :  229313042 :             }
#     399                 :      12736 :             int nId = vvNew[nUBucket][nUBucketPos];
#     400                 :      12736 :             assert(mapInfo.count(nId) == 1);
#     401                 :      12736 :             CAddrInfo& info = mapInfo[nId];
#     402         [ +  + ]:      12736 :             if (insecure_rand.randbits(30) < fChanceFactor * info.GetChance() * (1 << 30))
#     403                 :      12680 :                 return info;
#     404                 :         56 :             fChanceFactor *= 1.2;
#     405                 :         56 :         }
#     406                 :      12680 :     }
#     407                 :      12704 : }
#     408                 :            : 
#     409                 :            : #ifdef DEBUG_ADDRMAN
#     410                 :            : int CAddrMan::Check_()
#     411                 :     157667 : {
#     412                 :     157667 :     std::set<int> setTried;
#     413                 :     157667 :     std::map<int, int> mapNew;
#     414                 :            : 
#     415         [ -  + ]:     157667 :     if (vRandom.size() != (size_t)(nTried + nNew))
#     416                 :          0 :         return -7;
#     417                 :            : 
#     418         [ +  + ]:  285553633 :     for (const auto& entry : mapInfo) {
#     419                 :  285553633 :         int n = entry.first;
#     420                 :  285553633 :         const CAddrInfo& info = entry.second;
#     421         [ +  + ]:  285553633 :         if (info.fInTried) {
#     422         [ -  + ]:    1177484 :             if (!info.nLastSuccess)
#     423                 :          0 :                 return -1;
#     424         [ -  + ]:    1177484 :             if (info.nRefCount)
#     425                 :          0 :                 return -2;
#     426                 :    1177484 :             setTried.insert(n);
#     427                 :  284376149 :         } else {
#     428 [ -  + ][ -  + ]:  284376149 :             if (info.nRefCount < 0 || info.nRefCount > ADDRMAN_NEW_BUCKETS_PER_ADDRESS)
#     429                 :  284376149 :                 return -3;
#     430         [ -  + ]:  284376149 :             if (!info.nRefCount)
#     431                 :          0 :                 return -4;
#     432                 :  284376149 :             mapNew[n] = info.nRefCount;
#     433                 :  284376149 :         }
#     434         [ -  + ]:  285553633 :         if (mapAddr[info] != n)
#     435                 :          0 :             return -5;
#     436 [ -  + ][ -  + ]:  285553633 :         if (info.nRandomPos < 0 || (size_t)info.nRandomPos >= vRandom.size() || vRandom[info.nRandomPos] != n)
#                 [ -  + ]
#     437                 :          0 :             return -14;
#     438         [ -  + ]:  285553633 :         if (info.nLastTry < 0)
#     439                 :          0 :             return -6;
#     440         [ -  + ]:  285553633 :         if (info.nLastSuccess < 0)
#     441                 :          0 :             return -8;
#     442                 :  285553633 :     }
#     443                 :            : 
#     444         [ -  + ]:     157667 :     if (setTried.size() != (size_t)nTried)
#     445                 :          0 :         return -9;
#     446         [ -  + ]:     157667 :     if (mapNew.size() != (size_t)nNew)
#     447                 :          0 :         return -10;
#     448                 :            : 
#     449         [ +  + ]:   40520419 :     for (int n = 0; n < ADDRMAN_TRIED_BUCKET_COUNT; n++) {
#     450         [ +  + ]: 2623578880 :         for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
#     451         [ +  + ]: 2583216128 :              if (vvTried[n][i] != -1) {
#     452         [ -  + ]:    1177484 :                  if (!setTried.count(vvTried[n][i]))
#     453                 :          0 :                      return -11;
#     454         [ -  + ]:    1177484 :                  if (mapInfo[vvTried[n][i]].GetTriedBucket(nKey, m_asmap) != n)
#     455                 :          0 :                      return -17;
#     456         [ -  + ]:    1177484 :                  if (mapInfo[vvTried[n][i]].GetBucketPosition(nKey, false, n) != i)
#     457                 :          0 :                      return -18;
#     458                 :    1177484 :                  setTried.erase(vvTried[n][i]);
#     459                 :    1177484 :              }
#     460                 : 2583216128 :         }
#     461                 :   40362752 :     }
#     462                 :            : 
#     463         [ +  + ]:  161608675 :     for (int n = 0; n < ADDRMAN_NEW_BUCKET_COUNT; n++) {
#     464         [ +  + ]:10494315520 :         for (int i = 0; i < ADDRMAN_BUCKET_SIZE; i++) {
#     465         [ +  + ]:10332864512 :             if (vvNew[n][i] != -1) {
#     466         [ -  + ]:  284376149 :                 if (!mapNew.count(vvNew[n][i]))
#     467                 :          0 :                     return -12;
#     468         [ -  + ]:  284376149 :                 if (mapInfo[vvNew[n][i]].GetBucketPosition(nKey, true, n) != i)
#     469                 :          0 :                     return -19;
#     470         [ +  - ]:  284376149 :                 if (--mapNew[vvNew[n][i]] == 0)
#     471                 :  284376149 :                     mapNew.erase(vvNew[n][i]);
#     472                 :  284376149 :             }
#     473                 :10332864512 :         }
#     474                 :  161451008 :     }
#     475                 :            : 
#     476         [ -  + ]:     157667 :     if (setTried.size())
#     477                 :          0 :         return -13;
#     478         [ -  + ]:     157667 :     if (mapNew.size())
#     479                 :          0 :         return -15;
#     480         [ +  + ]:     157667 :     if (nKey.IsNull())
#     481                 :      11474 :         return -16;
#     482                 :            : 
#     483                 :     146193 :     return 0;
#     484                 :     146193 : }
#     485                 :            : #endif
#     486                 :            : 
#     487                 :            : void CAddrMan::GetAddr_(std::vector<CAddress>& vAddr, size_t max_addresses, size_t max_pct, std::optional<Network> network)
#     488                 :        257 : {
#     489                 :        257 :     size_t nNodes = vRandom.size();
#     490         [ +  + ]:        257 :     if (max_pct != 0) {
#     491                 :        236 :         nNodes = max_pct * nNodes / 100;
#     492                 :        236 :     }
#     493         [ +  + ]:        257 :     if (max_addresses != 0) {
#     494                 :        239 :         nNodes = std::min(nNodes, max_addresses);
#     495                 :        239 :     }
#     496                 :            : 
#     497                 :            :     // gather a list of random nodes, skipping those of low quality
#     498                 :        257 :     const int64_t now{GetAdjustedTime()};
#     499         [ +  + ]:      47481 :     for (unsigned int n = 0; n < vRandom.size(); n++) {
#     500         [ +  + ]:      47236 :         if (vAddr.size() >= nNodes)
#     501                 :         12 :             break;
#     502                 :            : 
#     503                 :      47224 :         int nRndPos = insecure_rand.randrange(vRandom.size() - n) + n;
#     504                 :      47224 :         SwapRandom(n, nRndPos);
#     505                 :      47224 :         assert(mapInfo.count(vRandom[n]) == 1);
#     506                 :            : 
#     507                 :      47224 :         const CAddrInfo& ai = mapInfo[vRandom[n]];
#     508                 :            : 
#     509                 :            :         // Filter by network (optional)
#     510 [ +  + ][ +  + ]:      47224 :         if (network != std::nullopt && ai.GetNetClass() != network) continue;
#                 [ +  + ]
#     511                 :            : 
#     512                 :            :         // Filter for quality
#     513         [ -  + ]:      20790 :         if (ai.IsTerrible(now)) continue;
#     514                 :            : 
#     515                 :      20790 :         vAddr.push_back(ai);
#     516                 :      20790 :     }
#     517                 :        257 : }
#     518                 :            : 
#     519                 :            : void CAddrMan::Connected_(const CService& addr, int64_t nTime)
#     520                 :        350 : {
#     521                 :        350 :     CAddrInfo* pinfo = Find(addr);
#     522                 :            : 
#     523                 :            :     // if not found, bail out
#     524         [ +  - ]:        350 :     if (!pinfo)
#     525                 :        350 :         return;
#     526                 :            : 
#     527                 :          0 :     CAddrInfo& info = *pinfo;
#     528                 :            : 
#     529                 :            :     // check whether we are talking about the exact same CService (including same port)
#     530         [ #  # ]:          0 :     if (info != addr)
#     531                 :          0 :         return;
#     532                 :            : 
#     533                 :            :     // update info
#     534                 :          0 :     int64_t nUpdateInterval = 20 * 60;
#     535         [ #  # ]:          0 :     if (nTime - info.nTime > nUpdateInterval)
#     536                 :          0 :         info.nTime = nTime;
#     537                 :          0 : }
#     538                 :            : 
#     539                 :            : void CAddrMan::SetServices_(const CService& addr, ServiceFlags nServices)
#     540                 :        346 : {
#     541                 :        346 :     CAddrInfo* pinfo = Find(addr);
#     542                 :            : 
#     543                 :            :     // if not found, bail out
#     544         [ +  - ]:        346 :     if (!pinfo)
#     545                 :        346 :         return;
#     546                 :            : 
#     547                 :          0 :     CAddrInfo& info = *pinfo;
#     548                 :            : 
#     549                 :            :     // check whether we are talking about the exact same CService (including same port)
#     550         [ #  # ]:          0 :     if (info != addr)
#     551                 :          0 :         return;
#     552                 :            : 
#     553                 :            :     // update info
#     554                 :          0 :     info.nServices = nServices;
#     555                 :          0 : }
#     556                 :            : 
#     557                 :            : void CAddrMan::ResolveCollisions_()
#     558                 :      18856 : {
#     559         [ +  + ]:      18866 :     for (std::set<int>::iterator it = m_tried_collisions.begin(); it != m_tried_collisions.end();) {
#     560                 :         10 :         int id_new = *it;
#     561                 :            : 
#     562                 :         10 :         bool erase_collision = false;
#     563                 :            : 
#     564                 :            :         // If id_new not found in mapInfo remove it from m_tried_collisions
#     565         [ -  + ]:         10 :         if (mapInfo.count(id_new) != 1) {
#     566                 :          0 :             erase_collision = true;
#     567                 :         10 :         } else {
#     568                 :         10 :             CAddrInfo& info_new = mapInfo[id_new];
#     569                 :            : 
#     570                 :            :             // Which tried bucket to move the entry to.
#     571                 :         10 :             int tried_bucket = info_new.GetTriedBucket(nKey, m_asmap);
#     572                 :         10 :             int tried_bucket_pos = info_new.GetBucketPosition(nKey, false, tried_bucket);
#     573         [ -  + ]:         10 :             if (!info_new.IsValid()) { // id_new may no longer map to a valid address
#     574                 :          0 :                 erase_collision = true;
#     575         [ +  - ]:         10 :             } else if (vvTried[tried_bucket][tried_bucket_pos] != -1) { // The position in the tried bucket is not empty
#     576                 :            : 
#     577                 :            :                 // Get the to-be-evicted address that is being tested
#     578                 :         10 :                 int id_old = vvTried[tried_bucket][tried_bucket_pos];
#     579                 :         10 :                 CAddrInfo& info_old = mapInfo[id_old];
#     580                 :            : 
#     581                 :            :                 // Has successfully connected in last X hours
#     582         [ +  + ]:         10 :                 if (GetAdjustedTime() - info_old.nLastSuccess < ADDRMAN_REPLACEMENT_HOURS*(60*60)) {
#     583                 :          8 :                     erase_collision = true;
#     584         [ +  - ]:          8 :                 } else if (GetAdjustedTime() - info_old.nLastTry < ADDRMAN_REPLACEMENT_HOURS*(60*60)) { // attempted to connect and failed in last X hours
#     585                 :            : 
#     586                 :            :                     // Give address at least 60 seconds to successfully connect
#     587         [ +  - ]:          2 :                     if (GetAdjustedTime() - info_old.nLastTry > 60) {
#     588         [ +  - ]:          2 :                         LogPrint(BCLog::ADDRMAN, "Replacing %s with %s in tried table\n", info_old.ToString(), info_new.ToString());
#     589                 :            : 
#     590                 :            :                         // Replaces an existing address already in the tried table with the new address
#     591                 :          2 :                         Good_(info_new, false, GetAdjustedTime());
#     592                 :          2 :                         erase_collision = true;
#     593                 :          2 :                     }
#     594         [ #  # ]:          2 :                 } else if (GetAdjustedTime() - info_new.nLastSuccess > ADDRMAN_TEST_WINDOW) {
#     595                 :            :                     // If the collision hasn't resolved in some reasonable amount of time,
#     596                 :            :                     // just evict the old entry -- we must not be able to
#     597                 :            :                     // connect to it for some reason.
#     598         [ #  # ]:          0 :                     LogPrint(BCLog::ADDRMAN, "Unable to test; replacing %s with %s in tried table anyway\n", info_old.ToString(), info_new.ToString());
#     599                 :          0 :                     Good_(info_new, false, GetAdjustedTime());
#     600                 :          0 :                     erase_collision = true;
#     601                 :          0 :                 }
#     602                 :         10 :             } else { // Collision is not actually a collision anymore
#     603                 :          0 :                 Good_(info_new, false, GetAdjustedTime());
#     604                 :          0 :                 erase_collision = true;
#     605                 :          0 :             }
#     606                 :         10 :         }
#     607                 :            : 
#     608         [ +  - ]:         10 :         if (erase_collision) {
#     609                 :         10 :             m_tried_collisions.erase(it++);
#     610                 :         10 :         } else {
#     611                 :          0 :             it++;
#     612                 :          0 :         }
#     613                 :         10 :     }
#     614                 :      18856 : }
#     615                 :            : 
#     616                 :            : CAddrInfo CAddrMan::SelectTriedCollision_()
#     617                 :        218 : {
#     618         [ +  + ]:        218 :     if (m_tried_collisions.size() == 0) return CAddrInfo();
#     619                 :            : 
#     620                 :         10 :     std::set<int>::iterator it = m_tried_collisions.begin();
#     621                 :            : 
#     622                 :            :     // Selects a random element from m_tried_collisions
#     623                 :         10 :     std::advance(it, insecure_rand.randrange(m_tried_collisions.size()));
#     624                 :         10 :     int id_new = *it;
#     625                 :            : 
#     626                 :            :     // If id_new not found in mapInfo remove it from m_tried_collisions
#     627         [ -  + ]:         10 :     if (mapInfo.count(id_new) != 1) {
#     628                 :          0 :         m_tried_collisions.erase(it);
#     629                 :          0 :         return CAddrInfo();
#     630                 :          0 :     }
#     631                 :            : 
#     632                 :         10 :     const CAddrInfo& newInfo = mapInfo[id_new];
#     633                 :            : 
#     634                 :            :     // which tried bucket to move the entry to
#     635                 :         10 :     int tried_bucket = newInfo.GetTriedBucket(nKey, m_asmap);
#     636                 :         10 :     int tried_bucket_pos = newInfo.GetBucketPosition(nKey, false, tried_bucket);
#     637                 :            : 
#     638                 :         10 :     int id_old = vvTried[tried_bucket][tried_bucket_pos];
#     639                 :            : 
#     640                 :         10 :     return mapInfo[id_old];
#     641                 :         10 : }
#     642                 :            : 
#     643                 :            : std::vector<bool> CAddrMan::DecodeAsmap(fs::path path)
#     644                 :          5 : {
#     645                 :          5 :     std::vector<bool> bits;
#     646                 :          5 :     FILE *filestr = fsbridge::fopen(path, "rb");
#     647                 :          5 :     CAutoFile file(filestr, SER_DISK, CLIENT_VERSION);
#     648         [ -  + ]:          5 :     if (file.IsNull()) {
#     649                 :          0 :         LogPrintf("Failed to open asmap file from disk\n");
#     650                 :          0 :         return bits;
#     651                 :          0 :     }
#     652                 :          5 :     fseek(filestr, 0, SEEK_END);
#     653                 :          5 :     int length = ftell(filestr);
#     654                 :          5 :     LogPrintf("Opened asmap file %s (%d bytes) from disk\n", path, length);
#     655                 :          5 :     fseek(filestr, 0, SEEK_SET);
#     656                 :          5 :     char cur_byte;
#     657         [ +  + ]:        241 :     for (int i = 0; i < length; ++i) {
#     658                 :        236 :         file >> cur_byte;
#     659         [ +  + ]:       2124 :         for (int bit = 0; bit < 8; ++bit) {
#     660                 :       1888 :             bits.push_back((cur_byte >> bit) & 1);
#     661                 :       1888 :         }
#     662                 :        236 :     }
#     663         [ +  + ]:          5 :     if (!SanityCheckASMap(bits)) {
#     664                 :          1 :         LogPrintf("Sanity check of asmap file %s failed\n", path);
#     665                 :          1 :         return {};
#     666                 :          1 :     }
#     667                 :          4 :     return bits;
#     668                 :          4 : }
#     669                 :            : 
#     670                 :            : void CAddrMan::ResetI2PPorts()
#     671                 :        261 : {
#     672         [ +  + ]:     267525 :     for (int bucket = 0; bucket < ADDRMAN_NEW_BUCKET_COUNT; ++bucket) {
#     673         [ +  + ]:   17372160 :         for (int i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
#     674                 :   17104896 :             const auto id = vvNew[bucket][i];
#     675         [ +  + ]:   17104896 :             if (id == -1) {
#     676                 :   17104108 :                 continue;
#     677                 :   17104108 :             }
#     678                 :        788 :             auto& addr_info = mapInfo[id];
#     679 [ +  + ][ +  + ]:        788 :             if (!addr_info.IsI2P() || addr_info.GetPort() == i2p::sam::PORT_SAM31) {
#     680                 :        782 :                 continue;
#     681                 :        782 :             }
#     682                 :            : 
#     683                 :          6 :             auto addr_info_newport = addr_info;
#     684                 :            :             // The below changes addr_info_newport.GetKey() which is used in finding
#     685                 :            :             // a bucket and a position within that bucket. So a re-bucketing may be necessary.
#     686                 :          6 :             addr_info_newport.port = i2p::sam::PORT_SAM31;
#     687                 :            : 
#     688                 :            :             // Reposition entries of vvNew within the same bucket because we don't know the source
#     689                 :            :             // address which led to the decision to store the entry in vvNew[bucket] so we can't
#     690                 :            :             // re-evaluate that decision, but even if we could, CAddrInfo::GetNewBucket() does not
#     691                 :            :             // use CAddrInfo::GetKey() so it would end up in the same bucket as before the port
#     692                 :            :             // change.
#     693                 :          6 :             const auto i_target = addr_info_newport.GetBucketPosition(nKey, true, bucket);
#     694                 :            : 
#     695         [ +  + ]:          6 :             if (i_target == i) { // No need to re-position.
#     696                 :          2 :                 addr_info = addr_info_newport;
#     697                 :          2 :                 continue;
#     698                 :          2 :             }
#     699                 :            : 
#     700                 :            :             // Reposition from i to i_target or delete if the target position is occupied.
#     701                 :            : 
#     702         [ +  + ]:          4 :             if (vvNew[bucket][i_target] == -1) {
#     703                 :          2 :                 vvNew[bucket][i_target] = id;
#     704                 :          2 :                 vvNew[bucket][i] = -1;
#     705                 :          2 :                 addr_info = addr_info_newport;
#     706                 :          2 :             } else {
#     707                 :          2 :                 ClearNew(bucket, i);
#     708                 :          2 :             }
#     709                 :          4 :         }
#     710                 :     267264 :     }
#     711                 :            : 
#     712         [ +  + ]:      67077 :     for (int bucket = 0; bucket < ADDRMAN_TRIED_BUCKET_COUNT; ++bucket) {
#     713         [ +  + ]:    4343040 :         for (int i = 0; i < ADDRMAN_BUCKET_SIZE; ++i) {
#     714                 :    4276224 :             const auto id = vvTried[bucket][i];
#     715         [ +  + ]:    4276224 :             if (id == -1) {
#     716                 :    4276214 :                 continue;
#     717                 :    4276214 :             }
#     718                 :         10 :             auto& addr_info = mapInfo[id];
#     719 [ +  + ][ +  + ]:         10 :             if (!addr_info.IsI2P() || addr_info.GetPort() == i2p::sam::PORT_SAM31) {
#     720                 :          4 :                 continue;
#     721                 :          4 :             }
#     722                 :            : 
#     723                 :          6 :             auto addr_info_newport = addr_info;
#     724                 :            :             // The below changes addr_info_newport.GetKey() which is used in finding
#     725                 :            :             // a bucket and a position within that bucket. So a re-bucketing may be necessary.
#     726                 :          6 :             addr_info_newport.port = i2p::sam::PORT_SAM31;
#     727                 :            : 
#     728                 :          6 :             const auto bucket_target = addr_info_newport.GetTriedBucket(nKey, m_asmap);
#     729                 :          6 :             const auto i_target = addr_info_newport.GetBucketPosition(nKey, false, bucket_target);
#     730                 :            : 
#     731 [ +  + ][ +  - ]:          6 :             if (bucket_target == bucket && i_target == i) { // No need to re-position.
#     732                 :          2 :                 addr_info = addr_info_newport;
#     733                 :          2 :                 continue;
#     734                 :          2 :             }
#     735                 :            : 
#     736                 :            :             // Reposition from (bucket, i) to (bucket_target, i_target) or delete if the target
#     737                 :            :             // position is occupied.
#     738                 :            : 
#     739         [ +  + ]:          4 :             if (vvTried[bucket_target][i_target] == -1) {
#     740                 :          2 :                 vvTried[bucket_target][i_target] = id;
#     741                 :          2 :                 vvTried[bucket][i] = -1;
#     742                 :          2 :                 addr_info = addr_info_newport;
#     743                 :          2 :             } else {
#     744                 :          2 :                 vvTried[bucket][i] = -1;
#     745                 :          2 :                 SwapRandom(addr_info.nRandomPos, vRandom.size() - 1);
#     746                 :          2 :                 vRandom.pop_back();
#     747                 :          2 :                 mapAddr.erase(addr_info);
#     748                 :          2 :                 mapInfo.erase(id);
#     749                 :          2 :                 --nTried;
#     750                 :          2 :             }
#     751                 :          4 :         }
#     752                 :      66816 :     }
#     753                 :        261 : }

Generated by: LCOV version 1.14