已合并
增加aicpu.csv dp.csv aicpu_mi.csv的c化导出逻辑 #473
wangzixuan创建于 9 天前
增加aicpu.csv dp.csv aicpu_mi.csv的c化导出逻辑 #473
已合并
wangzixuan创建于 9 天前
17 个文件变更+1569-10
@@ -203,6 +203,12 @@ const std::string TABLE_NAME_UB = "UB";
203 203 
204const std::string PROCESSOR_NAME_BLOCK_DETAIL = "BLOCK_DETAIL";204const std::string PROCESSOR_NAME_BLOCK_DETAIL = "BLOCK_DETAIL";
205const std::string PROCESSOR_NAME_PAGE_FAULT = "PAGE_FAULT";205const std::string PROCESSOR_NAME_PAGE_FAULT = "PAGE_FAULT";
206+const std::string PROCESSOR_NAME_AICPU = "AICPU";
207+const std::string DB_NAME_AI_CPU = "ai_cpu.db";
208+const std::string TABLE_NAME_AI_CPU = "AiCpuData";
209+const std::string TABLE_NAME_AI_CPU_DP = "AiCpuDP";
210+const std::string DB_NAME_DATA_PREPROCESS = "data_preprocess.db";
211+const std::string TABLE_NAME_DATA_QUEUE = "DataQueue";
206 212 
207const std::string PROCESSOR_NAME_DPU = "DPU";213const std::string PROCESSOR_NAME_DPU = "DPU";
208const std::string TABLE_NAME_DPU_TASK = "DPU_TASK";214const std::string TABLE_NAME_DPU_TASK = "DPU_TASK";
@@ -0,0 +1,118 @@
1+/* -------------------------------------------------------------------------
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This file is part of the MindStudio project.
4+ *
5+ * MindStudio is licensed under Mulan PSL v2.
6+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
7+ * You may obtain a copy of Mulan PSL v2 at:
8+ *
9+ * http://license.coscl.org.cn/MulanPSL2
10+ *
11+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14+ * See the Mulan PSL v2 for more details.
15+ * -------------------------------------------------------------------------*/
16+ 
17+#include "analysis/csrc/application/summary/aicpu_assembler.h"
18+ 
19+#include <string>
20+#include <vector>
21+ 
22+#include "analysis/csrc/application/summary/summary_constant.h"
23+#include "analysis/csrc/infrastructure/utils/common_constant.h"
24+#include "analysis/csrc/infrastructure/utils/file.h"
25+#include "analysis/csrc/infrastructure/utils/utils.h"
26+ 
27+namespace Analysis
28+{
29+namespace Application
30+{
31+using namespace Analysis::Utils;
32+using namespace Analysis::Domain;
33+ 
34+AicpuAssembler::AicpuAssembler(const std::string &name, const std::string &profPath) : SummaryAssembler(name, profPath)
35+{
36+}
37+ 
38+uint8_t AicpuAssembler::AssembleData(DataInventory &dataInventory)
39+{
40+ auto aicpuData = dataInventory.GetPtr<std::vector<AicpuSummaryData>>();
41+ auto dpData = dataInventory.GetPtr<std::vector<AicpuDpData>>();
42+ auto miData = dataInventory.GetPtr<std::vector<AicpuMiData>>();
43+ bool written = false;
44+ if (aicpuData != nullptr && WriteAicpuCsv(*aicpuData))
45+ {
46+ written = true;
47+ }
48+ if (dpData != nullptr && WriteDpCsv(*dpData))
49+ {
50+ written = true;
51+ }
52+ if (miData != nullptr && WriteMiCsv(*miData))
53+ {
54+ written = true;
55+ }
56+ if (!written)
57+ {
58+ WARN("No data to export aicpu/dp/aicpu_mi summary");
59+ return DATA_NOT_EXIST;
60+ }
61+ return ASSEMBLE_SUCCESS;
62+}
63+ 
64+bool AicpuAssembler::WriteAicpuCsv(const std::vector<AicpuSummaryData> &data)
65+{
66+ if (data.empty())
67+ {
68+ return false;
69+ }
70+ headers_ = {"Timestamp(us)", "Node", "Compute_time(us)", "Memcpy_time(us)", "Task_time(us)",
71+ "Dispatch_time(us)", "Total_time(us)", "Stream ID", "Task ID"};
72+ res_.clear();
73+ for (const auto &item : data)
74+ {
75+ res_.emplace_back(std::vector<std::string>{
76+ DivideByPowersOfTenWithPrecision(item.timestampNs), item.nodeName, DoubleToStr(item.computeTimeUs),
77+ DoubleToStr(item.memcpyTimeUs), DoubleToStr(item.taskTimeUs), DoubleToStr(item.dispatchTimeUs),
78+ DoubleToStr(item.totalTimeUs), std::to_string(item.streamId), std::to_string(item.taskId)});
79+ }
80+ WriteToFile(File::PathJoin({profPath_, Analysis::Common::OUTPUT_PATH, AICPU_NAME}), {});
81+ return true;
82+}
83+ 
84+bool AicpuAssembler::WriteDpCsv(const std::vector<AicpuDpData> &data)
85+{
86+ if (data.empty())
87+ {
88+ return false;
89+ }
90+ headers_ = {"Timestamp(us)", "Action", "Source", "Cached Buffer Size"};
91+ res_.clear();
92+ for (const auto &item : data)
93+ {
94+ res_.emplace_back(std::vector<std::string>{DivideByPowersOfTenWithPrecision(item.timestamp), item.action,
95+ item.source, std::to_string(item.bufferSize)});
96+ }
97+ WriteToFile(File::PathJoin({profPath_, Analysis::Common::OUTPUT_PATH, AICPU_DP_NAME}), {});
98+ return true;
99+}
100+ 
101+bool AicpuAssembler::WriteMiCsv(const std::vector<AicpuMiData> &data)
102+{
103+ if (data.empty())
104+ {
105+ return false;
106+ }
107+ headers_ = {"Node Name", "Start Time(us)", "End Time(us)", "Queue Size"};
108+ res_.clear();
109+ for (const auto &item : data)
110+ {
111+ res_.emplace_back(std::vector<std::string>{item.nodeName, std::to_string(item.startTime),
112+ std::to_string(item.endTime), std::to_string(item.queueSize)});
113+ }
114+ WriteToFile(File::PathJoin({profPath_, Analysis::Common::OUTPUT_PATH, AICPU_MI_NAME}), {});
115+ return true;
116+}
117+} // namespace Application
118+} // namespace Analysis
@@ -0,0 +1,42 @@
1+/* -------------------------------------------------------------------------
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This file is part of the MindStudio project.
4+ *
5+ * MindStudio is licensed under Mulan PSL v2.
6+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
7+ * You may obtain a copy of Mulan PSL v2 at:
8+ *
9+ * http://license.coscl.org.cn/MulanPSL2
10+ *
11+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14+ * See the Mulan PSL v2 for more details.
15+ * -------------------------------------------------------------------------*/
16+ 
17+#ifndef ANALYSIS_APPLICATION_AICPU_ASSEMBLER_H
18+#define ANALYSIS_APPLICATION_AICPU_ASSEMBLER_H
19+ 
20+#include "analysis/csrc/application/summary/summary_assembler.h"
21+#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/aicpu_summary_data.h"
22+ 
23+namespace Analysis
24+{
25+namespace Application
26+{
27+class AicpuAssembler : public SummaryAssembler
28+{
29+ public:
30+ AicpuAssembler() = default;
31+ AicpuAssembler(const std::string &name, const std::string &profPath);
32+ 
33+ private:
34+ uint8_t AssembleData(DataInventory &dataInventory) override;
35+ bool WriteAicpuCsv(const std::vector<Domain::AicpuSummaryData> &data);
36+ bool WriteDpCsv(const std::vector<Domain::AicpuDpData> &data);
37+ bool WriteMiCsv(const std::vector<Domain::AicpuMiData> &data);
38+};
39+} // namespace Application
40+} // namespace Analysis
41+ 
42+#endif // ANALYSIS_APPLICATION_AICPU_ASSEMBLER_H
@@ -40,6 +40,9 @@ const std::string STEP_TRACE_SUMMARY_NAME = "step_trace";
40const std::string COMM_STATISTIC_NAME = "communication_statistic";40const std::string COMM_STATISTIC_NAME = "communication_statistic";
41const std::string OP_STATISTIC_NAME = "op_statistic";41const std::string OP_STATISTIC_NAME = "op_statistic";
42const std::string PAGE_FAULT_NAME = "page_fault";42const std::string PAGE_FAULT_NAME = "page_fault";
43+const std::string AICPU_NAME = "aicpu";
44+const std::string AICPU_DP_NAME = "dp";
45+const std::string AICPU_MI_NAME = "aicpu_mi";
43 46 
44const std::string SUMMARY_SUFFIX = ".csv";47const std::string SUMMARY_SUFFIX = ".csv";
45const std::string SLICE = "slice";48const std::string SLICE = "slice";
@@ -16,6 +16,9 @@
16 16 
17#include "analysis/csrc/application/summary/summary_factory.h"17#include "analysis/csrc/application/summary/summary_factory.h"
18 18 
19+#include <unordered_map>
20+ 
21+#include "analysis/csrc/application/summary/aicpu_assembler.h"
19#include "analysis/csrc/application/summary/api_statistic_assembler.h"22#include "analysis/csrc/application/summary/api_statistic_assembler.h"
20#include "analysis/csrc/application/summary/comm_statistic_assembler.h"23#include "analysis/csrc/application/summary/comm_statistic_assembler.h"
21#include "analysis/csrc/application/summary/fusion_op_assembler.h"24#include "analysis/csrc/application/summary/fusion_op_assembler.h"
@@ -53,6 +56,8 @@ std::unordered_map<std::string, AssemblerCreator> SummaryFactory::assemblerTable
53 { MAKE_SHARED0_NO_OPERATION(assembler, OpStatisticAssembler, PROCESSOR_NAME_OP_STATISTIC, profPath); }},56 { MAKE_SHARED0_NO_OPERATION(assembler, OpStatisticAssembler, PROCESSOR_NAME_OP_STATISTIC, profPath); }},
54 {PROCESSOR_NAME_PAGE_FAULT, [](const std::string& profPath, std::shared_ptr<SummaryAssembler>& assembler)57 {PROCESSOR_NAME_PAGE_FAULT, [](const std::string& profPath, std::shared_ptr<SummaryAssembler>& assembler)
55 { MAKE_SHARED0_NO_OPERATION(assembler, PageFaultAssembler, PROCESSOR_NAME_PAGE_FAULT, profPath); }},58 { MAKE_SHARED0_NO_OPERATION(assembler, PageFaultAssembler, PROCESSOR_NAME_PAGE_FAULT, profPath); }},
59+ {PROCESSOR_NAME_AICPU, [](const std::string& profPath, std::shared_ptr<SummaryAssembler>& assembler)
60+ { MAKE_SHARED0_NO_OPERATION(assembler, AicpuAssembler, PROCESSOR_NAME_AICPU, profPath); }},
56};61};
57 62 
58std::shared_ptr<SummaryAssembler> SummaryFactory::GetAssemblerByName(const std::string& processName,63std::shared_ptr<SummaryAssembler> SummaryFactory::GetAssemblerByName(const std::string& processName,
@@ -30,11 +30,11 @@ namespace Application
30{30{
31namespace31namespace
32{32{
33-const std::vector<std::string> DATA_ASSEMBLE_LIST{PROCESSOR_OP_SUMMARY, PROCESSOR_NAME_COMM_STATISTIC,33+const std::vector<std::string> DATA_ASSEMBLE_LIST{
34- PROCESSOR_NAME_OP_STATISTIC, PROCESSOR_NAME_NPU_MEM,34+ PROCESSOR_OP_SUMMARY, PROCESSOR_NAME_COMM_STATISTIC, PROCESSOR_NAME_OP_STATISTIC,
35- PROCESSOR_NAME_NPU_MODULE_MEM, PROCESSOR_NAME_API,35+ PROCESSOR_NAME_NPU_MEM, PROCESSOR_NAME_NPU_MODULE_MEM, PROCESSOR_NAME_API,
36- PROCESSOR_NAME_FUSION_OP, PROCESSOR_TASK_TIME_SUMMARY,36+ PROCESSOR_NAME_FUSION_OP, PROCESSOR_TASK_TIME_SUMMARY, PROCESSOR_NAME_STEP_TRACE,
37- PROCESSOR_NAME_STEP_TRACE, PROCESSOR_NAME_PAGE_FAULT};37+ PROCESSOR_NAME_PAGE_FAULT, PROCESSOR_NAME_AICPU};
38 38 
39const std::unordered_map<std::string, std::string> SUMMARY_DELIVERABLES{39const std::unordered_map<std::string, std::string> SUMMARY_DELIVERABLES{
40 {"op_summary", PROCESSOR_OP_SUMMARY},40 {"op_summary", PROCESSOR_OP_SUMMARY},
@@ -47,6 +47,9 @@ const std::unordered_map<std::string, std::string> SUMMARY_DELIVERABLES{
47 {"task_time", PROCESSOR_TASK_TIME_SUMMARY},47 {"task_time", PROCESSOR_TASK_TIME_SUMMARY},
48 {"step_trace", PROCESSOR_NAME_STEP_TRACE},48 {"step_trace", PROCESSOR_NAME_STEP_TRACE},
49 {"page_fault", PROCESSOR_NAME_PAGE_FAULT},49 {"page_fault", PROCESSOR_NAME_PAGE_FAULT},
50+ {"aicpu", PROCESSOR_NAME_AICPU},
51+ {"dp", PROCESSOR_NAME_AICPU},
52+ {"aicpu_mi", PROCESSOR_NAME_AICPU},
50};53};
51 54 
52} // namespace55} // namespace
@@ -19,6 +19,7 @@
19#include "analysis/csrc/application/database/db_constant.h"19#include "analysis/csrc/application/database/db_constant.h"
20#include "analysis/csrc/application/summary/summary_manager.h"20#include "analysis/csrc/application/summary/summary_manager.h"
21#include "analysis/csrc/domain/entities/hal/include/ascend_obj.h"21#include "analysis/csrc/domain/entities/hal/include/ascend_obj.h"
22+#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/aicpu_summary_data.h"
22#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/api_data.h"23#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/api_data.h"
23#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/ascend_task_data.h"24#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/ascend_task_data.h"
24#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/communication_info_data.h"25#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/communication_info_data.h"
@@ -69,6 +70,11 @@ REGISTER_SIMPLE_SUMMARY_NODE(PROCESSOR_NAME_NPU_MODULE_MEM, std::vector<NpuModul
69REGISTER_SIMPLE_SUMMARY_NODE(PROCESSOR_NAME_API, std::vector<ApiData>, PROCESSOR_NAME_API);70REGISTER_SIMPLE_SUMMARY_NODE(PROCESSOR_NAME_API, std::vector<ApiData>, PROCESSOR_NAME_API);
70REGISTER_SIMPLE_SUMMARY_NODE(PROCESSOR_NAME_STEP_TRACE, std::vector<TrainTraceData>, PROCESSOR_NAME_STEP_TRACE);71REGISTER_SIMPLE_SUMMARY_NODE(PROCESSOR_NAME_STEP_TRACE, std::vector<TrainTraceData>, PROCESSOR_NAME_STEP_TRACE);
71REGISTER_SIMPLE_SUMMARY_NODE(PROCESSOR_NAME_PAGE_FAULT, std::vector<PageFaultData>, PROCESSOR_NAME_PAGE_FAULT);72REGISTER_SIMPLE_SUMMARY_NODE(PROCESSOR_NAME_PAGE_FAULT, std::vector<PageFaultData>, PROCESSOR_NAME_PAGE_FAULT);
73+REGISTER_TOPO_NODE_SEQUENCE(typeid(void), TOPO_NODE(SUMMARY_GENERATION, PROCESSOR_NAME_AICPU), true,
74+ SummaryManager::CreateSummaryAssembler(PROCESSOR_NAME_AICPU),
75+ TOPO_DEPS(TOPO_NODE(DATA_PROCESSING, PROCESSOR_NAME_AICPU)), nullptr);
76+REGISTER_TOPO_NODE_DEPENDENT_DATA(TOPO_NODE(SUMMARY_GENERATION, PROCESSOR_NAME_AICPU), std::vector<AicpuSummaryData>,
77+ std::vector<AicpuDpData>, std::vector<AicpuMiData>);
72 78 
73REGISTER_TOPO_NODE_SEQUENCE(typeid(void), TOPO_NODE(SUMMARY_GENERATION, PROCESSOR_NAME_FUSION_OP), true,79REGISTER_TOPO_NODE_SEQUENCE(typeid(void), TOPO_NODE(SUMMARY_GENERATION, PROCESSOR_NAME_FUSION_OP), true,
74 SummaryManager::CreateSummaryAssembler(PROCESSOR_NAME_FUSION_OP),80 SummaryManager::CreateSummaryAssembler(PROCESSOR_NAME_FUSION_OP),
@@ -0,0 +1,329 @@
1+/* -------------------------------------------------------------------------
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This file is part of the MindStudio project.
4+ *
5+ * MindStudio is licensed under Mulan PSL v2.
6+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
7+ * You may obtain a copy of Mulan PSL v2 at:
8+ *
9+ * http://license.coscl.org.cn/MulanPSL2
10+ *
11+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14+ * See the Mulan PSL v2 for more details.
15+ * -------------------------------------------------------------------------*/
16+ 
17+#include "analysis/csrc/domain/data_process/ai_task/aicpu_processor.h"
18+ 
19+#include <algorithm>
20+#include <map>
21+#include <tuple>
22+ 
23+#include "analysis/csrc/domain/services/environment/context.h"
24+#include "analysis/csrc/infrastructure/dfx/error_code.h"
25+#include "analysis/csrc/infrastructure/utils/common_constant.h"
26+#include "analysis/csrc/infrastructure/utils/file.h"
27+#include "analysis/csrc/infrastructure/utils/utils.h"
28+ 
29+namespace Analysis
30+{
31+namespace Domain
32+{
33+using namespace Analysis::Domain::Environment;
34+using namespace Analysis::Utils;
35+using namespace Analysis::Common;
36+ 
37+namespace
38+{
39+using StreamTaskKey = std::tuple<uint16_t, uint32_t, uint32_t>;
40+using GeKey = std::tuple<uint16_t, uint32_t, uint32_t, uint32_t>;
41+ 
42+// AiCpuData / AiCpuDP 落盘为 ns,对齐 Python aicpuViewer:CSV 为 us
43+double NsToUs(double timeNs) { return timeNs / static_cast<double>(NS_TO_US); }
44+} // namespace
45+ 
46+AicpuProcessor::AicpuProcessor(const std::string &profPath) : DataProcessor(profPath) {}
47+ 
48+bool AicpuProcessor::Process(DataInventory &dataInventory)
49+{
50+ bool flag = true;
51+ std::vector<AicpuSummaryData> summaryData;
52+ std::vector<AicpuDpData> dpData;
53+ std::vector<AicpuMiData> miData;
54+ auto deviceList = Utils::File::GetFilesWithPrefix(profPath_, DEVICE_PREFIX);
55+ for (const auto &devicePath : deviceList)
56+ {
57+ flag = ProcessSingleDevice(devicePath, summaryData, dpData, miData) && flag;
58+ }
59+ 
60+ auto ascendTasks = dataInventory.GetPtr<std::vector<AscendTaskData>>();
61+ auto taskInfos = dataInventory.GetPtr<std::vector<TaskInfoData>>();
62+ auto version = Context::GetInstance().GetPlatformVersion();
63+ const bool isChipV6 = Context::IsChipV6(version);
64+ if (!isChipV6 && ascendTasks != nullptr)
65+ {
66+ MatchBatchId(summaryData, *ascendTasks);
67+ }
68+ if (taskInfos != nullptr)
69+ {
70+ MatchNodeName(summaryData, *taskInfos, isChipV6);
71+ }
72+ std::stable_sort(summaryData.begin(), summaryData.end(),
73+ [](const AicpuSummaryData &lhs, const AicpuSummaryData &rhs)
74+ { return lhs.timestampNs < rhs.timestampNs; });
75+ 
76+ flag = SaveToDataInventory<AicpuSummaryData>(std::move(summaryData), dataInventory, PROCESSOR_NAME_AICPU) && flag;
77+ flag = SaveToDataInventory<AicpuDpData>(std::move(dpData), dataInventory, PROCESSOR_NAME_AICPU) && flag;
78+ flag = SaveToDataInventory<AicpuMiData>(std::move(miData), dataInventory, PROCESSOR_NAME_AICPU) && flag;
79+ return flag;
80+}
81+ 
82+bool AicpuProcessor::ProcessSingleDevice(const std::string &devicePath, std::vector<AicpuSummaryData> &summaryData,
83+ std::vector<AicpuDpData> &dpData, std::vector<AicpuMiData> &miData)
84+{
85+ uint16_t deviceId = GetDeviceIdByDevicePath(devicePath);
86+ if (deviceId == INVALID_DEVICE_ID)
87+ {
88+ ERROR("the invalid deviceId cannot to be identified.");
89+ return false;
90+ }
91+ ProfTimeRecord timeRecord;
92+ if (!Context::GetInstance().GetProfTimeRecordInfo(timeRecord, profPath_, deviceId))
93+ {
94+ ERROR("GetProfTimeRecordInfo failed, profPath is %.", profPath_);
95+ return false;
96+ }
97+ bool flag = LoadAiCpuData(devicePath, deviceId, timeRecord, summaryData);
98+ flag = LoadDpData(devicePath, timeRecord, dpData) && flag;
99+ flag = LoadMiData(devicePath, miData) && flag;
100+ return flag;
101+}
102+ 
103+bool AicpuProcessor::LoadAiCpuData(const std::string &devicePath, uint16_t deviceId, const ProfTimeRecord &timeRecord,
104+ std::vector<AicpuSummaryData> &summaryData)
105+{
106+ DBInfo aicpuDB(DB_NAME_AI_CPU, TABLE_NAME_AI_CPU);
107+ std::string dbPath = Utils::File::PathJoin({devicePath, SQLITE, aicpuDB.dbName});
108+ if (!aicpuDB.ConstructDBRunner(dbPath) || aicpuDB.dbRunner == nullptr)
109+ {
110+ ERROR("Create % connection failed.", dbPath);
111+ return false;
112+ }
113+ auto status = CheckPathAndTable(dbPath, aicpuDB, false);
114+ if (status != CHECK_SUCCESS)
115+ {
116+ return status != CHECK_FAILED;
117+ }
118+ OriAiCpuData oriData;
119+ std::string sql{
120+ "SELECT stream_id, task_id, sys_start, sys_end, node_name, compute_time, memcpy_time, task_time, "
121+ "dispatch_time, total_time FROM " +
122+ aicpuDB.tableName + " ORDER BY sys_start"};
123+ if (!aicpuDB.dbRunner->QueryData(sql, oriData))
124+ {
125+ ERROR("Failed to obtain data from the % table.", aicpuDB.tableName);
126+ return false;
127+ }
128+ for (const auto &row : oriData)
129+ {
130+ AicpuSummaryData data;
131+ double sysStart = 0;
132+ double sysEnd = 0;
133+ double computeTime = 0;
134+ double memcpyTime = 0;
135+ double taskTime = 0;
136+ double dispatchTime = 0;
137+ double totalTime = 0;
138+ std::tie(data.streamId, data.taskId, sysStart, sysEnd, data.nodeName, computeTime, memcpyTime, taskTime,
139+ dispatchTime, totalTime) = row;
140+ data.deviceId = deviceId;
141+ HPFloat start{sysStart};
142+ HPFloat end{sysEnd};
143+ data.timestampNs = GetLocalTime(start, timeRecord).Uint64();
144+ data.endNs = GetLocalTime(end, timeRecord).Uint64();
145+ if (data.timestampNs < timeRecord.startTimeNs)
WangJie
WangJieWangJie5 天前

[Review] LoadAiCpuData 有 if (data.timestampNs < timeRecord.startTimeNs) 的判断,但 LoadDpData/LoadMiData 没有。导致同一次导出中 aicpu.csv 只覆盖采集窗口,而 dp/aicpu_mi 可能包含窗口前的数据。建议确认这是否符合预期,若需要一致性可补充同样的过滤。

likedislike
wangzixuan
5 天前 评论:
146+ {
147+ continue;
148+ }
149+ data.nodeName = data.nodeName.empty() ? NA : data.nodeName;
150+ data.computeTimeUs = NsToUs(computeTime);
151+ data.memcpyTimeUs = NsToUs(memcpyTime);
152+ data.taskTimeUs = NsToUs(taskTime);
153+ data.dispatchTimeUs = NsToUs(dispatchTime);
154+ data.totalTimeUs = NsToUs(totalTime);
155+ summaryData.push_back(data);
156+ }
157+ return true;
158+}
159+ 
160+bool AicpuProcessor::LoadDpData(const std::string &devicePath, const ProfTimeRecord &timeRecord,
161+ std::vector<AicpuDpData> &dpData)
162+{
163+ DBInfo dpDB(DB_NAME_AI_CPU, TABLE_NAME_AI_CPU_DP);
164+ std::string dbPath = Utils::File::PathJoin({devicePath, SQLITE, dpDB.dbName});
165+ if (!dpDB.ConstructDBRunner(dbPath) || dpDB.dbRunner == nullptr)
166+ {
167+ ERROR("Create % connection failed.", dbPath);
168+ return false;
169+ }
170+ auto status = CheckPathAndTable(dbPath, dpDB, false);
171+ if (status != CHECK_SUCCESS)
172+ {
173+ return status != CHECK_FAILED;
174+ }
175+ OriAiCpuDpData oriData;
176+ std::string sql{"SELECT timestamp, action, source, buffer_size FROM " + dpDB.tableName + " ORDER BY timestamp"};
177+ if (!dpDB.dbRunner->QueryData(sql, oriData))
178+ {
179+ ERROR("Failed to obtain data from the % table.", dpDB.tableName);
180+ return false;
181+ }
182+ for (const auto &row : oriData)
183+ {
184+ AicpuDpData data;
185+ double rawTimestamp = 0;
186+ std::tie(rawTimestamp, data.action, data.source, data.bufferSize) = row;
187+ HPFloat timestamp{rawTimestamp};
188+ data.timestamp = GetLocalTime(timestamp, timeRecord).Uint64();
189+ dpData.push_back(data);
190+ }
191+ return true;
192+}
193+ 
194+bool AicpuProcessor::LoadMiData(const std::string &devicePath, std::vector<AicpuMiData> &miData)
195+{
196+ DBInfo miDB(DB_NAME_DATA_PREPROCESS, TABLE_NAME_DATA_QUEUE);
197+ std::string dbPath = Utils::File::PathJoin({devicePath, SQLITE, miDB.dbName});
198+ if (!miDB.ConstructDBRunner(dbPath) || miDB.dbRunner == nullptr)
199+ {
200+ ERROR("Create % connection failed.", dbPath);
201+ return false;
202+ }
203+ auto status = CheckPathAndTable(dbPath, miDB, false);
204+ if (status != CHECK_SUCCESS)
205+ {
206+ return status != CHECK_FAILED;
207+ }
208+ OriAiCpuMiData oriData;
209+ std::string sql{"SELECT node_name, start_time, end_time, queue_size FROM " + miDB.tableName +
210+ " ORDER BY start_time"};
211+ if (!miDB.dbRunner->QueryData(sql, oriData))
212+ {
213+ ERROR("Failed to obtain data from the % table.", miDB.tableName);
214+ return false;
215+ }
216+ for (const auto &row : oriData)
217+ {
218+ AicpuMiData data;
219+ double startTime = 0;
220+ double endTime = 0;
221+ std::tie(data.nodeName, startTime, endTime, data.queueSize) = row;
222+ data.startTime = static_cast<uint64_t>(startTime);
Mrtutu
MrtutuMrtutu6 天前

严重程度: 提示

问题: LoadMiData 不调用 GetLocalTime,直接将 DB 中的 start_time/end_time 原值赋给 AicpuMiData.startTime/endTime;而 aicpu_assembler.cpp:108 的 CSV header 却写的是 "Start Time(us), End Time(us)"

原因: 这与 Python parity 一致(Python get_aicpu_mi_data 也不调用 trans_into_local_time),属历史遗留问题,不在本 PR 范围内。但 data_preprocess.dbDataQueue 表中存的 mi.mi.runStartTime 是原始 syscnt(落盘时未经过 GetTimeFromSyscnt),单位可能是 ns 而非 us,header 标称 us 易误导下游。

怎么改: 本 PR 不必处理;建议在后续 PR 中校准 MI 时间字段单位(要么在 LoadMiData 转 us,要么修正 header 为 "Start Time(ns)"),并补充对应的 wall-clock 偏移测试用例。

likedislike
wangzixuan
5 天前 评论:
223+ data.endTime = static_cast<uint64_t>(endTime);
224+ miData.push_back(data);
225+ }
226+ return true;
227+}
228+ 
229+void AicpuProcessor::MatchBatchId(std::vector<AicpuSummaryData> &summaryData,
230+ const std::vector<AscendTaskData> &ascendTasks)
231+{
232+ std::map<StreamTaskKey, std::vector<const AscendTaskData *>> taskMap;
233+ for (const auto &task : ascendTasks)
234+ {
235+ if (task.hostType != KERNEL_AICPU_TASK_TYPE)
236+ {
237+ continue;
238+ }
239+ taskMap[{task.deviceId, task.streamId, task.taskId}].push_back(&task);
240+ }
241+ for (auto &entry : taskMap)
242+ {
243+ std::sort(entry.second.begin(), entry.second.end(),
244+ [](const AscendTaskData *lhs, const AscendTaskData *rhs) { return lhs->timestamp < rhs->timestamp; });
245+ }
246+ 
247+ std::map<StreamTaskKey, std::vector<AicpuSummaryData *>> aicpuMap;
248+ for (auto &item : summaryData)
249+ {
250+ aicpuMap[{item.deviceId, item.streamId, item.taskId}].push_back(&item);
Mrtutu
MrtutuMrtutu6 天前

严重程度: 提示

问题: MatchBatchIdtaskMap 的每个 entry 按 lhs->timestamp 显式排序(241-243 行),但 aicpuMap 的每个 entry(本行 push_back)未显式排序,依赖 LoadAiCpuData 的 SQL ORDER BY sys_start 隐式保证顺序。

原因: 当前单设备场景下安全(SQL 已排序,单设备同 (stream, task) 组合内顺序稳定);但若未来 LoadAiCpuData 改为多线程或移除 SQL ORDER BY,会引入隐蔽的匹配 bug,且 taskMapaicpuMap 不对称,可读性差。

怎么改: 建议对 aicpuMap 也按 endNs 显式排序,与 taskMap 对称:

for (auto &entry : aicpuMap) {
    std::sort(entry.second.begin(), entry.second.end(),
              [](const AicpuSummaryData *lhs, const AicpuSummaryData *rhs) { return lhs->endNs < rhs->endNs; });
}
likedislike
wangzixuan
5 天前 评论:
251+ }
252+ for (auto &entry : aicpuMap)
253+ {
254+ std::sort(entry.second.begin(), entry.second.end(),
255+ [](const AicpuSummaryData *lhs, const AicpuSummaryData *rhs) { return lhs->endNs < rhs->endNs; });
256+ }
257+ 
258+ for (auto &entry : aicpuMap)
259+ {
260+ auto taskIt = taskMap.find(entry.first);
261+ if (taskIt == taskMap.end())
262+ {
263+ continue;
264+ }
265+ auto &aicpuList = entry.second;
266+ auto &taskList = taskIt->second;
267+ size_t aicpuIndex = 0;
268+ size_t taskIndex = 0;
269+ while (aicpuIndex < aicpuList.size() && taskIndex < taskList.size())
270+ {
271+ const uint64_t sysEndNs = aicpuList[aicpuIndex]->endNs;
272+ const auto *task = taskList[taskIndex];
273+ if (task->timestamp <= sysEndNs && sysEndNs <= task->end)
274+ {
275+ aicpuList[aicpuIndex]->batchId = task->batchId;
276+ ++aicpuIndex;
277+ }
278+ else if (sysEndNs < task->timestamp)
279+ {
280+ ++aicpuIndex;
281+ }
282+ else
283+ {
284+ ++taskIndex;
285+ }
286+ }
287+ }
288+}
289+ 
290+void AicpuProcessor::MatchNodeName(std::vector<AicpuSummaryData> &summaryData,
291+ const std::vector<TaskInfoData> &taskInfos, bool isChipV6)
292+{
293+ std::map<GeKey, std::string> geMap;
294+ std::map<StreamTaskKey, std::string> geMapV6;
295+ for (const auto &info : taskInfos)
296+ {
297+ if (!isChipV6 && info.taskType != AI_CPU)
298+ {
299+ continue;
300+ }
301+ if (isChipV6)
302+ {
303+ geMapV6.emplace(StreamTaskKey{info.deviceId, info.streamId, info.taskId}, info.opName);
304+ }
305+ else
306+ {
307+ geMap.emplace(GeKey{info.deviceId, info.streamId, info.taskId, info.batchId}, info.opName);
308+ }
309+ }
310+ for (auto &item : summaryData)
311+ {
312+ if (isChipV6)
313+ {
314+ auto it = geMapV6.find({item.deviceId, item.streamId, item.taskId});
315+ if (it != geMapV6.end())
316+ {
317+ item.nodeName = it->second;
318+ }
319+ continue;
320+ }
321+ auto it = geMap.find({item.deviceId, item.streamId, item.taskId, item.batchId});
Mrtutu
MrtutuMrtutu6 天前

严重程度: 提示

问题: MatchNodeName 非 V6 路径的查找 key 为 {deviceId, streamId, taskId, batchId},而 Python match_aicpu_with_ge_summary 使用的是 (stream_id, task_id, batch_id)(无 deviceId)。

原因: C++ 写法更精确(避免多设备同 (stream, task, batch) 串扰),且 ShouldNotCrossMatchDevicesAndShouldSortByTimestamp 用例也验证了这一行为,属于“良性偏离”。但 Python 端 ge_summary_dic.setdefault 对跨设备同 key 保留首条,C++ key 含 deviceId 后此场景不再触发,行为已实质改变。若下游消费方依赖与 Python 完全一致的输出,需确认此偏差已被知会。

怎么改: 无需修改;如希望与 Python 完全对齐,可改 key 为 {streamId, taskId, batchId},但需评估多设备串扰风险。

likedislike
wangzixuan
5 天前 评论:
322+ if (it != geMap.end())
323+ {
324+ item.nodeName = it->second;
325+ }
326+ }
327+}
328+} // namespace Domain
329+} // namespace Analysis
@@ -0,0 +1,62 @@
1+/* -------------------------------------------------------------------------
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This file is part of the MindStudio project.
4+ *
5+ * MindStudio is licensed under Mulan PSL v2.
6+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
7+ * You may obtain a copy of Mulan PSL v2 at:
8+ *
9+ * http://license.coscl.org.cn/MulanPSL2
10+ *
11+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14+ * See the Mulan PSL v2 for more details.
15+ * -------------------------------------------------------------------------*/
16+ 
17+#ifndef ANALYSIS_DOMAIN_AICPU_PROCESSOR_H
18+#define ANALYSIS_DOMAIN_AICPU_PROCESSOR_H
19+ 
20+#include <map>
21+#include <tuple>
22+#include <utility>
23+#include <vector>
24+ 
25+#include "analysis/csrc/domain/data_process/data_processor.h"
26+#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/aicpu_summary_data.h"
27+#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/ascend_task_data.h"
28+#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/task_info_data.h"
29+#include "analysis/csrc/infrastructure/utils/time_utils.h"
30+ 
31+namespace Analysis
32+{
33+namespace Domain
34+{
35+using OriAiCpuData =
36+ std::vector<std::tuple<uint32_t, uint32_t, double, double, std::string, double, double, double, double, double>>;
37+using OriAiCpuDpData = std::vector<std::tuple<double, std::string, std::string, uint64_t>>;
38+using OriAiCpuMiData = std::vector<std::tuple<std::string, double, double, uint64_t>>;
39+ 
40+class AicpuProcessor : public DataProcessor
41+{
42+ public:
43+ AicpuProcessor() = default;
44+ explicit AicpuProcessor(const std::string &profPath);
45+ 
46+ private:
47+ bool Process(DataInventory &dataInventory) override;
48+ bool ProcessSingleDevice(const std::string &devicePath, std::vector<AicpuSummaryData> &summaryData,
49+ std::vector<AicpuDpData> &dpData, std::vector<AicpuMiData> &miData);
50+ bool LoadAiCpuData(const std::string &devicePath, uint16_t deviceId, const Utils::ProfTimeRecord &timeRecord,
51+ std::vector<AicpuSummaryData> &summaryData);
52+ bool LoadDpData(const std::string &devicePath, const Utils::ProfTimeRecord &timeRecord,
53+ std::vector<AicpuDpData> &dpData);
54+ bool LoadMiData(const std::string &devicePath, std::vector<AicpuMiData> &miData);
55+ void MatchBatchId(std::vector<AicpuSummaryData> &summaryData, const std::vector<AscendTaskData> &ascendTasks);
56+ void MatchNodeName(std::vector<AicpuSummaryData> &summaryData, const std::vector<TaskInfoData> &taskInfos,
57+ bool isChipV6);
58+};
59+} // namespace Domain
60+} // namespace Analysis
61+ 
62+#endif // ANALYSIS_DOMAIN_AICPU_PROCESSOR_H
@@ -15,6 +15,7 @@
15 * -------------------------------------------------------------------------*/15 * -------------------------------------------------------------------------*/
16 16 
17#include "analysis/csrc/application/database/db_constant.h"17#include "analysis/csrc/application/database/db_constant.h"
18+#include "analysis/csrc/domain/data_process/ai_task/aicpu_processor.h"
18#include "analysis/csrc/domain/data_process/ai_task/api_processor.h"19#include "analysis/csrc/domain/data_process/ai_task/api_processor.h"
19#include "analysis/csrc/domain/data_process/ai_task/ccu_mission_processor.h"20#include "analysis/csrc/domain/data_process/ai_task/ccu_mission_processor.h"
20#include "analysis/csrc/domain/data_process/ai_task/communication_info_processor.h"21#include "analysis/csrc/domain/data_process/ai_task/communication_info_processor.h"
@@ -110,6 +111,10 @@ REGISTER_PROCESSOR(PCIeProcessor, PROCESSOR_NAME_PCIE, TOPO_DEPS());
110REGISTER_PROCESSOR(SioProcessor, PROCESSOR_NAME_SIO, TOPO_DEPS());111REGISTER_PROCESSOR(SioProcessor, PROCESSOR_NAME_SIO, TOPO_DEPS());
111REGISTER_PROCESSOR(SocBandwidthProcessor, PROCESSOR_NAME_SOC, TOPO_DEPS());112REGISTER_PROCESSOR(SocBandwidthProcessor, PROCESSOR_NAME_SOC, TOPO_DEPS());
112REGISTER_PROCESSOR(PageFaultProcessor, PROCESSOR_NAME_PAGE_FAULT, TOPO_DEPS());113REGISTER_PROCESSOR(PageFaultProcessor, PROCESSOR_NAME_PAGE_FAULT, TOPO_DEPS());
114+REGISTER_PROCESSOR_WITH_DATA(AicpuProcessor, PROCESSOR_NAME_AICPU,
115+ TOPO_DEPS(TOPO_NODE(DATA_PROCESSING, PROCESSOR_NAME_TASK),
116+ TOPO_NODE(DATA_PROCESSING, PROCESSOR_NAME_COMPUTE_TASK_INFO)),
117+ std::vector<AscendTaskData>, std::vector<TaskInfoData>);
113REGISTER_PROCESSOR(NicTimelineProcessor, PROCESSOR_NAME_NIC_TIMELINE, TOPO_DEPS());118REGISTER_PROCESSOR(NicTimelineProcessor, PROCESSOR_NAME_NIC_TIMELINE, TOPO_DEPS());
114REGISTER_PROCESSOR(RoCETimelineProcessor, PROCESSOR_NAME_ROCE_TIMELINE, TOPO_DEPS());119REGISTER_PROCESSOR(RoCETimelineProcessor, PROCESSOR_NAME_ROCE_TIMELINE, TOPO_DEPS());
115REGISTER_PROCESSOR(NicProcessor, PROCESSOR_NAME_NIC, TOPO_DEPS());120REGISTER_PROCESSOR(NicProcessor, PROCESSOR_NAME_NIC, TOPO_DEPS());
@@ -0,0 +1,61 @@
1+/* -------------------------------------------------------------------------
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This file is part of the MindStudio project.
4+ *
5+ * MindStudio is licensed under Mulan PSL v2.
6+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
7+ * You may obtain a copy of Mulan PSL v2 at:
8+ *
9+ * http://license.coscl.org.cn/MulanPSL2
10+ *
11+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14+ * See the Mulan PSL v2 for more details.
15+ * -------------------------------------------------------------------------*/
16+ 
17+#ifndef ANALYSIS_DOMAIN_AICPU_SUMMARY_DATA_H
18+#define ANALYSIS_DOMAIN_AICPU_SUMMARY_DATA_H
19+ 
20+#include <cstdint>
21+#include <string>
22+ 
23+namespace Analysis
24+{
25+namespace Domain
26+{
27+struct AicpuSummaryData
28+{
29+ uint16_t deviceId = UINT16_MAX;
30+ uint64_t timestampNs = UINT64_MAX;
31+ uint64_t endNs = UINT64_MAX;
32+ std::string nodeName;
33+ double computeTimeUs = 0;
34+ double memcpyTimeUs = 0;
35+ double taskTimeUs = 0;
36+ double dispatchTimeUs = 0;
37+ double totalTimeUs = 0;
38+ uint32_t streamId = UINT32_MAX;
39+ uint32_t taskId = UINT32_MAX;
40+ uint32_t batchId = UINT32_MAX;
41+};
42+ 
43+struct AicpuDpData
44+{
45+ uint64_t timestamp = 0;
46+ std::string action;
47+ std::string source;
48+ uint64_t bufferSize = 0;
49+};
50+ 
51+struct AicpuMiData
52+{
53+ std::string nodeName;
54+ uint64_t startTime = 0;
55+ uint64_t endTime = 0;
56+ uint64_t queueSize = 0;
57+};
58+} // namespace Domain
59+} // namespace Analysis
60+ 
61+#endif // ANALYSIS_DOMAIN_AICPU_SUMMARY_DATA_H
@@ -19,6 +19,7 @@
19#include <algorithm>19#include <algorithm>
20#include <unordered_map>20#include <unordered_map>
21 21 
22+#include "analysis/csrc/application/database/db_constant.h"
22#include "analysis/csrc/domain/services/device_context/load_host_data.h"23#include "analysis/csrc/domain/services/device_context/load_host_data.h"
23#include "analysis/csrc/domain/services/modeling/batch_id/batch_id.h"24#include "analysis/csrc/domain/services/modeling/batch_id/batch_id.h"
24#include "analysis/csrc/domain/services/parser/track/include/ts_track_parser.h"25#include "analysis/csrc/domain/services/parser/track/include/ts_track_parser.h"
@@ -34,6 +35,7 @@ namespace Analysis
34namespace Domain35namespace Domain
35{36{
36using namespace Utils;37using namespace Utils;
38+using namespace Analysis::Application;
37namespace39namespace
38{40{
39static const std::string MI_NAME = "GetNext_dequeue_wait";41static const std::string MI_NAME = "GetNext_dequeue_wait";
@@ -120,7 +122,7 @@ uint32_t AicpuPersistence::GenerateAndSaveNode(const std::string& deviceFilePath
120 total_time.Double());122 total_time.Double());
121 }123 }
122 124 
123- DBInfo dbInfo("ai_cpu.db", "AiCpuData");125+ DBInfo dbInfo(DB_NAME_AI_CPU, TABLE_NAME_AI_CPU);
124 MAKE_SHARED0_RETURN_VALUE(dbInfo.database, AicpuDB, ANALYSIS_ERROR);126 MAKE_SHARED0_RETURN_VALUE(dbInfo.database, AicpuDB, ANALYSIS_ERROR);
125 std::string dbPath = Utils::File::PathJoin({deviceFilePath, SQLITE, dbInfo.dbName});127 std::string dbPath = Utils::File::PathJoin({deviceFilePath, SQLITE, dbInfo.dbName});
126 INFO("Start to process %.", dbPath);128 INFO("Start to process %.", dbPath);
@@ -151,7 +153,7 @@ uint32_t AicpuPersistence::GenerateAndSaveDp(const std::string& deviceFilePath)
151 data.emplace_back(timeStamp.Double(), std::string(dp.dp.action), std::string(dp.dp.source), dp.dp.size);153 data.emplace_back(timeStamp.Double(), std::string(dp.dp.action), std::string(dp.dp.source), dp.dp.size);
152 }154 }
153 155 
154- DBInfo dbInfo("ai_cpu.db", "AiCpuDP");156+ DBInfo dbInfo(DB_NAME_AI_CPU, TABLE_NAME_AI_CPU_DP);
155 MAKE_SHARED0_RETURN_VALUE(dbInfo.database, AicpuDB, ANALYSIS_ERROR);157 MAKE_SHARED0_RETURN_VALUE(dbInfo.database, AicpuDB, ANALYSIS_ERROR);
156 std::string dbPath = Utils::File::PathJoin({deviceFilePath, SQLITE, dbInfo.dbName});158 std::string dbPath = Utils::File::PathJoin({deviceFilePath, SQLITE, dbInfo.dbName});
157 INFO("Start to process %.", dbPath);159 INFO("Start to process %.", dbPath);
@@ -213,7 +215,7 @@ uint32_t AicpuPersistence::GenerateAndSaveMi(const std::string& deviceFilePath)
213 mi.mi.runEndTime, mi.mi.runEndTime - mi.mi.runStartTime);215 mi.mi.runEndTime, mi.mi.runEndTime - mi.mi.runStartTime);
214 }216 }
215 217 
216- DBInfo dbInfo("data_preprocess.db", "DataQueue");218+ DBInfo dbInfo(DB_NAME_DATA_PREPROCESS, TABLE_NAME_DATA_QUEUE);
217 MAKE_SHARED0_RETURN_VALUE(dbInfo.database, DataPreprocessDB, ANALYSIS_ERROR);219 MAKE_SHARED0_RETURN_VALUE(dbInfo.database, DataPreprocessDB, ANALYSIS_ERROR);
218 std::string dbPath = Utils::File::PathJoin({deviceFilePath, SQLITE, dbInfo.dbName});220 std::string dbPath = Utils::File::PathJoin({deviceFilePath, SQLITE, dbInfo.dbName});
219 MAKE_SHARED_RETURN_VALUE(dbInfo.dbRunner, DBRunner, ANALYSIS_ERROR, dbPath);221 MAKE_SHARED_RETURN_VALUE(dbInfo.dbRunner, DBRunner, ANALYSIS_ERROR, dbPath);
@@ -117,6 +117,8 @@ class ParseDpData:
117 with AiCpuModel(os.path.dirname(dp_path)) as model:117 with AiCpuModel(os.path.dirname(dp_path)) as model:
118 data = model.get_all_data(DBNameConstant.TABLE_AI_CPU_DP)118 data = model.get_all_data(DBNameConstant.TABLE_AI_CPU_DP)
119 # 表内 timestamp 为 ns(Python/C++ 落盘对齐),CSV 头为 Timestamp(us)119 # 表内 timestamp 为 ns(Python/C++ 落盘对齐),CSV 头为 Timestamp(us)
120+ # 与 C++ LoadDpData 的 ORDER BY timestamp 对齐,保证 dp.csv 行序可重现
121+ data.sort(key=lambda row: row[0])
120 return [122 return [
121 (123 (
122 float(InfoConfReader().trans_into_local_time(float(row[0]))),124 float(InfoConfReader().trans_into_local_time(float(row[0]))),
@@ -195,7 +195,9 @@ class ParseAiCpuData:
195 if not conn or not curs:195 if not conn or not curs:
196 logging.warning("Can't connect %s", DBNameConstant.DB_CLUSTER_DATA_PREPROCESS)196 logging.warning("Can't connect %s", DBNameConstant.DB_CLUSTER_DATA_PREPROCESS)
197 return []197 return []
198- sql = "select node_name, start_time, end_time, queue_size from {0}".format(DBNameConstant.TABLE_DATA_QUEUE)198+ sql = "select node_name, start_time, end_time, queue_size from {0} order by start_time".format(
199+ DBNameConstant.TABLE_DATA_QUEUE
200+ )
199 aicpu_mi_data = DBManager.fetch_all_data(conn.cursor(), sql)201 aicpu_mi_data = DBManager.fetch_all_data(conn.cursor(), sql)
200 DBManager.destroy_db_connect(conn, curs)202 DBManager.destroy_db_connect(conn, curs)
201 return aicpu_mi_data203 return aicpu_mi_data
@@ -0,0 +1,279 @@
1+/* -------------------------------------------------------------------------
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This file is part of the MindStudio project.
4+ *
5+ * MindStudio is licensed under Mulan PSL v2.
6+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
7+ * You may obtain a copy of Mulan PSL v2 at:
8+ *
9+ * http://license.coscl.org.cn/MulanPSL2
10+ *
11+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14+ * See the Mulan PSL v2 for more details.
15+ * -------------------------------------------------------------------------*/
16+ 
17+#include <cstdint>
18+#include <memory>
19+#include <string>
20+#include <vector>
21+ 
22+#include "gtest/gtest.h"
23+#include "mockcpp/mockcpp.hpp"
24+ 
25+#include "analysis/csrc/application/database/db_constant.h"
26+#include "analysis/csrc/application/summary/aicpu_assembler.h"
27+#include "analysis/csrc/application/summary/summary_constant.h"
28+#include "analysis/csrc/application/summary/summary_factory.h"
29+#include "analysis/csrc/application/summary/summary_manager.h"
30+#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/aicpu_summary_data.h"
31+#include "analysis/csrc/infrastructure/dfx/error_code.h"
32+#include "analysis/csrc/infrastructure/utils/file.h"
33+#include "analysis/csrc/infrastructure/utils/utils.h"
34+ 
35+using namespace Analysis::Application;
36+using namespace Analysis::Domain;
37+using namespace Analysis::Utils;
38+ 
39+namespace {
40+const int DEPTH = 0;
41+const std::string BASE_PATH = "./aicpu_assembler_utest";
42+const std::string PROF_PATH = File::PathJoin({BASE_PATH, "PROF_0"});
43+const std::string RESULT_PATH = File::PathJoin({PROF_PATH, Analysis::Common::OUTPUT_PATH});
44+ 
45+const std::string AICPU_HEADER =
46+ "Timestamp(us),Node,Compute_time(us),Memcpy_time(us),Task_time(us),Dispatch_time(us),Total_time(us),Stream ID,Task "
47+ "ID";
48+const std::string DP_HEADER = "Timestamp(us),Action,Source,Cached Buffer Size";
49+const std::string MI_HEADER = "Node Name,Start Time(us),End Time(us),Queue Size";
50+ 
51+std::string FindCsvByName(const std::string &name)
52+{
53+ const std::vector<std::string> files = File::GetOriginData(RESULT_PATH, {name}, {});
54+ for (size_t i = 0; i < files.size(); ++i) {
55+ const std::string base = File::BaseName(files[i]);
56+ if (name == AICPU_NAME && base.find(AICPU_MI_NAME) == 0) {
57+ continue;
58+ }
59+ if (base.find(name) == 0) {
60+ return files[i];
61+ }
62+ }
63+ return "";
64+}
65+ 
66+std::vector<std::string> ReadCsvLines(const std::string &filePath)
67+{
68+ FileReader reader(filePath);
69+ std::vector<std::string> lines;
70+ EXPECT_EQ(Analysis::ANALYSIS_OK, reader.ReadText(lines));
71+ return lines;
72+}
73+ 
74+template <typename T>
75+void InjectVector(DataInventory &dataInventory, const std::vector<T> &data)
76+{
77+ std::shared_ptr<std::vector<T>> holder;
78+ MAKE_SHARED_NO_OPERATION(holder, std::vector<T>, data);
79+ dataInventory.Inject(holder);
80+}
81+ 
82+std::vector<AicpuSummaryData> GenerateAicpuData()
83+{
84+ std::vector<AicpuSummaryData> res;
85+ AicpuSummaryData first;
86+ first.deviceId = 0;
87+ first.timestampNs = 1000000;
88+ first.nodeName = "Conv2D";
89+ first.computeTimeUs = 1.5;
90+ first.memcpyTimeUs = 2.5;
91+ first.taskTimeUs = 500.0;
92+ first.dispatchTimeUs = 0.5;
93+ first.totalTimeUs = 10.5;
94+ first.streamId = 10;
95+ first.taskId = 20;
96+ res.push_back(first);
97+ 
98+ AicpuSummaryData second;
99+ second.deviceId = 0;
100+ second.timestampNs = 2000000;
101+ second.nodeName = "N/A";
102+ second.computeTimeUs = 3.0;
103+ second.memcpyTimeUs = 4.0;
104+ second.taskTimeUs = 8.0;
105+ second.dispatchTimeUs = 1.0;
106+ second.totalTimeUs = 20.0;
107+ second.streamId = 11;
108+ second.taskId = 30;
109+ res.push_back(second);
110+ return res;
111+}
112+ 
113+std::vector<AicpuDpData> GenerateDpData()
114+{
115+ std::vector<AicpuDpData> res;
116+ AicpuDpData first;
117+ first.timestamp = 1000000;
118+ first.action = "enqueue";
119+ first.source = "src0";
120+ first.bufferSize = 128;
121+ res.push_back(first);
122+ 
123+ AicpuDpData second;
124+ second.timestamp = 2500000;
125+ second.action = "dequeue";
126+ second.source = "src1";
127+ second.bufferSize = 256;
128+ res.push_back(second);
129+ return res;
130+}
131+ 
132+std::vector<AicpuMiData> GenerateMiData()
133+{
134+ std::vector<AicpuMiData> res;
135+ AicpuMiData first;
136+ first.nodeName = "QueueA";
137+ first.startTime = 100;
138+ first.endTime = 200;
139+ first.queueSize = 8;
140+ res.push_back(first);
141+ 
142+ AicpuMiData second;
143+ second.nodeName = "QueueB";
144+ second.startTime = 300;
145+ second.endTime = 400;
146+ second.queueSize = 16;
147+ res.push_back(second);
148+ return res;
149+}
150+} // namespace
151+ 
152+class AicpuAssemblerUTest : public testing::Test {
153+protected:
154+ void SetUp() override
155+ {
156+ if (File::Check(BASE_PATH)) {
157+ File::RemoveDir(BASE_PATH, DEPTH);
158+ }
159+ EXPECT_TRUE(File::CreateDir(BASE_PATH));
160+ EXPECT_TRUE(File::CreateDir(PROF_PATH));
161+ EXPECT_TRUE(File::CreateDir(RESULT_PATH));
162+ }
163+ 
164+ void TearDown() override
165+ {
166+ EXPECT_TRUE(File::RemoveDir(BASE_PATH, DEPTH));
167+ GlobalMockObject::verify();
168+ }
169+};
170+ 
171+TEST_F(AicpuAssemblerUTest, ShouldReturnTrueWhenDataNotExist)
172+{
173+ AicpuAssembler assembler(PROCESSOR_NAME_AICPU, PROF_PATH);
174+ DataInventory dataInventory;
175+ EXPECT_TRUE(assembler.Run(dataInventory));
176+ EXPECT_TRUE(FindCsvByName(AICPU_NAME).empty());
177+ EXPECT_TRUE(FindCsvByName(AICPU_DP_NAME).empty());
178+ EXPECT_TRUE(FindCsvByName(AICPU_MI_NAME).empty());
179+}
180+ 
181+TEST_F(AicpuAssemblerUTest, ShouldReturnTrueWhenEmptyVectorsInjected)
182+{
183+ DataInventory dataInventory;
184+ InjectVector(dataInventory, std::vector<AicpuSummaryData>());
185+ InjectVector(dataInventory, std::vector<AicpuDpData>());
186+ InjectVector(dataInventory, std::vector<AicpuMiData>());
187+ 
188+ AicpuAssembler assembler(PROCESSOR_NAME_AICPU, PROF_PATH);
189+ EXPECT_TRUE(assembler.Run(dataInventory));
190+ EXPECT_TRUE(FindCsvByName(AICPU_NAME).empty());
191+ EXPECT_TRUE(FindCsvByName(AICPU_DP_NAME).empty());
192+ EXPECT_TRUE(FindCsvByName(AICPU_MI_NAME).empty());
193+}
194+ 
195+TEST_F(AicpuAssemblerUTest, ShouldWriteThreeCsvWhenAllDataExist)
196+{
197+ DataInventory dataInventory;
198+ InjectVector(dataInventory, GenerateAicpuData());
199+ InjectVector(dataInventory, GenerateDpData());
200+ InjectVector(dataInventory, GenerateMiData());
201+ 
202+ AicpuAssembler assembler(PROCESSOR_NAME_AICPU, PROF_PATH);
203+ EXPECT_TRUE(assembler.Run(dataInventory));
204+ 
205+ const std::string aicpuFile = FindCsvByName(AICPU_NAME);
206+ const std::string dpFile = FindCsvByName(AICPU_DP_NAME);
207+ const std::string miFile = FindCsvByName(AICPU_MI_NAME);
208+ ASSERT_FALSE(aicpuFile.empty());
209+ ASSERT_FALSE(dpFile.empty());
210+ ASSERT_FALSE(miFile.empty());
211+ 
212+ std::vector<std::string> aicpuLines = ReadCsvLines(aicpuFile);
213+ ASSERT_EQ(3ul, aicpuLines.size());
214+ EXPECT_EQ(AICPU_HEADER, aicpuLines[0]);
215+ EXPECT_EQ("1000.000,Conv2D,1.5,2.5,500,0.5,10.5,10,20", aicpuLines[1]);
216+ EXPECT_EQ("2000.000,N/A,3,4,8,1,20,11,30", aicpuLines[2]);
217+ 
218+ std::vector<std::string> dpLines = ReadCsvLines(dpFile);
219+ ASSERT_EQ(3ul, dpLines.size());
220+ EXPECT_EQ(DP_HEADER, dpLines[0]);
221+ EXPECT_EQ("1000.000,enqueue,src0,128", dpLines[1]);
222+ EXPECT_EQ("2500.000,dequeue,src1,256", dpLines[2]);
223+ 
224+ std::vector<std::string> miLines = ReadCsvLines(miFile);
225+ ASSERT_EQ(3ul, miLines.size());
226+ EXPECT_EQ(MI_HEADER, miLines[0]);
227+ EXPECT_EQ("QueueA,100,200,8", miLines[1]);
228+ EXPECT_EQ("QueueB,300,400,16", miLines[2]);
229+}
230+ 
231+TEST_F(AicpuAssemblerUTest, ShouldWriteAicpuOnly)
232+{
233+ DataInventory dataInventory;
234+ InjectVector(dataInventory, GenerateAicpuData());
235+ 
236+ AicpuAssembler assembler(PROCESSOR_NAME_AICPU, PROF_PATH);
237+ EXPECT_TRUE(assembler.Run(dataInventory));
238+ EXPECT_FALSE(FindCsvByName(AICPU_NAME).empty());
239+ EXPECT_TRUE(FindCsvByName(AICPU_DP_NAME).empty());
240+ EXPECT_TRUE(FindCsvByName(AICPU_MI_NAME).empty());
241+}
242+ 
243+TEST_F(AicpuAssemblerUTest, ShouldWriteDpOnly)
244+{
245+ DataInventory dataInventory;
246+ InjectVector(dataInventory, GenerateDpData());
247+ 
248+ AicpuAssembler assembler(PROCESSOR_NAME_AICPU, PROF_PATH);
249+ EXPECT_TRUE(assembler.Run(dataInventory));
250+ EXPECT_TRUE(FindCsvByName(AICPU_NAME).empty());
251+ EXPECT_FALSE(FindCsvByName(AICPU_DP_NAME).empty());
252+ EXPECT_TRUE(FindCsvByName(AICPU_MI_NAME).empty());
253+}
254+ 
255+TEST_F(AicpuAssemblerUTest, ShouldWriteMiOnly)
256+{
257+ DataInventory dataInventory;
258+ InjectVector(dataInventory, GenerateMiData());
259+ 
260+ AicpuAssembler assembler(PROCESSOR_NAME_AICPU, PROF_PATH);
261+ EXPECT_TRUE(assembler.Run(dataInventory));
262+ EXPECT_TRUE(FindCsvByName(AICPU_NAME).empty());
263+ EXPECT_TRUE(FindCsvByName(AICPU_DP_NAME).empty());
264+ EXPECT_FALSE(FindCsvByName(AICPU_MI_NAME).empty());
265+}
266+ 
267+TEST_F(AicpuAssemblerUTest, ShouldGetAicpuAssemblerFromFactory)
268+{
269+ auto assembler = SummaryFactory::GetAssemblerByName(PROCESSOR_NAME_AICPU, PROF_PATH);
270+ EXPECT_NE(nullptr, assembler);
271+}
272+ 
273+TEST_F(AicpuAssemblerUTest, ShouldMapThreeDeliverablesToOneAssembler)
274+{
275+ std::vector<std::string> assemblers;
276+ ASSERT_TRUE(SummaryManager::GetAssemblerList({"aicpu", "dp", "aicpu_mi", "aicpu"}, assemblers));
277+ ASSERT_EQ(1ul, assemblers.size());
278+ EXPECT_EQ(PROCESSOR_NAME_AICPU, assemblers[0]);
279+}
@@ -66,7 +66,7 @@ TEST(SummaryManagerUTest, ShouldSelectAllDeliverablesWhenSelectionIsEmpty)
66{66{
67 std::vector<TopoNodeId> roots;67 std::vector<TopoNodeId> roots;
68 ASSERT_TRUE(SummaryManager::GetTopologyRoots({}, roots));68 ASSERT_TRUE(SummaryManager::GetTopologyRoots({}, roots));
69- EXPECT_EQ(roots.size(), 10UL);69+ EXPECT_EQ(roots.size(), 11UL);
70 std::unordered_set<TopoNodeId, TopoNodeIdHash> uniqueRoots;70 std::unordered_set<TopoNodeId, TopoNodeIdHash> uniqueRoots;
71 for (const auto& root : roots)71 for (const auto& root : roots)
72 {72 {
@@ -85,6 +85,19 @@ TEST(SummaryManagerUTest, ShouldSelectAndDeduplicateDeliverables)
85 EXPECT_EQ(assemblers[1], PROCESSOR_NAME_NPU_MEM);85 EXPECT_EQ(assemblers[1], PROCESSOR_NAME_NPU_MEM);
86}86}
87 87 
88+TEST(SummaryManagerUTest, ShouldDeduplicateAicpuFamilyDeliverables)
89+{
90+ std::vector<std::string> assemblers;
91+ ASSERT_TRUE(SummaryManager::GetAssemblerList({"aicpu", "dp", "aicpu_mi", "aicpu"}, assemblers));
92+ ASSERT_EQ(assemblers.size(), 1UL);
93+ EXPECT_EQ(assemblers[0], PROCESSOR_NAME_AICPU);
94+ 
95+ std::vector<TopoNodeId> roots;
96+ ASSERT_TRUE(SummaryManager::GetTopologyRoots({"dp", "aicpu_mi"}, roots));
97+ ASSERT_EQ(roots.size(), 1UL);
98+ EXPECT_EQ(roots.front(), (TopoNodeId{TopoNodeStage::SUMMARY_GENERATION, PROCESSOR_NAME_AICPU}));
99+}
100+ 
88TEST(SummaryManagerUTest, ShouldRejectUnknownDeliverable)101TEST(SummaryManagerUTest, ShouldRejectUnknownDeliverable)
89{102{
90 std::vector<TopoNodeId> roots;103 std::vector<TopoNodeId> roots;
@@ -0,0 +1,621 @@
1+/* -------------------------------------------------------------------------
2+ * Copyright (c) 2026 Huawei Technologies Co., Ltd.
3+ * This file is part of the MindStudio project.
4+ *
5+ * MindStudio is licensed under Mulan PSL v2.
6+ * You can use this software according to the terms and conditions of the Mulan PSL v2.
7+ * You may obtain a copy of Mulan PSL v2 at:
8+ *
9+ * http://license.coscl.org.cn/MulanPSL2
10+ *
11+ * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
12+ * EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
13+ * MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
14+ * See the Mulan PSL v2 for more details.
15+ * -------------------------------------------------------------------------*/
16+ 
17+#include <algorithm>
18+#include <cstdint>
19+#include <memory>
20+#include <string>
21+#include <tuple>
22+#include <vector>
23+ 
24+#include "gtest/gtest.h"
25+#include "mockcpp/mockcpp.hpp"
26+ 
27+#include "analysis/csrc/application/database/db_constant.h"
28+#include "analysis/csrc/domain/data_process/ai_task/aicpu_processor.h"
29+#include "analysis/csrc/domain/data_process/data_processor.h"
30+#include "analysis/csrc/domain/data_process/include/data_processor_factory.h"
31+#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/aicpu_summary_data.h"
32+#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/ascend_task_data.h"
33+#include "analysis/csrc/domain/entities/viewer_data/ai_task/include/task_info_data.h"
34+#include "analysis/csrc/domain/services/environment/context.h"
35+#include "analysis/csrc/infrastructure/db/include/database.h"
36+#include "analysis/csrc/infrastructure/db/include/db_runner.h"
37+#include "analysis/csrc/infrastructure/utils/common_constant.h"
38+#include "opensource/json/include/nlohmann/json.hpp"
39+ 
40+using namespace Analysis::Application;
41+using namespace Analysis::Common;
42+using namespace Analysis::Domain;
43+using namespace Analysis::Domain::Environment;
44+using namespace Analysis::Infra;
45+using namespace Analysis::Utils;
46+ 
47+namespace {
48+const int DEPTH = 0;
49+const std::string BASE_PATH = "./aicpu_processor_utest";
50+const std::string PROF_PATH = File::PathJoin({BASE_PATH, "PROF_0"});
51+ 
52+const TableColumns AICPU_TABLE_COLS = {
53+ {"stream_id", SQL_INTEGER_TYPE}, {"task_id", SQL_INTEGER_TYPE}, {"sys_start", SQL_NUMERIC_TYPE},
54+ {"sys_end", SQL_NUMERIC_TYPE}, {"node_name", SQL_TEXT_TYPE}, {"compute_time", SQL_REAL_TYPE},
55+ {"memcpy_time", SQL_REAL_TYPE}, {"task_time", SQL_REAL_TYPE}, {"dispatch_time", SQL_REAL_TYPE},
56+ {"total_time", SQL_REAL_TYPE},
57+};
58+ 
59+const TableColumns AICPU_DP_TABLE_COLS = {
60+ {"timestamp", SQL_NUMERIC_TYPE},
61+ {"action", SQL_TEXT_TYPE},
62+ {"source", SQL_TEXT_TYPE},
63+ {"buffer_size", SQL_INTEGER_TYPE},
64+};
65+ 
66+const TableColumns DATA_QUEUE_TABLE_COLS = {
C

【review】【测试】DP 单测采用零 boot 偏移,结构性掩盖 wall-clock 偏移缺陷

  • 问题描述:BuildTimeRecord("0", "0", "5") 使 startTimeNs=0、baseTimeNs=0,GetLocalTime 退化为恒等变换,DP 时间戳未转换在断言中不可见(断言 dp->at(0).timestamp == 1000000.0 即刻画了"不转换")
  • 问题原因:零偏移使 (startTimeNs - baseTimeNs)=0,掩盖了 LoadDpData 缺少偏移转换的缺陷,单测无法守护与 AICPU/Python 路径的一致性
  • 修改建议:补一个非零偏移用例(如 startUs>baseNs),断言 DP timestamp 经偏移后等于 aicpu 同基准的 wall-clock 值
likedislike
wangzixuan
5 天前 评论:
67+ {"node_name", SQL_TEXT_TYPE}, {"queue_size", SQL_INTEGER_TYPE}, {"start_time", SQL_REAL_TYPE},
68+ {"end_time", SQL_REAL_TYPE}, {"duration", SQL_REAL_TYPE},
69+};
70+ 
71+using AiCpuInsert = OriAiCpuData;
72+using DpInsert = OriAiCpuDpData;
73+using MiInsert = std::vector<std::tuple<std::string, uint64_t, double, double, double>>;
74+ 
75+nlohmann::json BuildTimeRecord(const std::string &startUs, const std::string &baseNs,
76+ const std::string &platformVersion)
77+{
78+ return nlohmann::json{
79+ {"startCollectionTimeBegin", startUs},
80+ {"endCollectionTimeEnd", "999999999"},
81+ {"startClockMonotonicRaw", baseNs},
82+ {"platform_version", platformVersion},
83+ };
84+}
85+ 
86+std::string DevicePath(uint16_t deviceId)
87+{
88+ return File::PathJoin({PROF_PATH, DEVICE_PREFIX + std::to_string(deviceId)});
89+}
90+ 
91+std::string SqlitePath(uint16_t deviceId)
92+{
93+ return File::PathJoin({DevicePath(deviceId), SQLITE});
94+}
95+ 
96+void CreateDeviceDir(uint16_t deviceId)
97+{
98+ EXPECT_TRUE(File::CreateDir(DevicePath(deviceId)));
99+ EXPECT_TRUE(File::CreateDir(SqlitePath(deviceId)));
100+}
101+ 
102+void InsertTable(const std::string &dbPath, const std::string &tableName, const TableColumns &cols)
103+{
104+ std::shared_ptr<DBRunner> dbRunner;
105+ MAKE_SHARED_RETURN_VOID(dbRunner, DBRunner, dbPath);
106+ EXPECT_TRUE(dbRunner->CreateTable(tableName, cols));
107+}
108+ 
109+template <typename T>
110+void InsertTableData(const std::string &dbPath, const std::string &tableName, const TableColumns &cols, const T &data)
111+{
112+ std::shared_ptr<DBRunner> dbRunner;
113+ MAKE_SHARED_RETURN_VOID(dbRunner, DBRunner, dbPath);
114+ EXPECT_TRUE(dbRunner->CreateTable(tableName, cols));
115+ if (!data.empty()) {
116+ EXPECT_TRUE(dbRunner->InsertData(tableName, data));
117+ }
118+}
119+ 
120+void WriteAiCpuData(uint16_t deviceId, const AiCpuInsert &data)
121+{
122+ InsertTableData(File::PathJoin({SqlitePath(deviceId), DB_NAME_AI_CPU}), TABLE_NAME_AI_CPU, AICPU_TABLE_COLS, data);
123+}
124+ 
125+void WriteDpData(uint16_t deviceId, const DpInsert &data)
126+{
127+ InsertTableData(File::PathJoin({SqlitePath(deviceId), DB_NAME_AI_CPU}), TABLE_NAME_AI_CPU_DP, AICPU_DP_TABLE_COLS,
128+ data);
129+}
130+ 
131+void WriteMiData(uint16_t deviceId, const MiInsert &data)
132+{
133+ InsertTableData(File::PathJoin({SqlitePath(deviceId), DB_NAME_DATA_PREPROCESS}), TABLE_NAME_DATA_QUEUE,
134+ DATA_QUEUE_TABLE_COLS, data);
135+}
136+ 
137+AscendTaskData MakeAscendTask(uint16_t deviceId, uint32_t streamId, uint32_t taskId, uint64_t startNs, uint64_t endNs,
138+ uint32_t batchId, const std::string &hostType)
139+{
140+ AscendTaskData task;
141+ task.deviceId = deviceId;
142+ task.streamId = streamId;
143+ task.taskId = taskId;
144+ task.timestamp = startNs;
145+ task.end = endNs;
146+ task.batchId = batchId;
147+ task.hostType = hostType;
148+ return task;
149+}
150+ 
151+TaskInfoData MakeTaskInfo(uint16_t deviceId, uint32_t streamId, uint32_t taskId, uint32_t batchId,
152+ const std::string &taskType, const std::string &opName)
153+{
154+ TaskInfoData info;
155+ info.deviceId = deviceId;
156+ info.streamId = streamId;
157+ info.taskId = taskId;
158+ info.batchId = batchId;
159+ info.taskType = taskType;
160+ info.opName = opName;
161+ return info;
162+}
163+ 
164+template <typename T>
165+void InjectVector(DataInventory &dataInventory, const std::vector<T> &data)
166+{
167+ std::shared_ptr<std::vector<T>> holder;
168+ MAKE_SHARED_NO_OPERATION(holder, std::vector<T>, data);
169+ dataInventory.Inject(holder);
170+}
171+ 
172+const AicpuSummaryData *FindSummary(const std::vector<AicpuSummaryData> &data, uint16_t deviceId, uint32_t streamId,
173+ uint32_t taskId, uint64_t timestampNs)
174+{
175+ for (size_t i = 0; i < data.size(); ++i) {
176+ if (data[i].deviceId == deviceId && data[i].streamId == streamId && data[i].taskId == taskId &&
177+ data[i].timestampNs == timestampNs) {
178+ return &data[i];
179+ }
180+ }
181+ return nullptr;
182+}
183+} // namespace
184+ 
185+class AicpuProcessorUTest : public testing::Test {
186+protected:
187+ void SetUp() override
188+ {
189+ if (File::Check(BASE_PATH)) {
190+ File::RemoveDir(BASE_PATH, DEPTH);
191+ }
192+ EXPECT_TRUE(File::CreateDir(BASE_PATH));
193+ EXPECT_TRUE(File::CreateDir(PROF_PATH));
194+ MOCKER_CPP(&Environment::Context::GetInfoByDeviceId).stubs().will(returnValue(BuildTimeRecord("0", "0", "5")));
195+ }
196+ 
197+ void TearDown() override
198+ {
199+ GlobalMockObject::verify();
200+ if (File::Check(BASE_PATH)) {
201+ EXPECT_TRUE(File::RemoveDir(BASE_PATH, DEPTH));
202+ }
203+ }
204+};
205+ 
206+TEST_F(AicpuProcessorUTest, ShouldLoadAllThreeTypesAndFillDerivedFields)
207+{
208+ CreateDeviceDir(0);
209+ AiCpuInsert aicpuData{
210+ {10, 20, 1000000.0, 1500000.0, "", 1500.0, 2500.0, 500000.0, 500.0, 10500.0},
211+ {11, 30, 4000000.0, 3500000.0, "InvEnd", 3000.0, 4000.0, 8000.0, 1000.0, 20000.0},
212+ };
213+ DpInsert dpData{
214+ {1000000.0, "enqueue", "src0", 128},
215+ {2000000.0, "dequeue", "src1", 256},
216+ };
217+ MiInsert miData{
218+ {"QueueA", 8, 100.7, 200.2, 99.5},
219+ {"QueueB", 16, 300.0, 400.0, 100.0},
220+ };
221+ WriteAiCpuData(0, aicpuData);
222+ WriteDpData(0, dpData);
223+ WriteMiData(0, miData);
224+ 
225+ DataInventory dataInventory;
226+ AicpuProcessor processor(PROF_PATH);
227+ EXPECT_TRUE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
228+ 
229+ auto summary = dataInventory.GetPtr<std::vector<AicpuSummaryData>>();
230+ ASSERT_NE(nullptr, summary);
231+ ASSERT_EQ(2ul, summary->size());
232+ EXPECT_EQ(0u, summary->at(0).deviceId);
233+ EXPECT_EQ(10u, summary->at(0).streamId);
234+ EXPECT_EQ(20u, summary->at(0).taskId);
235+ EXPECT_EQ(1000000ull, summary->at(0).timestampNs);
236+ EXPECT_EQ(1500000ull, summary->at(0).endNs);
237+ EXPECT_EQ(NA, summary->at(0).nodeName);
238+ EXPECT_DOUBLE_EQ(1.5, summary->at(0).computeTimeUs);
239+ EXPECT_DOUBLE_EQ(2.5, summary->at(0).memcpyTimeUs);
240+ EXPECT_DOUBLE_EQ(500.0, summary->at(0).taskTimeUs);
241+ EXPECT_DOUBLE_EQ(0.5, summary->at(0).dispatchTimeUs);
242+ EXPECT_DOUBLE_EQ(10.5, summary->at(0).totalTimeUs);
243+ EXPECT_EQ(UINT32_MAX, summary->at(0).batchId);
244+ 
245+ EXPECT_EQ("InvEnd", summary->at(1).nodeName);
246+ EXPECT_DOUBLE_EQ(3.0, summary->at(1).computeTimeUs);
247+ EXPECT_DOUBLE_EQ(4.0, summary->at(1).memcpyTimeUs);
248+ EXPECT_DOUBLE_EQ(8.0, summary->at(1).taskTimeUs);
249+ EXPECT_DOUBLE_EQ(1.0, summary->at(1).dispatchTimeUs);
250+ EXPECT_DOUBLE_EQ(20.0, summary->at(1).totalTimeUs);
251+ 
252+ auto dp = dataInventory.GetPtr<std::vector<AicpuDpData>>();
253+ ASSERT_NE(nullptr, dp);
254+ ASSERT_EQ(2ul, dp->size());
255+ EXPECT_EQ(1000000ull, dp->at(0).timestamp);
256+ EXPECT_EQ("enqueue", dp->at(0).action);
257+ EXPECT_EQ("src0", dp->at(0).source);
258+ EXPECT_EQ(128ull, dp->at(0).bufferSize);
259+ 
260+ auto mi = dataInventory.GetPtr<std::vector<AicpuMiData>>();
261+ ASSERT_NE(nullptr, mi);
262+ ASSERT_EQ(2ul, mi->size());
263+ EXPECT_EQ("QueueA", mi->at(0).nodeName);
264+ EXPECT_EQ(100ull, mi->at(0).startTime);
265+ EXPECT_EQ(200ull, mi->at(0).endTime);
266+ EXPECT_EQ(8ull, mi->at(0).queueSize);
267+}
268+ 
269+TEST_F(AicpuProcessorUTest, ShouldSkipRecordsBeforeCollectStart)
270+{
271+ MOCKER_CPP(&Environment::Context::GetInfoByDeviceId).reset();
272+ MOCKER_CPP(&Environment::Context::GetInfoByDeviceId).stubs().will(returnValue(BuildTimeRecord("2", "2000", "5")));
273+ CreateDeviceDir(0);
274+ AiCpuInsert aicpuData{
275+ {1, 1, 1000.0, 1500.0, "BeforeStart", 1.0, 1.0, 500.0, 1.0, 3.0},
276+ {1, 2, 5000.0, 6000.0, "AfterStart", 2.0, 2.0, 1000.0, 2.0, 6.0},
277+ };
278+ WriteAiCpuData(0, aicpuData);
279+ 
280+ DataInventory dataInventory;
281+ AicpuProcessor processor(PROF_PATH);
282+ EXPECT_TRUE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
283+ 
284+ auto summary = dataInventory.GetPtr<std::vector<AicpuSummaryData>>();
285+ ASSERT_NE(nullptr, summary);
286+ ASSERT_EQ(1ul, summary->size());
287+ EXPECT_EQ("AfterStart", summary->at(0).nodeName);
288+ EXPECT_EQ(5000ull, summary->at(0).timestampNs);
289+}
290+ 
291+TEST_F(AicpuProcessorUTest, ShouldFillBatchAndNodeWhenNonV6Matched)
292+{
293+ CreateDeviceDir(0);
294+ AiCpuInsert aicpuData{
295+ {10, 20, 1000000.0, 1500000.0, "", 1.0, 1.0, 0.0, 1.0, 3.0},
296+ {10, 20, 2000000.0, 2500000.0, "Raw", 1.0, 1.0, 0.0, 1.0, 3.0},
297+ {11, 30, 3000000.0, 3100000.0, "KeepMe", 1.0, 1.0, 0.0, 1.0, 3.0},
298+ };
299+ WriteAiCpuData(0, aicpuData);
300+ 
301+ std::vector<AscendTaskData> tasks;
302+ tasks.push_back(MakeAscendTask(0, 10, 20, 900000, 1600000, 7, KERNEL_AICPU_TASK_TYPE));
303+ tasks.push_back(MakeAscendTask(0, 10, 20, 1900000, 2600000, 8, KERNEL_AICPU_TASK_TYPE));
304+ tasks.push_back(MakeAscendTask(0, 10, 20, 500000, 4000000, 99, KERNEL_AICORE_TASK_TYPE));
305+ tasks.push_back(MakeAscendTask(0, 11, 30, 2900000, 3200000, 1, KERNEL_AICPU_TASK_TYPE));
306+ 
307+ std::vector<TaskInfoData> geInfos;
308+ geInfos.push_back(MakeTaskInfo(0, 10, 20, 7, AI_CPU, "Conv2D"));
309+ geInfos.push_back(MakeTaskInfo(0, 10, 20, 8, AI_CPU, "MatMul"));
310+ geInfos.push_back(MakeTaskInfo(0, 11, 30, 1, "AI_CORE", "ShouldNotMatch"));
311+ 
312+ DataInventory dataInventory;
313+ InjectVector(dataInventory, tasks);
314+ InjectVector(dataInventory, geInfos);
315+ 
316+ AicpuProcessor processor(PROF_PATH);
317+ EXPECT_TRUE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
318+ 
319+ auto summary = dataInventory.GetPtr<std::vector<AicpuSummaryData>>();
320+ ASSERT_NE(nullptr, summary);
321+ ASSERT_EQ(3ul, summary->size());
322+ 
323+ const AicpuSummaryData *first = FindSummary(*summary, 0, 10, 20, 1000000ull);
324+ const AicpuSummaryData *second = FindSummary(*summary, 0, 10, 20, 2000000ull);
325+ const AicpuSummaryData *third = FindSummary(*summary, 0, 11, 30, 3000000ull);
326+ ASSERT_NE(nullptr, first);
327+ ASSERT_NE(nullptr, second);
328+ ASSERT_NE(nullptr, third);
329+ EXPECT_EQ(7u, first->batchId);
330+ EXPECT_EQ("Conv2D", first->nodeName);
331+ EXPECT_EQ(8u, second->batchId);
332+ EXPECT_EQ("MatMul", second->nodeName);
333+ EXPECT_EQ(1u, third->batchId);
334+ EXPECT_EQ("KeepMe", third->nodeName);
335+}
336+ 
337+TEST_F(AicpuProcessorUTest, ShouldSkipUnmatchedAicpuWithoutConsumingLaterTask)
338+{
339+ CreateDeviceDir(0);
340+ AiCpuInsert aicpuData{
341+ {10, 20, 1000000.0, 1100000.0, "Gap", 1.0, 1.0, 0.0, 1.0, 3.0},
342+ {10, 20, 2000000.0, 2300000.0, "Hit", 1.0, 1.0, 0.0, 1.0, 3.0},
343+ };
344+ WriteAiCpuData(0, aicpuData);
345+ 
346+ std::vector<AscendTaskData> tasks;
347+ tasks.push_back(MakeAscendTask(0, 10, 20, 2000000, 2500000, 8, KERNEL_AICPU_TASK_TYPE));
348+ std::vector<TaskInfoData> geInfos;
349+ geInfos.push_back(MakeTaskInfo(0, 10, 20, 8, AI_CPU, "MatchedOp"));
350+ 
351+ DataInventory dataInventory;
352+ InjectVector(dataInventory, tasks);
353+ InjectVector(dataInventory, geInfos);
354+ 
355+ AicpuProcessor processor(PROF_PATH);
356+ EXPECT_TRUE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
357+ 
358+ auto summary = dataInventory.GetPtr<std::vector<AicpuSummaryData>>();
359+ ASSERT_NE(nullptr, summary);
360+ ASSERT_EQ(2ul, summary->size());
361+ EXPECT_EQ(UINT32_MAX, summary->at(0).batchId);
362+ EXPECT_EQ("Gap", summary->at(0).nodeName);
363+ EXPECT_EQ(8u, summary->at(1).batchId);
364+ EXPECT_EQ("MatchedOp", summary->at(1).nodeName);
365+}
366+ 
367+TEST_F(AicpuProcessorUTest, ShouldMatchNodeByStreamTaskAndSkipBatchOnV6)
368+{
369+ MOCKER_CPP(&Environment::Context::GetPlatformVersion).stubs().will(returnValue(static_cast<uint16_t>(Chip::CHIP_V6_1_0)));
370+ CreateDeviceDir(0);
371+ AiCpuInsert aicpuData{
372+ {10, 20, 1000000.0, 1500000.0, "Raw", 1.0, 1.0, 0.0, 1.0, 3.0},
373+ };
374+ WriteAiCpuData(0, aicpuData);
375+ 
376+ std::vector<AscendTaskData> tasks;
377+ tasks.push_back(MakeAscendTask(0, 10, 20, 900000, 1600000, 7, KERNEL_AICPU_TASK_TYPE));
378+ std::vector<TaskInfoData> geInfos;
379+ geInfos.push_back(MakeTaskInfo(0, 10, 20, 99, "AI_CORE", "V6Op"));
380+ 
381+ DataInventory dataInventory;
382+ InjectVector(dataInventory, tasks);
383+ InjectVector(dataInventory, geInfos);
384+ 
385+ AicpuProcessor processor(PROF_PATH);
386+ EXPECT_TRUE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
387+ 
388+ auto summary = dataInventory.GetPtr<std::vector<AicpuSummaryData>>();
389+ ASSERT_NE(nullptr, summary);
390+ ASSERT_EQ(1ul, summary->size());
391+ EXPECT_EQ(UINT32_MAX, summary->at(0).batchId);
392+ EXPECT_EQ("V6Op", summary->at(0).nodeName);
393+}
394+ 
395+TEST_F(AicpuProcessorUTest, ShouldNotCrossMatchDevicesAndShouldSortByTimestamp)
396+{
397+ CreateDeviceDir(0);
398+ CreateDeviceDir(1);
399+ AiCpuInsert device0{
400+ {10, 20, 3000000.0, 3500000.0, "Dev0", 1.0, 1.0, 0.0, 1.0, 3.0},
401+ };
402+ AiCpuInsert device1{
403+ {10, 20, 1000000.0, 1500000.0, "Dev1", 1.0, 1.0, 0.0, 1.0, 3.0},
404+ };
405+ WriteAiCpuData(0, device0);
406+ WriteAiCpuData(1, device1);
407+ 
408+ std::vector<AscendTaskData> tasks;
409+ tasks.push_back(MakeAscendTask(0, 10, 20, 2900000, 3600000, 7, KERNEL_AICPU_TASK_TYPE));
410+ tasks.push_back(MakeAscendTask(1, 10, 20, 900000, 1600000, 8, KERNEL_AICPU_TASK_TYPE));
411+ std::vector<TaskInfoData> geInfos;
412+ geInfos.push_back(MakeTaskInfo(0, 10, 20, 7, AI_CPU, "Op0"));
413+ geInfos.push_back(MakeTaskInfo(1, 10, 20, 8, AI_CPU, "Op1"));
414+ 
415+ DataInventory dataInventory;
416+ InjectVector(dataInventory, tasks);
417+ InjectVector(dataInventory, geInfos);
418+ 
419+ AicpuProcessor processor(PROF_PATH);
420+ EXPECT_TRUE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
421+ 
422+ auto summary = dataInventory.GetPtr<std::vector<AicpuSummaryData>>();
423+ ASSERT_NE(nullptr, summary);
424+ ASSERT_EQ(2ul, summary->size());
425+ EXPECT_EQ(1u, summary->at(0).deviceId);
426+ EXPECT_EQ(1000000ull, summary->at(0).timestampNs);
427+ EXPECT_EQ(8u, summary->at(0).batchId);
428+ EXPECT_EQ("Op1", summary->at(0).nodeName);
429+ EXPECT_EQ(0u, summary->at(1).deviceId);
430+ EXPECT_EQ(3000000ull, summary->at(1).timestampNs);
431+ EXPECT_EQ(7u, summary->at(1).batchId);
432+ EXPECT_EQ("Op0", summary->at(1).nodeName);
433+}
434+ 
435+TEST_F(AicpuProcessorUTest, ShouldApplyWallClockOffsetToDpAndAicpuTimestamp)
436+{
437+ MOCKER_CPP(&Environment::Context::GetInfoByDeviceId).reset();
438+ MOCKER_CPP(&Environment::Context::GetInfoByDeviceId).stubs().will(returnValue(BuildTimeRecord("100", "1000", "5")));
439+ CreateDeviceDir(0);
440+ const double rawNs = 1000000.0;
441+ const uint64_t expectedLocalNs = static_cast<uint64_t>(rawNs) + 100UL * MILLI_SECOND - 1000UL;
442+ AiCpuInsert aicpuData{
443+ {10, 20, rawNs, 1500000.0, "OffsetNode", 1500.0, 2500.0, 500000.0, 500.0, 10500.0},
444+ };
445+ DpInsert dpData{
446+ {rawNs, "enqueue", "src0", 128},
447+ };
448+ WriteAiCpuData(0, aicpuData);
449+ WriteDpData(0, dpData);
450+ 
451+ DataInventory dataInventory;
452+ AicpuProcessor processor(PROF_PATH);
453+ EXPECT_TRUE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
454+ 
455+ auto summary = dataInventory.GetPtr<std::vector<AicpuSummaryData>>();
456+ ASSERT_NE(nullptr, summary);
457+ ASSERT_EQ(1ul, summary->size());
458+ EXPECT_EQ(expectedLocalNs, summary->at(0).timestampNs);
459+ 
460+ auto dp = dataInventory.GetPtr<std::vector<AicpuDpData>>();
461+ ASSERT_NE(nullptr, dp);
462+ ASSERT_EQ(1ul, dp->size());
463+ EXPECT_EQ(expectedLocalNs, dp->at(0).timestamp);
464+}
465+ 
466+TEST_F(AicpuProcessorUTest, ShouldKeepEqualTimestampOrderAfterStableSort)
467+{
468+ CreateDeviceDir(0);
469+ AiCpuInsert aicpuData{
470+ {1, 1, 1000000.0, 1100000.0, "First", 1000.0, 1000.0, 100000.0, 1000.0, 3000.0},
471+ {2, 2, 1000000.0, 1200000.0, "Second", 1000.0, 1000.0, 200000.0, 1000.0, 3000.0},
472+ };
473+ WriteAiCpuData(0, aicpuData);
474+ 
475+ DataInventory dataInventory;
476+ AicpuProcessor processor(PROF_PATH);
477+ EXPECT_TRUE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
478+ 
479+ auto summary = dataInventory.GetPtr<std::vector<AicpuSummaryData>>();
480+ ASSERT_NE(nullptr, summary);
481+ ASSERT_EQ(2ul, summary->size());
482+ EXPECT_EQ("First", summary->at(0).nodeName);
483+ EXPECT_EQ("Second", summary->at(1).nodeName);
484+ EXPECT_EQ(summary->at(0).timestampNs, summary->at(1).timestampNs);
485+}
486+ 
487+TEST_F(AicpuProcessorUTest, ShouldSucceedWhenAicpuOnly)
488+{
489+ CreateDeviceDir(0);
490+ AiCpuInsert aicpuData{
491+ {1, 2, 1000000.0, 1100000.0, "OnlyAicpu", 1.0, 1.0, 0.0, 1.0, 3.0},
492+ };
493+ WriteAiCpuData(0, aicpuData);
494+ 
495+ DataInventory dataInventory;
496+ AicpuProcessor processor(PROF_PATH);
497+ EXPECT_TRUE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
498+ ASSERT_NE(nullptr, dataInventory.GetPtr<std::vector<AicpuSummaryData>>());
499+ EXPECT_EQ(nullptr, dataInventory.GetPtr<std::vector<AicpuDpData>>());
500+ EXPECT_EQ(nullptr, dataInventory.GetPtr<std::vector<AicpuMiData>>());
501+}
502+ 
503+TEST_F(AicpuProcessorUTest, ShouldSucceedWhenDpOnly)
504+{
505+ CreateDeviceDir(0);
506+ DpInsert dpData{
507+ {1000000.0, "enqueue", "src", 64},
508+ };
509+ WriteDpData(0, dpData);
510+ 
511+ DataInventory dataInventory;
512+ AicpuProcessor processor(PROF_PATH);
513+ EXPECT_TRUE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
514+ EXPECT_EQ(nullptr, dataInventory.GetPtr<std::vector<AicpuSummaryData>>());
515+ ASSERT_NE(nullptr, dataInventory.GetPtr<std::vector<AicpuDpData>>());
516+ EXPECT_EQ(1ul, dataInventory.GetPtr<std::vector<AicpuDpData>>()->size());
517+ EXPECT_EQ(nullptr, dataInventory.GetPtr<std::vector<AicpuMiData>>());
518+}
519+ 
520+TEST_F(AicpuProcessorUTest, ShouldSucceedWhenMiOnly)
521+{
522+ CreateDeviceDir(0);
523+ MiInsert miData{
524+ {"OnlyMi", 4, 11.0, 22.0, 11.0},
525+ };
526+ WriteMiData(0, miData);
527+ 
528+ DataInventory dataInventory;
529+ AicpuProcessor processor(PROF_PATH);
530+ EXPECT_TRUE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
531+ EXPECT_EQ(nullptr, dataInventory.GetPtr<std::vector<AicpuSummaryData>>());
532+ EXPECT_EQ(nullptr, dataInventory.GetPtr<std::vector<AicpuDpData>>());
533+ ASSERT_NE(nullptr, dataInventory.GetPtr<std::vector<AicpuMiData>>());
534+ EXPECT_EQ("OnlyMi", dataInventory.GetPtr<std::vector<AicpuMiData>>()->at(0).nodeName);
535+}
536+ 
537+TEST_F(AicpuProcessorUTest, ShouldSucceedWhenTablesMissing)
538+{
539+ CreateDeviceDir(0);
540+ InsertTable(File::PathJoin({SqlitePath(0), DB_NAME_AI_CPU}), "DummyTable",
541+ TableColumns{{"id", SQL_INTEGER_TYPE}});
542+ InsertTable(File::PathJoin({SqlitePath(0), DB_NAME_DATA_PREPROCESS}), "DummyTable",
543+ TableColumns{{"id", SQL_INTEGER_TYPE}});
544+ 
545+ DataInventory dataInventory;
546+ AicpuProcessor processor(PROF_PATH);
547+ EXPECT_TRUE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
548+ EXPECT_EQ(nullptr, dataInventory.GetPtr<std::vector<AicpuSummaryData>>());
549+ EXPECT_EQ(nullptr, dataInventory.GetPtr<std::vector<AicpuDpData>>());
550+ EXPECT_EQ(nullptr, dataInventory.GetPtr<std::vector<AicpuMiData>>());
551+}
552+ 
553+TEST_F(AicpuProcessorUTest, ShouldSucceedWhenNoDeviceDir)
554+{
555+ DataInventory dataInventory;
556+ AicpuProcessor processor(PROF_PATH);
557+ EXPECT_TRUE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
558+ EXPECT_EQ(nullptr, dataInventory.GetPtr<std::vector<AicpuSummaryData>>());
559+}
560+ 
561+TEST_F(AicpuProcessorUTest, ShouldReturnFalseWhenInvalidDeviceId)
562+{
563+ CreateDeviceDir(0);
564+ WriteAiCpuData(0, AiCpuInsert{{1, 1, 1000000.0, 1100000.0, "x", 1.0, 1.0, 0.0, 1.0, 3.0}});
565+ MOCKER_CPP(&Utils::GetDeviceIdByDevicePath).stubs().will(returnValue(static_cast<uint16_t>(INVALID_DEVICE_ID)));
566+ 
567+ DataInventory dataInventory;
568+ AicpuProcessor processor(PROF_PATH);
569+ EXPECT_FALSE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
570+}
571+ 
572+TEST_F(AicpuProcessorUTest, ShouldReturnFalseWhenGetProfTimeRecordInfoFailed)
573+{
574+ CreateDeviceDir(0);
575+ MOCKER_CPP(&Environment::Context::GetProfTimeRecordInfo).stubs().will(returnValue(false));
576+ 
577+ DataInventory dataInventory;
578+ AicpuProcessor processor(PROF_PATH);
579+ EXPECT_FALSE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
580+}
581+ 
582+TEST_F(AicpuProcessorUTest, ShouldReturnFalseWhenConstructDBRunnerFailed)
583+{
584+ CreateDeviceDir(0);
585+ WriteAiCpuData(0, AiCpuInsert{{1, 1, 1000000.0, 1100000.0, "x", 1.0, 1.0, 0.0, 1.0, 3.0}});
586+ MOCKER_CPP(&DBInfo::ConstructDBRunner).stubs().will(returnValue(false));
587+ 
588+ DataInventory dataInventory;
589+ AicpuProcessor processor(PROF_PATH);
590+ EXPECT_FALSE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
591+}
592+ 
593+TEST_F(AicpuProcessorUTest, ShouldReturnFalseWhenFileCheckFailed)
594+{
595+ CreateDeviceDir(0);
596+ WriteAiCpuData(0, AiCpuInsert{{1, 1, 1000000.0, 1100000.0, "x", 1.0, 1.0, 0.0, 1.0, 3.0}});
597+ MOCKER_CPP(&FileReader::Check).stubs().will(returnValue(false));
598+ 
599+ DataInventory dataInventory;
600+ AicpuProcessor processor(PROF_PATH);
601+ EXPECT_FALSE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
602+}
603+ 
604+TEST_F(AicpuProcessorUTest, ShouldReturnFalseWhenSaveToDataInventoryFailed)
605+{
606+ CreateDeviceDir(0);
607+ WriteAiCpuData(0, AiCpuInsert{{1, 1, 1000000.0, 1100000.0, "x", 1.0, 1.0, 0.0, 1.0, 3.0}});
608+ MOCKER_CPP(&DataProcessor::SaveToDataInventory<AicpuSummaryData>).stubs().will(returnValue(false));
609+ 
610+ DataInventory dataInventory;
611+ AicpuProcessor processor(PROF_PATH);
612+ EXPECT_FALSE(processor.Run(dataInventory, PROCESSOR_NAME_AICPU));
613+}
614+ 
615+TEST_F(AicpuProcessorUTest, ShouldRegisterAicpuProcessor)
616+{
617+ const auto *definition = TopoNodeRegistry::FindProcessorByName(PROCESSOR_NAME_AICPU);
618+ ASSERT_NE(definition, nullptr);
619+ EXPECT_EQ(definition->runtimeType, std::type_index(typeid(AicpuProcessor)));
620+ EXPECT_TRUE(static_cast<bool>(definition->creatorFactory));
621+}