LCOV - code coverage report
Current view: top level - legacy/ascend910/framework/common/src/topo - topoinfo_exchange_agent.cc (source / functions) Coverage Total Hit
Test: coverage.info Lines: 40.9 % 663 271
Test Date: 2026-08-18 17:47:01 Functions: 36.2 % 47 17

            Line data    Source code
       1              : /**
       2              :  * Copyright (c) 2025 Huawei Technologies Co., Ltd.
       3              :  * This program is free software, you can redistribute it and/or modify it under the terms and conditions of
       4              :  * CANN Open Software License Agreement Version 2.0 (the "License").
       5              :  * Please refer to the License for details. You may not use this file except in compliance with the License.
       6              :  * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
       7              :  * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
       8              :  * See LICENSE in the root of the software repository for the full text of the License.
       9              :  */
      10              : 
      11              : #include "topoinfo_exchange_agent.h"
      12              : #include <iostream>
      13              : #include <sstream>
      14              : #include <cstring>
      15              : #include "externalinput_pub.h"
      16              : #include "adapter_error_manager_pub.h"
      17              : #include "config.h"
      18              : #include "sal_pub.h"
      19              : #include "device_capacity.h"
      20              : 
      21              : namespace hccl {
      22              : constexpr s32 DEVICE_LOGIC_ID_LENGTH = 4;
      23              : constexpr u32 AGENT_MAX_RETRY_TIME = 3;
      24              : 
      25           21 : TopoInfoExchangeAgent::TopoInfoExchangeAgent(
      26              :     HcclIpAddress& serverIp, u32 serverPort, std::string identifier, HcclNetDevCtx netDevCtx,
      27           21 :     HcclBasicRankInfo localRankInfo)
      28           21 :     : serverIP_(serverIp),
      29           21 :       serverPort_(serverPort),
      30           21 :       identifier_(identifier),
      31           21 :       localRankInfo_(localRankInfo),
      32           21 :       clusterTopoInfo_(),
      33           21 :       netDevCtx_(netDevCtx),
      34           42 :       isRetry_(GetExternalInputInterSuperPodRetryEnable())
      35           21 : {}
      36              : 
      37            0 : TopoInfoExchangeAgent::TopoInfoExchangeAgent(
      38              :     HcclIpAddress& serverIp, u32 serverPort, std::string identifier, HcclNetDevCtx netDevCtx,
      39            0 :     HcclBasicRankInfo localRankInfo, u32 connSize, u32 connRank)
      40            0 :     : serverIP_(serverIp),
      41            0 :       serverPort_(serverPort),
      42            0 :       identifier_(identifier),
      43            0 :       localRankInfo_(localRankInfo),
      44            0 :       clusterTopoInfo_(),
      45            0 :       netDevCtx_(netDevCtx),
      46            0 :       connSize_(connSize),
      47            0 :       connRank_(connRank),
      48            0 :       isRetry_(GetExternalInputInterSuperPodRetryEnable())
      49            0 : {}
      50              : 
      51            0 : TopoInfoExchangeAgent::TopoInfoExchangeAgent(
      52              :     HcclIpAddress& serverIp, u32 serverPort, std::string identifier, HcclNetDevCtx netDevCtx,
      53            0 :     HcclBasicRankInfo localRankInfo, HcclRankHandle rankInfo)
      54            0 :     : serverIP_(serverIp),
      55            0 :       serverPort_(serverPort),
      56            0 :       identifier_(identifier),
      57            0 :       localRankInfo_(localRankInfo),
      58            0 :       localRankHandle_(rankInfo),
      59            0 :       clusterTopoInfo_(),
      60            0 :       netDevCtx_(netDevCtx),
      61            0 :       isRetry_(GetExternalInputInterSuperPodRetryEnable())
      62            0 : {}
      63              : 
      64           21 : TopoInfoExchangeAgent::~TopoInfoExchangeAgent() { Teardown(); }
      65              : 
      66            0 : HcclResult TopoInfoExchangeAgent::SetIsInterSuperPodRetryEnable(bool isInterSuperPodRetryEnable)
      67              : {
      68            0 :     isRetry_ = isInterSuperPodRetryEnable;
      69            0 :     return HCCL_SUCCESS;
      70              : }
      71              : 
      72            0 : HcclResult TopoInfoExchangeAgent::Setup()
      73              : {
      74            0 :     connSize_ = localRankInfo_.rankSize;
      75            0 :     connRank_ = localRankInfo_.rank;
      76              :     // 填充要发送的localRankHandle的值
      77            0 :     localRankHandle_.rankId = localRankInfo_.rank;
      78            0 :     HcclResult ret = ConnectWithRetry(serverIP_, serverPort_, socket_);
      79            0 :     CHK_PRT_RET(
      80              :         ret != HCCL_SUCCESS,
      81              :         HCCL_ERROR(
      82              :             "[TopoInfoExchangeAgent][Setup]TopoExchangeAgent: "
      83              :             "connect server[%s : %u] failed",
      84              :             serverIP_.GetReadableAddress(), serverPort_),
      85              :         ret);
      86            0 :     HCCL_INFO(
      87              :         "TopoExchangeAgent: client connect with server ip[%s] port[%u] success.", serverIP_.GetReadableAddress(),
      88              :         serverPort_);
      89              : 
      90            0 :     if (!isByMasterInfo_ && localRankInfo_.rankSize > TOPO_HIERARCHICAL_ENABLE_THRESHOLD) {
      91            0 :         ret = socket_->Send(&localRankHandle_, sizeof(localRankHandle_));
      92            0 :         CHK_PRT_RET(
      93              :             ret != HCCL_SUCCESS,
      94              :             HCCL_ERROR(
      95              :                 "[SendRankHandle]errNo[0x%016llx] rankID[%s] send localRankHandle to remote by"
      96              :                 "client fdHandle failed, ret[%u]",
      97              :                 HCCL_ERROR_CODE(HCCL_E_TCP_TRANSFER), localRankInfo_.rank, ret),
      98              :             ret);
      99              : 
     100            0 :         CHK_RET(RecvGrpLeaderInfo(socket_, grpLeaderInfo_));
     101            0 :         u32 grpIndex = localRankInfo_.rank / TOPO_MAX_GROUP_SIZE;
     102            0 :         grpLeader_ = grpLeaderInfo_.GroupLeaderList[grpIndex];
     103            0 :     } else {
     104            0 :         CHK_RET(DetectClusterTopoInfo(socket_, clusterTopoInfo_));
     105            0 :         ret = VerifyClusterInfo(clusterTopoInfo_);
     106            0 :         if (ret != HCCL_SUCCESS) {
     107            0 :             auto current = g_broadcastStage.load(std::memory_order_acquire);
     108            0 :             if (current == BroadcastStage::Started) {
     109            0 :                 std::unique_lock<std::mutex> lock(g_broadcast_stage_mutex);
     110            0 :                 std::chrono::seconds timeout(MAX_WAIT_BROADCAST_SECONDS);
     111            0 :                 g_broadcast_stage_cv.wait_for(lock, timeout, [] {
     112            0 :                     return g_broadcastStage.load(std::memory_order_relaxed) == BroadcastStage::Completed;
     113              :                 });
     114            0 :             }
     115            0 :             HCCL_ERROR(
     116              :                 "[TopoInfoExchangeAgent][Setup]VerifyCluseterInfo failed, g_broadcastStage[%d]",
     117              :                 g_broadcastStage.load());
     118              :         }
     119              : 
     120            0 :         return ret;
     121              :     }
     122              : 
     123            0 :     return HCCL_SUCCESS;
     124              : }
     125              : 
     126            0 : HcclResult TopoInfoExchangeAgent::SetupRank(std::shared_ptr<HcclSocket> socket)
     127              : {
     128            0 :     CHK_RET(RecvGrpLeaderInfo(socket, grpLeaderInfo_));
     129            0 :     u32 grpIndex = localRankInfo_.rank / TOPO_MAX_GROUP_SIZE;
     130            0 :     grpLeader_ = grpLeaderInfo_.GroupLeaderList[grpIndex];
     131            0 :     return HCCL_SUCCESS;
     132              : }
     133              : 
     134            0 : HcclResult TopoInfoExchangeAgent::SetupMember()
     135              : {
     136            0 :     HcclResult ret = Connect(serverIP_, serverPort_, socket_);
     137            0 :     CHK_PRT_RET(
     138              :         ret != HCCL_SUCCESS,
     139              :         HCCL_ERROR(
     140              :             "[TopoInfoExchangeAgent][Setup]SetupGroupMember: "
     141              :             "connect server[%s : %u] failed",
     142              :             serverIP_.GetReadableAddress(), serverPort_),
     143              :         ret);
     144            0 :     HCCL_INFO(
     145              :         "SetupGroupMember: client connect with server ip[%s] port[%u] success.", serverIP_.GetReadableAddress(),
     146              :         serverPort_);
     147              : 
     148            0 :     CHK_RET(DetectClusterTopoInfo(socket_, clusterTopoInfo_));
     149              : 
     150            0 :     CHK_RET(VerifyClusterInfo(clusterTopoInfo_));
     151              : 
     152            0 :     return HCCL_SUCCESS;
     153              : }
     154              : 
     155           21 : HcclResult TopoInfoExchangeAgent::Teardown()
     156              : {
     157           21 :     CHK_RET(Disconnect(socket_));
     158           21 :     return HCCL_SUCCESS;
     159              : }
     160              : 
     161            0 : HcclResult TopoInfoExchangeAgent::GetConnection(std::shared_ptr<HcclSocket>& socket)
     162              : {
     163            0 :     socket = socket_;
     164            0 :     return HCCL_SUCCESS;
     165              : }
     166              : 
     167            0 : HcclResult TopoInfoExchangeAgent::GetGroupLeader(HcclRankHandle& rankHandle)
     168              : {
     169            0 :     rankHandle = grpLeader_;
     170            0 :     return HCCL_SUCCESS;
     171              : }
     172              : 
     173            0 : HcclResult TopoInfoExchangeAgent::SetupByMasterInfo()
     174              : {
     175            0 :     isByMasterInfo_ = true;
     176            0 :     CHK_RET(Setup());
     177            0 :     return HCCL_SUCCESS;
     178              : }
     179              : 
     180              : HcclResult
     181            0 : TopoInfoExchangeAgent::DetectClusterTopoInfo(std::shared_ptr<HcclSocket> socket, RankTable_t& clusterTopoInfo)
     182              : {
     183            0 :     RankTable_t localBasicInfo;
     184            0 :     CHK_RET(ConstructRankTableMsg(localBasicInfo));
     185            0 :     CHK_RET(SendClusterInfo(socket, localBasicInfo));
     186            0 :     HCCL_INFO("topo exchange client send rank basic info success.");
     187              : 
     188            0 :     CHK_RET(RecvClusterInfo(socket, clusterTopoInfo));
     189            0 :     HCCL_INFO("topo exchange client get rank basic info success.");
     190              : 
     191              :     // 按照rankId排序
     192            0 :     std::vector<RankInfo_t>& rankList = clusterTopoInfo_.rankList;
     193            0 :     sort(rankList.begin(), rankList.end(), [](const RankInfo_t& a, const RankInfo_t& b) {
     194            0 :         return a.rankId < b.rankId;
     195              :     });
     196              : 
     197            0 :     CHK_RET(SetServerIdx(clusterTopoInfo));
     198            0 :     CHK_RET(GroupSuperPodsByRankContinuity(clusterTopoInfo));
     199            0 :     CHK_RET(SetSuperPodIdx(clusterTopoInfo));
     200            0 :     return HCCL_SUCCESS;
     201            0 : }
     202              : 
     203            0 : HcclResult TopoInfoExchangeAgent::GroupSuperPodsByRankContinuity(RankTable_t& clusterInfo) const
     204              : {
     205              :     // 按照superPodId将节点分组,相同superPodId在一个组
     206              :     // clusterInfo已经按照rankId排好序,按顺序插入到新的subRankTable中,不需要再排序
     207            0 :     std::map<std::string, std::vector<RankInfo_t*>> podGroupClusters;
     208            0 :     for (auto& rankInfo : clusterInfo.rankList) {
     209            0 :         rankInfo.originalSuperPodId = rankInfo.superPodId; // 把用户配置的原始superPodId先保存下来
     210            0 :         podGroupClusters[rankInfo.superPodId].emplace_back(&rankInfo);
     211              :     }
     212            0 :     std::set<std::string> superPodIdSet;
     213            0 :     std::map<std::string, std::pair<u32, u32>> superPodIdRanges; // 记录每个逻辑超节点的rank id范围
     214            0 :     for (auto& subCluster : podGroupClusters) {
     215            0 :         auto& subClusterInfo = subCluster.second;
     216            0 :         if (subClusterInfo.size() <= 1) {
     217            0 :             continue;
     218              :         }
     219            0 :         u32 groupId = 0;
     220            0 :         superPodIdSet.insert(subCluster.first);
     221            0 :         RankInfo_t preRank = *(subClusterInfo[0]);
     222            0 :         superPodIdRanges[preRank.superPodId] = {preRank.rankId, preRank.rankId}; // 初始化范围
     223            0 :         for (u32 i = 1; i < subClusterInfo.size(); ++i) {
     224            0 :             RankInfo_t& curRank = *(subClusterInfo[i]);
     225              :             // 当前的curRank和上一个preRank的rankId不连续,分配新的逻辑超节点ID
     226            0 :             if (curRank.rankId != preRank.rankId + 1) {
     227            0 :                 std::string newSuperPodId = curRank.originalSuperPodId + "_HCCLSPLIT_" + std::to_string(groupId);
     228            0 :                 curRank.superPodId = newSuperPodId;
     229            0 :                 groupId++;
     230            0 :                 superPodIdRanges[curRank.superPodId] = {curRank.rankId, curRank.rankId}; // 初始化新的范围
     231            0 :             } else {
     232              :                 // 同一个sub通信域两个rank原始逻辑超节点是一致的
     233              :                 // rankId连续 上一个rank的superPodId可能已经重新分配,需要更新当前superPodId为上一个rank的
     234            0 :                 curRank.superPodId = preRank.superPodId;
     235            0 :                 superPodIdRanges[curRank.superPodId].second = curRank.rankId; // 更新最大rank id
     236              :             }
     237            0 :             superPodIdSet.insert(curRank.superPodId);
     238            0 :             preRank = curRank;
     239              :         }
     240            0 :     }
     241              :     // 打印每个逻辑超节点的rank id范围,只打印包含_HCCLSPLIT_的逻辑超节点
     242            0 :     for (const auto& entry : superPodIdRanges) {
     243            0 :         auto superPodId = entry.first;
     244            0 :         if (superPodId.find("_HCCLSPLIT_") != std::string::npos) {
     245            0 :             auto range = entry.second;
     246            0 :             HCCL_RUN_INFO(
     247              :                 "[TopoInfoExchangeAgent][%s]Split superPod, ID[%s], rank range[%u, %u]", __func__, superPodId.c_str(),
     248              :                 range.first, range.second);
     249              :         }
     250            0 :     }
     251            0 :     clusterInfo.superPodNum = superPodIdSet.size();
     252            0 :     return HCCL_SUCCESS;
     253            0 : }
     254              : 
     255            0 : HcclResult TopoInfoExchangeAgent::SetServerIdx(RankTable_t& clusterInfo) const
     256              : {
     257              :     struct ServerSortInfo {
     258              :         u32 serverPosition;
     259              :         u32 selectedRankId;
     260              :     };
     261            0 :     std::vector<ServerSortInfo> serverSortInfoVec;
     262            0 :     for (u32 i = 0; i < clusterInfo.serverList.size(); i++) {
     263            0 :         for (u32 j = 0; j < clusterInfo.rankList.size(); j++) {
     264            0 :             if (clusterInfo.rankList[j].serverId == clusterInfo.serverList[i].serverId) {
     265              :                 // 每个server的rankid都是连续的,只需要取每个server里任意一个rankid进行排序
     266              :                 ServerSortInfo serverSortInfo;
     267            0 :                 serverSortInfo.serverPosition = i;
     268            0 :                 serverSortInfo.selectedRankId = clusterInfo.rankList[j].rankId;
     269            0 :                 serverSortInfoVec.push_back(serverSortInfo);
     270            0 :                 break;
     271              :             }
     272              :         }
     273              :     }
     274            0 :     sort(serverSortInfoVec.begin(), serverSortInfoVec.end(), [](const ServerSortInfo& a, const ServerSortInfo& b) {
     275            0 :         return a.selectedRankId < b.selectedRankId;
     276              :     });
     277              :     // 遍历ranklist,根据serverid获取serveridx
     278            0 :     for (u32 serverIdx = 0; serverIdx < serverSortInfoVec.size(); serverIdx++) {
     279            0 :         for (u32 j = 0; j < clusterInfo.rankList.size(); j++) {
     280            0 :             if (clusterInfo.rankList[j].serverId
     281            0 :                 == clusterInfo.serverList[serverSortInfoVec[serverIdx].serverPosition].serverId) {
     282            0 :                 clusterInfo.rankList[j].serverIdx = serverIdx;
     283              :             }
     284              :         }
     285              :     }
     286            0 :     return HCCL_SUCCESS;
     287            0 : }
     288              : 
     289            1 : HcclResult TopoInfoExchangeAgent::SetSuperPodIdx(RankTable_t& clusterInfo) const
     290              : {
     291            1 :     std::map<std::string, u32> spodIdToIdx;
     292            1 :     bool isDiffDeviceType = false;
     293            1 :     DevType standardDevType = DevType::DEV_TYPE_NOSOC;
     294            1 :     if (clusterInfo.rankList.size() > 0) {
     295            1 :         standardDevType = clusterInfo.rankList[0].deviceInfo.deviceType;
     296              :     }
     297            4 :     for (u32 i = 0; i < clusterInfo.rankList.size(); ++i) {
     298            3 :         RankInfo_t& rankInfo = clusterInfo.rankList[i];
     299            3 :         if (rankInfo.deviceInfo.deviceType != standardDevType) {
     300            0 :             isDiffDeviceType = true;
     301              :         }
     302              : 
     303            3 :         if (isDiffDeviceType) {
     304            0 :             rankInfo.superPodIdx = spodIdToIdx.size();
     305            3 :         } else if (spodIdToIdx.find(rankInfo.superPodId) == spodIdToIdx.end()) {
     306            2 :             rankInfo.superPodIdx = spodIdToIdx.size();
     307            2 :             spodIdToIdx.insert({rankInfo.superPodId, rankInfo.superPodIdx});
     308            1 :         } else if (spodIdToIdx[rankInfo.superPodId] + 1 == spodIdToIdx.size()) {
     309            0 :             rankInfo.superPodIdx = spodIdToIdx[rankInfo.superPodId];
     310              :         } else {
     311            1 :             u32 preIndex = (i > 0) ? i - 1 : i;
     312            1 :             RankInfo_t& preRankInfo = clusterInfo.rankList[preIndex];
     313            1 :             u32 index = 0;
     314            1 :             for (; index < preIndex; index++) {
     315            1 :                 RankInfo_t& tmpRankInfo = clusterInfo.rankList[index];
     316            1 :                 if (tmpRankInfo.superPodId == rankInfo.superPodId) {
     317            1 :                     break;
     318              :                 }
     319              :             }
     320              :             // 超节点内rank id不连续
     321            1 :             HCCL_RUN_WARNING(
     322              :                 "rank in superPodId is not continuous, pre: rank[%u] superPodId[%s], "
     323              :                 "cur: rank[%u] superPodId[%s], ",
     324              :                 preRankInfo.rankId, preRankInfo.superPodId.c_str(), rankInfo.rankId, rankInfo.superPodId.c_str());
     325            1 :             rankInfo.superPodIdx = spodIdToIdx[rankInfo.superPodId];
     326              :         }
     327            3 :         HCCL_INFO(
     328              :             "SetSuperPodIdx rankList[%u]: rankId[%u], superPodId[%s], superPodIdx[%u], sdid[%u]", i, rankInfo.rankId,
     329              :             rankInfo.superPodId.c_str(), rankInfo.superPodIdx, rankInfo.superDeviceId);
     330              :     }
     331            1 :     return HCCL_SUCCESS;
     332            1 : }
     333              : 
     334            0 : HcclResult TopoInfoExchangeAgent::GetClusterTopoInfo(RankTable_t& clusterInfo)
     335              : {
     336            0 :     clusterInfo.nicDeploy = clusterTopoInfo_.nicDeploy;
     337            0 :     clusterInfo.deviceNum = clusterTopoInfo_.deviceNum;
     338            0 :     clusterInfo.serverNum = clusterTopoInfo_.serverNum;
     339            0 :     clusterInfo.superPodNum = clusterTopoInfo_.superPodNum;
     340            0 :     clusterInfo.rankNum = clusterTopoInfo_.rankNum;
     341            0 :     clusterInfo.rankList = clusterTopoInfo_.rankList;
     342            0 :     clusterInfo.serverList = clusterTopoInfo_.serverList;
     343              : 
     344            0 :     return HCCL_SUCCESS;
     345              : }
     346            0 : HcclResult TopoInfoExchangeAgent::GetIdentifier(u32& identify)
     347              : {
     348            0 :     identify = identifierNum_;
     349            0 :     return HCCL_SUCCESS;
     350              : }
     351            0 : HcclResult TopoInfoExchangeAgent::Connect(HcclIpAddress& serverIp, u32 port, std::shared_ptr<HcclSocket>& socket)
     352              : {
     353            0 :     std::string tag = TOPO_DETECT_TAG + "_" + identifier_ + "_" + std::to_string(port);
     354            0 :     EXCEPTION_CATCH(
     355              :         (socket = std::make_shared<HcclSocket>(tag, netDevCtx_, serverIp, port, HcclSocketRole::SOCKET_ROLE_CLIENT)),
     356              :         return HCCL_E_PTR);
     357            0 :     CHK_SMART_PTR_NULL(socket);
     358            0 :     CHK_RET(socket->Init());
     359            0 :     CHK_RET(socket->Connect());
     360              : 
     361            0 :     return GetConnection(serverIp, port, socket);
     362            0 : }
     363              : 
     364              : HcclResult
     365            0 : TopoInfoExchangeAgent::ConnectWithRetry(HcclIpAddress& serverIp, u32 port, std::shared_ptr<HcclSocket>& socket)
     366              : {
     367            0 :     u32 retryTime = 1;
     368            0 :     HcclResult ret = HCCL_SUCCESS;
     369            0 :     while (retryTime <= AGENT_MAX_RETRY_TIME) {
     370            0 :         std::string tag = TOPO_DETECT_TAG + "_" + identifier_ + "_" + std::to_string(port);
     371            0 :         EXCEPTION_CATCH(
     372              :             (socket
     373              :              = std::make_shared<HcclSocket>(tag, netDevCtx_, serverIp, port, HcclSocketRole::SOCKET_ROLE_CLIENT)),
     374              :             return HCCL_E_PTR);
     375            0 :         CHK_SMART_PTR_NULL(socket);
     376            0 :         CHK_RET(socket->Init());
     377            0 :         CHK_RET(socket->Connect());
     378              : 
     379            0 :         CHK_RET(GetConnection(serverIp, port, socket));
     380              : 
     381            0 :         ret = TryRecvFromServer(socket, retryTime);
     382            0 :         if (ret == HCCL_SUCCESS) {
     383            0 :             break;
     384              :         } else {
     385            0 :             retryTime++;
     386              :         }
     387            0 :     }
     388            0 :     return ret;
     389              : }
     390              : 
     391            3 : HcclResult TopoInfoExchangeAgent::TryRecvFromServer(std::shared_ptr<HcclSocket>& socket, u32 retryTime)
     392              : {
     393              :     // client端获取socket之后尝试从server接收数据,若在一定时间内没有接收到,则重新发起建链请求
     394            3 :     u32 timeout = GetExternalInputHcclLinkTimeOut() / AGENT_MAX_RETRY_TIME;
     395            3 :     char recvMsgBuf[sizeof(TOPO_EXCHANGE_CHECK_MESSAGE)] = {0};
     396            3 :     auto ret = HCCL_SUCCESS;
     397            3 :     if (retryTime == AGENT_MAX_RETRY_TIME) {
     398            3 :         ret = socket->Recv(recvMsgBuf, sizeof(TOPO_EXCHANGE_CHECK_MESSAGE), timeout);
     399              :     } else {
     400              :         // 重试时打印RUN_WARN日志
     401            0 :         SetErrToWarnSwitch(true);
     402            0 :         ret = socket->Recv(recvMsgBuf, sizeof(TOPO_EXCHANGE_CHECK_MESSAGE), timeout);
     403            0 :         SetErrToWarnSwitch(false);
     404              :     }
     405              : 
     406            3 :     if (ret == HCCL_SUCCESS) {
     407            2 :         HCCL_RUN_INFO("[%s]recvMes %s", __func__, recvMsgBuf);
     408              :         // 校验收到的是否正确,server端使用的是固定消息
     409            2 :         if (strncmp(recvMsgBuf, TOPO_EXCHANGE_CHECK_MESSAGE, sizeof(TOPO_EXCHANGE_CHECK_MESSAGE)) != 0) {
     410            1 :             HCCL_ERROR(
     411              :                 "[%s]recv message check failed, expect [%s], but recv [%s]", __func__, TOPO_EXCHANGE_CHECK_MESSAGE,
     412              :                 recvMsgBuf);
     413            1 :             return HCCL_E_INTERNAL;
     414              :         }
     415            1 :     } else if (retryTime < AGENT_MAX_RETRY_TIME) {
     416            0 :         HCCL_RUN_WARNING("[%s]client recv from server failed, will try to connect with server again.", __func__);
     417              :     } else {
     418            1 :         HCCL_ERROR("[%s]failed to recv messages from server with %u times", __func__, AGENT_MAX_RETRY_TIME);
     419              :     }
     420              : 
     421            2 :     return ret;
     422              : }
     423              : 
     424            0 : void TopoInfoExchangeAgent::PrintSocketTimeoutReasons(
     425              :     HcclIpAddress& serverIp, u32 port, std::shared_ptr<HcclSocket>& socket)
     426              : {
     427            0 :     HCCL_ERROR("current rank connect to server timeout, maybe due to following reasons:");
     428            0 :     HCCL_ERROR(
     429              :         "1. local host ip is [%s], server host ip and port is [%s:%u], Please check the network connectivity. "
     430              :         "If it is not connected, modify the network configuration or use HCCL_SOCKET_IFNAME and HCCL_IF_BASE_PORT to "
     431              :         "specify ifname and server port.",
     432              :         socket->GetLocalIp().GetReadableIP(), serverIp.GetReadableIP(), port);
     433            0 :     HCCL_ERROR(
     434              :         "2. Check whether any other exceptions have occurred on server[%s] or "
     435              :         "whether the time difference between the execution of hcom on ranks exceeds the timeout threshold.",
     436              :         serverIp.GetReadableIP());
     437            0 : }
     438              : 
     439            0 : HcclResult TopoInfoExchangeAgent::GetConnection(HcclIpAddress& serverIp, u32 port, std::shared_ptr<HcclSocket>& socket)
     440              : {
     441            0 :     auto startTime = std::chrono::steady_clock::now();
     442            0 :     auto timeout = std::chrono::seconds(GetExternalInputHcclLinkTimeOut());
     443              :     while (true) {
     444            0 :         std::string errormessage = "1. The current node " + std::string(serverIp.GetReadableIP())
     445            0 :                                    + " is disconnected from the host of the root node "
     446            0 :                                    + std::string(localRankHandle_.ip)
     447              :                                    + ". "
     448            0 :                                      "2. the timeout set by the HCCL_CONNECT_TIMEOUT environment variable is too short";
     449            0 :         if ((std::chrono::steady_clock::now() - startTime) >= timeout) {
     450            0 :             RPT_INPUT_ERR(
     451              :                 true, "EI0015", std::vector<std::string>({"error_reason"}), std::vector<std::string>({errormessage}));
     452            0 :             HCCL_ERROR(
     453              :                 "[%s][%s] topo exchange agent get socket timeout! timeout[%lld s]", LOG_KEYWORDS_INIT_GROUP.c_str(),
     454              :                 LOG_KEYWORDS_RANKTABLE_DETECT.c_str(), timeout);
     455            0 :             PrintSocketTimeoutReasons(serverIp, port, socket);
     456            0 :             sleep(WAIT_ERROR_BROADCAST_TIME);
     457            0 :             return HCCL_E_TIMEOUT;
     458              :         }
     459            0 :         HcclSocketStatus status = socket->GetStatus();
     460            0 :         if (status == HcclSocketStatus::SOCKET_CONNECTING) {
     461            0 :             SaluSleep(ONE_MILLISECOND_OF_USLEEP);
     462            0 :         } else if (status != HcclSocketStatus::SOCKET_OK) {
     463            0 :             HCCL_ERROR("[Get][Connection]server: get socket failed ret[%d]", status);
     464            0 :             return HCCL_E_TCP_CONNECT;
     465              :         } else {
     466            0 :             HCCL_INFO("TopoInfoExchangeAgent get socket success.");
     467            0 :             std::string agentID;
     468            0 :             if (isByMasterInfo_) {
     469            0 :                 agentID = localRankInfo_.superPodId + "/";
     470            0 :                 GenerateAgentID(localRankInfo_, agentID);
     471              :             } else {
     472            0 :                 std::string rankID = std::to_string(connRank_);
     473            0 :                 agentID = std::string(16 - rankID.length(), '0') + rankID; // agent id为rank id,16位,左对齐补零
     474            0 :             }
     475            0 :             char agentBuf[MAX_AGENT_BUF_SIZE] = {0};
     476            0 :             s32 sRet = memcpy_s(agentBuf, sizeof(agentBuf), agentID.c_str(), agentID.size());
     477            0 :             CHK_PRT_RET(sRet != EOK, HCCL_ERROR("memcpy_s failed, errorno[%d]", sRet), HCCL_E_MEMORY);
     478            0 :             HcclResult ret = socket->Send(&agentBuf, sizeof(agentBuf));
     479            0 :             CHK_PRT_RET(
     480              :                 ret != HCCL_SUCCESS,
     481              :                 HCCL_ERROR(
     482              :                     "[Get][Connection]errNo[0x%016llx] agentID[%s] send local rank id to remote "
     483              :                     "by client fdHandle failed, ret[%u]",
     484              :                     HCCL_ERROR_CODE(HCCL_E_TCP_TRANSFER), agentBuf, ret),
     485              :                 ret);
     486            0 :             ret = socket->Send(&connSize_, sizeof(connSize_));
     487            0 :             CHK_PRT_RET(
     488              :                 ret != HCCL_SUCCESS,
     489              :                 HCCL_ERROR(
     490              :                     "[Get][Connection]errNo[0x%016llx] rank[%u] send local rank num[%u] to "
     491              :                     "remote by client fdHandle failed, ret[%u]",
     492              :                     HCCL_ERROR_CODE(HCCL_E_TCP_TRANSFER), localRankInfo_.rank, localRankInfo_.rankSize, ret),
     493              :                 ret);
     494            0 :             HCCL_INFO(
     495              :                 "local rank[%u] get socket connection with server[%s] port[%u] success.", localRankInfo_.rank,
     496              :                 serverIp.GetReadableAddress(), port);
     497            0 :             break;
     498            0 :         }
     499            0 :     }
     500            0 :     return HCCL_SUCCESS;
     501            0 : }
     502              : 
     503            0 : std::string TopoInfoExchangeAgent::Dec2Hex(s32 i, u32 width)
     504              : {
     505            0 :     std::string temp;
     506            0 :     std::stringstream ss;
     507            0 :     ss << std::hex << i;
     508            0 :     ss >> temp;
     509            0 :     if (width > temp.size()) {
     510            0 :         return std::string((width - temp.size()), '0') + temp;
     511              :     } else {
     512            0 :         HCCL_WARNING("Dec2Hex: length[%u] is over width[%u]", temp.size(), width);
     513              :     }
     514            0 :     return temp;
     515            0 : }
     516              : 
     517            0 : void TopoInfoExchangeAgent::GenerateAgentID(HcclBasicRankInfo& localRankInfo, std::string& agentID)
     518              : {
     519            0 :     struct in_addr addr = localRankInfo.hostIP.GetBinaryAddress().addr;
     520            0 :     struct in6_addr addr6 = localRankInfo.hostIP.GetBinaryAddress().addr6;
     521            0 :     if (localRankInfo.hostIP.IsIPv6()) {
     522            0 :         for (size_t i = 0; i < sizeof(addr6.s6_addr); i++) {
     523            0 :             agentID += Dec2Hex(addr6.s6_addr[i], 2); // 转换为2位十六进制数据,左对齐补零
     524              :         }
     525              :     } else {
     526            0 :         for (size_t i = 0; i < sizeof(addr.s_addr) / sizeof(u8); i++) {
     527            0 :             agentID += Dec2Hex(*(reinterpret_cast<u8*>(&addr.s_addr) + i), 2); // 转换为2位十六进制数据,左对齐补零
     528              :         }
     529              :     }
     530            0 :     agentID.append("/");
     531            0 :     std::string devID = std::to_string(localRankInfo.deviceLogicID);
     532            0 :     CHK_PRT_RET(devID.size() > DEVICE_LOGIC_ID_LENGTH, HCCL_ERROR("deviceLogicID[%s] is invalid", devID.c_str()), );
     533              :     // device id转换为4位十进制数字,左对齐补零
     534            0 :     agentID.append(std::string((DEVICE_LOGIC_ID_LENGTH - devID.size()), '0') + devID);
     535            0 :     HCCL_INFO("GenerateAgentID agentID[%s]", agentID.c_str());
     536            0 :     return;
     537            0 : }
     538              : 
     539           21 : HcclResult TopoInfoExchangeAgent::Disconnect(std::shared_ptr<HcclSocket>& socket)
     540              : {
     541           21 :     CHK_RET(DisconnectSocket(socket));
     542           21 :     socket = nullptr;
     543              : 
     544           21 :     return HCCL_SUCCESS;
     545              : }
     546              : 
     547            0 : HcclResult TopoInfoExchangeAgent::RecvGrpLeaderInfo(std::shared_ptr<HcclSocket> socket, GroupLeader_t& leaderInfo)
     548              : {
     549              :     // 每次获取之前先清空 保证填充之后的数据是最新的
     550            0 :     leaderInfo.grpLeaderNum = 0;
     551            0 :     leaderInfo.GroupLeaderList.clear();
     552            0 :     CHK_RET(RecvGrpLeaderInfoMsg(socket, leaderInfo));
     553            0 :     return HCCL_SUCCESS;
     554              : }
     555              : 
     556              : HcclResult
     557            0 : TopoInfoExchangeAgent::SendGroupLeaderPortInfo(std::shared_ptr<HcclSocket> socket, HcclRankHandle& rankHandle)
     558              : {
     559            0 :     CHK_RET(GetConnection(socket));
     560            0 :     HcclResult ret = socket->Send(&rankHandle, sizeof(rankHandle));
     561            0 :     CHK_PRT_RET(
     562              :         ret != HCCL_SUCCESS,
     563              :         HCCL_ERROR(
     564              :             "[TopoInfoExchangeAgent][SendGroupLeaderPortInfo]errNo[0x%016llx] "
     565              :             "send grpleader port info fail",
     566              :             HCCL_ERROR_CODE(ret)),
     567              :         ret);
     568            0 :     return HCCL_SUCCESS;
     569              : }
     570              : 
     571            0 : HcclResult TopoInfoExchangeAgent::ConstructRankTableMsg(RankTable_t& clusterInfo)
     572              : {
     573            0 :     RankInfo_t myRankInfo;
     574            0 :     myRankInfo.rankId = localRankInfo_.rank;
     575            0 :     myRankInfo.hostIp = localRankInfo_.hostIP;
     576            0 :     myRankInfo.hostPort = localRankInfo_.hostPort;
     577            0 :     myRankInfo.deviceInfo.devicePhyId = localRankInfo_.devicePhysicID;
     578            0 :     myRankInfo.deviceInfo.deviceIp = localRankInfo_.deviceIP;
     579            0 :     myRankInfo.deviceInfo.deviceType = localRankInfo_.deviceType;
     580            0 :     myRankInfo.deviceInfo.backupDeviceIp = localRankInfo_.backupDeviceIP;
     581            0 :     myRankInfo.deviceInfo.port = localRankInfo_.deviceNicPort;
     582            0 :     myRankInfo.deviceInfo.vnicPort = localRankInfo_.deviceVnicPort;
     583            0 :     myRankInfo.deviceInfo.backupPort = localRankInfo_.backupDevicePort;
     584            0 :     myRankInfo.superPodId = localRankInfo_.superPodId;
     585            0 :     myRankInfo.superDeviceId = localRankInfo_.superDeviceId;
     586            0 :     myRankInfo.tlsStatus = localRankInfo_.tlsStatus;
     587            0 :     ConstructRankTableServerId(myRankInfo.serverId);
     588              : 
     589            0 :     ServerInfo_t myServerInfo;
     590            0 :     myServerInfo.serverId = myRankInfo.serverId;
     591              : 
     592            0 :     clusterInfo.nicDeploy = localRankInfo_.nicDeploy;
     593            0 :     clusterInfo.rankList.push_back(myRankInfo);
     594            0 :     clusterInfo.serverList.push_back(myServerInfo);
     595            0 :     return HCCL_SUCCESS;
     596            0 : }
     597              : 
     598            0 : void TopoInfoExchangeAgent::ConstructRankTableServerId(std::string& serverId)
     599              : {
     600            0 :     serverId = localRankInfo_.hostIP.GetReadableIP();
     601              :     // 配置逻辑超节点时, serverId要根据逻辑超节点划分
     602            0 :     if (localRankInfo_.deviceType == DevType::DEV_TYPE_910_93 && GetExternalInputLogicSuperPodId().empty() == false) {
     603            0 :         serverId += "_" + GetExternalInputLogicSuperPodId();
     604              :     }
     605            0 :     HCCL_INFO("ConstructRankTableServerId serverId %s", serverId.c_str());
     606            0 : }
     607              : 
     608            0 : HcclResult TopoInfoExchangeAgent::SetTransportInfo(RankTable_t& clusterInfo)
     609              : {
     610            0 :     CHK_PRT_RET(
     611              :         clusterInfo.rankList.size() <= localRankInfo_.rank,
     612              :         HCCL_ERROR(
     613              :             "[Set][TransportInfo]rank list is "
     614              :             "invalid. size[%zu] should be greater than myRank[%u].",
     615              :             clusterInfo.rankList.size(), localRankInfo_.rank),
     616              :         HCCL_E_INTERNAL);
     617            0 :     RankInfo_t& myRankInfo = clusterInfo.rankList[localRankInfo_.rank];
     618            0 :     TransportInfo_t transportInfo = {};
     619              : 
     620            0 :     for (u32 index = 0; index < clusterInfo.rankList.size(); index++) {
     621            0 :         transportInfo.dstRankId = clusterInfo.rankList[index].rankId;
     622            0 :         HcclResult ret = DetectTransportType(myRankInfo, clusterInfo.rankList[index], transportInfo.transportType);
     623            0 :         CHK_PRT_RET(
     624              :             ret != HCCL_SUCCESS,
     625              :             HCCL_ERROR(
     626              :                 "[Set][TransportInfo]rank[%u] detect transport type failed, ret[%u]. "
     627              :                 "remote[%u]",
     628              :                 localRankInfo_.rank, ret, transportInfo.dstRankId),
     629              :             ret);
     630            0 :         myRankInfo.transportInfo.push_back(transportInfo);
     631              :     }
     632            0 :     return HCCL_SUCCESS;
     633              : }
     634              : 
     635            0 : HcclResult TopoInfoExchangeAgent::DetectTransportType(
     636              :     const RankInfo_t& localRankInfo, const RankInfo_t& remoteRankInfo, TransportType& transportType) const
     637              : {
     638            0 :     if (remoteRankInfo.serverId == localRankInfo.serverId) {
     639            0 :         transportType = TransportType::TRANS_TYPE_P2P;
     640              :     }
     641            0 :     return HCCL_SUCCESS;
     642              : }
     643              : 
     644            4 : HcclResult TopoInfoExchangeAgent::VerifyClusterInfo(RankTable_t& clusterInfo)
     645              : {
     646            4 :     std::string errormessage;
     647              : 
     648            4 :     if (clusterInfo.rankList.size() != localRankInfo_.rankSize) {
     649            2 :         errormessage = "The number of ranks[" + std::to_string(localRankInfo_.rankSize)
     650            2 :                        + "]passed by the communicator initialization interface does not match the number of ranks["
     651            4 :                        + std::to_string(clusterInfo.rankList.size())
     652            1 :                        + "]obtained during cluster information negotiction.";
     653            1 :         HCCL_ERROR(
     654              :             "[%s][%s]%s", LOG_KEYWORDS_INIT_GROUP.c_str(), LOG_KEYWORDS_RANKTABLE_DETECT.c_str(), errormessage.c_str());
     655            1 :         return HCCL_E_PARA;
     656              :     }
     657              : 
     658            3 :     if (clusterInfo.rankNum != localRankInfo_.rankSize) {
     659            2 :         errormessage = "The number of ranks[" + std::to_string(localRankInfo_.rankSize)
     660            2 :                        + "]passed by the communicator initialization interface does not match the number of ranks["
     661            3 :                        + std::to_string(clusterInfo.rankNum) + "] obtained during cluster information negotiction.";
     662            1 :         HCCL_ERROR(
     663              :             "[%s][%s]%s", LOG_KEYWORDS_INIT_GROUP.c_str(), LOG_KEYWORDS_RANKTABLE_DETECT.c_str(), errormessage.c_str());
     664            1 :         return HCCL_E_PARA;
     665              :     }
     666              : 
     667            2 :     if (clusterInfo.serverNum != clusterInfo.serverList.size()) {
     668            4 :         errormessage = "server num[" + std::to_string(clusterInfo.serverNum) + "] is different with server list size["
     669            6 :                        + std::to_string(clusterInfo.serverList.size()) + "] in total topo rank info";
     670           14 :         RPT_INPUT_ERR(
     671              :             true, "EI0015", std::vector<std::string>({"error_reason"}), std::vector<std::string>({errormessage}));
     672            2 :         HCCL_ERROR(
     673              :             "[%s][%s]%s", LOG_KEYWORDS_INIT_GROUP.c_str(), LOG_KEYWORDS_RANKTABLE_DETECT.c_str(), errormessage.c_str());
     674            2 :         return HCCL_E_PARA;
     675              :     }
     676              : 
     677            0 :     if (clusterInfo.nicDeploy != localRankInfo_.nicDeploy) {
     678            0 :         errormessage = "nicDeploy[" + std::to_string(static_cast<int>(localRankInfo_.nicDeploy))
     679            0 :                        + "] is different with nicDeploy[" + std::to_string(static_cast<int>(clusterInfo.nicDeploy))
     680            0 :                        + "] in total topo rank info";
     681            0 :         RPT_INPUT_ERR(
     682              :             true, "EI0015", std::vector<std::string>({"error_reason"}), std::vector<std::string>({errormessage}));
     683            0 :         HCCL_ERROR(
     684              :             "[%s][%s]%s", LOG_KEYWORDS_INIT_GROUP.c_str(), LOG_KEYWORDS_RANKTABLE_DETECT.c_str(), errormessage.c_str());
     685            0 :         return HCCL_E_PARA;
     686              :     }
     687              : 
     688            0 :     CHK_RET(VerifyClusterRankID(clusterInfo));
     689            0 :     if (localRankInfo_.nicDeploy == NICDeployment::NIC_DEPLOYMENT_DEVICE) {
     690            0 :         CHK_RET(VerifyClusterDeviceIP(clusterInfo));
     691            0 :         CHK_RET(VerifyClusterBackupDeviceIP(clusterInfo));
     692              :     }
     693            0 :     std::map<std::string, std::vector<RankInfo_t>> serverMap;
     694            0 :     for (uint32_t i = 0; i < clusterInfo.rankList.size(); i++) {
     695            0 :         auto iter = serverMap.find(clusterInfo.rankList[i].serverId);
     696            0 :         if (iter == serverMap.end()) {
     697            0 :             std::vector<RankInfo_t> vec;
     698            0 :             vec.push_back(clusterInfo.rankList[i]);
     699            0 :             serverMap.insert({clusterInfo.rankList[i].serverId, vec});
     700            0 :         } else {
     701            0 :             serverMap[clusterInfo.rankList[i].serverId].push_back(clusterInfo.rankList[i]);
     702              :         }
     703              :     }
     704              : 
     705            0 :     if (clusterInfo.serverNum != serverMap.size()) {
     706            0 :         errormessage = "server num[" + std::to_string(clusterInfo.serverNum) + "] is different with server num["
     707            0 :                        + std::to_string(serverMap.size()) + "] in total topo rank info";
     708            0 :         RPT_INPUT_ERR(
     709              :             true, "EI0015", std::vector<std::string>({"error_reason"}), std::vector<std::string>({errormessage}));
     710            0 :         HCCL_ERROR(
     711              :             "[%s][%s]%s", LOG_KEYWORDS_INIT_GROUP.c_str(), LOG_KEYWORDS_RANKTABLE_DETECT.c_str(), errormessage.c_str());
     712            0 :         return HCCL_E_PARA;
     713              :     }
     714              : 
     715            0 :     uint32_t deviceNumInServer = 0;
     716            0 :     for (auto& server : serverMap) {
     717            0 :         CHK_PRT_RET(
     718              :             (server.second.size() == 0),
     719              :             HCCL_ERROR(
     720              :                 "[%s][%s]server ip[%s] has %u device.", LOG_KEYWORDS_INIT_GROUP.c_str(),
     721              :                 LOG_KEYWORDS_RANKTABLE_DETECT.c_str(), server.first.c_str(), server.second.size()),
     722              :             HCCL_E_PARA);
     723              : 
     724            0 :         if (deviceNumInServer != 0) {
     725            0 :             HCCL_WARNING(
     726              :                 "[%s][%s]server ip[%s] has %u devices, other server has %u.", LOG_KEYWORDS_INIT_GROUP.c_str(),
     727              :                 LOG_KEYWORDS_RANKTABLE_DETECT.c_str(), server.first.c_str(), server.second.size(), deviceNumInServer);
     728              :         }
     729            0 :         deviceNumInServer = server.second.size();
     730            0 :         HcclResult ret = VerifyServerDevicePhysicID(server.second);
     731            0 :         CHK_PRT_RET(
     732              :             ret != HCCL_SUCCESS,
     733              :             HCCL_ERROR(
     734              :                 "[%s][%s]server id[%s] verify device physic id failed.", LOG_KEYWORDS_INIT_GROUP.c_str(),
     735              :                 LOG_KEYWORDS_RANKTABLE_DETECT.c_str(), server.first.c_str()),
     736              :             HCCL_E_PARA);
     737              :     }
     738              : 
     739            0 :     bool useSuperPodMode = false;
     740            0 :     CHK_RET(IsSuperPodMode(useSuperPodMode));
     741              :     bool isSinglePodInterHccs
     742            0 :         = clusterInfo.superPodNum == 1 && GetExternalInputInterHccsDisable() == false && useSuperPodMode;
     743              :     // 单超节点,并且节点间走HCCS场景,不校验ip family
     744            0 :     if (clusterInfo.serverNum > 1 && !isSinglePodInterHccs) {
     745            0 :         CHK_RET(CheckRankIpFamily(clusterInfo.rankList));
     746              :     }
     747              : 
     748              :     // 超节点校验
     749            0 :     CHK_RET(VerifyClusterSuperPodInfo(clusterInfo.rankList));
     750              : 
     751              :     // TLS开关一致性校验
     752            0 :     CHK_RET(VerifyClusterTlsConsistency(clusterInfo));
     753            0 :     return HCCL_SUCCESS;
     754            6 : }
     755              : 
     756            1 : HcclResult TopoInfoExchangeAgent::VerifyClusterDeviceIP(const RankTable_t& clusterInfo)
     757              : {
     758            1 :     if (clusterInfo.rankList.size() == 1) {
     759            0 :         return HCCL_SUCCESS;
     760              :     }
     761            1 :     if (clusterInfo.serverList.size() == 1) {
     762              :         // 单机场景对 device ip不做要求
     763            0 :         return HCCL_SUCCESS;
     764              :     }
     765            1 :     bool useSuperPodMode = false;
     766            1 :     CHK_RET(IsSuperPodMode(useSuperPodMode));
     767            1 :     if (clusterInfo.superPodNum == 1 && GetExternalInputInterHccsDisable() == false && useSuperPodMode) {
     768              :         // 单超节点,并且节点间走HCCS场景,device ip不做要求
     769            0 :         return HCCL_SUCCESS;
     770              :     }
     771            1 :     for (u32 i = 0; i < (clusterInfo.rankList.size() - 1); i++) {
     772            1 :         for (u32 j = (i + 1); j < clusterInfo.rankList.size(); j++) {
     773            2 :             bool isErr = HasRepeatedIP(
     774            1 :                 clusterInfo.rankList[i].deviceInfo.deviceIp, clusterInfo.rankList[j].deviceInfo.deviceIp);
     775            1 :             if (isErr) {
     776              :                 std::string errormessage = "The device IP address "
     777            2 :                                            + std::string(clusterInfo.rankList[i].deviceInfo.deviceIp[0].GetReadableIP())
     778            4 :                                            + " of rank " + std::to_string(clusterInfo.rankList[i].rankId) + " on node "
     779            3 :                                            + clusterInfo.rankList[i].serverId + " is the same as the device IP address "
     780            4 :                                            + std::string(clusterInfo.rankList[j].deviceInfo.deviceIp[0].GetReadableIP())
     781            4 :                                            + " of rank " + std::to_string(clusterInfo.rankList[j].rankId) + " on node "
     782            2 :                                            + clusterInfo.rankList[j].serverId;
     783            7 :                 RPT_INPUT_ERR(
     784              :                     true, "EI0015", std::vector<std::string>({"error_reason"}),
     785              :                     std::vector<std::string>({errormessage}));
     786              : 
     787            1 :                 HCCL_ERROR(
     788              :                     "[%s][%s]%s", LOG_KEYWORDS_INIT_GROUP.c_str(), LOG_KEYWORDS_RANKTABLE_DETECT.c_str(),
     789              :                     errormessage.c_str());
     790            1 :                 return HCCL_E_PARA;
     791            1 :             }
     792              :         }
     793              :     }
     794            0 :     return HCCL_SUCCESS;
     795            1 : }
     796              : 
     797            1 : HcclResult TopoInfoExchangeAgent::VerifyClusterBackupDeviceIP(RankTable_t& clusterInfo)
     798              : {
     799            1 :     if (localRankInfo_.deviceType != DevType::DEV_TYPE_910_93 || !isRetry_) {
     800              :         // 未开启重执行,则无需 backup device ip
     801            0 :         return HCCL_SUCCESS;
     802              :     }
     803            1 :     bool useSuperPodMode = false;
     804            1 :     CHK_RET(IsSuperPodMode(useSuperPodMode));
     805            1 :     if (!useSuperPodMode || clusterInfo.superPodNum == 1) {
     806              :         // 非多超节点场景,backup device ip 不做要求
     807            0 :         return HCCL_SUCCESS;
     808              :     }
     809            1 :     if (clusterInfo.rankList.size() == 1 || clusterInfo.serverList.size() == 1) {
     810              :         // 单卡或单机场景对 device ip 不做要求
     811            0 :         return HCCL_SUCCESS;
     812              :     }
     813              : 
     814            1 :     std::unordered_map<std::string, s32> devIp2PhyId;
     815            3 :     for (auto& rankInfo : clusterInfo.rankList) {
     816            4 :         for (auto& devIp : rankInfo.deviceInfo.deviceIp) {
     817            2 :             devIp2PhyId.emplace(devIp.GetReadableIP(), rankInfo.deviceInfo.devicePhyId);
     818              :         }
     819              :     }
     820              : 
     821            3 :     for (auto& rankInfo : clusterInfo.rankList) {
     822            4 :         for (auto& backupDevIp : rankInfo.deviceInfo.backupDeviceIp) {
     823            2 :             if (backupDevIp.IsInvalid()) {
     824            1 :                 continue;
     825              :             }
     826            2 :             std::string backupIpStr = std::string(backupDevIp.GetReadableIP());
     827            2 :             if (devIp2PhyId.find(backupIpStr) == devIp2PhyId.end()) {
     828            1 :                 HCCL_RUN_WARNING(
     829              :                     "[Verify][ClusterBackupDeviceIP]"
     830              :                     "backup devIp[%s] for devicePhyId[%d] is not in this comm. "
     831              :                     "The validation of this backup ip could not be verified! "
     832              :                     "Please notice it might be an invalid backup ip!",
     833              :                     backupIpStr.c_str(), rankInfo.deviceInfo.devicePhyId);
     834            1 :                 continue;
     835              :             }
     836              : 
     837            1 :             s32 backupDevPhyId = devIp2PhyId[backupIpStr];
     838            1 :             std::string errormessage;
     839            1 :             if (backupDevPhyId == rankInfo.deviceInfo.devicePhyId) {
     840            0 :                 errormessage = "PhyId[" + std::to_string(backupDevPhyId) + "] for backup devIp[" + backupIpStr
     841            0 :                                + "] is the same with self devicephyId["
     842            0 :                                + std::to_string(rankInfo.deviceInfo.devicePhyId)
     843            0 :                                + "]. Please do not use self ip as backup ip";
     844            0 :                 RPT_INPUT_ERR(
     845              :                     true, "EI0015", std::vector<std::string>({"error_reason"}),
     846              :                     std::vector<std::string>({errormessage}));
     847            0 :                 HCCL_ERROR(
     848              :                     "[%s][%s]errNo[0x%016llx], %s", LOG_KEYWORDS_INIT_GROUP.c_str(),
     849              :                     LOG_KEYWORDS_RANKTABLE_DETECT.c_str(), HCOM_ERROR_CODE(HCCL_E_PARA), errormessage.c_str());
     850            0 :                 return HCCL_E_PARA;
     851              :             }
     852              : 
     853            1 :             LinkTypeInServer linkType = LinkTypeInServer::RESERVED_LINK_TYPE;
     854            1 :             CHK_RET(hrtGetPairDeviceLinkType(rankInfo.deviceInfo.devicePhyId, backupDevPhyId, linkType));
     855            1 :             if (linkType != LinkTypeInServer::SIO_TYPE) {
     856            0 :                 RPT_INPUT_ERR(
     857              :                     true, "EI0014", std::vector<std::string>({"value", "variable", "expect"}),
     858              :                     std::vector<std::string>(
     859              :                         {std::to_string(backupDevPhyId),
     860              :                          " \"backup_device_ip of "
     861              :                          "rank "
     862              :                              + std::to_string(rankInfo.rankId) + "\" ",
     863              :                          " \"is device_ip another Die under the same NPU\" "}));
     864            0 :                 errormessage = "Value " + std::to_string(backupDevPhyId)
     865            0 :                                + " for rankTable variable \"backup_device_ip of "
     866            0 :                                  "rank "
     867            0 :                                + std::to_string(rankInfo.rankId)
     868            0 :                                + "\" is invalid, expected value \"is device_ip another Die under the same NPU\".";
     869              : 
     870            0 :                 HCCL_ERROR(
     871              :                     "[%s][%s]errNo[0x%016llx], %s", LOG_KEYWORDS_INIT_GROUP.c_str(),
     872              :                     LOG_KEYWORDS_RANKTABLE_CHECK.c_str(), HCOM_ERROR_CODE(HCCL_E_PARA), errormessage.c_str());
     873            0 :                 return HCCL_E_PARA;
     874              :             }
     875            2 :         }
     876              :     }
     877            1 :     return HCCL_SUCCESS;
     878            1 : }
     879              : 
     880            1 : bool TopoInfoExchangeAgent::HasRepeatedIP(
     881              :     const std::vector<HcclIpAddress>& deviceAIP, const std::vector<HcclIpAddress>& deviceBIP) const
     882              : {
     883            1 :     for (u32 i = 0; i < deviceAIP.size(); i++) {
     884            1 :         for (u32 j = 0; j < deviceBIP.size(); j++) {
     885            1 :             if (deviceAIP[i] == deviceBIP[j]) {
     886            1 :                 HCCL_WARNING("device ip[%s] is repeated.", deviceAIP[i].GetReadableAddress());
     887            1 :                 return true;
     888              :             }
     889              :         }
     890              :     }
     891            0 :     return false;
     892              : }
     893              : 
     894            1 : HcclResult TopoInfoExchangeAgent::VerifyClusterRankID(const RankTable_t& clusterInfo) const
     895              : {
     896            1 :     if (clusterInfo.rankList.size() == 1) {
     897            0 :         return HCCL_SUCCESS;
     898              :     }
     899            1 :     for (u32 i = 0; i < (clusterInfo.rankList.size() - 1); i++) {
     900            1 :         for (u32 j = (i + 1); j < clusterInfo.rankList.size(); j++) {
     901            1 :             bool isErr = (clusterInfo.rankList[i].rankId == clusterInfo.rankList[j].rankId);
     902            1 :             if (isErr) {
     903              :                 std::string errormessage
     904            2 :                     = "Rank ID " + std::to_string(clusterInfo.rankList[i].rankId) + " of device ID "
     905            4 :                       + std::to_string(clusterInfo.rankList[i].deviceInfo.devicePhyId) + " on node "
     906            3 :                       + clusterInfo.rankList[i].serverId + " is the same as that of device ID "
     907            4 :                       + std::to_string(clusterInfo.rankList[j].deviceInfo.devicePhyId) + " on node "
     908            2 :                       + clusterInfo.rankList[j].serverId;
     909            7 :                 RPT_INPUT_ERR(
     910              :                     true, "EI0015", std::vector<std::string>({"error_reason"}),
     911              :                     std::vector<std::string>({errormessage}));
     912            1 :                 HCCL_ERROR(
     913              :                     "[%s][%s]%s", LOG_KEYWORDS_INIT_GROUP.c_str(), LOG_KEYWORDS_RANKTABLE_DETECT.c_str(),
     914              :                     errormessage.c_str());
     915            1 :                 return HCCL_E_PARA;
     916            1 :             }
     917              :         }
     918              :     }
     919            0 :     return HCCL_SUCCESS;
     920            1 : }
     921              : 
     922            1 : HcclResult TopoInfoExchangeAgent::VerifyServerDevicePhysicID(const std::vector<RankInfo_t>& serverInfo) const
     923              : {
     924            1 :     if (serverInfo.size() == 1) {
     925            0 :         return HCCL_SUCCESS;
     926              :     }
     927            1 :     for (u32 i = 0; i < (serverInfo.size() - 1); i++) {
     928            1 :         for (u32 j = (i + 1); j < serverInfo.size(); j++) {
     929            1 :             bool isErr = (serverInfo[i].deviceInfo.devicePhyId == serverInfo[j].deviceInfo.devicePhyId);
     930            1 :             if (isErr) {
     931            2 :                 std::string errormessage = "Rank " + std::to_string(serverInfo[i].rankId) + " of node "
     932            3 :                                            + serverInfo[i].serverId + " has the same physical device ID "
     933            4 :                                            + std::to_string(serverInfo[i].deviceInfo.devicePhyId) + " as the rank "
     934            3 :                                            + std::to_string(serverInfo[j].rankId);
     935            7 :                 RPT_INPUT_ERR(
     936              :                     true, "EI0015", std::vector<std::string>({"error_reason"}),
     937              :                     std::vector<std::string>({errormessage}));
     938            1 :                 HCCL_ERROR(
     939              :                     "[%s][%s]%s", LOG_KEYWORDS_INIT_GROUP.c_str(), LOG_KEYWORDS_RANKTABLE_DETECT.c_str(),
     940              :                     errormessage.c_str());
     941            1 :                 return HCCL_E_PARA;
     942            1 :             }
     943              :         }
     944              :     }
     945            0 :     return HCCL_SUCCESS;
     946            1 : }
     947              : 
     948            2 : HcclResult TopoInfoExchangeAgent::VerifyClusterSuperPodInfo(const std::vector<RankInfo_t>& rankInfo) const
     949              : {
     950            2 :     DevType curDevType = rankInfo.begin()->deviceInfo.deviceType;
     951            5 :     for (auto curRankInfo : rankInfo) {
     952            3 :         if (curDevType != curRankInfo.deviceInfo.deviceType) {
     953            0 :             HCCL_DEBUG("[Verify][SuperPodInfo] mix device type, does not need verify superPod info");
     954            0 :             return HCCL_SUCCESS;
     955              :         }
     956            3 :     }
     957              : 
     958            2 :     bool useSuperPodMode = false;
     959            2 :     CHK_RET(IsSuperPodMode(useSuperPodMode));
     960            2 :     CHK_PRT_RET(
     961              :         useSuperPodMode == false, HCCL_DEBUG("[Verify][SuperPodInfo] does not need verify superPod info"),
     962              :         HCCL_SUCCESS);
     963              : 
     964            2 :     std::string errormessage = "";
     965              :     // 获取每个超节点内的serverId
     966            2 :     std::map<std::string, std::set<std::string>> superPodSrvIdMap;       // super_pod_id -> serverId
     967            2 :     std::map<std::string, std::unordered_map<u32, u32>> superPodSdidMap; // super_pod_id -> superDeviceId
     968            3 :     for (u32 i = 0; i < rankInfo.size(); i++) {
     969              :         // 超节点模式下, 校验superPodId和sdid值有效
     970            5 :         if ((rankInfo[i].superPodId.empty() || rankInfo[i].superDeviceId == INVALID_UINT)
     971            5 :             && rankInfo[i].deviceInfo.deviceType == DevType::DEV_TYPE_910_93) {
     972           14 :             RPT_INPUT_ERR(
     973              :                 true, "EI0014", std::vector<std::string>({"value", "variable", "expect"}),
     974              :                 std::vector<std::string>(
     975              :                     {std::to_string(rankInfo[i].superDeviceId), "super_device_id",
     976              :                      "is less than the communication size " + std::to_string(rankInfo.size())
     977              :                          + " and must be unique"}));
     978            2 :             errormessage = "Value " + std::to_string(rankInfo[i].superDeviceId)
     979            1 :                            + " for rankTable variable superDeviceId is invalid, "
     980            1 :                              "expected value is less than the communication size "
     981            3 :                            + std::to_string(rankInfo.size()) + " and must be unique.";
     982              : 
     983            1 :             HCCL_ERROR(
     984              :                 "[%s][%s]%s", LOG_KEYWORDS_INIT_GROUP.c_str(), LOG_KEYWORDS_RANKTABLE_CHECK.c_str(),
     985              :                 errormessage.c_str());
     986            2 :             return HCCL_E_PARA;
     987              :         }
     988              : 
     989            2 :         auto iter = superPodSrvIdMap.find(rankInfo[i].superPodId);
     990            2 :         if (iter == superPodSrvIdMap.end()) {
     991            1 :             std::set<std::string> serverIdSet;
     992            1 :             serverIdSet.insert(rankInfo[i].serverId);
     993            1 :             superPodSrvIdMap.insert({rankInfo[i].superPodId, serverIdSet});
     994            2 :         } else if (iter->second.find(rankInfo[i].serverId) == iter->second.end()) {
     995            0 :             iter->second.insert(rankInfo[i].serverId);
     996              :         }
     997              : 
     998            2 :         auto it = superPodSdidMap.find(rankInfo[i].superPodId);
     999            2 :         if (it == superPodSdidMap.end()) {
    1000            1 :             std::unordered_map<u32, u32> superDeviceIdSet;
    1001            1 :             superDeviceIdSet.insert({rankInfo[i].superDeviceId, rankInfo[i].rankId});
    1002            1 :             superPodSdidMap.insert({rankInfo[i].superPodId, superDeviceIdSet});
    1003            2 :         } else if (it->second.find(rankInfo[i].superDeviceId) == it->second.end()) {
    1004            0 :             it->second.insert({rankInfo[i].superDeviceId, rankInfo[i].rankId});
    1005              :         } else {
    1006              :             // 超节点内superDeviceId在超节点内唯一
    1007            1 :             if (it->second.find(rankInfo[i].superDeviceId) != it->second.end()) {
    1008           15 :                 RPT_INPUT_ERR(
    1009              :                     true, "EI0014", std::vector<std::string>({"value", "variable", "expect"}),
    1010              :                     std::vector<std::string>(
    1011              :                         {std::to_string(rankInfo[i].superDeviceId),
    1012              :                          " \"Device Id of server Id " + rankInfo[i].serverId + "\" ", "is unique"}));
    1013            2 :                 errormessage = "Value " + std::to_string(rankInfo[i].superDeviceId)
    1014            1 :                                + " for rankTable "
    1015            1 :                                  "variable \"Device Id of server Id "
    1016            2 :                                + rankInfo[i].serverId + "\" is invalid, expected value is unique.";
    1017            1 :                 HCCL_ERROR(
    1018              :                     "[%s][%s]%s", LOG_KEYWORDS_INIT_GROUP.c_str(), LOG_KEYWORDS_RANKTABLE_CHECK.c_str(),
    1019              :                     errormessage.c_str());
    1020            1 :                 return HCCL_E_PARA;
    1021              :             }
    1022              :         }
    1023              :     }
    1024              : 
    1025              :     // 校验每个超节点内的server数量一致
    1026            0 :     u32 serverNumPerPod = 0;
    1027            0 :     for (auto iter = superPodSrvIdMap.begin(); iter != superPodSrvIdMap.end(); ++iter) {
    1028            0 :         if (iter == superPodSrvIdMap.begin()) {
    1029            0 :             serverNumPerPod = superPodSrvIdMap.begin()->second.size();
    1030              :         }
    1031            0 :         u32 serverNumCurPod = iter->second.size();
    1032            0 :         if (serverNumPerPod != serverNumCurPod) {
    1033            0 :             HCCL_DEBUG(
    1034              :                 "[Verify][SuperPodInfo]serverNum[%u] in superPod[%s] and serverNum[%u] in superPod[%s] "
    1035              :                 "are different.",
    1036              :                 serverNumPerPod, superPodSrvIdMap.begin()->first.c_str(), serverNumCurPod, iter->first.c_str());
    1037              :         }
    1038              :     }
    1039              : 
    1040            0 :     return HCCL_SUCCESS;
    1041            8 : }
    1042              : 
    1043            7 : HcclResult TopoInfoExchangeAgent::VerifyClusterTlsConsistency(const RankTable_t& clusterInfo)
    1044              : {
    1045            7 :     bool isSupportCheckTlsStatus = true; // 用于标识是否存在不支持查询Tls开关状态的情况
    1046            7 :     bool isTlsConsistent = true;         // 用于标识TLS开关状态是否一致
    1047            7 :     std::unordered_map<std::string, std::vector<u32>> tlsEnableRank;
    1048            7 :     std::unordered_map<std::string, std::vector<u32>> tlsDisableRank;
    1049            7 :     std::unordered_map<std::string, std::vector<u32>> tlsUnknownRank;
    1050           35 :     for (auto& rankInfo : clusterInfo.rankList) {
    1051           28 :         if (rankInfo.tlsStatus == TlsStatus::ENABLE) {
    1052           15 :             AddRankInfoToTlsStatusMap(rankInfo, tlsEnableRank);
    1053           13 :         } else if (rankInfo.tlsStatus == TlsStatus::DISABLE) {
    1054            9 :             AddRankInfoToTlsStatusMap(rankInfo, tlsDisableRank);
    1055              :         } else {
    1056            4 :             isSupportCheckTlsStatus = false;
    1057            4 :             AddRankInfoToTlsStatusMap(rankInfo, tlsUnknownRank);
    1058              :         }
    1059              :     }
    1060              :     // 将不一致的卡信息汇总成一个string
    1061           14 :     std::string tlsInconsistentEnableStr = "";
    1062           14 :     std::string tlsInconsistentDisableStr = "";
    1063            7 :     std::string tlsInconsistentTlsType = "";
    1064            7 :     if (!tlsEnableRank.empty() && !tlsDisableRank.empty()) {
    1065            5 :         isTlsConsistent = false;
    1066            5 :         tlsInconsistentTlsType = (tlsEnableRank.size() >= tlsDisableRank.size()) ? "Disable" : "Enable";
    1067            5 :         GenerateTlsStatusStr(tlsInconsistentEnableStr, tlsEnableRank);
    1068            5 :         GenerateTlsStatusStr(tlsInconsistentDisableStr, tlsDisableRank);
    1069              :     }
    1070              :     // 将不支持查询的卡的信息汇总成一个string
    1071            7 :     std::string tlsUnknownRankStr = "";
    1072            7 :     if (!isSupportCheckTlsStatus) {
    1073            3 :         GenerateTlsStatusStr(tlsUnknownRankStr, tlsUnknownRank);
    1074              :     }
    1075           11 :     tlsUnknownRankStr = tlsUnknownRankStr.empty() ? "N/A" : tlsUnknownRankStr;
    1076              :     // 四种不同情况
    1077            7 :     if (isTlsConsistent && isSupportCheckTlsStatus) {
    1078              :         // 1.通信域所有卡都支持查询TLS开关状态,并且TLS开关状态都是一致的。
    1079            1 :         HCCL_INFO("[Verify][TlsConsistency] All ranks tlsStatus are consistent");
    1080            6 :     } else if (!isTlsConsistent && isSupportCheckTlsStatus) {
    1081              :         // 2.通信域所有卡都支持查询TLS开关状态,但是TLS开关状态存在不一致,报错。
    1082            3 :         ReportTlsConfigurationError(tlsInconsistentTlsType, tlsInconsistentEnableStr, tlsInconsistentDisableStr, "N/A");
    1083            3 :         return HCCL_E_PARA;
    1084            3 :     } else if (isTlsConsistent && !isSupportCheckTlsStatus) {
    1085              :         // 3.通信域内的部分卡不支持查询TLS开关状态,目前能查询到的卡的TLS开关状态是一致的,打印warning提醒
    1086            1 :         HCCL_RUN_WARNING(
    1087              :             "[Verify][TlsConsistency] Some ranks do not support to check tlsStatus, "
    1088              :             "not support serverId/rankId: %s",
    1089              :             tlsUnknownRankStr.c_str());
    1090              :     } else {
    1091              :         // 4.通信域内的部分卡不支持查询TLS开关状态,但是目前能查询到的卡的TLS开关状态已经不一致,报错
    1092            2 :         ReportTlsConfigurationError(
    1093              :             tlsInconsistentTlsType, tlsInconsistentEnableStr, tlsInconsistentDisableStr, tlsUnknownRankStr);
    1094            2 :         return HCCL_E_PARA;
    1095              :     }
    1096            2 :     return HCCL_SUCCESS;
    1097            7 : }
    1098              : 
    1099           28 : void TopoInfoExchangeAgent::AddRankInfoToTlsStatusMap(
    1100              :     const RankInfo_t& rankInfo, std::unordered_map<std::string, std::vector<u32>>& tlsStatusRankMap)
    1101              : {
    1102           28 :     auto iter = tlsStatusRankMap.find(rankInfo.serverId);
    1103           28 :     if (iter == tlsStatusRankMap.end()) {
    1104           15 :         std::vector<u32> tlsStatusRankList;
    1105           15 :         tlsStatusRankList.push_back(rankInfo.rankId);
    1106           15 :         tlsStatusRankMap.insert({rankInfo.serverId, tlsStatusRankList});
    1107           15 :     } else {
    1108           13 :         iter->second.push_back(rankInfo.rankId);
    1109              :     }
    1110           56 :     return;
    1111              : }
    1112              : 
    1113           13 : void TopoInfoExchangeAgent::GenerateTlsStatusStr(
    1114              :     std::string& tlsStatusStr, const std::unordered_map<std::string, std::vector<u32>>& tlsStatusRankMap)
    1115              : {
    1116           26 :     for (const auto& rankIt : tlsStatusRankMap) {
    1117           13 :         tlsStatusStr += ("[" + rankIt.first + "/");
    1118           35 :         for (const auto& rank : rankIt.second) {
    1119           22 :             tlsStatusStr += std::to_string(rank) + ",";
    1120              :         }
    1121           13 :         if (!tlsStatusStr.empty() && tlsStatusStr.back() == ',') {
    1122           13 :             tlsStatusStr = tlsStatusStr.substr(0, tlsStatusStr.size() - 1); // 删除逗号
    1123              :         }
    1124           13 :         tlsStatusStr += "];";
    1125              :     }
    1126           13 :     return;
    1127              : }
    1128              : 
    1129            5 : void TopoInfoExchangeAgent::ReportTlsConfigurationError(
    1130              :     const std::string& tlsInconsistentTlsType, const std::string& tlsInconsistentEnableStr,
    1131              :     const std::string& tlsInconsistentDisableStr, const std::string& tlsUnknownRankStr)
    1132              : {
    1133              :     std::string errormessage
    1134           10 :         = "Value " + tlsInconsistentTlsType
    1135            5 :           + " for config \"tls\" is invalid. Expected: \"All ranks are consistent. Current status: "
    1136            5 :             "rankList for enabled tls: "
    1137           10 :           + tlsInconsistentEnableStr + " rankList for disabled tls:" + tlsInconsistentDisableStr
    1138            5 :           + " rankList for query failure tls:" + tlsUnknownRankStr + ".\"";
    1139           70 :     RPT_INPUT_ERR(
    1140              :         true, "EI0016", std::vector<std::string>({"value", "variable", "expect"}),
    1141              :         std::vector<std::string>(
    1142              :             {tlsInconsistentTlsType, " \"tls\" ",
    1143              :              " \"All ranks are consistent. Current status: rankList for enabled tls:" + tlsInconsistentEnableStr
    1144              :                  + "; "
    1145              :                    "rankList for disabled tls:"
    1146              :                  + tlsInconsistentDisableStr + " rankList for query failure tls:" + tlsUnknownRankStr + ".\" "}));
    1147            5 :     HCCL_ERROR(
    1148              :         "[%s][%s] %s", LOG_KEYWORDS_INIT_GROUP.c_str(), LOG_KEYWORDS_RANKTABLE_CHECK.c_str(), errormessage.c_str());
    1149           20 : }
    1150              : } // namespace hccl
        

Generated by: LCOV version 2.0-1