* Copyright (c) 2026 Huawei Technologies Co., Ltd.
* This file is part of the MindStudio project.
*
* MindStudio is licensed under Mulan PSL v2.
* You can use this software according to the terms and conditions of the Mulan PSL v2.
* You may obtain a copy of Mulan PSL v2 at:
*
* http://license.coscl.org.cn/MulanPSL2
*
* 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 FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------*/
#include <vector>
#include <unordered_map>
#include "analysis/csrc/application/timeline/block_detail_assembler.h"
namespace Analysis {
namespace Application {
using namespace Analysis::Utils;
namespace {
const std::string AIC_EARLIEST = "AIC Earliest";
const std::string AIC_LATEST = "AIC Latest";
const std::string AIV_EARLIEST = "AIV Earliest";
const std::string AIV_LATEST = "AIV Latest";
const std::vector<std::string> THREAD_ARGS_NAMES = {AIC_EARLIEST, AIC_LATEST, AIV_EARLIEST, AIV_LATEST};
const size_t THREAD_ARGS_NAMES_SIZE = 4;
}
void BlockDetailEvent::ProcessArgs(JsonWriter& ostream)
{
ostream["Physic Stream Id"] << streamId_;
ostream["Task Id"] << taskId_;
ostream["Batch Id"] << batchId_;
ostream["Subtask Id"] << subTaskId_;
ostream["Core Id"] << coreId_;
}
BlockDetailAssembler::BlockDetailAssembler() : JsonAssembler(PROCESS_BLOCK_DETAIL, {{MSPROF_JSON_FILE, FileCategory::MSPROF}}) {}
uint8_t BlockDetailAssembler::AssembleData(DataInventory &dataInventory, JsonWriter &ostream, const std::string &profPath)
{
auto blockDetailData = dataInventory.GetPtr<std::vector<BlockDetailData>>();
auto taskInfoData = dataInventory.GetPtr<std::vector<TaskInfoData>>();
auto ascendTaskData = dataInventory.GetPtr<std::vector<AscendTaskData>>();
if (blockDetailData == nullptr) {
WARN("Can't get blockDetailData from dataInventory.");
return DATA_NOT_EXIST;
}
if (ascendTaskData == nullptr) {
WARN("AscendTask not exists, can't export task time data.");
return DATA_NOT_EXIST;
}
std::unordered_map<uint32_t, std::vector<BlockDetailData>> blockDetailDataMap;
if (!Reserve(*blockDetailData, blockDetailData->size())) {
ERROR("Reserve for block detail data failed.");
return ASSEMBLE_FAILED;
}
for (const auto& data : *blockDetailData) {
blockDetailDataMap[data.taskId].emplace_back(std::move(data));
}
if (taskInfoData == nullptr) {
WARN("No ge data collected, maybe the TaskInfo table is not created, now try to export data with no ge data.");
} else {
FormatTaskInfoData(*taskInfoData);
}
auto layerInfo = GetLayerInfo(PROCESS_BLOCK_DETAIL);
auto pidMap = GenerateBlockDetailTrace(*ascendTaskData, blockDetailDataMap,
layerInfo.sortIndex, profPath, res_);
if (res_.empty()) {
ERROR("Can't Generate any block detail process data.");
return ASSEMBLE_FAILED;
}
GenerateHWMetaData(pidMap, layerInfo, res_);
for (const auto &node : res_) {
node->DumpJson(ostream);
}
ostream << ",";
return ASSEMBLE_SUCCESS;
}
std::unordered_map<uint16_t, uint32_t> BlockDetailAssembler::GenerateBlockDetailTrace(
std::vector<AscendTaskData> &ascendTaskData,
std::unordered_map<uint32_t, std::vector<BlockDetailData>> &blockDetailDataMap,
uint32_t sortIndex,
const std::string &profPath,
std::vector<std::shared_ptr<TraceEvent>> &res)
{
std::unordered_map<uint16_t, uint32_t> pidMap;
for (const auto& taskData : ascendTaskData) {
auto blockDetailIt = blockDetailDataMap.find(taskData.taskId);
if (blockDetailIt == blockDetailDataMap.end()) {
continue;
}
const auto& pmuDataList = blockDetailIt->second;
auto pmuGroup = GetBlockPmuGroup(pmuDataList, taskData);
if (pmuGroup.empty()) {
continue;
}
std::string opName;
TaskId taskId{static_cast<uint16_t>(taskData.streamId), static_cast<uint16_t>(taskData.batchId),
taskData.taskId, taskData.contextId, taskData.deviceId};
auto taskDataIt = formatedTaskInfo_.find(taskId);
if (taskDataIt != formatedTaskInfo_.end()) {
opName = taskDataIt->second.first;
} else {
WARN("Failed to find opName for deviceId[%u], taskId[%u]", taskData.deviceId, taskData.taskId);
}
auto pid = GetDevicePid(pidMap, taskData.deviceId, profPath, sortIndex);
std::vector<PmuTimelineRow> block_pmu_timeline_data;
SetPmuResultList(pmuGroup, pid, block_pmu_timeline_data, opName, res);
GenerateBlockPmuTimelineData(block_pmu_timeline_data, res);
}
return pidMap;
}
std::vector<const BlockDetailData*> BlockDetailAssembler::GetBlockPmuGroup(
const std::vector<BlockDetailData>& pmuDataList,
const AscendTaskData& taskData) {
std::vector<const BlockDetailData*> result;
if (!Reserve(result, (pmuDataList.size() / THREAD_ARGS_NAMES_SIZE))) {
ERROR("Reserve for pmu data list failed.");
return result;
}
uint64_t taskStart = taskData.timestamp;
uint64_t taskEnd = taskData.timestamp + taskData.duration;
for (const auto& pmuData : pmuDataList) {
uint64_t pmuStart = pmuData.timestamp;
uint64_t pmuEnd = pmuData.timestamp + pmuData.duration;
if (!(taskEnd < pmuStart || pmuEnd < taskStart)) {
result.push_back(&pmuData);
}
}
return result;
}
void BlockDetailAssembler::FormatTaskInfoData(const std::vector<TaskInfoData> &taskInfoData)
{
if (taskInfoData.empty()) {
WARN("task info data is empty, no task info data for task time, check ge info please.");
return;
}
for (const auto &taskInfoDatum : taskInfoData) {
TaskId taskId{static_cast<uint16_t>(taskInfoDatum.streamId), static_cast<uint16_t>(taskInfoDatum.batchId),
taskInfoDatum.taskId, taskInfoDatum.contextId, taskInfoDatum.deviceId};
formatedTaskInfo_.insert({taskId, {taskInfoDatum.opName, taskInfoDatum.taskType}});
}
}
void BlockDetailAssembler::SetPmuResultList(const std::vector<const BlockDetailData*>& datas,
uint32_t pid,
std::vector<PmuTimelineRow>& block_pmu_timeline_data,
const std::string& opName,
std::vector<std::shared_ptr<TraceEvent>> &res) {
std::vector<const BlockDetailData*> aic_datas;
std::vector<const BlockDetailData*> aiv_datas;
for (const auto* data : datas) {
if (!data) continue;
if (data->coreType == 0) {
aic_datas.push_back(data);
} else if (data->coreType == 1) {
aiv_datas.push_back(data);
}
}
auto sortByTimeStamp = [](const BlockDetailData* a, const BlockDetailData* b) {
return a->timestamp < b->timestamp;
};
std::sort(aic_datas.begin(), aic_datas.end(), sortByTimeStamp);
std::sort(aiv_datas.begin(), aiv_datas.end(), sortByTimeStamp);
const BlockDetailData* aic_earliest = aic_datas.empty() ? nullptr : aic_datas.front();
const BlockDetailData* aic_latest = aic_datas.empty() ? nullptr : aic_datas.back();
const BlockDetailData* aiv_earliest = aiv_datas.empty() ? nullptr : aiv_datas.front();
const BlockDetailData* aiv_latest = aiv_datas.empty() ? nullptr : aiv_datas.back();
const BlockDetailData* data_list[THREAD_ARGS_NAMES_SIZE] = {aic_earliest, aic_latest, aiv_earliest, aiv_latest};
for (size_t i = 0; i < THREAD_ARGS_NAMES_SIZE; ++i) {
const auto* data = data_list[i];
if (!data) continue;
PmuTimelineRow row;
row.name = "Stream " + std::to_string(data->streamId) + " " + opName;
row.pid = pid;
row.tid = i + 1;
row.timestamp = data->timestamp;
row.duration = data->duration;
row.streamId = data->streamId;
row.taskId = data->taskId;
row.batchId = data->batchId;
row.subtaskId = data->subtaskId;
row.coreId = data->coreId;
std::shared_ptr<MetaDataNameEvent> threadNameEvent;
MAKE_SHARED_RETURN_VOID(threadNameEvent, MetaDataNameEvent, pid, row.tid,
META_DATA_THREAD_NAME, THREAD_ARGS_NAMES[i]);
res.push_back(std::shared_ptr<TraceEvent>(std::move(threadNameEvent)));
block_pmu_timeline_data.push_back(std::move(row));
}
}
void BlockDetailAssembler::GenerateBlockPmuTimelineData(
const std::vector<PmuTimelineRow>& block_pmu_timeline_data,
std::vector<std::shared_ptr<TraceEvent>> &res)
{
for (const auto &data : block_pmu_timeline_data) {
std::shared_ptr<BlockDetailEvent> event;
MAKE_SHARED_RETURN_VOID(event, BlockDetailEvent, data.pid, data.tid, data.duration,
DivideByPowersOfTenWithPrecision(data.timestamp), data.name,
data.streamId, data.taskId, data.batchId, data.subtaskId, data.coreId);
res.push_back(event);
}
}
}
}