* Copyright (c) 2025 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 function.cpp
* \brief
*/
#include "interface/interpreter/function.h"
#include "interface/interpreter/flow_verifier.h"
#include "interface/interpreter/interpreter_log.h"
#include "interface/configs/config_manager.h"
#include "interface/utils/file_utils.h"
#include <chrono>
#include <cstdio>
#include <ctime>
#include <iomanip>
#include <sstream>
#include <vector>
namespace npu::tile_fwk {
namespace {
std::string MakeVerifyRunTimestampTag()
{
const auto now = std::chrono::high_resolution_clock::now();
const std::time_t time = std::chrono::system_clock::to_time_t(std::chrono::system_clock::now());
const auto us = std::chrono::duration_cast<std::chrono::microseconds>(now.time_since_epoch()).count() % 1000000;
std::stringstream timestamp;
timestamp << std::put_time(std::localtime(&time), "%Y%m%d_%H%M%S");
timestamp << "_" << std::setw(0x6) << std::setfill('0') << us;
return timestamp.str();
}
std::vector<std::string> MakeOpInfoCsvHeader()
{
return {
"NO.",
"PHASE_NAME",
"PATH_FUNC:func_magicname",
"PATH_FUNC:funcmagic",
"PATH_FUNC:hash",
"LOOP_INFO",
"ROOT_FUNC:functype",
"ROOT_FUNC:graphtype",
"ROOT_FUNC:funcmagic",
"ROOT_FUNC:hash",
"FUNC:functype",
"FUNC:graphtype",
"FUNC:funcmagic",
"FUNC:hash",
":rawmagic",
":rawshape",
":datatype",
":format",
":symbol",
":magic",
":offset",
":shape",
":validshape",
"EVAL:dynvalidshape",
"ROOT_CALL:opmagic",
"ROOT_CALL:rawmagic",
":opmagic",
":opcode",
"OP_ATTR_SYM_OFFSET",
"OP_ATTR_ATOMIC",
"OP_IO_FLAG",
"TIMESTAMP",
"FILENAME",
"INPUT_FILENAMES",
":inputValidShape",
":inputRawMagic",
};
}
std::vector<std::string> MakeProgrameInfoCsvHeader()
{
return {
"NO.",
"A>PHASE_NAME",
"B>PHASE_NAME",
"PATH_FUNC:func_magicname",
"PATH_FUNC:funcmagic",
"PATH_FUNC:hash",
"LOOP_INFO",
"IO_FLAG",
"A>:rawmagic",
"B>:rawmagic",
":rawshape",
":datatype",
":format",
":symbol",
":shape",
":validshape",
"A>TIMESTAMP",
"A>FILENAME",
"B>TIMESTAMP",
"B>FILENAME",
"AB>RESULT",
"AB>rtol/atol",
"AB>fail_cnt/warn_cnt/tol_cnt",
"AB>total_cnt/zero_cnt/infnan_cnt",
"AB>mre",
"AB>mre_top8",
"AB>mre_top1permil",
"AB>mae",
"AB>mae_top8",
"AB>mae_top1permil",
"A>max",
"A>min",
"A>avg",
"A>aavg",
"A>zero",
"A>infnan",
"B>max",
"B>min",
"B>avg",
"B>aavg",
"B>zero",
"B>infnan",
};
}
}
FunctionInterpreter::FunctionInterpreter()
: interpreterSyncSimulation_(std::make_shared<InterpreterSyncSimulationState>()), interpreterThreadPool_(0x3)
{
dumpPath = config::GetVerifyOption<std::string>(KEY_PASS_VERIFY_SAVE_TENSOR_DIR);
if (dumpPath.empty()) {
dumpPath = config::LogTopFolder();
}
dumpPath = dumpPath + "/" + "verify_" + MakeVerifyRunTimestampTag() + "/";
CreateMultiLevelDir(dumpPath);
interpreter::SetLogFilePath(dumpPath + "interpreter.log");
const std::string opResultFilePath = dumpPath + "verify_graph_data_metainfo.csv";
execOpResultFile = fopen(opResultFilePath.c_str(), "w");
ASSERT(OpDumpScene::DUMP_OPEN_FILE_FAILED, execOpResultFile != nullptr)
<< "open file failed : "<< opResultFilePath.c_str();
const std::string programeResultFilePath = dumpPath + "verify_graph_result_brief.csv";
execProgrameResultFile = fopen(programeResultFilePath.c_str(), "w");
ASSERT(OpDumpScene::DUMP_OPEN_FILE_FAILED, execProgrameResultFile != nullptr)
<< "open file failed : "<< programeResultFilePath.c_str();
const std::string dumpErrorFilePath = dumpPath + "verify_graph_result_brief.log";
execDumpErrorFile = fopen(dumpErrorFilePath.c_str(), "w");
ASSERT(OpDumpScene::DUMP_OPEN_FILE_FAILED, execDumpErrorFile != nullptr)
<< "open file failed : "<< dumpErrorFilePath.c_str();
auto opCsvHeader = MakeOpInfoCsvHeader();
auto programeCsvHeader = MakeProgrameInfoCsvHeader();
WriteCsvRow(opCsvHeader, opInfoRowNum, execOpResultFile);
WriteCsvRow(programeCsvHeader, ProgrameRowNum, execProgrameResultFile);
}
constexpr int MAX_IDENT_LEVEL = 20;
const std::unordered_set<std::string> copyOpCode = {
"COPY_IN", "COPY_OUT", "L1_TO_L0A", "L1_TO_L0B", "L1_TO_L0At", "FIX_COPY_IN_QUANT_PRE",
"L1_TO_L0Bt", "L0C_COPY_L1", "L1_TO_BT", "TRANSPOSE_MOVEIN", "TRANSPOSE_MOVEOUT", "INDEX_OUTCAST",
"RESHAPE_COPY_IN", "RESHAPE_COPY_OUT", "INDEX_ADD", "SHMEM_PUT", "SHMEM_LOAD", "SHMEM_SET", "SHMEM_SIGNL",
"L1_COPY_IN_A_SCALE", "L1_COPY_IN_B_SCALE", "L1_TO_L0A_SCALE", "L1_TO_L0B_SCALE"};
const std::unordered_set<std::string> convertOpCode = {
"L0C_COPY_UB", "CONVERT", "UB_COPY_ND2NZ", "UB_COPY_L1_ND", "UB_COPY_L1"};
static std::string HtmlEscape(const std::string& src, bool escapeLineBreak = true)
{
std::string ret;
for (auto& c : src) {
switch (c) {
case '<':
ret += "<";
break;
case '>':
ret += ">";
break;
case '&':
ret += "&";
break;
case '\n':
if (escapeLineBreak) {
ret += "<br/>";
}
ret.push_back(c);
break;
default:
ret.push_back(c);
}
}
return ret;
}
void FunctionInterpreter::DumpFunctionHead(Function* func)
{
if (execDumpLevel < EXEC_DUMP_LEVEL_OPERATION) {
return;
}
int indent = GetFrameSize();
auto head = func->DumpSSATitle();
auto raw = func->DumpSSARawTensor(indent);
auto incast = func->DumpSSAIncast(indent);
auto outcast = func->DumpSSAOutcast(indent);
auto attr = func->DumpSSAAttribute(indent);
auto symbol = DumpSymbolDict();
if (!execDumpFile) {
return;
}
fprintf(execDumpFile, "<div class=\"function indent_%d\">%s</div>", indent, execDumpFuncKey.c_str());
fprintf(execDumpFile, "<div class=\"function indent_%d\">frameIndex=%s</div>", indent, GetFrameCurrIndex().c_str());
fprintf(execDumpFile, "<div class=\"function indent_%d\">%s</div>", indent, HtmlEscape(head).c_str());
fprintf(execDumpFile, "<div class=\"function indent_%d\">%s</div>", indent, HtmlEscape(raw).c_str());
fprintf(execDumpFile, "<div class=\"function indent_%d\">%s</div>", indent, HtmlEscape(incast).c_str());
fprintf(execDumpFile, "<div class=\"function indent_%d\">%s</div>", indent, HtmlEscape(outcast).c_str());
fprintf(execDumpFile, "<div class=\"function indent_%d\">%s</div>", indent, HtmlEscape(attr).c_str());
fprintf(execDumpFile, "<div class=\"function indent_%d\">%s</div>", indent, HtmlEscape(symbol).c_str());
}
void FunctionInterpreter::DumpOperation(Operation* op)
{
if (execDumpLevel < EXEC_DUMP_LEVEL_OPERATION)
return;
int indent = GetFrameSize();
auto dump = op->Dump();
if (execDumpFile) {
std::string tensorId = GetDumpTensorId(GetFrameCurr(), op);
std::string operationId = GetDumpOperationId(GetFrameCurr(), op);
fprintf(
execDumpFile,
"<div class=\"indent_%d\" id=\"%s\">"
" <div class=\"operation\" id=\"%s\">%s</div>"
"</div>\n",
indent, tensorId.c_str(), operationId.c_str(), HtmlEscape(dump).c_str());
}
}
std::string FunctionInterpreter::GetDumpFilePath(
const std::string& lv0, const std::string& lv1, const std::string& filename)
{
std::string baseDirName = lv0 + "/" + lv1;
if (!IsPathExist(baseDirName)) {
CreateDir(baseDirName);
}
return baseDirName + "/" + filename;
}
void FunctionInterpreter::DumpBinary(
std::vector<int64_t>& shape, std::vector<int64_t>& stride, std::vector<int64_t>& offset, FILE* fdata, uint8_t* data,
size_t dtypeSize)
{
if (shape.size() > 1) {
for (int64_t k = 0; k < shape[0]; k++) {
auto newOffset = std::vector<int64_t>(offset.begin() + 1, offset.end());
auto newStride = std::vector<int64_t>(stride.begin() + 1, stride.end());
auto newShape = std::vector<int64_t>(shape.begin() + 1, shape.end());
auto newData = data + offset[0] * dtypeSize * stride[0] + k * stride[0] * dtypeSize;
DumpBinary(newShape, newStride, newOffset, fdata, newData, dtypeSize);
}
} else {
size_t res = fwrite(data + offset[0] * dtypeSize, dtypeSize, shape[0], fdata);
if (res != static_cast<size_t>(shape[0])) {
INTERPRETER_LOGW("Write size is not equal actual size.");
}
}
}
void FunctionInterpreter::DumpTensorBinary(
const std::shared_ptr<LogicalTensor>& tensor, const std::shared_ptr<LogicalTensorData>& dataView)
{
if (execDumpLevel < EXEC_DUMP_LEVEL_TENSOR || !execDumpFile)
return;
std::string dumpTensorDirName = GetDumpFrameDirName();
std::string dumpTensorFileName = GetDumpTensorFileName(tensor);
std::string dumpTensorFilePath = GetDumpFilePath(execDumpDir, dumpTensorDirName, dumpTensorFileName);
dataView->Save(dumpTensorFilePath);
}
void FunctionInterpreter::DumpTensorBinary(
const std::shared_ptr<LogicalTensorData>& dataView, std::string dumpTensorFileName, bool isRaw)
{
std::string dumpTensorFilePath = execDumpDir + "/" + dumpTensorFileName;
auto rawShape = dataView->GetData()->GetShape();
if (std::any_of(rawShape.begin(), rawShape.end(), [](const int64_t& val) { return val <= 0; })) {
INTERPRETER_LOGW("The tensor size is not greater than 0.");
return;
}
auto validShape = dataView->GetValidShape();
auto offset = dataView->GetOffset();
if (isRaw) {
validShape = rawShape;
std::fill(offset.begin(), offset.end(), 0);
}
if (std::any_of(validShape.begin(), validShape.end(), [](const int64_t& val) { return val <= 0; })) {
return;
}
auto stride = dataView->GetData()->GetStride();
if (offset.size() != validShape.size() || stride.size() != validShape.size()) {
return;
}
FILE* fdata = fopen(dumpTensorFilePath.c_str(), "wb");
if (fdata == nullptr) {
INTERPRETER_LOGE(OpDumpScene::DUMP_OPEN_FILE_FAILED, "Failed to open file: %s", dumpTensorFilePath.c_str());
return;
}
DumpBinary(validShape, stride, offset, fdata, dataView->GetData()->data(), BytesOf(dataView->GetDataType()));
fclose(fdata);
}
std::shared_ptr<LogicalTensorData> FunctionInterpreter::LoadTensorBinary(
const std::shared_ptr<LogicalTensor>& tensor, const std::string filepath)
{
if (!FileExist(filepath)) {
return nullptr;
}
std::vector<int64_t> shape = tensor->GetShape();
if (std::any_of(shape.begin(), shape.end(), [](int64_t num) { return num <= 0; })) {
return nullptr;
}
FILE* fdata = fopen(filepath.c_str(), "rb");
if (fdata == nullptr) {
INTERPRETER_LOGE(OpDumpScene::DUMP_OPEN_FILE_FAILED, "Failed to open file: %s", filepath.c_str());
return nullptr;
}
auto data = std::make_shared<RawTensorData>(static_cast<DataType>(tensor->Datatype()), shape);
if (fread(data->data(), 1, data->size(), fdata) != data->size()) {
fclose(fdata);
return nullptr;
}
auto dataView = std::make_shared<LogicalTensorData>(data, shape, shape, std::vector<int64_t>(shape.size(), 0));
fclose(fdata);
return dataView;
}
void FunctionInterpreter::FillOperationBasicInfo(Operation* op, FunctionFrame* frame, std::vector<std::string>& opInfo)
{
opInfo[toIndex(OpInfoCsvHeader::rootFuncID)] = std::to_string(frame->rootFuncIndex);
opInfo[toIndex(OpInfoCsvHeader::rootFuncHash)] = std::to_string(frame->rootFuncHash) + "'";
opInfo[toIndex(OpInfoCsvHeader::rootFuncType)] = frame->rootFuncType;
opInfo[toIndex(OpInfoCsvHeader::rootFuncGraphType)] = frame->rootFuncGraphType;
opInfo[toIndex(OpInfoCsvHeader::funcID)] = std::to_string(frame->funcIndex);
opInfo[toIndex(OpInfoCsvHeader::funcHash)] = std::to_string(frame->funcHash) + "'";
opInfo[toIndex(OpInfoCsvHeader::funcType)] = frame->funcType;
opInfo[toIndex(OpInfoCsvHeader::funcGraphType)] = frame->funcGraphType;
opInfo[toIndex(OpInfoCsvHeader::passName)] = execDumpPassName;
opInfo[toIndex(OpInfoCsvHeader::pathFuncMagicName)] = execDumpFunPath;
opInfo[toIndex(OpInfoCsvHeader::pathFuncMagic)] = std::to_string(pathFuncMagic);
opInfo[toIndex(OpInfoCsvHeader::pathFuncHash)] = std::to_string(pathFuncHash) + "'";
opInfo[toIndex(OpInfoCsvHeader::loopInfo)] = GetLoopSymbolString();
opInfo[toIndex(OpInfoCsvHeader::opCode)] = op->GetOpcodeStr();
opInfo[toIndex(OpInfoCsvHeader::opMagic)] = std::to_string(op->GetOpMagic());
if (frame->callop != nullptr) {
opInfo[toIndex(OpInfoCsvHeader::callopMagic)] = std::to_string(frame->callop->GetOpMagic());
}
}
void FunctionInterpreter::FillOperationOffsetInfo(
Operation* op, FunctionFrame* frame, const std::vector<SymbolicScalar>& linearArgList,
std::vector<std::string>& opInfo)
{
if (convertOpCode.count(op->GetOpcodeStr())) {
return;
}
auto opAttr = std::static_pointer_cast<ViewOpAttribute>(op->GetOpAttribute());
if (opAttr) {
if (copyOpCode.count(op->GetOpcodeStr())) {
auto copyAttr = std::static_pointer_cast<CopyOpAttribute>(op->GetOpAttribute());
auto offset = copyAttr->IsCopyOut() ? copyAttr->GetToOffset() : copyAttr->GetFromOffset();
auto offsetView = GetOperationInterpreterForThisThread().EvaluateOpImmediate(frame, offset);
opInfo[toIndex(OpInfoCsvHeader::attrOffset)] = ShapeToString(offsetView);
} else {
Offset offsetView = EvaluateOffset(opAttr->GetFromOffset(), opAttr->GetFromDynOffset(), linearArgList);
opInfo[toIndex(OpInfoCsvHeader::attrOffset)] = ShapeToString(offsetView);
}
}
if (op->HasAttribute(OP_ATTR_PREFIX + "atomic_add")) {
opInfo[toIndex(OpInfoCsvHeader::attrAtomic)] = "True";
} else {
opInfo[toIndex(OpInfoCsvHeader::attrAtomic)] = "False";
}
}
void FunctionInterpreter::FillOperationInputInfo(
Operation* op, FunctionFrame* frame, const std::vector<std::shared_ptr<LogicalTensorData>>* ioperandDataViewList,
std::vector<std::string>& opInfo)
{
auto iopSize = op->GetIOperands().size();
for (size_t k = 0; k < iopSize; k++) {
if (k >= ioperandDataViewList->size()) {
return;
}
auto dataView = ioperandDataViewList->at(k);
if (k > 0) {
opInfo[toIndex(OpInfoCsvHeader::inputTensors)] += ", ";
opInfo[toIndex(OpInfoCsvHeader::inputValidShape)] += ", ";
opInfo[toIndex(OpInfoCsvHeader::inputRawMagic)] += ", ";
}
opInfo[toIndex(OpInfoCsvHeader::inputValidShape)] += ShapeToString(dataView->GetValidShape());
opInfo[toIndex(OpInfoCsvHeader::inputRawMagic)] +=
std::to_string(op->GetIOperands()[k]->GetRawTensor()->GetRawMagic());
auto it = frame->tensorDataBinDict.find(op->GetIOperands()[k]);
if (it != frame->tensorDataBinDict.end()) {
opInfo[toIndex(OpInfoCsvHeader::inputTensors)] += it->second;
}
if (op->GetOpcode() == Opcode::OP_COPY_IN) {
auto itTmp = frame->callopDataViewTensorDict.find(dataView);
if (itTmp != frame->callopDataViewTensorDict.end()) {
opInfo[toIndex(OpInfoCsvHeader::callopRawMagic)] =
std::to_string(itTmp->second->GetRawTensor()->GetRawMagic());
}
}
}
}
void FunctionInterpreter::FillOperationOutputInfo(
Operation* op, FunctionFrame* frame, const std::vector<std::shared_ptr<LogicalTensorData>>* ooperandDataViewList,
const std::vector<SymbolicScalar>& linearArgList, int indent, std::vector<std::string>& opInfo)
{
auto oopSize = op->GetOOperands().size();
for (size_t k = 0; k < oopSize; k++) {
if (k < ooperandDataViewList->size()) {
auto dataView = ooperandDataViewList->at(k);
std::string dumpTensorFileName = GetDumpTensorFileName(op->GetOOperands()[k], op, frame);
bool isRaw = (op->GetOpcode() == Opcode::OP_COPY_OUT || op->GetOpcode() == Opcode::OP_ASSEMBLE);
DumpTensorBinary(dataView, dumpTensorFileName, isRaw);
if (execDumpFile) {
fprintf(execDumpFile, "<div class=\"detail indent_%d\">%s</a></div>\n",
indent, dumpTensorFileName.c_str());
}
struct timeval tv;
gettimeofday(&tv, nullptr);
auto ts = tv.tv_sec * 1000000 + tv.tv_usec;
frame->tensorDataBinDict[op->GetOOperands()[k]] = dumpTensorFileName;
opInfo[toIndex(OpInfoCsvHeader::tensorMagic)] = std::to_string(op->GetOOperands()[k]->GetMagic());
opInfo[toIndex(OpInfoCsvHeader::rawTensorMagic)] =
std::to_string(op->GetOOperands()[k]->GetRawTensor()->GetRawMagic());
opInfo[toIndex(OpInfoCsvHeader::outputShape)] = ShapeToString(dataView->GetShape());
opInfo[toIndex(OpInfoCsvHeader::outputRawShape)] = ShapeToString(dataView->GetData()->GetShape());
opInfo[toIndex(OpInfoCsvHeader::outputValidShape)] = ShapeToString(dataView->GetValidShape());
opInfo[toIndex(OpInfoCsvHeader::outputDynValidShape)] =
ShapeToString(EvaluateValidShape((op->GetOOperands()[k]->GetDynValidShape()), linearArgList));
opInfo[toIndex(OpInfoCsvHeader::outputDtype)] = DataType2String(dataView->GetDataType(), true);
opInfo[toIndex(OpInfoCsvHeader::tensorOffset)] = ShapeToString(dataView->GetOffset());
opInfo[toIndex(OpInfoCsvHeader::outputTensor)] = dumpTensorFileName;
opInfo[toIndex(OpInfoCsvHeader::timeStamp)] = std::to_string(ts) + "'";
opInfo[toIndex(OpInfoCsvHeader::outputSymbol)] = op->GetOOperands()[k]->GetRawTensor()->GetSymbol();
opInfo[toIndex(OpInfoCsvHeader::outputFormat)] =
std::to_string(op->GetOOperands()[k]->GetRawTensor()->format);
opInfo[toIndex(OpInfoCsvHeader::ioflag)] = "o" + std::to_string(k);
if (op->GetOpcode() == Opcode::OP_COPY_OUT) {
auto itTmp = frame->callopDataViewTensorDict.find(dataView);
if (itTmp != frame->callopDataViewTensorDict.end()) {
opInfo[toIndex(OpInfoCsvHeader::callopRawMagic)] =
std::to_string(itTmp->second->GetRawTensor()->GetRawMagic());
}
}
}
WriteCsvRow(opInfo, opInfoRowNum, execOpResultFile);
}
}
void FunctionInterpreter::DumpOperationTensor(
Operation* op, FunctionFrame* frame, const std::vector<std::shared_ptr<LogicalTensorData>>* ooperandDataViewList,
const std::vector<std::shared_ptr<LogicalTensorData>>* ioperandDataViewList)
{
if (execDumpLevel < EXEC_DUMP_LEVEL_TENSOR || !execDumpFile)
return;
int indent = GetFrameSize();
std::vector<SymbolicScalar> linearArgList;
if (frame->callopAttr != nullptr) {
linearArgList = frame->callopAttr->GetLinearArgList();
}
std::vector<std::string> opInfo(toIndex(OpInfoCsvHeader::COL_COUNT));
FillOperationBasicInfo(op, frame, opInfo);
FillOperationOffsetInfo(op, frame, linearArgList, opInfo);
FillOperationInputInfo(op, frame, ioperandDataViewList, opInfo);
FillOperationOutputInfo(op, frame, ooperandDataViewList, linearArgList, indent, opInfo);
}
void FunctionInterpreter::DumpPassTensorDiff(
const std::shared_ptr<FunctionCaptureExecution>& captureExecution,
const std::shared_ptr<FunctionCaptureExecution>& captureGolden)
{
if (execDumpLevel < EXEC_DUMP_LEVEL_TENSOR)
return;
if (captureExecution->GetFrameList().size() != captureGolden->GetFrameList().size())
return;
std::vector<double> tolerance = config::GetVerifyOption<std::vector<double>>(KEY_PASS_VERIFY_ERROR_TOL);
float rtol = static_cast<float>(tolerance[0]);
float atol = static_cast<float>(tolerance[1]);
std::string dumpStyleFilePath = execDumpDir + "/entry_" + std::to_string(captureIndex) + ".css";
execDumpStyleFile = fopen(dumpStyleFilePath.c_str(), "w");
if (execDumpStyleFile == nullptr) {
INTERPRETER_LOGE(OpDumpScene::DUMP_OPEN_FILE_FAILED, "Failed to open file: %s", dumpStyleFilePath.c_str());
return;
}
for (size_t idx = 0; idx < captureGolden->GetFrameList().size(); idx++) {
auto frameExecution = captureExecution->GetFrameList()[idx];
auto frameGolden = captureGolden->GetFrameList()[idx];
const auto& tensorDictExecution = frameExecution->tensorDataBinDict;
const auto& tensorDictGolden = frameGolden->tensorDataBinDict;
std::vector<std::shared_ptr<LogicalTensor>> tensorList;
for (auto& [tensor, dataViewExecution] : tensorDictExecution) {
(void)dataViewExecution;
if (tensorDictGolden.count(tensor)) {
tensorList.push_back(tensor);
}
}
for (size_t k = 0; k < tensorList.size(); k++) {
auto tensor = tensorList[k];
auto executionFileName = tensorDictExecution.find(tensor)->second;
auto dataViewExecution = LoadTensorBinary(tensor, execDumpDir + "/" + executionFileName);
auto filename = tensorDictGolden.find(tensor)->second;
auto dataViewGolden = LoadTensorBinary(tensor, dumpPath + "/tensor_graph/" + filename);
if (!dataViewExecution || !dataViewGolden) {
continue;
}
auto compare = FlowVerifier::VerifyResult(dataViewGolden, dataViewExecution, rtol, atol);
std::string tensorId = GetDumpTensorId(frameExecution, tensor);
if (compare.Check()) {
fprintf(execDumpStyleFile, "#%s { background-color: LightGreen; }\n", tensorId.c_str());
} else {
fprintf(execDumpStyleFile, "#%s { background-color: LightCoral; }\n", tensorId.c_str());
}
}
}
fclose(execDumpStyleFile);
}
void FunctionInterpreter::DumpBegin()
{
frameCount = 0;
execDumpDir = dumpPath + execDumpFuncKey;
CreateMultiLevelDir(execDumpDir);
if (execDumpLevel < EXEC_DUMP_LEVEL_OPERATION)
return;
std::string styleFilePath = dumpPath + "/verifier.css";
if (GetFileSize(styleFilePath) == 0) {
FILE* fcss = fopen(styleFilePath.c_str(), "w");
if (fcss == nullptr) {
INTERPRETER_LOGE(OpDumpScene::DUMP_OPEN_FILE_FAILED, "Failed to open file: %s", styleFilePath.c_str());
} else {
for (int i = 0; i < MAX_IDENT_LEVEL; i++) {
fprintf(fcss, ".indent_%d { margin-left: %dpx; }\n", i, i * 50);
}
fprintf(fcss, ".table_head { width: 10%%; }\n");
fprintf(fcss, ".tensor_data { vertical-align: top; }\n");
fclose(fcss);
}
}
std::string dumpFilePath = execDumpDir + "/entry_" + std::to_string(captureIndex) + ".html";
execDumpFile = fopen(dumpFilePath.c_str(), "w");
if (execDumpFile == nullptr) {
INTERPRETER_LOGE(OpDumpScene::DUMP_OPEN_FILE_FAILED, "Failed to open file: %s", dumpFilePath.c_str());
return;
}
fprintf(
execDumpFile, R"HTML(
<html>
<head>
<link rel="stylesheet" type="text/css" href="../verifier.css">
<link rel="stylesheet" type="text/css" href="entry_%s.css">
</head>
<body>
)HTML",
std::to_string(captureIndex).c_str());
}
void FunctionInterpreter::DumpEnd()
{
if (execDumpLevel < EXEC_DUMP_LEVEL_OPERATION || execDumpFile == nullptr)
return;
fprintf(execDumpFile, R"HTML(
</body>
</html>
)HTML");
fclose(execDumpFile);
}
}