* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
* \file dump_host_topo.cpp
* \brief
*/
#include "dump_host_topo.h"
#include <fstream>
#include <initializer_list>
#include <set>
#include <sstream>
#include <string>
#include <utility>
#include "interface/configs/config_manager.h"
#include "interface/function/function.h"
#include "interface/tensor/tensor_slot.h"
#include "interface/utils/file_utils.h"
#include "interface/utils/string_utils.h"
#include "machine/utils/dynamic/dev_encode_function.h"
#include "machine/utils/dynamic/dev_encode_operation.h"
#include "machine/utils/dynamic/dev_encode_tensor.h"
#include "tilefwk/pypto_fwk_log.h"
namespace npu::tile_fwk::topo_dump {
namespace {
constexpr const char* kDepVerifyDumpSubDir = "dep_verify_dump";
const std::string& DepVerifyDumpDir()
{
static const std::string dir = []() {
std::string d = config::LogTopFolder() + "/" + kDepVerifyDumpSubDir;
(void)CreateDir(d);
return d;
}();
return dir;
}
inline bool DumpEnabled()
{
static const bool cached =
(config::GetDebugOption<int64_t>(CFG_RUNTIME_DBEUG_MODE) == CFG_DEBUG_VERIFY);
return cached;
}
std::string CsvQuote(const std::string& s)
{
std::string out;
out.reserve(s.size() + 2);
out.push_back('"');
for (char c : s) {
if (c == '"') {
out.push_back('"');
}
out.push_back(c);
}
out.push_back('"');
return out;
}
std::ofstream OpenCsv(const std::string& fileName,
std::initializer_list<const char*> header,
std::string& outPath, bool enabled)
{
std::ofstream ofs;
if (!enabled) {
return ofs;
}
outPath = DepVerifyDumpDir() + "/" + fileName;
ofs.open(outPath);
if (!ofs.is_open()) {
return ofs;
}
bool first = true;
for (const char* col : header) {
if (!first) {
ofs << ',';
}
ofs << col;
first = false;
}
ofs << '\n';
return ofs;
}
void WriteCellMatchDesc(std::ostream& os, const dynamic::DevCellMatchTableDesc& desc)
{
int dim = desc.GetDimensionSize();
os << '[';
for (int d = 0; d < dim; ++d) {
if (d != 0) {
os << ';';
}
os << desc.GetCellShape(d);
}
os << "]," << desc.GetStride(0);
}
void CollectTensorAndFuncNamesPerFrontendSlot(
const TensorSlotManager& slotManager,
std::unordered_map<int, std::string>& feSlotToTensorName,
std::unordered_map<int, std::string>& feSlotToFuncName)
{
for (const auto& kv : slotManager.slotIndexDict) {
int feIdx = kv.second;
auto nameIt = slotManager.slotNameDict.find(kv.first);
if (nameIt != slotManager.slotNameDict.end() && !nameIt->second.empty()) {
StringUtils::AppendUniqueToken(feSlotToTensorName[feIdx], nameIt->second);
}
auto funcIt = slotManager.slotFuncNameDict.find(kv.first);
if (funcIt == slotManager.slotFuncNameDict.end() || funcIt->second.empty()) {
continue;
}
const std::string& joined = funcIt->second;
size_t start = 0;
while (start <= joined.size()) {
size_t sep = joined.find(';', start);
size_t end = (sep == std::string::npos) ? joined.size() : sep;
if (end > start) {
StringUtils::AppendUniqueToken(feSlotToFuncName[feIdx], joined.substr(start, end - start));
}
if (sep == std::string::npos) {
break;
}
start = sep + 1;
}
}
}
}
void DumpSlotMapping(const TensorSlotManager& slotManager,
const std::unordered_map<int, int>& slotIdxMapping,
const IncastOutcastLink& inoutLink)
{
if (!DumpEnabled()) {
return;
}
std::set<int> inputRuntimeSlots(
inoutLink.inputSlotIndexList.begin(), inoutLink.inputSlotIndexList.end());
std::set<int> outputRuntimeSlots(
inoutLink.outputSlotIndexList.begin(), inoutLink.outputSlotIndexList.end());
std::unordered_map<int, std::string> feSlotToTensorName;
std::unordered_map<int, std::string> feSlotToFuncName;
CollectTensorAndFuncNamesPerFrontendSlot(slotManager, feSlotToTensorName, feSlotToFuncName);
std::ostringstream blob;
blob << "frontendSlotIdx,runtimeSlotIdx,slotRole,tensorName,funcRawName\n";
for (const auto& slot : slotIdxMapping) {
const char* role = "INTERNAL";
bool isInput = inputRuntimeSlots.count(slot.second) != 0;
bool isOutput = outputRuntimeSlots.count(slot.second) != 0;
if (isInput && isOutput) {
role = "INOUT";
} else if (isInput) {
role = "INPUT";
} else if (isOutput) {
role = "OUTPUT";
}
blob << slot.first << ',' << slot.second << ',' << role << ','
<< CsvQuote(feSlotToTensorName[slot.first]) << ','
<< CsvQuote(feSlotToFuncName[slot.first]) << '\n';
}
const std::string path = DepVerifyDumpDir() + "/slot_mapping.csv";
std::ofstream ofs(path);
if (!ofs.is_open()) {
return;
}
ofs << blob.str();
ofs.close();
MACHINE_LOGD("SlotMapping dumped to %s, total %zu entries",
path.c_str(), slotIdxMapping.size());
}
StaticTopoCsvWriter::StaticTopoCsvWriter()
{
ofs_ = OpenCsv(
"static_topo.csv",
{"funcKey", "rootHash", "rawName", "opIdx",
"incastSlots", "outcastSlots", "staticSuccessors"},
path_, DumpEnabled());
}
StaticTopoCsvWriter::~StaticTopoCsvWriter()
{
if (ofs_.is_open()) {
ofs_.close();
}
}
bool StaticTopoCsvWriter::Enabled() const
{
return ofs_.is_open();
}
void StaticTopoCsvWriter::WriteFunction(int devRootKey, dynamic::DevAscendFunction& funcBin)
{
if (!ofs_.is_open()) {
return;
}
auto& os = ofs_;
for (size_t opIdx = 0; opIdx < funcBin.GetOperationSize(); opIdx++) {
os << devRootKey << ',' << funcBin.rootHash << ','
<< CsvQuote(funcBin.GetRawName()) << ',' << opIdx << ',';
os << '[';
for (size_t i = 0; i < funcBin.GetIncastSize(); i++) {
auto& incast = funcBin.GetIncast(i);
if (i > 0) {
os << ';';
}
for (size_t j = 0; j < incast.fromSlotList.size(); j++) {
if (j > 0) {
os << '/';
}
os << funcBin.At(incast.fromSlotList, j);
}
}
os << "],[";
for (size_t i = 0; i < funcBin.GetOutcastSize(); i++) {
auto& outcast = funcBin.GetOutcast(i);
if (i > 0) {
os << ';';
}
for (size_t j = 0; j < outcast.toSlotList.size(); j++) {
if (j > 0) {
os << '/';
}
os << funcBin.At(outcast.toSlotList, j);
}
}
os << ']';
auto& succList = funcBin.GetOperationDepGraphSuccList(opIdx);
for (size_t j = 0; j < succList.size(); j++) {
os << ',' << funcBin.At(succList, j);
}
os << '\n';
}
}
SlotCellTableCsvWriter::SlotCellTableCsvWriter(bool fillContent)
{
ofs_ = OpenCsv(
"slot_cell_table.csv",
{"slotIdx", "stitchPolicy", "rootHash", "funcKey",
"cellShape", "cellCount", "outcastCount"},
path_, fillContent && DumpEnabled());
}
SlotCellTableCsvWriter::~SlotCellTableCsvWriter()
{
if (ofs_.is_open()) {
ofs_.close();
}
}
bool SlotCellTableCsvWriter::Enabled() const
{
return ofs_.is_open();
}
void SlotCellTableCsvWriter::WritePartial(int slotIdx,
const dynamic::DevCellMatchTableDesc& desc,
size_t outcastCount)
{
if (!ofs_.is_open()) {
return;
}
ofs_ << slotIdx << ",partial,0,-1,";
WriteCellMatchDesc(ofs_, desc);
ofs_ << ',' << outcastCount << '\n';
}
void SlotCellTableCsvWriter::WriteFullCover(int slotIdx, uint64_t rootHash, int funcKey,
const dynamic::DevCellMatchTableDesc& desc)
{
if (!ofs_.is_open()) {
return;
}
ofs_ << slotIdx << ",fullcover," << rootHash << ',' << funcKey << ',';
WriteCellMatchDesc(ofs_, desc);
ofs_ << ",1\n";
}
}