* Copyright (c) 2025 Huawei Technologies Co., Ltd.
* This program is free software, you can redistribute it and/or modify it under the terms and conditions of
* CANN Open Software License Agreement Version 2.0 (the "License").
* Please refer to the License for details. You may not use this file except in compliance with the License.
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED,
* INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE.
* See LICENSE in the root of the software repository for the full text of the License.
*/
* \file test_node_scope_tagging.cpp
* \brief Unit tests for verifying scope tagging functionality on nodes
*/
#include <gtest/gtest.h>
#include <memory>
#include <bitset>
#define private public
#define protected public
#include "super_kernel.h"
#include "sk_node.h"
#include "sk_types.h"
#include "sk_graph.h"
#include "sk_log.h"
#include "sk_options_manager.h"
#include "sk_lock_detector.h"
class TestLockDetector: public ::testing::Test {
protected:
void SetUp() override {
opts = std::make_unique<SuperKernelOptionsManager>();
graph = std::make_unique<SuperKernelGraph>();
lockDetector = std::make_unique<LockDetector>(*graph, *opts);
lockDetector->Reset();
LockDetector::GetDeviceCores();
}
void ConfigureValueBreakerBypass(uint32_t value)
{
aclskOption option {};
option.optionType = aclskOptionType::AGGRESSIVE_OPT_STRATEGIES;
option.aggressiveOpts.valueBreakerBypass = value;
opts->SetOptOptionValue(&option);
}
void TearDown() override {
lockDetector->Reset();
graph.reset();
}
SuperKernelBaseNode* CreateWaitNode(uint64_t nodeId, uint32_t streamIdx, uint64_t preNodeId = INVALID_TASK_ID, uint64_t nextNodeId = INVALID_TASK_ID, uint64_t notifyNodeId = INVALID_TASK_ID) {
auto node = std::make_unique<SuperKernelMemoryNode>(
nullptr, ACL_MODEL_RI_TASK_VALUE_WAIT, 0, streamIdx, INVALID_STREAM_ID, INVALID_TASK_ID);
node->SetNodeId(nodeId);
node->SetNextNodeId(nextNodeId);
node->SetPreNodeId(preNodeId);
node->nodeInfos.syncInfos.correspondingNotifyNodeId = notifyNodeId;
node->isFusible = true;
node->nodeType = SkNodeType::NODE_WAIT;
SuperKernelBaseNode* ptr = node.get();
graph->graphMap[nodeId] = std::move(node);
return ptr;
}
SuperKernelBaseNode* CreateNotifyNode(uint64_t nodeId, uint32_t streamIdx, uint64_t preNodeId = INVALID_TASK_ID, uint64_t nextNodeId = INVALID_TASK_ID, uint64_t eventId = INVALID_TASK_ID, std::vector<uint64_t> waitNodeIds = {}) {
auto node = std::make_unique<SuperKernelMemoryNode>(
nullptr, ACL_MODEL_RI_TASK_VALUE_WRITE, 0, streamIdx, INVALID_STREAM_ID, INVALID_TASK_ID);
node->SetNodeId(nodeId);
node->SetNextNodeId(nextNodeId);
node->SetPreNodeId(preNodeId);
node->nodeInfos.syncInfos.eventId = eventId;
node->isFusible = true;
node->nodeType = SkNodeType::NODE_NOTIFY;
node->nodeInfos.syncInfos.correspondingWaitNodeIds = waitNodeIds;
SuperKernelBaseNode* ptr = node.get();
graph->graphMap[nodeId] = std::move(node);
return ptr;
}
SuperKernelBaseNode* CreateResetNode(uint64_t nodeId, uint32_t streamIdx, uint64_t preNodeId = INVALID_TASK_ID,
uint64_t nextNodeId = INVALID_TASK_ID, uint64_t eventId = INVALID_TASK_ID)
{
auto node = std::make_unique<SuperKernelMemoryNode>(
nullptr, ACL_MODEL_RI_TASK_VALUE_WRITE, 0, streamIdx, INVALID_STREAM_ID, INVALID_TASK_ID);
node->SetNodeId(nodeId);
node->SetNextNodeId(nextNodeId);
node->SetPreNodeId(preNodeId);
node->nodeInfos.syncInfos.eventId = eventId;
node->nodeInfos.syncInfos.memoryValue = SK_DEFAULT_RESET_VALUE;
node->isFusible = true;
node->nodeType = SkNodeType::NODE_RESET;
SuperKernelBaseNode* ptr = node.get();
graph->graphMap[nodeId] = std::move(node);
return ptr;
}
SuperKernelBaseNode* CreateKernelNodeWithCores(uint64_t nodeId, uint32_t streamIdx, uint64_t preNodeId, uint64_t nextNodeId,
uint32_t numBlocks, SkKernelType kernelType) {
auto node = std::make_unique<SuperKernelKernelNode>(
nullptr, ACL_MODEL_RI_TASK_KERNEL, 0, streamIdx, INVALID_STREAM_ID, INVALID_TASK_ID);
node->SetNodeId(nodeId);
node->SetNextNodeId(nextNodeId);
node->SetPreNodeId(preNodeId);
node->nodeType = SkNodeType::NODE_KERNEL;
node->isFusible = true;
node->nodeInfos.kernelInfos.numBlocks = numBlocks;
node->nodeInfos.kernelInfos.kernelType = kernelType;
if (kernelType == SkKernelType::AIC_ONLY || kernelType == SkKernelType::MIX_AIC_1_0) {
node->nodeInfos.kernelInfos.cubeNum = numBlocks;
node->nodeInfos.kernelInfos.vecNum = 0;
} else if (kernelType == SkKernelType::AIV_ONLY || kernelType == SkKernelType::MIX_AIV_1_0) {
node->nodeInfos.kernelInfos.cubeNum = 0;
node->nodeInfos.kernelInfos.vecNum = numBlocks;
} else if (kernelType == SkKernelType::MIX_AIC_1_1) {
node->nodeInfos.kernelInfos.cubeNum = numBlocks;
node->nodeInfos.kernelInfos.vecNum = numBlocks;
} else if (kernelType == SkKernelType::MIX_AIC_1_2) {
node->nodeInfos.kernelInfos.cubeNum = numBlocks;
node->nodeInfos.kernelInfos.vecNum = numBlocks << 1;
}
SuperKernelBaseNode* ptr = node.get();
graph->graphMap[nodeId] = std::move(node);
return ptr;
}
void SetupStreams(const std::vector<std::vector<uint64_t>>& streamNodes) {
graph->streams.clear();
graph->headNodes.clear();
for (const auto& nodes : streamNodes) {
graph->streams.emplace_back();
if (!nodes.empty()) {
graph->headNodes.push_back(nodes[0]);
} else {
graph->headNodes.push_back(INVALID_TASK_ID);
}
}
}
void SetupEvent(uint64_t eventId, uint64_t notifyNodeId, const std::vector<uint64_t>& waitNodeIds) {
EventInfos eventInfo;
eventInfo.notifyNodeId = notifyNodeId;
for (auto waitNodeId : waitNodeIds) {
eventInfo.waitNodeIdList.insert(waitNodeId);
}
graph->eventToNodes[eventId] = eventInfo;
std::cout << "[SetupEvent] event=1 , notifyNodeId=" << graph->eventToNodes[1].notifyNodeId << ", waitNodeIds= [";
for (size_t i = 0; i < graph->eventToNodes[1].waitNodeIdList.size(); ++i) {
if (i > 0) std::cout << ",";
std::cout << waitNodeIds[i];
}
std::cout << "]" << std::endl;
std::cout.flush();
}
std::unique_ptr<SuperKernelGraph> graph;
std::unique_ptr<SuperKernelOptionsManager> opts;
std::unique_ptr<LockDetector> lockDetector;
};
TEST_F(TestLockDetector, SingleStreamKernelFirst) {
stream0: k0(8c) -> n1(eventid=1) -> k2(4c) -> n3(notify) -> k4(8v) -> k5(6c,6v) -> w6(eventid=1) -> k7(4c,8v) -> k8(8c,8v)
↑ ↓
└───────────────────────────────────────────────────────────────────┘
*/
auto* k0 = CreateKernelNodeWithCores(0, 0, INVALID_TASK_ID, 1, 8, SkKernelType::AIC_ONLY);
auto* n1 = CreateNotifyNode(1, 0, 0, 2, 1);
auto* k2 = CreateKernelNodeWithCores(2, 0, 1, 3, 4, SkKernelType::AIC_ONLY);
auto* n3 = CreateNotifyNode(3, 0, 2, 4, 2);
auto* k4 = CreateKernelNodeWithCores(4, 0, 3, 5, 8, SkKernelType::AIV_ONLY);
auto* k5 = CreateKernelNodeWithCores(5, 0, 4, 6, 6, SkKernelType::MIX_AIC_1_1);
auto* w6 = CreateWaitNode(6, 0, 5, 7, 1);
auto* k7 = CreateKernelNodeWithCores(7, 0, 6, 8, 4, SkKernelType::MIX_AIC_1_2);
auto* k8 = CreateKernelNodeWithCores(8, 0, 7, INVALID_TASK_ID, 8, SkKernelType::MIX_AIC_1_1);
SetupStreams({{0, 1, 2, 3, 4, 5, 6, 7, 8}});
SetupEvent(1, 1, {6});
EXPECT_TRUE(lockDetector->IsFusible(*k2));
EXPECT_TRUE(k2->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 4);
EXPECT_EQ(lockDetector->superKernelVecNum, 0);
EXPECT_TRUE(lockDetector->IsFusible(*n3));
EXPECT_TRUE(n3->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 4);
EXPECT_EQ(lockDetector->superKernelVecNum, 0);
EXPECT_TRUE(lockDetector->IsFusible(*k4));
EXPECT_TRUE(k4->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 4);
EXPECT_EQ(lockDetector->superKernelVecNum, 8);
EXPECT_TRUE(lockDetector->IsFusible(*k5));
EXPECT_TRUE(k5->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 6);
EXPECT_EQ(lockDetector->superKernelVecNum, 8);
EXPECT_TRUE(lockDetector->IsFusible(*w6));
EXPECT_TRUE(k5->isVisited);
EXPECT_TRUE(lockDetector->IsFusible(*k7));
EXPECT_TRUE(k5->isVisited);
EXPECT_TRUE(lockDetector->IsFusible(*k8));
EXPECT_EQ(lockDetector->skStreamIds, std::unordered_set<uint32_t>{0});
lockDetector->Reset();
EXPECT_FALSE(k2->isVisited);
EXPECT_FALSE(n3->isVisited);
EXPECT_FALSE(k4->isVisited);
EXPECT_FALSE(k5->isVisited);
EXPECT_FALSE(w6->isVisited);
EXPECT_FALSE(k7->isVisited);
EXPECT_FALSE(k8->isVisited);
}
TEST_F(TestLockDetector, SingleStreamWaitFirst) {
stream0: k0(8c) -> notify(eventid=1) -> k2(4c) -> n3(notify) -> k4(8v) -> k5(6c,6v) -> w6(eventid=1) -> k7(4c,8v) -> k8(8c,8v)
↑
┌───────────────────────────────────────────────────────────────┘
↑
stream1: k9(4c,8v) -> notify(eventid=1) -> k11(4c,4v)
*/
auto* k0 = CreateKernelNodeWithCores(0, 0, INVALID_TASK_ID, 1, 8, SkKernelType::AIC_ONLY);
auto* n1 = CreateNotifyNode(1, 0, 0, 2, 10, {});
auto* k2 = CreateKernelNodeWithCores(2, 0, 1, 3, 4, SkKernelType::AIC_ONLY);
auto* n3 = CreateNotifyNode(3, 0, 2, 4, 11, {});
auto* k4 = CreateKernelNodeWithCores(4, 0, 3, 5, 8, SkKernelType::AIV_ONLY);
auto* k5 = CreateKernelNodeWithCores(5, 0, 4, 6, 6, SkKernelType::MIX_AIC_1_1);
auto* w6 = CreateWaitNode(6, 0, 5, 7, 10);
auto* k7 = CreateKernelNodeWithCores(7, 0, 6, 8, 4, SkKernelType::MIX_AIC_1_2);
auto* k8 = CreateKernelNodeWithCores(8, 0, 7, INVALID_TASK_ID, 8, SkKernelType::MIX_AIC_1_1);
auto* k9 = CreateKernelNodeWithCores(9, 1, INVALID_TASK_ID, 10, 4, SkKernelType::MIX_AIC_1_2);
auto* n10 = CreateNotifyNode(10, 1, 9, 11, 1, {6});
auto* k11 = CreateKernelNodeWithCores(11, 1, 10, INVALID_TASK_ID, 4, SkKernelType::MIX_AIC_1_1);
SetupStreams({{0, 1, 2, 3, 4, 5, 6, 7, 8}, {9, 10, 11}});
SetupEvent(1, 10, {6});
EXPECT_TRUE(lockDetector->IsFusible(*k2));
EXPECT_TRUE(k2->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 4);
EXPECT_EQ(lockDetector->superKernelVecNum, 0);
EXPECT_TRUE(lockDetector->IsFusible(*n3));
EXPECT_TRUE(n3->isVisited);
EXPECT_TRUE(lockDetector->IsFusible(*k4));
EXPECT_TRUE(k4->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 4);
EXPECT_EQ(lockDetector->superKernelVecNum, 8);
EXPECT_TRUE(lockDetector->IsFusible(*k5));
EXPECT_TRUE(k5->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 6);
EXPECT_EQ(lockDetector->superKernelVecNum, 8);
EXPECT_TRUE(lockDetector->IsFusible(*w6));
EXPECT_TRUE(w6->isVisited);
EXPECT_TRUE(k9->isVisited);
EXPECT_TRUE(n10->isVisited);
EXPECT_TRUE(lockDetector->IsFusible(*k7));
EXPECT_TRUE(k7->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 6);
EXPECT_EQ(lockDetector->superKernelVecNum, 8);
EXPECT_TRUE(lockDetector->IsFusible(*k8));
EXPECT_EQ(lockDetector->superKernelCubeNum, 8);
EXPECT_EQ(lockDetector->superKernelVecNum, 8);
EXPECT_EQ(lockDetector->skStreamIds, std::unordered_set<uint32_t>{0});
lockDetector->Reset();
EXPECT_FALSE(k0->isVisited);
EXPECT_FALSE(n1->isVisited);
EXPECT_FALSE(k2->isVisited);
EXPECT_FALSE(n3->isVisited);
EXPECT_FALSE(k4->isVisited);
EXPECT_FALSE(k5->isVisited);
EXPECT_FALSE(w6->isVisited);
EXPECT_FALSE(k7->isVisited);
EXPECT_FALSE(k8->isVisited);
EXPECT_FALSE(k9->isVisited);
EXPECT_FALSE(n10->isVisited);
EXPECT_FALSE(k11->isVisited);
}
TEST_F(TestLockDetector, SingleStreamWaitFirstRejects) {
stream0: k0(8c) -> notify(eventid=1) -> k2(4c) -> n3(notify) -> k4(8v) -> k5(6c,6v) -> w6(eventid=1) -> k7(4c,8v) -> k8(8c,8v)
↑
┌───────────────────────────────────────────────────────────────┘
↑
stream1: k9(24c,24v) -> notify(eventid=1) -> k11(4c,4v)
*/
auto* k0 = CreateKernelNodeWithCores(0, 0, INVALID_TASK_ID, 1, 8, SkKernelType::AIC_ONLY);
auto* n1 = CreateNotifyNode(1, 0, 0, 2, 10, {});
auto* k2 = CreateKernelNodeWithCores(2, 0, 1, 3, 4, SkKernelType::AIC_ONLY);
auto* n3 = CreateNotifyNode(3, 0, 2, 4, 11, {});
auto* k4 = CreateKernelNodeWithCores(4, 0, 3, 5, 8, SkKernelType::AIV_ONLY);
auto* k5 = CreateKernelNodeWithCores(5, 0, 4, 6, 6, SkKernelType::MIX_AIC_1_1);
auto* w6 = CreateWaitNode(6, 0, 5, 7, 10);
auto* k7 = CreateKernelNodeWithCores(7, 0, 6, 8, 4, SkKernelType::MIX_AIC_1_2);
auto* k8 = CreateKernelNodeWithCores(8, 0, 7, INVALID_TASK_ID, 8, SkKernelType::MIX_AIC_1_1);
auto* k9 = CreateKernelNodeWithCores(9, 1, INVALID_TASK_ID, 10, 24, SkKernelType::MIX_AIC_1_2);
auto* n10 = CreateNotifyNode(10, 1, 9, 11, 1, {6});
auto* k11 = CreateKernelNodeWithCores(11, 1, 10, INVALID_TASK_ID, 4, SkKernelType::MIX_AIC_1_1);
SetupStreams({{0, 1, 2, 3, 4, 5, 6, 7, 8}, {9, 10, 11}});
SetupEvent(1, 10, {6});
EXPECT_TRUE(lockDetector->IsFusible(*k2));
EXPECT_TRUE(k2->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 4);
EXPECT_EQ(lockDetector->superKernelVecNum, 0);
EXPECT_TRUE(lockDetector->IsFusible(*n3));
EXPECT_TRUE(n3->isVisited);
EXPECT_TRUE(lockDetector->IsFusible(*k4));
EXPECT_TRUE(k4->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 4);
EXPECT_EQ(lockDetector->superKernelVecNum, 8);
EXPECT_TRUE(lockDetector->IsFusible(*k5));
EXPECT_TRUE(k5->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 6);
EXPECT_EQ(lockDetector->superKernelVecNum, 8);
EXPECT_FALSE(lockDetector->IsFusible(*w6));
EXPECT_FALSE(n10->isVisited);
EXPECT_EQ(lockDetector->skStreamIds, std::unordered_set<uint32_t>{0});
lockDetector->Reset();
EXPECT_FALSE(k0->isVisited);
EXPECT_FALSE(n1->isVisited);
EXPECT_FALSE(k2->isVisited);
EXPECT_FALSE(n3->isVisited);
EXPECT_FALSE(k4->isVisited);
EXPECT_FALSE(k5->isVisited);
EXPECT_FALSE(w6->isVisited);
EXPECT_FALSE(k7->isVisited);
EXPECT_FALSE(k8->isVisited);
EXPECT_FALSE(k9->isVisited);
EXPECT_FALSE(n10->isVisited);
EXPECT_FALSE(k11->isVisited);
}
TEST_F(TestLockDetector, SingleStreamMultiWait) {
stream0: k0(8c) -> notify(eventid=3) -> k2(4c) -> w3(eid=1) -> k4(8v) -> k5(6c,6v) -> w6(eid=2) -> k7(4c,8v) -> k8(8c,8v)
↑ ↑
┌─────────────────────────┘ ┌───────────────────────────────┘
↑ ↑
stream1: k9(4c,8v) -> notify(eventid=1) -> k11(4c,4v) -> notify(eid=2)
*/
auto* k0 = CreateKernelNodeWithCores(0, 0, INVALID_TASK_ID, 1, 8, SkKernelType::AIC_ONLY);
auto* n1 = CreateNotifyNode(1, 0, 0, 2, 10, {});
auto* k2 = CreateKernelNodeWithCores(2, 0, 1, 3, 10, SkKernelType::MIX_AIC_1_2);
auto* w3 = CreateWaitNode(3, 0, 2, 4, 10);
auto* k4 = CreateKernelNodeWithCores(4, 0, 3, 5, 8, SkKernelType::AIV_ONLY);
auto* k5 = CreateKernelNodeWithCores(5, 0, 4, 6, 6, SkKernelType::MIX_AIC_1_1);
auto* w6 = CreateWaitNode(6, 0, 5, 7, 12);
auto* k7 = CreateKernelNodeWithCores(7, 0, 6, 8, 4, SkKernelType::MIX_AIC_1_2);
auto* k8 = CreateKernelNodeWithCores(8, 0, 7, INVALID_TASK_ID, 12, SkKernelType::MIX_AIC_1_1);
auto* k9 = CreateKernelNodeWithCores(9, 1, INVALID_TASK_ID, 10, 4, SkKernelType::MIX_AIC_1_2);
auto* n10 = CreateNotifyNode(10, 1, 9, 11, 1, {3});
auto* k11 = CreateKernelNodeWithCores(11, 1, 10, INVALID_TASK_ID, 4, SkKernelType::MIX_AIC_1_1);
auto* n12 = CreateNotifyNode(12, 1, 11, INVALID_TASK_ID, 2, {6});
SetupStreams({{0, 1, 2, 3, 4, 5, 6, 7, 8}, {9, 10, 11}});
SetupEvent(1, 10, {6});
EXPECT_TRUE(lockDetector->IsFusible(*k2));
EXPECT_TRUE(k2->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 10);
EXPECT_EQ(lockDetector->superKernelVecNum, 20);
EXPECT_TRUE(lockDetector->IsFusible(*w3));
EXPECT_TRUE(w3->isVisited);
EXPECT_TRUE(k9->isVisited);
EXPECT_TRUE(lockDetector->IsFusible(*k4));
EXPECT_TRUE(k4->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 10);
EXPECT_EQ(lockDetector->superKernelVecNum, 20);
EXPECT_TRUE(lockDetector->IsFusible(*k5));
EXPECT_TRUE(k5->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 10);
EXPECT_EQ(lockDetector->superKernelVecNum, 20);
EXPECT_TRUE(lockDetector->IsFusible(*w6));
EXPECT_TRUE(w6->isVisited);
EXPECT_TRUE(k9->isVisited);
EXPECT_TRUE(n10->isVisited);
EXPECT_TRUE(lockDetector->IsFusible(*k7));
EXPECT_TRUE(k7->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 10);
EXPECT_EQ(lockDetector->superKernelVecNum, 20);
EXPECT_TRUE(lockDetector->IsFusible(*k8));
EXPECT_EQ(lockDetector->superKernelCubeNum, 12);
EXPECT_EQ(lockDetector->superKernelVecNum, 20);
EXPECT_EQ(lockDetector->skStreamIds, std::unordered_set<uint32_t>{0});
lockDetector->Reset();
EXPECT_FALSE(k0->isVisited);
EXPECT_FALSE(n1->isVisited);
EXPECT_FALSE(k2->isVisited);
EXPECT_FALSE(w3->isVisited);
EXPECT_FALSE(k4->isVisited);
EXPECT_FALSE(k5->isVisited);
EXPECT_FALSE(w6->isVisited);
EXPECT_FALSE(k7->isVisited);
EXPECT_FALSE(k8->isVisited);
EXPECT_FALSE(k9->isVisited);
EXPECT_FALSE(n10->isVisited);
EXPECT_FALSE(k11->isVisited);
EXPECT_FALSE(n12->isVisited);
}
TEST_F(TestLockDetector, SingleStreamExceedDeviceCores) {
auto* k0 = CreateKernelNodeWithCores(0, 0, INVALID_TASK_ID, 1, 40, SkKernelType::AIC_ONLY);
SetupStreams({{0}});
EXPECT_FALSE(lockDetector->IsFusible(*k0));
lockDetector->Reset();
}
TEST_F(TestLockDetector, SingleStreamNotifyOutsideSK) {
auto* k0 = CreateKernelNodeWithCores(0, 0, INVALID_TASK_ID, 1, 20, SkKernelType::AIC_ONLY);
auto* w1 = CreateWaitNode(1, 0, 0, INVALID_TASK_ID, 10);
SetupStreams({{0}});
SetupEvent(1, 10, {1});
EXPECT_TRUE(lockDetector->IsFusible(*k0));
EXPECT_FALSE(lockDetector->IsFusible(*w1));
lockDetector->Reset();
}
TEST_F(TestLockDetector, SingleStreamNotifyHasCore) {
stream0: k0(8c) -> notify(eventid=1) -> k2(4c) -> n3(notify) -> W4
stream1: n5
*/
auto* k0 = CreateKernelNodeWithCores(0, 0, INVALID_TASK_ID, 1, 8, SkKernelType::AIC_ONLY);
auto* n1 = CreateNotifyNode(1, 0, 0, 2, 10, {});
auto* k2 = CreateKernelNodeWithCores(2, 0, 1, 3, 4, SkKernelType::AIC_ONLY);
auto* n3 = CreateNotifyNode(3, 0, 2, 4, 11, {});
auto* w4 = CreateWaitNode(4, 0, 3, INVALID_TASK_ID, 5);
auto* n5 = CreateNotifyNode(5, 1, INVALID_TASK_ID, INVALID_TASK_ID, 1, {4});
SetupStreams({{0, 1, 2, 3, 4}, {5}});
SetupEvent(1, 5, {4});
n5->SetNotifyExpandVecNum(40);
EXPECT_TRUE(lockDetector->IsFusible(*k2));
EXPECT_TRUE(lockDetector->IsFusible(*n3));
EXPECT_FALSE(lockDetector->IsFusible(*w4));
lockDetector->Reset();
}
TEST_F(TestLockDetector, ResetNodeNoCoreResourceCanFuse)
{
auto* reset = CreateResetNode(1, 0, INVALID_TASK_ID, INVALID_TASK_ID, 1);
SetupStreams({{1}});
EXPECT_TRUE(lockDetector->IsFusible(*reset));
EXPECT_TRUE(reset->isVisited);
EXPECT_EQ(lockDetector->GetDeadlockReason(), DeadlockFailReason::NOT_FIND_DEADLOCK);
EXPECT_EQ(lockDetector->superKernelCubeNum, 0);
EXPECT_EQ(lockDetector->superKernelVecNum, 0);
EXPECT_EQ(lockDetector->skStreamIds, std::unordered_set<uint32_t>{0});
}
TEST_F(TestLockDetector, ValueBackedPairedWaitKeepsDeadlockDetectionWithValueBreakerPairedFlag)
{
auto* notify = CreateNotifyNode(10, 1, INVALID_TASK_ID, INVALID_TASK_ID, 1, {1});
auto* wait = CreateWaitNode(1, 0, INVALID_TASK_ID, INVALID_TASK_ID, 10);
wait->nodeInfos.syncInfos.addrValue = reinterpret_cast<void*>(0x1234);
ConfigureValueBreakerBypass(ACLSK_VALUE_BREAKER_BYPASS_PAIRED_WAIT);
EXPECT_FALSE(lockDetector->IsFusible(*wait));
EXPECT_FALSE(wait->isVisited);
EXPECT_EQ(lockDetector->GetDeadlockReason(), DeadlockFailReason::FIRST_WAIT);
(void)notify;
}
TEST_F(TestLockDetector, ValueBackedUnpairedWaitBypassesDeadlockDetectionWithValueBreakerBypass)
{
auto* wait = CreateWaitNode(1, 0, INVALID_TASK_ID, INVALID_TASK_ID, INVALID_TASK_ID);
wait->nodeInfos.syncInfos.addrValue = reinterpret_cast<void*>(0x1234);
ConfigureValueBreakerBypass(ACLSK_VALUE_BREAKER_BYPASS_UNPAIRED_WAIT);
EXPECT_TRUE(lockDetector->IsFusible(*wait));
EXPECT_TRUE(wait->isVisited);
EXPECT_EQ(lockDetector->GetDeadlockReason(), DeadlockFailReason::NOT_FIND_DEADLOCK);
}
TEST_F(TestLockDetector, notifyInOtherSKWithSameStream)
{
stream0: k0(8c) -> notify(eventid=3) -> k2(4c) -> w3(eid=1) -> k4(8v) -> k5(6c,6v) -> w6(eid=2) -> k7(4c,8v) -> k8(8c,8v)
↑ ↑
┌─────────────────────────┘ ┌───────────────────────────────┘
↑ ↑
stream1: k9(4c,8v) -> notify(eventid=1) -> k11(4c,4v) -> notify(eid=2)
*/
auto* k0 = CreateKernelNodeWithCores(0, 0, INVALID_TASK_ID, 1, 8, SkKernelType::AIC_ONLY);
auto* n1 = CreateNotifyNode(1, 0, 0, 2, 10, {});
auto* k2 = CreateKernelNodeWithCores(2, 0, 1, 3, 10, SkKernelType::MIX_AIC_1_2);
auto* w3 = CreateWaitNode(3, 0, 2, 4, 10);
auto* k4 = CreateKernelNodeWithCores(4, 0, 3, 5, 8, SkKernelType::AIV_ONLY);
auto* k5 = CreateKernelNodeWithCores(5, 0, 4, 6, 6, SkKernelType::MIX_AIC_1_1);
auto* w6 = CreateWaitNode(6, 0, 5, 7, 12);
auto* k7 = CreateKernelNodeWithCores(7, 0, 6, 8, 4, SkKernelType::MIX_AIC_1_2);
auto* k8 = CreateKernelNodeWithCores(8, 0, 7, INVALID_TASK_ID, 12, SkKernelType::MIX_AIC_1_1);
auto* k9 = CreateKernelNodeWithCores(9, 1, INVALID_TASK_ID, 10, 20, SkKernelType::MIX_AIC_1_2);
auto* n10 = CreateNotifyNode(10, 1, 9, 11, 1, {3});
n10->SetScopeStreamIds({0, 1});
k9->SetScopeStreamIds({0, 1});
n10->SetNotifyExpandVecNum(20);
n10->SetNotifyExpandCubeNum(40);
auto* k11 = CreateKernelNodeWithCores(11, 1, 10, INVALID_TASK_ID, 4, SkKernelType::MIX_AIC_1_1);
auto* n12 = CreateNotifyNode(12, 1, 11, INVALID_TASK_ID, 2, {6});
SetupStreams({{0, 1, 2, 3, 4, 5, 6, 7, 8}, {9, 10, 11}});
SetupEvent(1, 10, {6});
EXPECT_TRUE(lockDetector->IsFusible(*k2));
EXPECT_TRUE(k2->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 10);
EXPECT_EQ(lockDetector->superKernelVecNum, 20);
EXPECT_TRUE(lockDetector->IsFusible(*w3));
EXPECT_TRUE(w3->isVisited);
EXPECT_FALSE(k9->isVisited);
EXPECT_TRUE(lockDetector->IsFusible(*k4));
EXPECT_TRUE(k4->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 10);
EXPECT_EQ(lockDetector->superKernelVecNum, 20);
}
TEST_F(TestLockDetector, notifyInOtherSKWithoutSameStream)
{
stream0: k0(8c) -> notify(eventid=3) -> k2(4c) -> w3(eid=1) -> k4(8v) -> k5(6c,6v) -> w6(eid=2) -> k7(4c,8v) -> k8(8c,8v)
↑ ↑
┌─────────────────────────┘ ┌───────────────────────────────┘
↑ ↑
stream1: k9(4c,8v) -> notify(eventid=1) -> k11(4c,4v) -> notify(eid=2)
*/
auto* k0 = CreateKernelNodeWithCores(0, 0, INVALID_TASK_ID, 1, 8, SkKernelType::AIC_ONLY);
auto* n1 = CreateNotifyNode(1, 0, 0, 2, 10, {});
auto* k2 = CreateKernelNodeWithCores(2, 0, 1, 3, 10, SkKernelType::MIX_AIC_1_2);
auto* w3 = CreateWaitNode(3, 0, 2, 4, 10);
auto* k4 = CreateKernelNodeWithCores(4, 0, 3, 5, 8, SkKernelType::AIV_ONLY);
auto* k5 = CreateKernelNodeWithCores(5, 0, 4, 6, 6, SkKernelType::MIX_AIC_1_1);
auto* w6 = CreateWaitNode(6, 0, 5, 7, 12);
auto* k7 = CreateKernelNodeWithCores(7, 0, 6, 8, 4, SkKernelType::MIX_AIC_1_2);
auto* k8 = CreateKernelNodeWithCores(8, 0, 7, INVALID_TASK_ID, 12, SkKernelType::MIX_AIC_1_1);
auto* k9 = CreateKernelNodeWithCores(9, 1, INVALID_TASK_ID, 10, 20, SkKernelType::MIX_AIC_1_2);
auto* n10 = CreateNotifyNode(10, 1, 9, 11, 1, {3});
n10->SetScopeStreamIds({1});
k9->SetScopeStreamIds({1});
n10->SetNotifyExpandVecNum(20);
n10->SetNotifyExpandCubeNum(40);
auto* k11 = CreateKernelNodeWithCores(11, 1, 10, INVALID_TASK_ID, 4, SkKernelType::MIX_AIC_1_1);
auto* n12 = CreateNotifyNode(12, 1, 11, INVALID_TASK_ID, 2, {6});
SetupStreams({{0, 1, 2, 3, 4, 5, 6, 7, 8}, {9, 10, 11}});
SetupEvent(1, 10, {6});
EXPECT_TRUE(lockDetector->IsFusible(*k2));
EXPECT_TRUE(k2->isVisited);
EXPECT_EQ(lockDetector->superKernelCubeNum, 10);
EXPECT_EQ(lockDetector->superKernelVecNum, 20);
EXPECT_FALSE(lockDetector->IsFusible(*w3));
EXPECT_FALSE(w3->isVisited);
EXPECT_FALSE(k9->isVisited);
}