* 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.
*/
#include "inc/ffts_utils.h"
#include <mutex>
#include <sys/time.h>
#include <sstream>
#include <climits>
#include <cmath>
#include <numeric>
#include "inc/ffts_error_codes.h"
#include "engine/engine_manager.h"
#include "base/err_msg.h"
#include "platform/platform_info.h"
#include "graph/utils/graph_utils.h"
#include "graph/utils/node_utils.h"
#include "graph/debug/ge_attr_define.h"
#include "runtime/rt.h"
#include "framework/common/framework_types_internal.h"
#include "common/sgt_slice_type.h"
namespace ffts {
std::mutex g_report_error_msg_mutex;
FFTSMode GetPlatformFFTSMode() {
string soc_version = EngineManager::Instance(kFFTSPlusCoreName).GetSocVersion();
fe::PlatFormInfos platform_infos;
fe::OptionalInfos opti_compilation_infos;
auto ret = fe::PlatformInfoManager::Instance().GetPlatformInfos(soc_version, platform_infos,
opti_compilation_infos);
if (ret != SUCCESS) {
FFTS_LOGW("Failed to retrieve platform information using SOC version [%s].", soc_version.c_str());
return FFTSMode::FFTS_MODE_NO_FFTS;
}
string ffts_mode = "";
platform_infos.GetPlatformRes(kSocInfo, kFFTSMode, ffts_mode);
FFTS_LOGD("GetPlatformFFTSMode [%s].", ffts_mode.c_str());
if (ffts_mode == kFFTSPlus) {
return FFTSMode::FFTS_MODE_FFTS_PLUS;
} else if (ffts_mode == kFFTS) {
return FFTSMode::FFTS_MODE_FFTS;
}
return FFTSMode::FFTS_MODE_NO_FFTS;
}
int64_t GetMicroSecondTime() {
struct timeval tv = {0, 0};
int ret = gettimeofday(&tv, nullptr);
if (ret != 0) {
return 0;
}
if (tv.tv_sec < 0 || tv.tv_usec < 0) {
return 0;
}
int64_t micro_multiples = 1000000;
int64_t second = tv.tv_sec;
FFTS_INT64_MULCHECK(second, micro_multiples);
int64_t second_to_micro = second * micro_multiples;
FFTS_INT64_ADDCHECK(second_to_micro, tv.tv_usec);
return second_to_micro + tv.tv_usec;
}
void LogErrorMessage(std::string error_code, const std::map<std::string, std::string> &args_map) {
std::vector<const char*> args_keys;
std::vector<const char*> args_values;
for (const auto &item : args_map) {
args_keys.push_back(item.first.c_str());
args_values.push_back(item.second.c_str());
}
int result = REPORT_PREDEFINED_ERR_MSG(error_code.c_str(), args_keys, args_values);
FFTS_LOGE_IF(result != 0, "Faild to call ReportErrMessage.");
}
std::string RealPath(const std::string &path) {
if (path.empty()) {
FFTS_LOGI("The path string is a nullptr.");
return "";
}
if (path.size() >= PATH_MAX) {
FFTS_LOGI("file path %s is too long!", path.c_str());
return "";
}
char resoved_path[PATH_MAX] = {0x00};
std::string res;
if (realpath(path.c_str(), resoved_path) != nullptr) {
res = resoved_path;
} else {
FFTS_LOGI("Path '%s' does not exist.", path.c_str());
}
return res;
}
void DumpGraph(const ge::ComputeGraph &graph, const std::string &suffix) {
std::shared_ptr<ge::ComputeGraph> compute_graph_ptr = FFTSComGraphMakeShared<ge::ComputeGraph>(graph);
ge::GraphUtils::DumpGEGraph(compute_graph_ptr, suffix);
}
bool IsSubGraphData(const ge::OpDescPtr &op_desc_ptr) {
if (op_desc_ptr == nullptr) {
return false;
}
if (op_desc_ptr->GetType() == CONSTANTOP || op_desc_ptr->GetType() == CONSTANT) {
return true;
}
if (op_desc_ptr->GetType() != DATA) {
return false;
}
return op_desc_ptr->HasAttr(ge::ATTR_NAME_PARENT_NODE_INDEX);
}
bool IsSubGraphDataWithControlEdge(const ge::NodePtr &node, uint32_t recurise_cnt) {
if (recurise_cnt == 0) {
FFTS_LOGI("Recursive count reduced to 0, will not continue processing.");
return false;
}
ge::OpDescPtr op_desc_ptr = node->GetOpDesc();
if (op_desc_ptr == nullptr) {
return false;
}
if (IsPhonyOp(op_desc_ptr) || op_desc_ptr->GetType() == CONSTANTOP || op_desc_ptr->GetType() == CONSTANT ||
op_desc_ptr->GetType() == REFDATA) {
FFTS_LOGD("Op type: %s, name: %s, recursive_cnt is %u.", op_desc_ptr->GetType().c_str(),
op_desc_ptr->GetName().c_str(), recurise_cnt);
for (const auto &in_node : node->GetInAllNodes()) {
FFTS_CHECK(in_node == nullptr, FFTS_LOGW("in_node is null"), return false);
auto in_node_desc = in_node->GetOpDesc();
if (in_node_desc == nullptr) {
return false;
}
FFTS_LOGD("Get node type: %s, name: %s, from node type: %s, name: %s.", op_desc_ptr->GetType().c_str(),
op_desc_ptr->GetName().c_str(), in_node_desc->GetType().c_str(), in_node_desc->GetName().c_str());
bool res = IsSubGraphDataWithControlEdge(in_node, recurise_cnt - 1);
if (!res) {
return false;
}
}
return true;
}
if (op_desc_ptr->GetType() != DATA) {
return false;
}
return op_desc_ptr->HasAttr(ge::ATTR_NAME_PARENT_NODE_INDEX);
}
bool IsPhonyOp(const ge::OpDescPtr &op_desc_ptr) {
if (op_desc_ptr == nullptr) {
return false;
}
if (op_desc_ptr->HasAttr(ge::ATTR_NAME_NOTASK)) {
return true;
}
return (kPhonyTypes.count(op_desc_ptr->GetType()) != 0);
}
Status IsNoEdge(const ge::NodePtr ¶nt_node, const ge::NodePtr &child_node) {
for (auto &out_node : parant_node->GetOutAllNodes()) {
if (out_node == child_node) {
FFTS_LOGD("out_node: %s, child_node: %s.",
out_node->GetOpDesc()->GetName().c_str(), child_node->GetOpDesc()->GetName().c_str());
return false;
}
}
return true;
}
bool IsSubGraphNetOutput(const ge::OpDescPtr &op_desc_ptr) {
if (op_desc_ptr == nullptr || op_desc_ptr->GetType() != NETOUTPUT) {
return false;
}
for (auto &tensor : op_desc_ptr->GetAllInputsDescPtr()) {
if (ge::AttrUtils::HasAttr(tensor, ge::ATTR_NAME_PARENT_NODE_INDEX)) {
return true;
}
}
return false;
}
void GenerateSameAtomicNodesMap(std::vector<ge::NodePtr> &graph_nodes,
SameAtomicNodeMap &same_memset_nodes_map) {
for (auto &node : graph_nodes) {
ThreadSliceMapPtr slice_info_ptr = nullptr;
FFTS_CHECK(node == nullptr, FFTS_LOGW("node is null"), return);
slice_info_ptr = node->GetOpDesc()->TryGetExtAttr(ffts::kAttrSgtStructInfo, slice_info_ptr);
if (slice_info_ptr == nullptr) {
continue;
}
if (slice_info_ptr->same_atomic_clean_nodes.empty()) {
continue;
}
auto same_memset_nodes = slice_info_ptr->same_atomic_clean_nodes;
if (same_memset_nodes_map.find(same_memset_nodes) == same_memset_nodes_map.end()) {
same_memset_nodes_map[same_memset_nodes] = {node};
} else {
same_memset_nodes_map[same_memset_nodes].emplace_back(node);
}
}
}
void SetBitOne(const uint32_t pos, uint32_t &bm) {
if (pos > sizeof(uint32_t) * kOneByteBitNum) {
FFTS_LOGW("Set bit at position %u within the uint32_t range.", pos);
}
bm |= static_cast<uint32_t>(0x1U << pos);
}
void SetBitOne(const uint32_t pos, uint64_t &bm) {
if (pos > sizeof(uint64_t) * kOneByteBitNum) {
FFTS_LOGW("Set bit to position %u over uint64_t range.", pos);
}
bm |= static_cast<uint64_t>(0x1UL << pos);
}
void DeleteNode(ge::NodePtr &node) {
PrintNode(node);
(void)ge::GraphUtils::IsolateNode(node, {});
(void)ge::GraphUtils::RemoveNodeWithoutRelink(node->GetOwnerComputeGraph(), node);
}
void PrintNode(const ge::NodePtr &node) {
if (CheckLogLevel(static_cast<int32_t>(FFTS), DLOG_DEBUG) != 1) {
return;
}
FFTS_LOGD("===============================printcurrentnode start=================================");
if (node == nullptr) {
return;
}
FFTS_LOGD("Node name = %s, type = %s.", node->GetName().c_str(), node->GetType().c_str());
FFTS_LOGD("===============================printcurrentnode end=================================");
}
void PrintNodeAttrExtNode(const ge::NodePtr &node, std::string attrname) {
if (CheckLogLevel(static_cast<int32_t>(FFTS), DLOG_DEBUG) != 1) {
return;
}
FFTS_LOGD("===============================printnodeattrext start=================================");
if (node == nullptr) {
return;
}
ge::OpDescPtr op_desc = node->GetOpDesc();
FFTS_LOGD("Node name = %s, type = %s attrname = %s.", node->GetName().c_str(), node->GetType().c_str(),
attrname.c_str());
ge::NodePtr attrnode = nullptr;
attrnode = op_desc->TryGetExtAttr(attrname, attrnode);
if (attrnode == nullptr) {
FFTS_LOGD("Node has not dst attr node");
} else {
PrintNode(attrnode);
}
FFTS_LOGD("===============================printnodeattrext end=================================");
}
void PrintNodeAttrExtNodes(const ge::NodePtr &node, std::string attrname) {
if (CheckLogLevel(static_cast<int32_t>(FFTS), DLOG_DEBUG) != 1) {
return;
}
FFTS_LOGD("===============================printnodeattrext start=================================");
if (node == nullptr) {
return;
}
ge::OpDescPtr op_desc = node->GetOpDesc();
FFTS_LOGD("Node name = %s, type = %s attrname = %s.", node->GetName().c_str(), node->GetType().c_str(),
attrname.c_str());
std::shared_ptr<std::vector<ge::NodePtr>> attrnodes = nullptr;
attrnodes = op_desc->TryGetExtAttr(attrname, attrnodes);
if (attrnodes != nullptr && (*attrnodes).size() != 0) {
for (const auto &attr_node : (*attrnodes)) {
PrintNode(attr_node);
}
} else {
FFTS_LOGD("Node does not have dst attr nodes");
}
FFTS_LOGD("===============================printnodeattrext end=================================");
}
Status UnfoldPartionCallOnlyOneDepth(ge::ComputeGraph& graph, const std::string &node_type) {
FFTS_LOGD("UnfoldPartionCallOnlyOneDepth graphname = %s node_type = %s", graph.GetName().c_str(), node_type.c_str());
for (auto &node : graph.GetDirectNode()) {
FFTS_CHECK_NOTNULL(node);
if (node->GetType() != node_type) {
continue;
}
FFTS_LOGD("UnfoldPartionCallOnlyOneDepth node_name = %s.", node->GetName().c_str());
auto func_graph = node->GetOwnerComputeGraph();
FFTS_CHECK_NOTNULL(func_graph);
ge::ComputeGraphPtr src_graph = func_graph->TryGetExtAttr("part_src_graph", ge::ComputeGraphPtr());
FFTS_CHECK_NOTNULL(src_graph);
auto root_graph = ge::GraphUtils::FindRootGraph(src_graph);
FFTS_CHECK_NOTNULL(root_graph);
std::vector<ge::ComputeGraphPtr> sub_graphs = root_graph->GetAllSubgraphs();
for (const auto &sub_graph : sub_graphs) {
FFTS_CHECK_NOTNULL(sub_graph);
if (sub_graph->GetParentNode()->GetName() == node->GetName() &&
ge::GraphUtils::UnfoldGraph(sub_graph, func_graph, node, nullptr) != ge::GRAPH_SUCCESS) {
REPORT_FFTS_ERROR("[OptimizeFusedGraph][UnfoldSubGraph] UnfoldSubGraph failed.");
return FAILED;
}
}
}
return SUCCESS;
}
bool IsMemoryEmpty(const ge::GeTensorDesc &tensor_desc) {
auto memory_size_calc_type = static_cast<int64_t>(ge::MemorySizeCalcType::NORMAL);
(void)ge::AttrUtils::GetInt(tensor_desc, ge::ATTR_NAME_MEMORY_SIZE_CALC_TYPE, memory_size_calc_type);
return memory_size_calc_type == static_cast<int64_t>(ge::MemorySizeCalcType::ALWAYS_EMPTY);
}
}