已合并
feat(aclrtc): 支持 ACLNN Kernel 常量化编译 #5103
XuebinYang创建于 26 天前
feat(aclrtc): 支持 ACLNN Kernel 常量化编译 #5103
已合并
共 24 个文件变更+5407-39
| @@ -16,10 +16,10 @@ set(ACLRTC_TEST_HEADERS_FILES | |||
| 16 | 16 | ||
| 17 | file(GLOB ACLRTC_SRC_FILES | 17 | file(GLOB ACLRTC_SRC_FILES |
| 18 | ${ASCENDC_DIR}/tools/aclrtc/*.cpp | 18 | ${ASCENDC_DIR}/tools/aclrtc/*.cpp |
| 19 | + ${ASCENDC_DIR}/tools/aclrtc/specialization/*.cpp | ||
| 19 | ) | 20 | ) |
| 20 | 21 | ||
| 21 | -set(ACLRTC_RESOURCE_REGISTRY_SRC_FILES | 22 | +set(ACLRTC_RESOURCE_REGISTRY_DEPENDENCY_SRC_FILES |
| 22 | - ${ASCENDC_DIR}/tools/aclrtc/specialization/resource_registry.cpp | ||
| 23 | ${ASCENDC_DIR}/tools/build/common/file_utils.cpp | 23 | ${ASCENDC_DIR}/tools/build/common/file_utils.cpp |
| 24 | ) | 24 | ) |
| 25 | 25 | ||
| @@ -31,7 +31,7 @@ file(GLOB TEST_ACLRTC_SRC_FILES | |||
| 31 | 31 | ||
| 32 | add_executable(ascendc_ut_aclrtc | 32 | add_executable(ascendc_ut_aclrtc |
| 33 | ${ACLRTC_SRC_FILES} | 33 | ${ACLRTC_SRC_FILES} |
| 34 | - ${ACLRTC_RESOURCE_REGISTRY_SRC_FILES} | 34 | + ${ACLRTC_RESOURCE_REGISTRY_DEPENDENCY_SRC_FILES} |
| 35 | ${TEST_ACLRTC_SRC_FILES} | 35 | ${TEST_ACLRTC_SRC_FILES} |
| 36 | ) | 36 | ) |
| 37 | 37 | ||
| @@ -42,6 +42,7 @@ target_compile_definitions(ascendc_ut_aclrtc PRIVATE | |||
| 42 | 42 | ||
| 43 | target_include_directories(ascendc_ut_aclrtc PRIVATE | 43 | target_include_directories(ascendc_ut_aclrtc PRIVATE |
| 44 | ${ACLRTC_TEST_HEADERS_FILES} | 44 | ${ACLRTC_TEST_HEADERS_FILES} |
| 45 | + ${ASCENDC_DIR}/tools/aclrtc | ||
| 45 | ${ASCENDC_DIR}/tools/aclrtc/specialization | 46 | ${ASCENDC_DIR}/tools/aclrtc/specialization |
| 46 | ${ASCENDC_DIR}/tools/build/common | 47 | ${ASCENDC_DIR}/tools/build/common |
| 47 | ${ASCEND_CANN_PACKAGE_PATH}/include | 48 | ${ASCEND_CANN_PACKAGE_PATH}/include |
| @@ -0,0 +1,350 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | +namespace { | ||
| 30 | +namespace fs = boost::filesystem; | ||
| 31 | +using namespace std::chrono_literals; | ||
| 32 | +using ascendc::aclrtc::CompilationCommand; | ||
| 33 | +using ascendc::aclrtc::CompilationCommandExecutionLimits; | ||
| 34 | +using ascendc::aclrtc::CompilationCommandKind; | ||
| 35 | +using ascendc::aclrtc::CompilationPlanExecutor; | ||
| 36 | +using ascendc::aclrtc::CompilationProcessResult; | ||
| 37 | +using ascendc::aclrtc::CompilationProcessTermination; | ||
| 38 | +using ascendc::aclrtc::CompilationVariant; | ||
| 39 | +using ascendc::aclrtc::KernelCompilationPlan; | ||
| 40 | +using ascendc::aclrtc::KernelSpecializationDiagnostics; | ||
| 41 | +using namespace mockcpp; | ||
| 42 | + | ||
| 43 | +int FailCommandOutputPoll(pollfd*, nfds_t, int) | ||
| 44 | +{ | ||
| 45 | + errno = EIO; | ||
| 46 | + return -1; | ||
| 47 | +} | ||
| 48 | + | ||
| 49 | +int FailCommandLeaderInspection(idtype_t, id_t, siginfo_t*, int) | ||
| 50 | +{ | ||
| 51 | + errno = ECHILD; | ||
| 52 | + return -1; | ||
| 53 | +} | ||
| 54 | + | ||
| 55 | +static_assert( | ||
| 56 | + std::is_same<std::underlying_type_t<CompilationVariant>, uint32_t>::value, | ||
| 57 | + "CompilationVariant must have a stable width"); | ||
| 58 | +static_assert( | ||
| 59 | + std::is_same<std::underlying_type_t<CompilationCommandKind>, uint32_t>::value, | ||
| 60 | + "CompilationCommandKind must have a stable width"); | ||
| 61 | +static_assert( | ||
| 62 | + std::is_same<std::underlying_type_t<CompilationProcessTermination>, uint32_t>::value, | ||
| 63 | + "CompilationProcessTermination must have a stable width"); | ||
| 64 | +static_assert( | ||
| 65 | + std::is_same<decltype(CompilationProcessResult::terminationCode), int32_t>::value, | ||
| 66 | + "terminationCode must have a stable width"); | ||
| 67 | +static_assert( | ||
| 68 | + std::is_same<decltype(CompilationCommandExecutionLimits::capturedOutputByteLimit), uint64_t>::value, | ||
| 69 | + "capturedOutputByteLimit must have a stable width"); | ||
| 70 | + | ||
| 71 | +CompilationCommand CreateCommand( | ||
| 72 | + CompilationCommandKind commandKind, const char* executable, std::vector<std::string> arguments) | ||
| 73 | +{ | ||
| 74 | + return {commandKind, 0U, "test-command", executable, std::move(arguments)}; | ||
| 75 | +} | ||
| 76 | + | ||
| 77 | +class ScopedDiagnosticCurrentDirectory final { | ||
| 78 | +public: | ||
| 79 | + ScopedDiagnosticCurrentDirectory() | ||
| 80 | + : previousDirectoryPath_(fs::current_path()), | ||
| 81 | + temporaryDirectoryPath_(fs::temp_directory_path() / fs::unique_path("aclrtc_executor_diagnostics_%%%%-%%%%")) | ||
| 82 | + { | ||
| 83 | + fs::create_directories(temporaryDirectoryPath_); | ||
| 84 | + fs::current_path(temporaryDirectoryPath_); | ||
| 85 | + } | ||
| 86 | + | ||
| 87 | + ~ScopedDiagnosticCurrentDirectory() | ||
| 88 | + { | ||
| 89 | + boost::system::error_code ignoredError; | ||
| 90 | + fs::current_path(previousDirectoryPath_, ignoredError); | ||
| 91 | + fs::remove_all(temporaryDirectoryPath_, ignoredError); | ||
| 92 | + } | ||
| 93 | + | ||
| 94 | +private: | ||
| 95 | + fs::path previousDirectoryPath_; | ||
| 96 | + fs::path temporaryDirectoryPath_; | ||
| 97 | +}; | ||
| 98 | + | ||
| 99 | +std::string ReadTextFile(const fs::path& filePath) | ||
| 100 | +{ | ||
| 101 | + std::ifstream inputStream(filePath.string()); | ||
| 102 | + return {std::istreambuf_iterator<char>(inputStream), std::istreambuf_iterator<char>()}; | ||
| 103 | +} | ||
| 104 | + | ||
| 105 | +TEST(CompilationPlanExecutorTest, ProductionLimitsMatchReviewedPerCommandTimeouts) | ||
| 106 | +{ | ||
| 107 | + const CompilationCommandExecutionLimits limits = CompilationCommandExecutionLimits::ProductionDefaults(); | ||
| 108 | + EXPECT_EQ(limits.compileCommandTimeout, 5min); | ||
| 109 | + EXPECT_EQ(limits.objectCopyAndLinkCommandTimeout, 1min); | ||
| 110 | + EXPECT_EQ(limits.terminationGracePeriod, 5s); | ||
| 111 | + EXPECT_EQ(limits.capturedOutputByteLimit, 4U * 1024U * 1024U); | ||
| 112 | +} | ||
| 113 | + | ||
| 114 | +TEST(CompilationPlanExecutorTest, ExecutesCommandsWithoutShellParsing) | ||
| 115 | +{ | ||
| 116 | + boost::system::error_code ignoredError; | ||
| 117 | + fs::remove("/tmp/aclrtc_should_not_exist", ignoredError); | ||
| 118 | + KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_success"); | ||
| 119 | + CompilationPlanExecutor executor(diagnostics, CompilationCommandExecutionLimits::ProductionDefaults()); | ||
| 120 | + KernelCompilationPlan plan; | ||
| 121 | + plan.compilationCommands.push_back( | ||
| 122 | + CreateCommand(CompilationCommandKind::Compile, "/bin/echo", {"$(touch /tmp/aclrtc_should_not_exist);literal"})); | ||
| 123 | + | ||
| 124 | + EXPECT_EQ(executor.ExecuteCompilationPlan(plan), 0); | ||
| 125 | + EXPECT_FALSE(fs::exists("/tmp/aclrtc_should_not_exist")); | ||
| 126 | +} | ||
| 127 | + | ||
| 128 | +TEST(CompilationPlanExecutorTest, RejectsEmbeddedNullBeforeSpawningProcess) | ||
| 129 | +{ | ||
| 130 | + KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_embedded_null"); | ||
| 131 | + CompilationPlanExecutor executor(diagnostics, CompilationCommandExecutionLimits::ProductionDefaults()); | ||
| 132 | + | ||
| 133 | + std::string executableWithEmbeddedNull("/bin/true"); | ||
| 134 | + executableWithEmbeddedNull.push_back('\0'); | ||
| 135 | + executableWithEmbeddedNull += "/bin/false"; | ||
| 136 | + KernelCompilationPlan executablePlan; | ||
| 137 | + CompilationCommand executableCommand = CreateCommand(CompilationCommandKind::Compile, "/bin/true", {}); | ||
| 138 | + executableCommand.executablePath = fs::path(executableWithEmbeddedNull); | ||
| 139 | + executablePlan.compilationCommands.push_back(std::move(executableCommand)); | ||
| 140 | + EXPECT_EQ(executor.ExecuteCompilationPlan(executablePlan), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 141 | + | ||
| 142 | + std::string argumentWithEmbeddedNull("accepted-prefix"); | ||
| 143 | + argumentWithEmbeddedNull.push_back('\0'); | ||
| 144 | + argumentWithEmbeddedNull += "discarded-suffix"; | ||
| 145 | + KernelCompilationPlan argumentPlan; | ||
| 146 | + argumentPlan.compilationCommands.push_back( | ||
| 147 | + CreateCommand(CompilationCommandKind::Compile, "/bin/true", {argumentWithEmbeddedNull})); | ||
| 148 | + EXPECT_EQ(executor.ExecuteCompilationPlan(argumentPlan), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 149 | +} | ||
| 150 | + | ||
| 151 | +TEST(CompilationPlanExecutorTest, MapsCompileAndLinkFailuresToDifferentErrors) | ||
| 152 | +{ | ||
| 153 | + KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_failure"); | ||
| 154 | + CompilationPlanExecutor executor(diagnostics, CompilationCommandExecutionLimits::ProductionDefaults()); | ||
| 155 | + KernelCompilationPlan plan; | ||
| 156 | + plan.compilationCommands.push_back(CreateCommand(CompilationCommandKind::Compile, "/bin/false", {})); | ||
| 157 | + EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_ERROR_COMPILATION); | ||
| 158 | + | ||
| 159 | + plan.compilationCommands[0].commandKind = CompilationCommandKind::Link; | ||
| 160 | + EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_ERROR_LINKING); | ||
| 161 | +} | ||
| 162 | + | ||
| 163 | +TEST(CompilationPlanExecutorTest, UsesInjectedShortTimeoutInUnitTests) | ||
| 164 | +{ | ||
| 165 | + KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_timeout"); | ||
| 166 | + CompilationCommandExecutionLimits limits; | ||
| 167 | + limits.compileCommandTimeout = 20ms; | ||
| 168 | + limits.objectCopyAndLinkCommandTimeout = 20ms; | ||
| 169 | + limits.terminationGracePeriod = 20ms; | ||
| 170 | + limits.capturedOutputByteLimit = 1024U; | ||
| 171 | + CompilationPlanExecutor executor(diagnostics, limits); | ||
| 172 | + KernelCompilationPlan plan; | ||
| 173 | + plan.compilationCommands.push_back(CreateCommand(CompilationCommandKind::Compile, "/bin/sleep", {"10"})); | ||
| 174 | + | ||
| 175 | + const auto startedAt = std::chrono::steady_clock::now(); | ||
| 176 | + EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_ERROR_COMPILATION); | ||
| 177 | + EXPECT_LT(std::chrono::steady_clock::now() - startedAt, 2s); | ||
| 178 | +} | ||
| 179 | + | ||
| 180 | +TEST(CompilationPlanExecutorTest, EnforcesTimeoutWhileCommandContinuouslyProducesOutput) | ||
| 181 | +{ | ||
| 182 | + KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_continuous_output"); | ||
| 183 | + CompilationCommandExecutionLimits limits{20ms, 20ms, 20ms, 1024U}; | ||
| 184 | + CompilationPlanExecutor executor(diagnostics, limits); | ||
| 185 | + KernelCompilationPlan plan; | ||
| 186 | + plan.compilationCommands.push_back(CreateCommand( | ||
| 187 | + CompilationCommandKind::Compile, "/bin/sh", {"-c", "yes & yes & yes & yes & yes & yes & yes & yes & wait"})); | ||
| 188 | + | ||
| 189 | + const auto startedAt = std::chrono::steady_clock::now(); | ||
| 190 | + EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_ERROR_COMPILATION); | ||
| 191 | + EXPECT_LT(std::chrono::steady_clock::now() - startedAt, 2s); | ||
| 192 | +} | ||
| 193 | + | ||
| 194 | +TEST(CompilationPlanExecutorTest, WaitsWithoutBusyPollingAfterCommandOutputCloses) | ||
| 195 | +{ | ||
| 196 | + KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_closed_output"); | ||
| 197 | + CompilationCommandExecutionLimits limits{1s, 1s, 20ms, 1024U}; | ||
| 198 | + CompilationPlanExecutor executor(diagnostics, limits); | ||
| 199 | + KernelCompilationPlan plan; | ||
| 200 | + plan.compilationCommands.push_back( | ||
| 201 | + CreateCommand(CompilationCommandKind::Compile, "/bin/sh", {"-c", "exec >/dev/null 2>&1; sleep 0.25"})); | ||
| 202 | + | ||
| 203 | + timespec cpuStartedAt{}; | ||
| 204 | + timespec cpuFinishedAt{}; | ||
| 205 | + ASSERT_EQ(clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpuStartedAt), 0); | ||
| 206 | + EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 207 | + ASSERT_EQ(clock_gettime(CLOCK_THREAD_CPUTIME_ID, &cpuFinishedAt), 0); | ||
| 208 | + const int64_t consumedCpuNanoseconds = | ||
| 209 | + (static_cast<int64_t>(cpuFinishedAt.tv_sec) - static_cast<int64_t>(cpuStartedAt.tv_sec)) * 1000000000LL + | ||
| 210 | + static_cast<int64_t>(cpuFinishedAt.tv_nsec) - static_cast<int64_t>(cpuStartedAt.tv_nsec); | ||
| 211 | + EXPECT_LT(consumedCpuNanoseconds, 100000000LL); | ||
| 212 | +} | ||
| 213 | + | ||
| 214 | +TEST(CompilationPlanExecutorTest, WaitsForOutputPipeClosureAfterCommandLeaderExits) | ||
| 215 | +{ | ||
| 216 | + KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_leader_exit"); | ||
| 217 | + CompilationCommandExecutionLimits limits{500ms, 500ms, 20ms, 1024U}; | ||
| 218 | + CompilationPlanExecutor executor(diagnostics, limits); | ||
| 219 | + KernelCompilationPlan plan; | ||
| 220 | + plan.compilationCommands.push_back( | ||
| 221 | + CreateCommand(CompilationCommandKind::Compile, "/bin/sh", {"-c", "sleep 0.3 & exec /bin/true"})); | ||
| 222 | + | ||
| 223 | + const auto startedAt = std::chrono::steady_clock::now(); | ||
| 224 | + EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 225 | + EXPECT_GE(std::chrono::steady_clock::now() - startedAt, 200ms); | ||
| 226 | +} | ||
| 227 | + | ||
| 228 | +TEST(CompilationPlanExecutorTest, TimeoutTerminatesDescendantsAfterCommandLeaderExits) | ||
| 229 | +{ | ||
| 230 | + static uint64_t sequence = 0U; | ||
| 231 | + const fs::path descendantMarkerPath = | ||
| 232 | + fs::temp_directory_path() / | ||
| 233 | + ("aclrtc_descendant_marker_" + std::to_string(getpid()) + "_" + std::to_string(sequence++)); | ||
| 234 | + KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_process_group"); | ||
| 235 | + CompilationCommandExecutionLimits limits{20ms, 20ms, 20ms, 1024U}; | ||
| 236 | + CompilationPlanExecutor executor(diagnostics, limits); | ||
| 237 | + KernelCompilationPlan plan; | ||
| 238 | + const std::string commandScript = | ||
| 239 | + "(trap '' TERM; count=0; while [ $count -lt 100 ]; do printf x >> '" + descendantMarkerPath.string() + | ||
| 240 | + "'; count=$((count + 1)); sleep 0.01; done; rm -f '" + descendantMarkerPath.string() + "') & exec /bin/true"; | ||
| 241 | + plan.compilationCommands.push_back( | ||
| 242 | + CreateCommand(CompilationCommandKind::Compile, "/bin/sh", {"-c", commandScript})); | ||
| 243 | + | ||
| 244 | + ASSERT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_ERROR_COMPILATION); | ||
| 245 | + // An orphaned descendant can remain as a zombie until the container init reaps it, so do not use kill(pid, 0) | ||
| 246 | + // as the assertion. A heartbeat that stops growing after the timeout directly verifies that the descendant did | ||
| 247 | + // not continue. | ||
| 248 | + const uintmax_t markerSizeAfterTimeout = | ||
| 249 | + fs::exists(descendantMarkerPath) ? fs::file_size(descendantMarkerPath) : 0U; | ||
| 250 | + std::this_thread::sleep_for(100ms); | ||
| 251 | + const uintmax_t markerSizeAfterWait = fs::exists(descendantMarkerPath) ? fs::file_size(descendantMarkerPath) : 0U; | ||
| 252 | + EXPECT_EQ(markerSizeAfterWait, markerSizeAfterTimeout); | ||
| 253 | + boost::system::error_code ignoredError; | ||
| 254 | + fs::remove(descendantMarkerPath, ignoredError); | ||
| 255 | +} | ||
| 256 | + | ||
| 257 | +TEST(CompilationPlanExecutorTest, RecordsSpawnFailureAsInfrastructureFailure) | ||
| 258 | +{ | ||
| 259 | + ScopedDiagnosticCurrentDirectory diagnosticCurrentDirectory; | ||
| 260 | + const std::string specializationSessionId = "executor_spawn_failure"; | ||
| 261 | + KernelSpecializationDiagnostics diagnostics(fs::current_path(), specializationSessionId); | ||
| 262 | + CompilationPlanExecutor executor(diagnostics, CompilationCommandExecutionLimits::ProductionDefaults()); | ||
| 263 | + KernelCompilationPlan plan; | ||
| 264 | + plan.compilationCommands.push_back(CreateCommand(CompilationCommandKind::Compile, "/path/that/does/not/exist", {})); | ||
| 265 | + EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 266 | + | ||
| 267 | + const std::string compilationLog = ReadTextFile(fs::current_path() / "aclrtc_compile.log"); | ||
| 268 | + EXPECT_NE(compilationLog.find("termination=infrastructure_failure"), std::string::npos); | ||
| 269 | + EXPECT_EQ(compilationLog.find("termination=exited code=0"), std::string::npos); | ||
| 270 | +} | ||
| 271 | + | ||
| 272 | +TEST(CompilationPlanExecutorTest, BoundsCapturedProcessOutput) | ||
| 273 | +{ | ||
| 274 | + ScopedDiagnosticCurrentDirectory diagnosticCurrentDirectory; | ||
| 275 | + const std::string specializationSessionId = "executor_output_limit"; | ||
| 276 | + KernelSpecializationDiagnostics diagnostics(fs::current_path(), specializationSessionId); | ||
| 277 | + CompilationCommandExecutionLimits limits = CompilationCommandExecutionLimits::ProductionDefaults(); | ||
| 278 | + limits.capturedOutputByteLimit = 8U; | ||
| 279 | + CompilationPlanExecutor executor(diagnostics, limits); | ||
| 280 | + KernelCompilationPlan plan; | ||
| 281 | + plan.compilationCommands.push_back( | ||
| 282 | + CreateCommand(CompilationCommandKind::Compile, "/usr/bin/printf", {"0123456789abcdef"})); | ||
| 283 | + EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 284 | + | ||
| 285 | + const std::string compilationLog = ReadTextFile(fs::current_path() / "aclrtc_compile.log"); | ||
| 286 | + EXPECT_NE(compilationLog.find("output_truncated=true"), std::string::npos); | ||
| 287 | + EXPECT_NE(compilationLog.find("01234567"), std::string::npos); | ||
| 288 | + EXPECT_EQ(compilationLog.find("89abcdef"), std::string::npos); | ||
| 289 | +} | ||
| 290 | + | ||
| 291 | +TEST(CompilationPlanExecutorTest, ReportsToolProcessTerminatedBySignal) | ||
| 292 | +{ | ||
| 293 | + KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_signaled"); | ||
| 294 | + CompilationPlanExecutor executor(diagnostics, CompilationCommandExecutionLimits::ProductionDefaults()); | ||
| 295 | + KernelCompilationPlan plan; | ||
| 296 | + plan.compilationCommands.push_back( | ||
| 297 | + CreateCommand(CompilationCommandKind::Compile, "/bin/sh", {"-c", "kill -TERM $$"})); | ||
| 298 | + | ||
| 299 | + EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_ERROR_COMPILATION); | ||
| 300 | +} | ||
| 301 | + | ||
| 302 | +TEST(CompilationPlanExecutorTest, ReportsPipeCreationFailureWhenFileDescriptorsAreExhausted) | ||
| 303 | +{ | ||
| 304 | + KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_pipe_failure"); | ||
| 305 | + CompilationPlanExecutor executor(diagnostics, CompilationCommandExecutionLimits::ProductionDefaults()); | ||
| 306 | + KernelCompilationPlan plan; | ||
| 307 | + plan.compilationCommands.push_back(CreateCommand(CompilationCommandKind::Compile, "/bin/true", {})); | ||
| 308 | + | ||
| 309 | + struct rlimit originalFileDescriptorLimit {}; | ||
| 310 | + ASSERT_EQ(getrlimit(RLIMIT_NOFILE, &originalFileDescriptorLimit), 0); | ||
| 311 | + struct rlimit exhaustedFileDescriptorLimit = originalFileDescriptorLimit; | ||
| 312 | + exhaustedFileDescriptorLimit.rlim_cur = 0U; | ||
| 313 | + ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &exhaustedFileDescriptorLimit), 0); | ||
| 314 | + const aclError executionResult = executor.ExecuteCompilationPlan(plan); | ||
| 315 | + const int restoreLimitResult = setrlimit(RLIMIT_NOFILE, &originalFileDescriptorLimit); | ||
| 316 | + | ||
| 317 | + ASSERT_EQ(restoreLimitResult, 0); | ||
| 318 | + EXPECT_EQ(executionResult, ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 319 | +} | ||
| 320 | + | ||
| 321 | +TEST(CompilationPlanExecutorTest, ReportsCommandOutputPollFailure) | ||
| 322 | +{ | ||
| 323 | + KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_poll_failure"); | ||
| 324 | + CompilationCommandExecutionLimits limits{1s, 1s, 20ms, 1024U}; | ||
| 325 | + CompilationPlanExecutor executor(diagnostics, limits); | ||
| 326 | + KernelCompilationPlan plan; | ||
| 327 | + plan.compilationCommands.push_back(CreateCommand(CompilationCommandKind::Compile, "/bin/sleep", {"1"})); | ||
| 328 | + MOCKER(poll, int (*)(pollfd*, nfds_t, int)).expects(once()).will(invoke(FailCommandOutputPoll)); | ||
| 329 | + | ||
| 330 | + const aclError executionResult = executor.ExecuteCompilationPlan(plan); | ||
| 331 | + | ||
| 332 | + GlobalMockObject::verify(); | ||
| 333 | + EXPECT_EQ(executionResult, ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 334 | +} | ||
| 335 | + | ||
| 336 | +TEST(CompilationPlanExecutorTest, ReportsCommandLeaderInspectionFailure) | ||
| 337 | +{ | ||
| 338 | + KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_waitid_failure"); | ||
| 339 | + CompilationCommandExecutionLimits limits{1s, 1s, 20ms, 1024U}; | ||
| 340 | + CompilationPlanExecutor executor(diagnostics, limits); | ||
| 341 | + KernelCompilationPlan plan; | ||
| 342 | + plan.compilationCommands.push_back(CreateCommand(CompilationCommandKind::Compile, "/bin/sleep", {"1"})); | ||
| 343 | + MOCKER(waitid, int (*)(idtype_t, id_t, siginfo_t*, int)).expects(once()).will(invoke(FailCommandLeaderInspection)); | ||
| 344 | + | ||
| 345 | + const aclError executionResult = executor.ExecuteCompilationPlan(plan); | ||
| 346 | + | ||
| 347 | + GlobalMockObject::verify(); | ||
| 348 | + EXPECT_EQ(executionResult, ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 349 | +} | ||
| 350 | +} // namespace | ||
| @@ -0,0 +1,820 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | +namespace { | ||
| 26 | +namespace fs = boost::filesystem; | ||
| 27 | +using Json = nlohmann::json; | ||
| 28 | +using ascendc::aclrtc::CompilationCommandKind; | ||
| 29 | +using ascendc::aclrtc::CompilationVariant; | ||
| 30 | +using ascendc::aclrtc::KernelCompilationPlan; | ||
| 31 | +using ascendc::aclrtc::KernelCompilationPlanBuilder; | ||
| 32 | +using ascendc::aclrtc::NormalizedKernelSpecializationRequest; | ||
| 33 | + | ||
| 34 | +class ScopedEnvironmentVariable final { | ||
| 35 | +public: | ||
| 36 | + ScopedEnvironmentVariable(const char* name, const std::string& value) : name_(name) | ||
| 37 | + { | ||
| 38 | + const char* previousValue = std::getenv(name); | ||
| 39 | + if (previousValue != nullptr) { | ||
| 40 | + previousValue_ = previousValue; | ||
| 41 | + wasSet_ = true; | ||
| 42 | + } | ||
| 43 | + setenv(name, value.c_str(), 1); | ||
| 44 | + } | ||
| 45 | + | ||
| 46 | + ~ScopedEnvironmentVariable() | ||
| 47 | + { | ||
| 48 | + if (wasSet_) { | ||
| 49 | + setenv(name_.c_str(), previousValue_.c_str(), 1); | ||
| 50 | + } else { | ||
| 51 | + unsetenv(name_.c_str()); | ||
| 52 | + } | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | +private: | ||
| 56 | + std::string name_; | ||
| 57 | + std::string previousValue_; | ||
| 58 | + bool wasSet_{false}; | ||
| 59 | +}; | ||
| 60 | + | ||
| 61 | +class KernelPlanTestWorkspace final { | ||
| 62 | +public: | ||
| 63 | + KernelPlanTestWorkspace() | ||
| 64 | + { | ||
| 65 | + static uint64_t sequence = 0; | ||
| 66 | + rootPath_ = fs::temp_directory_path() / | ||
| 67 | + ("aclrtc_plan_test_" + std::to_string(getpid()) + "_" + std::to_string(sequence++)); | ||
| 68 | + resourcePath_ = rootPath_ / "resource"; | ||
| 69 | + toolchainPath_ = rootPath_ / "toolchain"; | ||
| 70 | + sourceDirectoryPath_ = rootPath_ / "user_source"; | ||
| 71 | + fs::create_directories(resourcePath_ / "resources/include"); | ||
| 72 | + fs::create_directories(resourcePath_ / "resources/src"); | ||
| 73 | + fs::create_directories(toolchainPath_ / "bin"); | ||
| 74 | + fs::create_directories(sourceDirectoryPath_); | ||
| 75 | + WriteFile(sourceDirectoryPath_ / "add_custom.cpp", "// user source\n"); | ||
| 76 | + WriteFile(resourcePath_ / "resources/include/constants.h", "@@STATIC_TILING@@\n"); | ||
| 77 | + WriteFile(resourcePath_ / "resources/src/basic.cpp", "// basic\n"); | ||
| 78 | + WriteFile(resourcePath_ / "resources/src/sk.cpp", "// sk\n"); | ||
| 79 | + WriteExecutable(toolchainPath_ / "bin/bisheng"); | ||
| 80 | + WriteExecutable(toolchainPath_ / "bin/llvm-objcopy"); | ||
| 81 | + WriteExecutable(toolchainPath_ / "bin/ld.lld"); | ||
| 82 | + } | ||
| 83 | + | ||
| 84 | + ~KernelPlanTestWorkspace() | ||
| 85 | + { | ||
| 86 | + boost::system::error_code ignoredError; | ||
| 87 | + fs::remove_all(rootPath_, ignoredError); | ||
| 88 | + } | ||
| 89 | + | ||
| 90 | + const fs::path& ResourcePath() const { return resourcePath_; } | ||
| 91 | + const fs::path& ToolchainPath() const { return toolchainPath_; } | ||
| 92 | + const fs::path& SourceDirectoryPath() const { return sourceDirectoryPath_; } | ||
| 93 | + | ||
| 94 | +private: | ||
| 95 | + static void WriteFile(const fs::path& path, const std::string& contents) | ||
| 96 | + { | ||
| 97 | + std::ofstream stream(path.string()); | ||
| 98 | + stream << contents; | ||
| 99 | + } | ||
| 100 | + | ||
| 101 | + static void WriteExecutable(const fs::path& path) | ||
| 102 | + { | ||
| 103 | + WriteFile(path, "#!/bin/sh\nexit 0\n"); | ||
| 104 | + chmod(path.c_str(), 0755); | ||
| 105 | + } | ||
| 106 | + | ||
| 107 | + fs::path rootPath_; | ||
| 108 | + fs::path resourcePath_; | ||
| 109 | + fs::path toolchainPath_; | ||
| 110 | + fs::path sourceDirectoryPath_; | ||
| 111 | +}; | ||
| 112 | + | ||
| 113 | +Json CreateManifest() | ||
| 114 | +{ | ||
| 115 | + return Json{ | ||
| 116 | + {"schema_version", "1.0"}, | ||
| 117 | + {"source_file", "add_custom.cpp"}, | ||
| 118 | + {"options", | ||
| 119 | + {{"common_compile", Json::array({"-I", "${source_file_path}", "-I${env:ACLRTC_TEST_TOOLCHAIN}/include"})}}}, | ||
| 120 | + {"kernels", | ||
| 121 | + Json::array( | ||
| 122 | + {{{"kernel_name", "other_kernel"}, {"unsupported_future_field", Json::object()}}, | ||
| 123 | + {{"kernel_name", "add_custom_100000"}, | ||
| 124 | + {"constant_infos", Json::array( | ||
| 125 | + {{{"parameter_index", 0}, | ||
| 126 | + {"byte_size", 2}, | ||
| 127 | + {"file", "${resource}/resources/include/constants.h"}, | ||
| 128 | + {"template", "@@STATIC_TILING@@"}}})}, | ||
| 129 | + {"link_options", Json::array({"-m", "aicorelinux", "-r", "-Ttext=0", "-q", "-x"})}, | ||
| 130 | + {"objects", | ||
| 131 | + Json::array( | ||
| 132 | + {{{"object_name", "basic"}, | ||
| 133 | + {"object_type", "basic"}, | ||
| 134 | + {"commands", | ||
| 135 | + Json::array( | ||
| 136 | + {{{"type", "compile"}, | ||
| 137 | + {"stage", 0}, | ||
| 138 | + {"cmd", Json::array( | ||
| 139 | + {"${env:ACLRTC_TEST_TOOLCHAIN}/bin/bisheng", "${options:common_compile}", | ||
| 140 | + "${resource}/resources/src/basic.cpp", "-o", "${output}/basic.o"})}}})}, | ||
| 141 | + {"outputs", Json::array({"${output}/basic.o"})}}, | ||
| 142 | + {{"object_name", "sk"}, | ||
| 143 | + {"object_type", "sk"}, | ||
| 144 | + {"commands", Json::array( | ||
| 145 | + {{{"type", "compile"}, | ||
| 146 | + {"stage", 0}, | ||
| 147 | + {"cmd", Json::array( | ||
| 148 | + {"${env:ACLRTC_TEST_SK_TOOLCHAIN}/bin/bisheng", | ||
| 149 | + "${resource}/resources/src/sk.cpp", "-o", "${output}/sk.o"})}}, | ||
| 150 | + {{"type", "objcopy"}, | ||
| 151 | + {"stage", 1}, | ||
| 152 | + {"cmd", Json::array( | ||
| 153 | + {"${env:ACLRTC_TEST_TOOLCHAIN}/bin/llvm-objcopy", | ||
| 154 | + "--redefine-sym=old_symbol=new_symbol", "${output}/sk.o", | ||
| 155 | + "${output}/sk_split.o"})}}})}, | ||
| 156 | + {"outputs", Json::array({"${output}/sk.o", "${output}/sk_split.o"})}}})}}})}}; | ||
| 157 | +} | ||
| 158 | + | ||
| 159 | +NormalizedKernelSpecializationRequest CreateRequest( | ||
| 160 | + const KernelPlanTestWorkspace& workspace, const void* const* argumentAddresses, const uint64_t* argumentByteCounts) | ||
| 161 | +{ | ||
| 162 | + NormalizedKernelSpecializationRequest request; | ||
| 163 | + request.specializationSessionId = "session_1"; | ||
| 164 | + request.resourceId = "resource"; | ||
| 165 | + request.kernelName = "add_custom_100000"; | ||
| 166 | + request.outputElfPath = workspace.ResourcePath() / "published.elf"; | ||
| 167 | + request.kernelArgumentCount = 1; | ||
| 168 | + request.borrowedKernelArgumentDataPointers = argumentAddresses; | ||
| 169 | + request.borrowedKernelArgumentByteCounts = argumentByteCounts; | ||
| 170 | + return request; | ||
| 171 | +} | ||
| 172 | + | ||
| 173 | +TEST(KernelCompilationPlanBuilderTest, BuildsBasicPlanFromOnlySelectedManifestFields) | ||
| 174 | +{ | ||
| 175 | + KernelPlanTestWorkspace workspace; | ||
| 176 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 177 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 178 | + unsetenv("ACLRTC_TEST_SK_TOOLCHAIN"); | ||
| 179 | + const uint8_t tilingBytes[] = {0x01, 0xfe}; | ||
| 180 | + const void* argumentAddresses[] = {tilingBytes}; | ||
| 181 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 182 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); | ||
| 183 | + request.compilerOptions.basicOptions = {"-g", "-DVALUE=1"}; | ||
| 184 | + | ||
| 185 | + KernelCompilationPlan plan; | ||
| 186 | + EXPECT_EQ( | ||
| 187 | + KernelCompilationPlanBuilder( | ||
| 188 | + request, CreateManifest(), workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 189 | + .BuildCompilationPlan(plan), | ||
| 190 | + 0); | ||
| 191 | + | ||
| 192 | + ASSERT_EQ(plan.sourcePatches.size(), 1U); | ||
| 193 | + EXPECT_EQ(plan.sourcePatches[0].targetFilePath, workspace.ResourcePath() / "resources/include/constants.h"); | ||
| 194 | + EXPECT_EQ(plan.sourcePatches[0].replacementText, "{0x01, 0xfe}"); | ||
| 195 | + | ||
| 196 | + ASSERT_EQ(plan.compilationCommands.size(), 2U); | ||
| 197 | + const auto& compileCommand = plan.compilationCommands[0]; | ||
| 198 | + EXPECT_EQ(compileCommand.commandKind, CompilationCommandKind::Compile); | ||
| 199 | + EXPECT_EQ(compileCommand.executablePath, workspace.ToolchainPath() / "bin/bisheng"); | ||
| 200 | + EXPECT_EQ( | ||
| 201 | + compileCommand.commandArguments, | ||
| 202 | + (std::vector<std::string>{ | ||
| 203 | + "-I", workspace.SourceDirectoryPath().string(), "-I" + (workspace.ToolchainPath() / "include").string(), | ||
| 204 | + (workspace.ResourcePath() / "resources/src/basic.cpp").string(), "-g", "-DVALUE=1", "-o", | ||
| 205 | + (workspace.ResourcePath() / ".aclrtc_session_1/outputs/basic.o").string()})); | ||
| 206 | + EXPECT_EQ(plan.compilationCommands[1].commandKind, CompilationCommandKind::Link); | ||
| 207 | + EXPECT_EQ(plan.compilationCommands[1].executablePath, workspace.ToolchainPath() / "bin/ld.lld"); | ||
| 208 | + EXPECT_EQ(plan.linkedKernelElfPath, workspace.ResourcePath() / ".aclrtc_session_1/outputs/linked_kernel.elf"); | ||
| 209 | +} | ||
| 210 | + | ||
| 211 | +TEST(KernelCompilationPlanBuilderTest, BuildsPlanWithoutOptionalExternalSourceFile) | ||
| 212 | +{ | ||
| 213 | + KernelPlanTestWorkspace workspace; | ||
| 214 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 215 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 216 | + const uint8_t tilingBytes[] = {0x01, 0x02}; | ||
| 217 | + const void* argumentAddresses[] = {tilingBytes}; | ||
| 218 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 219 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); | ||
| 220 | + Json manifest = CreateManifest(); | ||
| 221 | + manifest.erase("source_file"); | ||
| 222 | + manifest["options"]["common_compile"] = Json::array({"-I${env:ACLRTC_TEST_TOOLCHAIN}/include"}); | ||
| 223 | + | ||
| 224 | + KernelCompilationPlan plan; | ||
| 225 | + EXPECT_EQ( | ||
| 226 | + KernelCompilationPlanBuilder(request, manifest, workspace.ResourcePath(), fs::path()) | ||
| 227 | + .BuildCompilationPlan(plan), | ||
| 228 | + ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 229 | +} | ||
| 230 | + | ||
| 231 | +TEST(KernelCompilationPlanBuilderTest, RejectsCompileCommandWithoutOutputPath) | ||
| 232 | +{ | ||
| 233 | + KernelPlanTestWorkspace workspace; | ||
| 234 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 235 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 236 | + const uint8_t tilingBytes[] = {0x01, 0x02}; | ||
| 237 | + const void* argumentAddresses[] = {tilingBytes}; | ||
| 238 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 239 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); | ||
| 240 | + Json manifest = CreateManifest(); | ||
| 241 | + manifest["kernels"][1]["objects"][0]["commands"][0]["cmd"].erase(4); | ||
| 242 | + | ||
| 243 | + KernelCompilationPlan plan; | ||
| 244 | + EXPECT_EQ( | ||
| 245 | + KernelCompilationPlanBuilder(request, manifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 246 | + .BuildCompilationPlan(plan), | ||
| 247 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 248 | +} | ||
| 249 | + | ||
| 250 | +TEST(KernelCompilationPlanBuilderTest, DoesNotTreatNonOutputOptionAsCompilerOutput) | ||
| 251 | +{ | ||
| 252 | + KernelPlanTestWorkspace workspace; | ||
| 253 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 254 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 255 | + const uint8_t tilingBytes[] = {0x01, 0x02}; | ||
| 256 | + const void* argumentAddresses[] = {tilingBytes}; | ||
| 257 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 258 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); | ||
| 259 | + Json manifest = CreateManifest(); | ||
| 260 | + manifest["kernels"][1]["objects"][0]["commands"][0]["cmd"][3] = "-opaque-pointers"; | ||
| 261 | + | ||
| 262 | + KernelCompilationPlan plan; | ||
| 263 | + EXPECT_EQ( | ||
| 264 | + KernelCompilationPlanBuilder(request, manifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 265 | + .BuildCompilationPlan(plan), | ||
| 266 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 267 | +} | ||
| 268 | + | ||
| 269 | +TEST(KernelCompilationPlanBuilderTest, AcceptsCommandWithoutStageAndPreservesManifestOrder) | ||
| 270 | +{ | ||
| 271 | + KernelPlanTestWorkspace workspace; | ||
| 272 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 273 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 274 | + const uint8_t tilingBytes[] = {0x01, 0x02}; | ||
| 275 | + const void* argumentAddresses[] = {tilingBytes}; | ||
| 276 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 277 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); | ||
| 278 | + Json manifest = CreateManifest(); | ||
| 279 | + manifest["kernels"][1]["objects"][0]["commands"][0].erase("stage"); | ||
| 280 | + | ||
| 281 | + KernelCompilationPlan plan; | ||
| 282 | + ASSERT_EQ( | ||
| 283 | + KernelCompilationPlanBuilder(request, manifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 284 | + .BuildCompilationPlan(plan), | ||
| 285 | + 0); | ||
| 286 | + ASSERT_EQ(plan.compilationCommands.size(), 2U); | ||
| 287 | + EXPECT_EQ(plan.compilationCommands[0].diagnosticLabel, "basic/compile[0]"); | ||
| 288 | + EXPECT_FALSE(plan.compilationCommands[0].parallelStage.has_value()); | ||
| 289 | + EXPECT_EQ(plan.compilationCommands[1].commandKind, CompilationCommandKind::Link); | ||
| 290 | + EXPECT_FALSE(plan.compilationCommands[1].parallelStage.has_value()); | ||
| 291 | +} | ||
| 292 | + | ||
| 293 | +TEST(KernelCompilationPlanBuilderTest, PreservesExplicitStagesAndManifestCommandOrder) | ||
| 294 | +{ | ||
| 295 | + KernelPlanTestWorkspace workspace; | ||
| 296 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 297 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 298 | + ScopedEnvironmentVariable skToolchainEnvironment("ACLRTC_TEST_SK_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 299 | + const uint8_t tilingBytes[] = {0x01, 0x02}; | ||
| 300 | + const void* argumentAddresses[] = {tilingBytes}; | ||
| 301 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 302 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); | ||
| 303 | + request.compilationVariant = CompilationVariant::BasicWithSuperKernel; | ||
| 304 | + request.compilerOptions.superKernelOptions = {"-g"}; | ||
| 305 | + | ||
| 306 | + KernelCompilationPlan plan; | ||
| 307 | + ASSERT_EQ( | ||
| 308 | + KernelCompilationPlanBuilder( | ||
| 309 | + request, CreateManifest(), workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 310 | + .BuildCompilationPlan(plan), | ||
| 311 | + 0); | ||
| 312 | + ASSERT_EQ(plan.compilationCommands.size(), 4U); | ||
| 313 | + EXPECT_EQ(plan.compilationCommands[0].diagnosticLabel, "basic/compile[0]"); | ||
| 314 | + EXPECT_EQ(plan.compilationCommands[1].diagnosticLabel, "sk/compile[0]"); | ||
| 315 | + EXPECT_EQ(plan.compilationCommands[2].diagnosticLabel, "sk/objcopy[1]"); | ||
| 316 | + EXPECT_EQ(plan.compilationCommands[3].diagnosticLabel, "link"); | ||
| 317 | + EXPECT_EQ(plan.compilationCommands[0].parallelStage, 0U); | ||
| 318 | + EXPECT_EQ(plan.compilationCommands[1].parallelStage, 0U); | ||
| 319 | + EXPECT_EQ(plan.compilationCommands[2].parallelStage, 1U); | ||
| 320 | + EXPECT_FALSE(plan.compilationCommands[3].parallelStage.has_value()); | ||
| 321 | + EXPECT_NE( | ||
| 322 | + std::find( | ||
| 323 | + plan.compilationCommands[1].commandArguments.begin(), plan.compilationCommands[1].commandArguments.end(), | ||
| 324 | + "-g"), | ||
| 325 | + plan.compilationCommands[1].commandArguments.end()); | ||
| 326 | +} | ||
| 327 | + | ||
| 328 | +TEST(KernelCompilationPlanBuilderTest, SelectsAllRequestedObjectsInManifestOrder) | ||
| 329 | +{ | ||
| 330 | + KernelPlanTestWorkspace workspace; | ||
| 331 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 332 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 333 | + ScopedEnvironmentVariable skToolchainEnvironment("ACLRTC_TEST_SK_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 334 | + const uint8_t tilingBytes[] = {0x01, 0x02}; | ||
| 335 | + const void* argumentAddresses[] = {tilingBytes}; | ||
| 336 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 337 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); | ||
| 338 | + request.compilationVariant = CompilationVariant::BasicWithSuperKernel; | ||
| 339 | + | ||
| 340 | + Json manifest = CreateManifest(); | ||
| 341 | + Json secondBasicObject = manifest["kernels"][1]["objects"][0]; | ||
| 342 | + secondBasicObject["object_name"] = "basic_aux"; | ||
| 343 | + secondBasicObject["commands"][0]["cmd"][4] = "${output}/basic_aux.o"; | ||
| 344 | + secondBasicObject["outputs"][0] = "${output}/basic_aux.o"; | ||
| 345 | + manifest["kernels"][1]["objects"].push_back(secondBasicObject); | ||
| 346 | + | ||
| 347 | + Json secondSuperKernelObject = manifest["kernels"][1]["objects"][1]; | ||
| 348 | + secondSuperKernelObject["object_name"] = "sk_aux"; | ||
| 349 | + secondSuperKernelObject["commands"][0]["cmd"][3] = "${output}/sk_aux.o"; | ||
| 350 | + secondSuperKernelObject["commands"][1]["cmd"][2] = "${output}/sk_aux.o"; | ||
| 351 | + secondSuperKernelObject["commands"][1]["cmd"][3] = "${output}/sk_aux_split.o"; | ||
| 352 | + secondSuperKernelObject["outputs"] = Json::array({"${output}/sk_aux.o", "${output}/sk_aux_split.o"}); | ||
| 353 | + manifest["kernels"][1]["objects"].push_back(secondSuperKernelObject); | ||
| 354 | + | ||
| 355 | + KernelCompilationPlan plan; | ||
| 356 | + ASSERT_EQ( | ||
| 357 | + KernelCompilationPlanBuilder(request, manifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 358 | + .BuildCompilationPlan(plan), | ||
| 359 | + 0); | ||
| 360 | + | ||
| 361 | + ASSERT_EQ(plan.compilationCommands.size(), 7U); | ||
| 362 | + EXPECT_EQ(plan.compilationCommands[0].diagnosticLabel, "basic/compile[0]"); | ||
| 363 | + EXPECT_EQ(plan.compilationCommands[1].diagnosticLabel, "sk/compile[0]"); | ||
| 364 | + EXPECT_EQ(plan.compilationCommands[2].diagnosticLabel, "sk/objcopy[1]"); | ||
| 365 | + EXPECT_EQ(plan.compilationCommands[3].diagnosticLabel, "basic_aux/compile[0]"); | ||
| 366 | + EXPECT_EQ(plan.compilationCommands[4].diagnosticLabel, "sk_aux/compile[0]"); | ||
| 367 | + EXPECT_EQ(plan.compilationCommands[5].diagnosticLabel, "sk_aux/objcopy[1]"); | ||
| 368 | + EXPECT_EQ(plan.compilationCommands[6].diagnosticLabel, "link"); | ||
| 369 | + | ||
| 370 | + const std::vector<std::string>& linkArguments = plan.compilationCommands.back().commandArguments; | ||
| 371 | + EXPECT_NE( | ||
| 372 | + std::find( | ||
| 373 | + linkArguments.begin(), linkArguments.end(), | ||
| 374 | + (workspace.ResourcePath() / ".aclrtc_session_1/outputs/basic_aux.o").string()), | ||
| 375 | + linkArguments.end()); | ||
| 376 | + EXPECT_NE( | ||
| 377 | + std::find( | ||
| 378 | + linkArguments.begin(), linkArguments.end(), | ||
| 379 | + (workspace.ResourcePath() / ".aclrtc_session_1/outputs/sk_aux_split.o").string()), | ||
| 380 | + linkArguments.end()); | ||
| 381 | +} | ||
| 382 | + | ||
| 383 | +TEST(KernelCompilationPlanBuilderTest, RequiresOnlyEnvironmentUsedBySelectedObjects) | ||
| 384 | +{ | ||
| 385 | + KernelPlanTestWorkspace workspace; | ||
| 386 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 387 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 388 | + unsetenv("ACLRTC_TEST_SK_TOOLCHAIN"); | ||
| 389 | + const uint8_t tilingBytes[] = {0x01, 0x02}; | ||
| 390 | + const void* argumentAddresses[] = {tilingBytes}; | ||
| 391 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 392 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); | ||
| 393 | + | ||
| 394 | + KernelCompilationPlan plan; | ||
| 395 | + EXPECT_EQ( | ||
| 396 | + KernelCompilationPlanBuilder( | ||
| 397 | + request, CreateManifest(), workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 398 | + .BuildCompilationPlan(plan), | ||
| 399 | + 0); | ||
| 400 | + | ||
| 401 | + request.compilationVariant = CompilationVariant::BasicWithSuperKernel; | ||
| 402 | + EXPECT_EQ( | ||
| 403 | + KernelCompilationPlanBuilder( | ||
| 404 | + request, CreateManifest(), workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 405 | + .BuildCompilationPlan(plan), | ||
| 406 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 407 | +} | ||
| 408 | + | ||
| 409 | +TEST(KernelCompilationPlanBuilderTest, RejectsMalformedEnvironmentPlaceholders) | ||
| 410 | +{ | ||
| 411 | + KernelPlanTestWorkspace workspace; | ||
| 412 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 413 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 414 | + const uint8_t tilingBytes[] = {0x01, 0x02}; | ||
| 415 | + const void* argumentDataPointers[] = {tilingBytes}; | ||
| 416 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 417 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentDataPointers, argumentByteCounts); | ||
| 418 | + KernelCompilationPlan plan; | ||
| 419 | + | ||
| 420 | + Json manifestWithUnterminatedPlaceholder = CreateManifest(); | ||
| 421 | + manifestWithUnterminatedPlaceholder["kernels"][1]["objects"][0]["commands"][0]["cmd"][2] = | ||
| 422 | + "${env:ACLRTC_TEST_TOOLCHAIN"; | ||
| 423 | + EXPECT_EQ( | ||
| 424 | + KernelCompilationPlanBuilder( | ||
| 425 | + request, manifestWithUnterminatedPlaceholder, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 426 | + .BuildCompilationPlan(plan), | ||
| 427 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 428 | + | ||
| 429 | + Json manifestWithEmptyEnvironmentName = CreateManifest(); | ||
| 430 | + manifestWithEmptyEnvironmentName["kernels"][1]["objects"][0]["commands"][0]["cmd"][2] = "${env:}"; | ||
| 431 | + EXPECT_EQ( | ||
| 432 | + KernelCompilationPlanBuilder( | ||
| 433 | + request, manifestWithEmptyEnvironmentName, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 434 | + .BuildCompilationPlan(plan), | ||
| 435 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 436 | +} | ||
| 437 | + | ||
| 438 | +TEST(KernelCompilationPlanBuilderTest, RejectsUndefinedOptionReference) | ||
| 439 | +{ | ||
| 440 | + KernelPlanTestWorkspace workspace; | ||
| 441 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 442 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 443 | + const uint8_t tilingBytes[] = {0x01, 0x02}; | ||
| 444 | + const void* argumentDataPointers[] = {tilingBytes}; | ||
| 445 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 446 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentDataPointers, argumentByteCounts); | ||
| 447 | + Json manifest = CreateManifest(); | ||
| 448 | + manifest["kernels"][1]["objects"][0]["commands"][0]["cmd"][1] = "${options:missing}"; | ||
| 449 | + | ||
| 450 | + KernelCompilationPlan plan; | ||
| 451 | + EXPECT_EQ( | ||
| 452 | + KernelCompilationPlanBuilder(request, manifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 453 | + .BuildCompilationPlan(plan), | ||
| 454 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 455 | +} | ||
| 456 | + | ||
| 457 | +TEST(KernelCompilationPlanBuilderTest, DoesNotRecursivelyExpandEnvironmentOrUserOptions) | ||
| 458 | +{ | ||
| 459 | + KernelPlanTestWorkspace workspace; | ||
| 460 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 461 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 462 | + ScopedEnvironmentVariable literalEnvironment("ACLRTC_TEST_LITERAL", "${options:not_reexpanded}"); | ||
| 463 | + const uint8_t tilingBytes[] = {0x01, 0x02}; | ||
| 464 | + const void* argumentAddresses[] = {tilingBytes}; | ||
| 465 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 466 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); | ||
| 467 | + request.compilerOptions.basicOptions = {"${env:USER_OPTION_STAYS_LITERAL}"}; | ||
| 468 | + Json manifest = CreateManifest(); | ||
| 469 | + manifest["options"]["common_compile"].push_back("${env:ACLRTC_TEST_LITERAL}"); | ||
| 470 | + | ||
| 471 | + KernelCompilationPlan plan; | ||
| 472 | + ASSERT_EQ( | ||
| 473 | + KernelCompilationPlanBuilder(request, manifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 474 | + .BuildCompilationPlan(plan), | ||
| 475 | + 0); | ||
| 476 | + const std::vector<std::string>& commandArguments = plan.compilationCommands.front().commandArguments; | ||
| 477 | + EXPECT_NE( | ||
| 478 | + std::find(commandArguments.begin(), commandArguments.end(), "${options:not_reexpanded}"), | ||
| 479 | + commandArguments.end()); | ||
| 480 | + EXPECT_NE( | ||
| 481 | + std::find(commandArguments.begin(), commandArguments.end(), "${env:USER_OPTION_STAYS_LITERAL}"), | ||
| 482 | + commandArguments.end()); | ||
| 483 | +} | ||
| 484 | + | ||
| 485 | +TEST(KernelCompilationPlanBuilderTest, AcceptsKernelWithoutLinkOptions) | ||
| 486 | +{ | ||
| 487 | + KernelPlanTestWorkspace workspace; | ||
| 488 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 489 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 490 | + Json manifest = CreateManifest(); | ||
| 491 | + manifest["kernels"][1].erase("link_options"); | ||
| 492 | + const uint8_t tilingBytes[] = {0x01, 0x02}; | ||
| 493 | + const void* argumentAddresses[] = {tilingBytes}; | ||
| 494 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 495 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); | ||
| 496 | + | ||
| 497 | + KernelCompilationPlan plan; | ||
| 498 | + EXPECT_EQ( | ||
| 499 | + KernelCompilationPlanBuilder(request, manifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 500 | + .BuildCompilationPlan(plan), | ||
| 501 | + 0); | ||
| 502 | + EXPECT_EQ(plan.sourcePatches.size(), 1U); | ||
| 503 | + ASSERT_EQ(plan.compilationCommands.size(), 2U); | ||
| 504 | + EXPECT_EQ(plan.compilationCommands.back().commandKind, CompilationCommandKind::Link); | ||
| 505 | +} | ||
| 506 | + | ||
| 507 | +TEST(KernelCompilationPlanBuilderTest, RejectsKernelWithoutStaticConstants) | ||
| 508 | +{ | ||
| 509 | + KernelPlanTestWorkspace workspace; | ||
| 510 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 511 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 512 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, nullptr, nullptr); | ||
| 513 | + KernelCompilationPlan plan; | ||
| 514 | + | ||
| 515 | + Json manifestWithoutConstants = CreateManifest(); | ||
| 516 | + manifestWithoutConstants["kernels"][1].erase("constant_infos"); | ||
| 517 | + EXPECT_EQ( | ||
| 518 | + KernelCompilationPlanBuilder( | ||
| 519 | + request, manifestWithoutConstants, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 520 | + .BuildCompilationPlan(plan), | ||
| 521 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 522 | + | ||
| 523 | + Json manifestWithEmptyConstants = CreateManifest(); | ||
| 524 | + manifestWithEmptyConstants["kernels"][1]["constant_infos"] = Json::array(); | ||
| 525 | + EXPECT_EQ( | ||
| 526 | + KernelCompilationPlanBuilder( | ||
| 527 | + request, manifestWithEmptyConstants, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 528 | + .BuildCompilationPlan(plan), | ||
| 529 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 530 | +} | ||
| 531 | + | ||
| 532 | +TEST(KernelCompilationPlanBuilderTest, RejectsMissingRuntimeConstantBuffer) | ||
| 533 | +{ | ||
| 534 | + KernelPlanTestWorkspace workspace; | ||
| 535 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 536 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 537 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, nullptr, nullptr); | ||
| 538 | + | ||
| 539 | + KernelCompilationPlan plan; | ||
| 540 | + EXPECT_EQ( | ||
| 541 | + KernelCompilationPlanBuilder( | ||
| 542 | + request, CreateManifest(), workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 543 | + .BuildCompilationPlan(plan), | ||
| 544 | + ascendc::aclrtc::ACLRTC_ERROR_INVALID_INPUT); | ||
| 545 | +} | ||
| 546 | + | ||
| 547 | +TEST(KernelCompilationPlanBuilderTest, RejectsMismatchedRuntimeConstantByteCount) | ||
| 548 | +{ | ||
| 549 | + KernelPlanTestWorkspace workspace; | ||
| 550 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 551 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 552 | + const uint8_t oversizedTilingBytes[] = {0x01, 0x02, 0x03}; | ||
| 553 | + const void* argumentDataPointers[] = {oversizedTilingBytes}; | ||
| 554 | + const uint64_t argumentByteCounts[] = {sizeof(oversizedTilingBytes)}; | ||
| 555 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentDataPointers, argumentByteCounts); | ||
| 556 | + | ||
| 557 | + KernelCompilationPlan plan; | ||
| 558 | + EXPECT_EQ( | ||
| 559 | + KernelCompilationPlanBuilder( | ||
| 560 | + request, CreateManifest(), workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 561 | + .BuildCompilationPlan(plan), | ||
| 562 | + ascendc::aclrtc::ACLRTC_ERROR_INVALID_INPUT); | ||
| 563 | +} | ||
| 564 | + | ||
| 565 | +TEST(KernelCompilationPlanBuilderTest, RejectsMissingResourceWorktree) | ||
| 566 | +{ | ||
| 567 | + KernelPlanTestWorkspace workspace; | ||
| 568 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 569 | + const uint8_t constantBytes[] = {0x01, 0x02}; | ||
| 570 | + const void* argumentDataPointers[] = {constantBytes}; | ||
| 571 | + const uint64_t argumentByteCounts[] = {sizeof(constantBytes)}; | ||
| 572 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentDataPointers, argumentByteCounts); | ||
| 573 | + KernelCompilationPlan plan; | ||
| 574 | + | ||
| 575 | + const fs::path missingWorktreePath = workspace.ResourcePath() / "missing"; | ||
| 576 | + EXPECT_EQ( | ||
| 577 | + KernelCompilationPlanBuilder(request, CreateManifest(), missingWorktreePath, workspace.SourceDirectoryPath()) | ||
| 578 | + .BuildCompilationPlan(plan), | ||
| 579 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 580 | +} | ||
| 581 | + | ||
| 582 | +TEST(KernelCompilationPlanBuilderTest, RejectsInvalidRuntimeConstantLocations) | ||
| 583 | +{ | ||
| 584 | + KernelPlanTestWorkspace workspace; | ||
| 585 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 586 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 587 | + const uint8_t constantBytes[] = {0x01, 0x02}; | ||
| 588 | + const void* validArgumentDataPointers[] = {constantBytes}; | ||
| 589 | + const void* nullArgumentDataPointers[] = {nullptr}; | ||
| 590 | + const uint64_t argumentByteCounts[] = {sizeof(constantBytes)}; | ||
| 591 | + KernelCompilationPlan plan; | ||
| 592 | + | ||
| 593 | + Json outOfRangeManifest = CreateManifest(); | ||
| 594 | + outOfRangeManifest["kernels"][1]["constant_infos"][0]["parameter_index"] = 1U; | ||
| 595 | + NormalizedKernelSpecializationRequest request = | ||
| 596 | + CreateRequest(workspace, validArgumentDataPointers, argumentByteCounts); | ||
| 597 | + EXPECT_EQ( | ||
| 598 | + KernelCompilationPlanBuilder( | ||
| 599 | + request, outOfRangeManifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 600 | + .BuildCompilationPlan(plan), | ||
| 601 | + ascendc::aclrtc::ACLRTC_ERROR_INVALID_INPUT); | ||
| 602 | + | ||
| 603 | + request = CreateRequest(workspace, nullArgumentDataPointers, argumentByteCounts); | ||
| 604 | + EXPECT_EQ( | ||
| 605 | + KernelCompilationPlanBuilder( | ||
| 606 | + request, CreateManifest(), workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 607 | + .BuildCompilationPlan(plan), | ||
| 608 | + ascendc::aclrtc::ACLRTC_ERROR_INVALID_INPUT); | ||
| 609 | +} | ||
| 610 | + | ||
| 611 | +TEST(KernelCompilationPlanBuilderTest, RejectsMalformedManifestCommands) | ||
| 612 | +{ | ||
| 613 | + KernelPlanTestWorkspace workspace; | ||
| 614 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 615 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 616 | + const uint8_t constantBytes[] = {0x01, 0x02}; | ||
| 617 | + const void* argumentDataPointers[] = {constantBytes}; | ||
| 618 | + const uint64_t argumentByteCounts[] = {sizeof(constantBytes)}; | ||
| 619 | + const NormalizedKernelSpecializationRequest request = | ||
| 620 | + CreateRequest(workspace, argumentDataPointers, argumentByteCounts); | ||
| 621 | + KernelCompilationPlan plan; | ||
| 622 | + | ||
| 623 | + Json unsupportedCommandManifest = CreateManifest(); | ||
| 624 | + unsupportedCommandManifest["kernels"][1]["objects"][0]["commands"][0]["type"] = "future-command"; | ||
| 625 | + EXPECT_EQ( | ||
| 626 | + KernelCompilationPlanBuilder( | ||
| 627 | + request, unsupportedCommandManifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 628 | + .BuildCompilationPlan(plan), | ||
| 629 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 630 | + | ||
| 631 | + Json emptyCommandManifest = CreateManifest(); | ||
| 632 | + emptyCommandManifest["kernels"][1]["objects"][0]["commands"][0]["cmd"] = Json::array(); | ||
| 633 | + EXPECT_EQ( | ||
| 634 | + KernelCompilationPlanBuilder( | ||
| 635 | + request, emptyCommandManifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 636 | + .BuildCompilationPlan(plan), | ||
| 637 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 638 | + | ||
| 639 | + Json expandedExecutableManifest = CreateManifest(); | ||
| 640 | + expandedExecutableManifest["kernels"][1]["objects"][0]["commands"][0]["cmd"][0] = "${options:common_compile}"; | ||
| 641 | + EXPECT_EQ( | ||
| 642 | + KernelCompilationPlanBuilder( | ||
| 643 | + request, expandedExecutableManifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 644 | + .BuildCompilationPlan(plan), | ||
| 645 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 646 | + | ||
| 647 | + Json relativeExecutableManifest = CreateManifest(); | ||
| 648 | + relativeExecutableManifest["kernels"][1]["objects"][0]["commands"][0]["cmd"][0] = "bisheng"; | ||
| 649 | + EXPECT_EQ( | ||
| 650 | + KernelCompilationPlanBuilder( | ||
| 651 | + request, relativeExecutableManifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 652 | + .BuildCompilationPlan(plan), | ||
| 653 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 654 | +} | ||
| 655 | + | ||
| 656 | +TEST(KernelCompilationPlanBuilderTest, RequiresBasicAndRequestedSuperKernelObjects) | ||
| 657 | +{ | ||
| 658 | + KernelPlanTestWorkspace workspace; | ||
| 659 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 660 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 661 | + ScopedEnvironmentVariable skToolchainEnvironment("ACLRTC_TEST_SK_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 662 | + const uint8_t constantBytes[] = {0x01, 0x02}; | ||
| 663 | + const void* argumentDataPointers[] = {constantBytes}; | ||
| 664 | + const uint64_t argumentByteCounts[] = {sizeof(constantBytes)}; | ||
| 665 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentDataPointers, argumentByteCounts); | ||
| 666 | + KernelCompilationPlan plan; | ||
| 667 | + | ||
| 668 | + Json manifestWithoutBasic = CreateManifest(); | ||
| 669 | + manifestWithoutBasic["kernels"][1]["objects"].erase(0); | ||
| 670 | + EXPECT_EQ( | ||
| 671 | + KernelCompilationPlanBuilder( | ||
| 672 | + request, manifestWithoutBasic, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 673 | + .BuildCompilationPlan(plan), | ||
| 674 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 675 | + | ||
| 676 | + request.compilationVariant = CompilationVariant::BasicWithSuperKernel; | ||
| 677 | + Json manifestWithoutSuperKernel = CreateManifest(); | ||
| 678 | + manifestWithoutSuperKernel["kernels"][1]["objects"].erase(1); | ||
| 679 | + EXPECT_EQ( | ||
| 680 | + KernelCompilationPlanBuilder( | ||
| 681 | + request, manifestWithoutSuperKernel, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 682 | + .BuildCompilationPlan(plan), | ||
| 683 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 684 | +} | ||
| 685 | + | ||
| 686 | +TEST(KernelCompilationPlanBuilderTest, RejectsUnresolvableLinkInputs) | ||
| 687 | +{ | ||
| 688 | + KernelPlanTestWorkspace workspace; | ||
| 689 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 690 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 691 | + const uint8_t constantBytes[] = {0x01, 0x02}; | ||
| 692 | + const void* argumentDataPointers[] = {constantBytes}; | ||
| 693 | + const uint64_t argumentByteCounts[] = {sizeof(constantBytes)}; | ||
| 694 | + const NormalizedKernelSpecializationRequest request = | ||
| 695 | + CreateRequest(workspace, argumentDataPointers, argumentByteCounts); | ||
| 696 | + KernelCompilationPlan plan; | ||
| 697 | + unsetenv("ACLRTC_TEST_MISSING_LINK_INPUT"); | ||
| 698 | + | ||
| 699 | + Json unresolvedLinkOptionManifest = CreateManifest(); | ||
| 700 | + unresolvedLinkOptionManifest["kernels"][1]["link_options"] = Json::array({"${env:ACLRTC_TEST_MISSING_LINK_INPUT}"}); | ||
| 701 | + EXPECT_EQ( | ||
| 702 | + KernelCompilationPlanBuilder( | ||
| 703 | + request, unresolvedLinkOptionManifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 704 | + .BuildCompilationPlan(plan), | ||
| 705 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 706 | + | ||
| 707 | + Json unresolvedObjectOutputManifest = CreateManifest(); | ||
| 708 | + unresolvedObjectOutputManifest["kernels"][1]["objects"][0]["outputs"][0] = "${env:ACLRTC_TEST_MISSING_LINK_INPUT}"; | ||
| 709 | + EXPECT_EQ( | ||
| 710 | + KernelCompilationPlanBuilder( | ||
| 711 | + request, unresolvedObjectOutputManifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 712 | + .BuildCompilationPlan(plan), | ||
| 713 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 714 | + | ||
| 715 | + ScopedEnvironmentVariable missingLinkerEnvironment( | ||
| 716 | + "ASCEND_HOME_PATH", (workspace.ResourcePath() / "missing-toolchain").string()); | ||
| 717 | + EXPECT_EQ( | ||
| 718 | + KernelCompilationPlanBuilder( | ||
| 719 | + request, CreateManifest(), workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 720 | + .BuildCompilationPlan(plan), | ||
| 721 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 722 | +} | ||
| 723 | + | ||
| 724 | +TEST(KernelCompilationPlanBuilderTest, RejectsInvalidManifestSelectionInputs) | ||
| 725 | +{ | ||
| 726 | + KernelPlanTestWorkspace workspace; | ||
| 727 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 728 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 729 | + const uint8_t constantBytes[] = {0x01, 0x02}; | ||
| 730 | + const void* argumentDataPointers[] = {constantBytes}; | ||
| 731 | + const uint64_t argumentByteCounts[] = {sizeof(constantBytes)}; | ||
| 732 | + NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentDataPointers, argumentByteCounts); | ||
| 733 | + KernelCompilationPlan plan; | ||
| 734 | + | ||
| 735 | + EXPECT_EQ( | ||
| 736 | + KernelCompilationPlanBuilder(request, CreateManifest(), "relative-worktree", workspace.SourceDirectoryPath()) | ||
| 737 | + .BuildCompilationPlan(plan), | ||
| 738 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 739 | + | ||
| 740 | + Json unsupportedSchemaManifest = CreateManifest(); | ||
| 741 | + unsupportedSchemaManifest["schema_version"] = "2.0"; | ||
| 742 | + EXPECT_EQ( | ||
| 743 | + KernelCompilationPlanBuilder( | ||
| 744 | + request, unsupportedSchemaManifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 745 | + .BuildCompilationPlan(plan), | ||
| 746 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 747 | + | ||
| 748 | + request.kernelName = "missing_kernel"; | ||
| 749 | + EXPECT_EQ( | ||
| 750 | + KernelCompilationPlanBuilder( | ||
| 751 | + request, CreateManifest(), workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 752 | + .BuildCompilationPlan(plan), | ||
| 753 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 754 | + | ||
| 755 | + request.kernelName = "add_custom_100000"; | ||
| 756 | + Json malformedSchemaManifest = CreateManifest(); | ||
| 757 | + malformedSchemaManifest["schema_version"] = Json::array(); | ||
| 758 | + EXPECT_EQ( | ||
| 759 | + KernelCompilationPlanBuilder( | ||
| 760 | + request, malformedSchemaManifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 761 | + .BuildCompilationPlan(plan), | ||
| 762 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 763 | +} | ||
| 764 | + | ||
| 765 | +TEST(KernelCompilationPlanBuilderTest, RejectsMissingEnvironmentVariablesAtEachExpansionLayer) | ||
| 766 | +{ | ||
| 767 | + KernelPlanTestWorkspace workspace; | ||
| 768 | + ScopedEnvironmentVariable toolchainEnvironment("ACLRTC_TEST_TOOLCHAIN", workspace.ToolchainPath().string()); | ||
| 769 | + ScopedEnvironmentVariable missingEnvironment("ACLRTC_TEST_MISSING_ENV", "temporarily-set"); | ||
| 770 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", workspace.ToolchainPath().string()); | ||
| 771 | + ASSERT_EQ(unsetenv("ACLRTC_TEST_MISSING_ENV"), 0); | ||
| 772 | + const uint8_t tilingBytes[] = {0x01, 0x02}; | ||
| 773 | + const void* argumentAddresses[] = {tilingBytes}; | ||
| 774 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 775 | + const NormalizedKernelSpecializationRequest request = | ||
| 776 | + CreateRequest(workspace, argumentAddresses, argumentByteCounts); | ||
| 777 | + | ||
| 778 | + Json manifest = CreateManifest(); | ||
| 779 | + manifest["options"]["common_compile"] = Json::array({"${env:ACLRTC_TEST_MISSING_ENV}/include"}); | ||
| 780 | + KernelCompilationPlan plan; | ||
| 781 | + EXPECT_NE( | ||
| 782 | + KernelCompilationPlanBuilder(request, manifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 783 | + .BuildCompilationPlan(plan), | ||
| 784 | + ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 785 | + | ||
| 786 | + manifest = CreateManifest(); | ||
| 787 | + manifest["kernels"][1]["constant_infos"][0]["file"] = "${env:ACLRTC_TEST_MISSING_ENV}/constants.h"; | ||
| 788 | + EXPECT_NE( | ||
| 789 | + KernelCompilationPlanBuilder(request, manifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 790 | + .BuildCompilationPlan(plan), | ||
| 791 | + ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 792 | + | ||
| 793 | + ASSERT_EQ(unsetenv("ASCEND_HOME_PATH"), 0); | ||
| 794 | + manifest = CreateManifest(); | ||
| 795 | + EXPECT_NE( | ||
| 796 | + KernelCompilationPlanBuilder(request, manifest, workspace.ResourcePath(), workspace.SourceDirectoryPath()) | ||
| 797 | + .BuildCompilationPlan(plan), | ||
| 798 | + ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 799 | +} | ||
| 800 | + | ||
| 801 | +TEST(KernelCompilationPlanBuilderTest, ReportsResourceWorktreeInspectionErrors) | ||
| 802 | +{ | ||
| 803 | + KernelPlanTestWorkspace workspace; | ||
| 804 | + const fs::path selfReferentialLink = workspace.ResourcePath().parent_path() / "worktree-link-loop"; | ||
| 805 | + boost::system::error_code symlinkError; | ||
| 806 | + fs::create_symlink(selfReferentialLink.filename(), selfReferentialLink, symlinkError); | ||
| 807 | + ASSERT_FALSE(symlinkError); | ||
| 808 | + const uint8_t tilingBytes[] = {0x01, 0x02}; | ||
| 809 | + const void* argumentAddresses[] = {tilingBytes}; | ||
| 810 | + const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | ||
| 811 | + const NormalizedKernelSpecializationRequest request = | ||
| 812 | + CreateRequest(workspace, argumentAddresses, argumentByteCounts); | ||
| 813 | + | ||
| 814 | + KernelCompilationPlan plan; | ||
| 815 | + EXPECT_NE( | ||
| 816 | + KernelCompilationPlanBuilder(request, CreateManifest(), selfReferentialLink, workspace.SourceDirectoryPath()) | ||
| 817 | + .BuildCompilationPlan(plan), | ||
| 818 | + ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 819 | +} | ||
| 820 | +} // namespace | ||
| @@ -0,0 +1,286 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | +namespace { | ||
| 24 | +namespace fs = boost::filesystem; | ||
| 25 | +using ascendc::aclrtc::KernelCompilationPlan; | ||
| 26 | +using ascendc::aclrtc::KernelCompilationWorkspace; | ||
| 27 | +using ascendc::aclrtc::WorktreeRetentionPolicy; | ||
| 28 | + | ||
| 29 | +fs::path CreateWorkspacePath(const std::string& name) | ||
| 30 | +{ | ||
| 31 | + static uint64_t sequence = 0; | ||
| 32 | + const fs::path path = | ||
| 33 | + fs::temp_directory_path() / (name + "_" + std::to_string(getpid()) + "_" + std::to_string(sequence++)); | ||
| 34 | + fs::create_directories(path); | ||
| 35 | + return path; | ||
| 36 | +} | ||
| 37 | + | ||
| 38 | +void WriteFile(const fs::path& path, const std::string& text) | ||
| 39 | +{ | ||
| 40 | + fs::create_directories(path.parent_path()); | ||
| 41 | + std::ofstream stream(path.string()); | ||
| 42 | + stream << text; | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | +std::string ReadFile(const fs::path& path) | ||
| 46 | +{ | ||
| 47 | + std::ifstream stream(path.string()); | ||
| 48 | + return std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>()); | ||
| 49 | +} | ||
| 50 | + | ||
| 51 | +TEST(KernelCompilationWorkspaceTest, CreatesOutputDirectoriesAndAppliesAllPatchesPerFile) | ||
| 52 | +{ | ||
| 53 | + const fs::path worktreePath = CreateWorkspacePath("aclrtc_workspace_patch"); | ||
| 54 | + const fs::path sourcePath = worktreePath / "resources/constants.h"; | ||
| 55 | + WriteFile(sourcePath, "A=@@A@@; B=@@B@@;\n"); | ||
| 56 | + KernelCompilationPlan plan; | ||
| 57 | + plan.requiredOutputDirectoryPaths = {worktreePath / "outputs/nested"}; | ||
| 58 | + plan.sourcePatches = { | ||
| 59 | + {sourcePath, "@@A@@", "{0x01}"}, | ||
| 60 | + {sourcePath, "@@B@@", "{0x02}"}, | ||
| 61 | + }; | ||
| 62 | + | ||
| 63 | + KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 64 | + EXPECT_EQ(workspace.CreateOutputDirectoriesAndApplySourcePatches(plan), 0); | ||
| 65 | + EXPECT_TRUE(fs::is_directory(worktreePath / "outputs/nested")); | ||
| 66 | + EXPECT_EQ(ReadFile(sourcePath), "A={0x01}; B={0x02};\n"); | ||
| 67 | + | ||
| 68 | + boost::system::error_code ignoredError; | ||
| 69 | + fs::remove_all(worktreePath, ignoredError); | ||
| 70 | +} | ||
| 71 | + | ||
| 72 | +TEST(KernelCompilationWorkspaceTest, ReplacesEveryOccurrenceOfTheSameSourceTemplate) | ||
| 73 | +{ | ||
| 74 | + const fs::path worktreePath = CreateWorkspacePath("aclrtc_workspace_repeated_template"); | ||
| 75 | + const fs::path sourcePath = worktreePath / "resources/constants.h"; | ||
| 76 | + WriteFile(sourcePath, "FIRST=@@VALUE@@; SECOND=@@VALUE@@;\n"); | ||
| 77 | + KernelCompilationPlan plan; | ||
| 78 | + plan.sourcePatches = {{sourcePath, "@@VALUE@@", "{0x01}"}}; | ||
| 79 | + | ||
| 80 | + KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 81 | + EXPECT_EQ(workspace.CreateOutputDirectoriesAndApplySourcePatches(plan), ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 82 | + EXPECT_EQ(ReadFile(sourcePath), "FIRST={0x01}; SECOND={0x01};\n"); | ||
| 83 | + | ||
| 84 | + boost::system::error_code ignoredError; | ||
| 85 | + fs::remove_all(worktreePath, ignoredError); | ||
| 86 | +} | ||
| 87 | + | ||
| 88 | +TEST(KernelCompilationWorkspaceTest, FailsWhenPatchTemplateIsMissing) | ||
| 89 | +{ | ||
| 90 | + const fs::path worktreePath = CreateWorkspacePath("aclrtc_workspace_missing_template"); | ||
| 91 | + const fs::path sourcePath = worktreePath / "resources/constants.h"; | ||
| 92 | + WriteFile(sourcePath, "unchanged\n"); | ||
| 93 | + KernelCompilationPlan plan; | ||
| 94 | + plan.sourcePatches = {{sourcePath, "@@MISSING@@", "{0x01}"}}; | ||
| 95 | + | ||
| 96 | + KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 97 | + EXPECT_EQ(workspace.CreateOutputDirectoriesAndApplySourcePatches(plan), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 98 | + EXPECT_EQ(ReadFile(sourcePath), "unchanged\n"); | ||
| 99 | + | ||
| 100 | + boost::system::error_code ignoredError; | ||
| 101 | + fs::remove_all(worktreePath, ignoredError); | ||
| 102 | +} | ||
| 103 | + | ||
| 104 | +TEST(KernelCompilationWorkspaceTest, FailsWhenPatchFileIsMissing) | ||
| 105 | +{ | ||
| 106 | + const fs::path worktreePath = CreateWorkspacePath("aclrtc_workspace_missing_file"); | ||
| 107 | + const fs::path missingSourcePath = worktreePath / "resources/missing_constants.h"; | ||
| 108 | + KernelCompilationPlan plan; | ||
| 109 | + plan.sourcePatches = {{missingSourcePath, "@@MISSING@@", "{0x01}"}}; | ||
| 110 | + | ||
| 111 | + KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 112 | + EXPECT_EQ(workspace.CreateOutputDirectoriesAndApplySourcePatches(plan), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 113 | + | ||
| 114 | + boost::system::error_code ignoredError; | ||
| 115 | + fs::remove_all(worktreePath, ignoredError); | ||
| 116 | +} | ||
| 117 | + | ||
| 118 | +TEST(KernelCompilationWorkspaceTest, FailsWhenPatchSourceCannotBeRead) | ||
| 119 | +{ | ||
| 120 | + const fs::path worktreePath = CreateWorkspacePath("aclrtc_workspace_unreadable_source"); | ||
| 121 | + const fs::path directoryUsedAsSource = worktreePath / "resources/constants.h"; | ||
| 122 | + fs::create_directories(directoryUsedAsSource); | ||
| 123 | + KernelCompilationPlan plan; | ||
| 124 | + plan.sourcePatches = {{directoryUsedAsSource, "@@VALUE@@", "{0x01}"}}; | ||
| 125 | + | ||
| 126 | + KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 127 | + EXPECT_EQ(workspace.CreateOutputDirectoriesAndApplySourcePatches(plan), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 128 | + EXPECT_TRUE(fs::is_directory(directoryUsedAsSource)); | ||
| 129 | + | ||
| 130 | + boost::system::error_code ignoredError; | ||
| 131 | + fs::remove_all(worktreePath, ignoredError); | ||
| 132 | +} | ||
| 133 | + | ||
| 134 | +TEST(KernelCompilationWorkspaceTest, ReportsSourceReadFailureAfterFileOpen) | ||
| 135 | +{ | ||
| 136 | + KernelCompilationPlan plan; | ||
| 137 | + plan.sourcePatches = {{"/proc/self/mem", "@@VALUE@@", "{0x01}"}}; | ||
| 138 | + | ||
| 139 | + KernelCompilationWorkspace workspace({}, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 140 | + EXPECT_EQ(workspace.CreateOutputDirectoriesAndApplySourcePatches(plan), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 141 | +} | ||
| 142 | + | ||
| 143 | +TEST(KernelCompilationWorkspaceTest, PreservesOriginalSourceWhenFinalPatchFlushFails) | ||
| 144 | +{ | ||
| 145 | + const fs::path worktreePath = CreateWorkspacePath("aclrtc_workspace_patch_flush_failure"); | ||
| 146 | + const fs::path sourcePath = worktreePath / "resources/constants.h"; | ||
| 147 | + WriteFile(sourcePath, "VALUE=@@VALUE@@;\n"); | ||
| 148 | + KernelCompilationPlan plan; | ||
| 149 | + plan.sourcePatches = {{sourcePath, "@@VALUE@@", "{0x01}"}}; | ||
| 150 | + | ||
| 151 | + struct rlimit originalFileSizeLimit {}; | ||
| 152 | + ASSERT_EQ(getrlimit(RLIMIT_FSIZE, &originalFileSizeLimit), 0); | ||
| 153 | + struct sigaction ignoreFileSizeSignal {}; | ||
| 154 | + struct sigaction originalFileSizeSignal {}; | ||
| 155 | + ignoreFileSizeSignal.sa_handler = SIG_IGN; | ||
| 156 | + ASSERT_EQ(sigemptyset(&ignoreFileSizeSignal.sa_mask), 0); | ||
| 157 | + ASSERT_EQ(sigaction(SIGXFSZ, &ignoreFileSizeSignal, &originalFileSizeSignal), 0); | ||
| 158 | + struct rlimit zeroFileSizeLimit = originalFileSizeLimit; | ||
| 159 | + zeroFileSizeLimit.rlim_cur = 0; | ||
| 160 | + ASSERT_EQ(setrlimit(RLIMIT_FSIZE, &zeroFileSizeLimit), 0); | ||
| 161 | + | ||
| 162 | + KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 163 | + const aclError patchResult = workspace.CreateOutputDirectoriesAndApplySourcePatches(plan); | ||
| 164 | + | ||
| 165 | + const int restoreLimitResult = setrlimit(RLIMIT_FSIZE, &originalFileSizeLimit); | ||
| 166 | + const int restoreSignalResult = sigaction(SIGXFSZ, &originalFileSizeSignal, nullptr); | ||
| 167 | + ASSERT_EQ(restoreLimitResult, 0); | ||
| 168 | + ASSERT_EQ(restoreSignalResult, 0); | ||
| 169 | + EXPECT_EQ(patchResult, ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 170 | + EXPECT_EQ(ReadFile(sourcePath), "VALUE=@@VALUE@@;\n"); | ||
| 171 | + | ||
| 172 | + boost::system::error_code ignoredError; | ||
| 173 | + fs::remove_all(worktreePath, ignoredError); | ||
| 174 | +} | ||
| 175 | + | ||
| 176 | +TEST(KernelCompilationWorkspaceTest, PreservesOriginalSourceWhenLargePatchWriteFails) | ||
| 177 | +{ | ||
| 178 | + const fs::path worktreePath = CreateWorkspacePath("aclrtc_workspace_patch_write_failure"); | ||
| 179 | + const fs::path sourcePath = worktreePath / "resources/constants.h"; | ||
| 180 | + WriteFile(sourcePath, "VALUE=@@VALUE@@;\n"); | ||
| 181 | + KernelCompilationPlan plan; | ||
| 182 | + plan.sourcePatches = {{sourcePath, "@@VALUE@@", std::string(64U * 1024U, 'x')}}; | ||
| 183 | + | ||
| 184 | + struct rlimit originalFileSizeLimit {}; | ||
| 185 | + ASSERT_EQ(getrlimit(RLIMIT_FSIZE, &originalFileSizeLimit), 0); | ||
| 186 | + struct sigaction ignoreFileSizeSignal {}; | ||
| 187 | + struct sigaction originalFileSizeSignal {}; | ||
| 188 | + ignoreFileSizeSignal.sa_handler = SIG_IGN; | ||
| 189 | + ASSERT_EQ(sigemptyset(&ignoreFileSizeSignal.sa_mask), 0); | ||
| 190 | + ASSERT_EQ(sigaction(SIGXFSZ, &ignoreFileSizeSignal, &originalFileSizeSignal), 0); | ||
| 191 | + struct rlimit zeroFileSizeLimit = originalFileSizeLimit; | ||
| 192 | + zeroFileSizeLimit.rlim_cur = 0U; | ||
| 193 | + ASSERT_EQ(setrlimit(RLIMIT_FSIZE, &zeroFileSizeLimit), 0); | ||
| 194 | + | ||
| 195 | + KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 196 | + const aclError patchResult = workspace.CreateOutputDirectoriesAndApplySourcePatches(plan); | ||
| 197 | + | ||
| 198 | + const int restoreLimitResult = setrlimit(RLIMIT_FSIZE, &originalFileSizeLimit); | ||
| 199 | + const int restoreSignalResult = sigaction(SIGXFSZ, &originalFileSizeSignal, nullptr); | ||
| 200 | + ASSERT_EQ(restoreLimitResult, 0); | ||
| 201 | + ASSERT_EQ(restoreSignalResult, 0); | ||
| 202 | + EXPECT_EQ(patchResult, ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 203 | + EXPECT_EQ(ReadFile(sourcePath), "VALUE=@@VALUE@@;\n"); | ||
| 204 | + | ||
| 205 | + boost::system::error_code ignoredError; | ||
| 206 | + fs::remove_all(worktreePath, ignoredError); | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +TEST(KernelCompilationWorkspaceTest, DeletesWorktreeByDefaultAndRetainsItWhenRequested) | ||
| 210 | +{ | ||
| 211 | + const fs::path removedWorktreePath = CreateWorkspacePath("aclrtc_workspace_remove"); | ||
| 212 | + { | ||
| 213 | + KernelCompilationWorkspace workspace(removedWorktreePath, WorktreeRetentionPolicy::RemoveAfterCompilation); | ||
| 214 | + } | ||
| 215 | + EXPECT_FALSE(fs::exists(removedWorktreePath)); | ||
| 216 | + | ||
| 217 | + const fs::path retainedWorktreePath = CreateWorkspacePath("aclrtc_workspace_retain"); | ||
| 218 | + { | ||
| 219 | + KernelCompilationWorkspace workspace(retainedWorktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 220 | + } | ||
| 221 | + EXPECT_TRUE(fs::is_directory(retainedWorktreePath)); | ||
| 222 | + boost::system::error_code ignoredError; | ||
| 223 | + fs::remove_all(retainedWorktreePath, ignoredError); | ||
| 224 | +} | ||
| 225 | + | ||
| 226 | +TEST(KernelCompilationWorkspaceTest, RemovesWorktreeBeforeOutputPublicationWhenNotRetained) | ||
| 227 | +{ | ||
| 228 | + const fs::path worktreePath = CreateWorkspacePath("aclrtc_workspace_prepublish_cleanup"); | ||
| 229 | + KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RemoveAfterCompilation); | ||
| 230 | + EXPECT_EQ(workspace.RemoveWorktreeBeforePublishingElfIfNeeded(), 0); | ||
| 231 | + EXPECT_FALSE(fs::exists(worktreePath)); | ||
| 232 | +} | ||
| 233 | + | ||
| 234 | +TEST(KernelCompilationWorkspaceTest, FailsWhenOutputDirectoryConflictsWithExistingFile) | ||
| 235 | +{ | ||
| 236 | + const fs::path worktreePath = CreateWorkspacePath("aclrtc_workspace_output_conflict"); | ||
| 237 | + const fs::path conflictingOutputPath = worktreePath / "outputs"; | ||
| 238 | + WriteFile(conflictingOutputPath, "not a directory"); | ||
| 239 | + KernelCompilationPlan plan; | ||
| 240 | + plan.requiredOutputDirectoryPaths = {conflictingOutputPath / "nested"}; | ||
| 241 | + | ||
| 242 | + KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 243 | + EXPECT_EQ(workspace.CreateOutputDirectoriesAndApplySourcePatches(plan), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 244 | + EXPECT_TRUE(fs::is_regular_file(conflictingOutputPath)); | ||
| 245 | + | ||
| 246 | + boost::system::error_code ignoredError; | ||
| 247 | + fs::remove_all(worktreePath, ignoredError); | ||
| 248 | +} | ||
| 249 | + | ||
| 250 | +TEST(KernelCompilationWorkspaceTest, PreservesSourceWhenTemporaryPatchFilenameIsTooLong) | ||
| 251 | +{ | ||
| 252 | + const fs::path worktreePath = CreateWorkspacePath("aclrtc_workspace_long_patch_name"); | ||
| 253 | + const fs::path sourcePath = worktreePath / std::string(245U, 'a'); | ||
| 254 | + WriteFile(sourcePath, "VALUE=@@VALUE@@;\n"); | ||
| 255 | + KernelCompilationPlan plan; | ||
| 256 | + plan.sourcePatches = {{sourcePath, "@@VALUE@@", "{0x01}"}}; | ||
| 257 | + | ||
| 258 | + KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 259 | + EXPECT_EQ(workspace.CreateOutputDirectoriesAndApplySourcePatches(plan), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 260 | + EXPECT_EQ(ReadFile(sourcePath), "VALUE=@@VALUE@@;\n"); | ||
| 261 | + | ||
| 262 | + boost::system::error_code ignoredError; | ||
| 263 | + fs::remove_all(worktreePath, ignoredError); | ||
| 264 | +} | ||
| 265 | + | ||
| 266 | +TEST(KernelCompilationWorkspaceTest, PrepublicationCleanupNoOpsForEmptyAndRetainedWorktrees) | ||
| 267 | +{ | ||
| 268 | + KernelCompilationWorkspace emptyWorkspace({}, WorktreeRetentionPolicy::RemoveAfterCompilation); | ||
| 269 | + EXPECT_EQ(emptyWorkspace.RemoveWorktreeBeforePublishingElfIfNeeded(), ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 270 | + | ||
| 271 | + const fs::path retainedWorktreePath = CreateWorkspacePath("aclrtc_workspace_retained_prepublish"); | ||
| 272 | + KernelCompilationWorkspace retainedWorkspace(retainedWorktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 273 | + EXPECT_EQ(retainedWorkspace.RemoveWorktreeBeforePublishingElfIfNeeded(), ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 274 | + EXPECT_TRUE(fs::is_directory(retainedWorktreePath)); | ||
| 275 | + | ||
| 276 | + boost::system::error_code ignoredError; | ||
| 277 | + fs::remove_all(retainedWorktreePath, ignoredError); | ||
| 278 | +} | ||
| 279 | + | ||
| 280 | +TEST(KernelCompilationWorkspaceTest, ReportsWorktreeRemovalFailure) | ||
| 281 | +{ | ||
| 282 | + KernelCompilationWorkspace workspace("/proc/self/status", WorktreeRetentionPolicy::RemoveAfterCompilation); | ||
| 283 | + EXPECT_EQ(workspace.RemoveWorktreeBeforePublishingElfIfNeeded(), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 284 | + EXPECT_EQ(workspace.GetWorktreePath(), "/proc/self/status"); | ||
| 285 | +} | ||
| 286 | +} // namespace | ||
| @@ -0,0 +1,308 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | +namespace { | ||
| 28 | +constexpr aclError ACL_ERROR_RTC_INVALID_INPUT = 176001; | ||
| 29 | +constexpr aclError ACL_ERROR_RTC_INVALID_OPTION = 176002; | ||
| 30 | +constexpr char RESOURCE_ID[] = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; | ||
| 31 | + | ||
| 32 | +aclrtcKernelSpecRequest CreateValidRequest() | ||
| 33 | +{ | ||
| 34 | + aclrtcKernelSpecRequest request{}; | ||
| 35 | + request.resourceId = RESOURCE_ID; | ||
| 36 | + request.kernelEntry = "add_custom_100000"; | ||
| 37 | + return request; | ||
| 38 | +} | ||
| 39 | + | ||
| 40 | +TEST(KernelSpecializationApiTest, RequestLayoutIsStableAndExtensible) | ||
| 41 | +{ | ||
| 42 | + static_assert(std::is_standard_layout_v<aclrtcKernelSpecRequest>); | ||
| 43 | + static_assert(std::extent_v<decltype(aclrtcKernelSpecRequest::reserved)> == 256); | ||
| 44 | + static_assert(std::is_same_v<decltype(aclrtcKernelSpecRequest::argsCount), uint64_t>); | ||
| 45 | + static_assert(std::is_same_v<decltype(aclrtcKernelSpecRequest::optionCount), uint64_t>); | ||
| 46 | + static_assert(std::is_same_v<decltype(aclrtcKernelSpecRequest::skOptionCount), uint64_t>); | ||
| 47 | + static_assert(offsetof(aclrtcKernelSpecRequest, resourceId) == 0U); | ||
| 48 | + static_assert(offsetof(aclrtcKernelSpecRequest, kernelEntry) == 8U); | ||
| 49 | + static_assert(offsetof(aclrtcKernelSpecRequest, argsCount) == 16U); | ||
| 50 | + static_assert(offsetof(aclrtcKernelSpecRequest, options) == 40U); | ||
| 51 | + static_assert(offsetof(aclrtcKernelSpecRequest, skOptions) == 56U); | ||
| 52 | + static_assert(offsetof(aclrtcKernelSpecRequest, reserved) == 72U); | ||
| 53 | + static_assert(sizeof(aclrtcKernelSpecRequest) == 328U); | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +TEST(KernelSpecializationApiTest, PublicApiVisibilityMacroIsEmptyForConsumers) | ||
| 57 | +{ | ||
| 58 | + EXPECT_STREQ(ACLRTC_STRINGIFY(ACLRTC_API), ""); | ||
| 59 | +} | ||
| 60 | + | ||
| 61 | +TEST(KernelSpecializationRequestTest, NormalizesBasicRequestAndCopiesOptions) | ||
| 62 | +{ | ||
| 63 | + const char* options[] = {"-g", "-DVALUE=1"}; | ||
| 64 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 65 | + request.options = options; | ||
| 66 | + request.optionCount = 2; | ||
| 67 | + | ||
| 68 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 69 | + EXPECT_EQ(ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), 0); | ||
| 70 | + EXPECT_EQ(normalizedRequest.resourceId, RESOURCE_ID); | ||
| 71 | + EXPECT_EQ(normalizedRequest.kernelName, "add_custom_100000"); | ||
| 72 | + EXPECT_EQ(normalizedRequest.outputElfPath, "/tmp/kernel.elf"); | ||
| 73 | + EXPECT_EQ(normalizedRequest.compilationVariant, ascendc::aclrtc::CompilationVariant::Basic); | ||
| 74 | + EXPECT_EQ(normalizedRequest.compilerOptions.basicOptions, (std::vector<std::string>{"-g", "-DVALUE=1"})); | ||
| 75 | + EXPECT_TRUE(normalizedRequest.compilerOptions.superKernelOptions.empty()); | ||
| 76 | +} | ||
| 77 | + | ||
| 78 | +TEST(KernelSpecializationRequestTest, AcceptsOpaqueResourceIdAndNormalizesRelativeOutputPath) | ||
| 79 | +{ | ||
| 80 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 81 | + request.resourceId = "vendor/operator/resource-v1"; | ||
| 82 | + | ||
| 83 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 84 | + ASSERT_EQ( | ||
| 85 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "relative/kernel.elf", normalizedRequest), 0); | ||
| 86 | + EXPECT_EQ(normalizedRequest.resourceId, request.resourceId); | ||
| 87 | + EXPECT_EQ(normalizedRequest.outputElfPath, boost::filesystem::absolute("relative/kernel.elf").lexically_normal()); | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +TEST(KernelSpecializationRequestTest, EnablesSuperKernelAndRemovesControlOption) | ||
| 91 | +{ | ||
| 92 | + const char* skOptions[] = {"-g", "--enable-super-kernel", "-DDCCI_OFF"}; | ||
| 93 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 94 | + request.skOptions = skOptions; | ||
| 95 | + request.skOptionCount = 3; | ||
| 96 | + | ||
| 97 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 98 | + EXPECT_EQ(ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), 0); | ||
| 99 | + EXPECT_EQ(normalizedRequest.compilationVariant, ascendc::aclrtc::CompilationVariant::BasicWithSuperKernel); | ||
| 100 | + EXPECT_EQ(normalizedRequest.compilerOptions.superKernelOptions, (std::vector<std::string>{"-g", "-DDCCI_OFF"})); | ||
| 101 | +} | ||
| 102 | + | ||
| 103 | +TEST(KernelSpecializationRequestTest, IgnoresSuperKernelOptionsWithoutEnable) | ||
| 104 | +{ | ||
| 105 | + const char* skOptions[] = {"-g", "-oignored"}; | ||
| 106 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 107 | + request.skOptions = skOptions; | ||
| 108 | + request.skOptionCount = 2; | ||
| 109 | + | ||
| 110 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 111 | + EXPECT_EQ(ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), 0); | ||
| 112 | + EXPECT_EQ(normalizedRequest.compilationVariant, ascendc::aclrtc::CompilationVariant::Basic); | ||
| 113 | + EXPECT_TRUE(normalizedRequest.compilerOptions.superKernelOptions.empty()); | ||
| 114 | +} | ||
| 115 | + | ||
| 116 | +TEST(KernelSpecializationRequestTest, RejectsInvalidAbiFields) | ||
| 117 | +{ | ||
| 118 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 119 | + | ||
| 120 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 121 | + EXPECT_EQ( | ||
| 122 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "", normalizedRequest), | ||
| 123 | + ACL_ERROR_RTC_INVALID_INPUT); | ||
| 124 | + | ||
| 125 | + request = CreateValidRequest(); | ||
| 126 | + request.resourceId = ""; | ||
| 127 | + EXPECT_EQ( | ||
| 128 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 129 | + ACL_ERROR_RTC_INVALID_INPUT); | ||
| 130 | + | ||
| 131 | + request = CreateValidRequest(); | ||
| 132 | + request.reserved[1] = 1; | ||
| 133 | + EXPECT_EQ( | ||
| 134 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 135 | + ACL_ERROR_RTC_INVALID_INPUT); | ||
| 136 | + | ||
| 137 | + request = CreateValidRequest(); | ||
| 138 | + request.optionCount = 1; | ||
| 139 | + EXPECT_EQ( | ||
| 140 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 141 | + ACL_ERROR_RTC_INVALID_INPUT); | ||
| 142 | +} | ||
| 143 | + | ||
| 144 | +TEST(KernelSpecializationRequestTest, RejectsSpecificRequestValidationFailures) | ||
| 145 | +{ | ||
| 146 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 147 | + | ||
| 148 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 149 | + request.reserved[7] = 1U; | ||
| 150 | + EXPECT_EQ( | ||
| 151 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 152 | + ACL_ERROR_RTC_INVALID_INPUT); | ||
| 153 | + | ||
| 154 | + request = CreateValidRequest(); | ||
| 155 | + request.optionCount = 1U; | ||
| 156 | + EXPECT_EQ( | ||
| 157 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 158 | + ACL_ERROR_RTC_INVALID_INPUT); | ||
| 159 | + | ||
| 160 | + const char* options[] = {"-o"}; | ||
| 161 | + request = CreateValidRequest(); | ||
| 162 | + request.options = options; | ||
| 163 | + request.optionCount = 1U; | ||
| 164 | + EXPECT_EQ( | ||
| 165 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 166 | + ACL_ERROR_RTC_INVALID_OPTION); | ||
| 167 | +} | ||
| 168 | + | ||
| 169 | +TEST(KernelSpecializationRequestTest, RejectsInvalidRequestInputs) | ||
| 170 | +{ | ||
| 171 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 172 | + | ||
| 173 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 174 | + request.resourceId = nullptr; | ||
| 175 | + EXPECT_EQ( | ||
| 176 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 177 | + ACL_ERROR_RTC_INVALID_INPUT); | ||
| 178 | + | ||
| 179 | + request = CreateValidRequest(); | ||
| 180 | + request.kernelEntry = ""; | ||
| 181 | + EXPECT_EQ( | ||
| 182 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 183 | + ACL_ERROR_RTC_INVALID_INPUT); | ||
| 184 | + | ||
| 185 | + const char* nullOption[] = {nullptr}; | ||
| 186 | + request = CreateValidRequest(); | ||
| 187 | + request.options = nullOption; | ||
| 188 | + request.optionCount = 1U; | ||
| 189 | + EXPECT_EQ( | ||
| 190 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 191 | + ACL_ERROR_RTC_INVALID_OPTION); | ||
| 192 | + | ||
| 193 | + const char* optionWithControlByte[] = {"-DVALUE=line\nbreak"}; | ||
| 194 | + request = CreateValidRequest(); | ||
| 195 | + request.options = optionWithControlByte; | ||
| 196 | + request.optionCount = 1U; | ||
| 197 | + EXPECT_EQ( | ||
| 198 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 199 | + ACL_ERROR_RTC_INVALID_OPTION); | ||
| 200 | + | ||
| 201 | + request = CreateValidRequest(); | ||
| 202 | + request.skOptions = nullOption; | ||
| 203 | + request.skOptionCount = 1U; | ||
| 204 | + EXPECT_EQ( | ||
| 205 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 206 | + ACL_ERROR_RTC_INVALID_OPTION); | ||
| 207 | +} | ||
| 208 | + | ||
| 209 | +TEST(KernelSpecializationRequestTest, RejectsCompilerOptionThatExceedsIndividualTextLimit) | ||
| 210 | +{ | ||
| 211 | + const std::string oversizedOption(1024U * 1024U + 1U, 'x'); | ||
| 212 | + const char* options[] = {oversizedOption.c_str()}; | ||
| 213 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 214 | + request.options = options; | ||
| 215 | + request.optionCount = 1U; | ||
| 216 | + | ||
| 217 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 218 | + EXPECT_EQ( | ||
| 219 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 220 | + ACL_ERROR_RTC_INVALID_OPTION); | ||
| 221 | +} | ||
| 222 | + | ||
| 223 | +TEST(KernelSpecializationRequestTest, RejectsCompilerOptionsThatExceedCombinedTextLimit) | ||
| 224 | +{ | ||
| 225 | + const std::string maximumLengthOption(1024U * 1024U, 'x'); | ||
| 226 | + const char* options[] = { | ||
| 227 | + maximumLengthOption.c_str(), maximumLengthOption.c_str(), maximumLengthOption.c_str(), | ||
| 228 | + maximumLengthOption.c_str(), maximumLengthOption.c_str()}; | ||
| 229 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 230 | + request.options = options; | ||
| 231 | + request.optionCount = 5U; | ||
| 232 | + | ||
| 233 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 234 | + EXPECT_EQ( | ||
| 235 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 236 | + ACL_ERROR_RTC_INVALID_OPTION); | ||
| 237 | +} | ||
| 238 | + | ||
| 239 | +TEST(KernelSpecializationRequestTest, RejectsTooManyCompilerOptions) | ||
| 240 | +{ | ||
| 241 | + const std::vector<const char*> options(4097U, "-g"); | ||
| 242 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 243 | + request.options = options.data(); | ||
| 244 | + request.optionCount = options.size(); | ||
| 245 | + | ||
| 246 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 247 | + EXPECT_EQ( | ||
| 248 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 249 | + ACL_ERROR_RTC_INVALID_INPUT); | ||
| 250 | +} | ||
| 251 | + | ||
| 252 | +TEST(KernelSpecializationRequestTest, RejectsOptionsThatCanRedirectCompilerInputsOrOutputs) | ||
| 253 | +{ | ||
| 254 | + const std::array<const char*, 6> invalidOptions = {"@response.rsp", "-o", "-onew.o", "--output", | ||
| 255 | + "--output=new.o", "line\nbreak"}; | ||
| 256 | + | ||
| 257 | + for (const char* invalidOption : invalidOptions) { | ||
| 258 | + SCOPED_TRACE(invalidOption); | ||
| 259 | + const char* options[] = {invalidOption}; | ||
| 260 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 261 | + request.options = options; | ||
| 262 | + request.optionCount = 1; | ||
| 263 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 264 | + EXPECT_EQ( | ||
| 265 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 266 | + ACL_ERROR_RTC_INVALID_OPTION); | ||
| 267 | + } | ||
| 268 | +} | ||
| 269 | + | ||
| 270 | +TEST(KernelSpecializationRequestTest, RejectsSuperKernelOptionsThatRedirectCompilerOutputWhenForwarded) | ||
| 271 | +{ | ||
| 272 | + const char* skOptions[] = {"--enable-super-kernel", "-o", "unexpected.elf"}; | ||
| 273 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 274 | + request.skOptions = skOptions; | ||
| 275 | + request.skOptionCount = 3U; | ||
| 276 | + | ||
| 277 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 278 | + EXPECT_EQ( | ||
| 279 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 280 | + ACL_ERROR_RTC_INVALID_OPTION); | ||
| 281 | +} | ||
| 282 | + | ||
| 283 | +TEST(KernelSpecializationRequestTest, TreatsShellMetacharactersAsLiteralArgvText) | ||
| 284 | +{ | ||
| 285 | + const char* options[] = {"-DVALUE=$(touch /tmp/not-executed);still-literal"}; | ||
| 286 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 287 | + request.options = options; | ||
| 288 | + request.optionCount = 1; | ||
| 289 | + | ||
| 290 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 291 | + EXPECT_EQ(ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), 0); | ||
| 292 | + ASSERT_EQ(normalizedRequest.compilerOptions.basicOptions.size(), 1U); | ||
| 293 | + EXPECT_EQ(normalizedRequest.compilerOptions.basicOptions[0], options[0]); | ||
| 294 | +} | ||
| 295 | + | ||
| 296 | +TEST(KernelSpecializationRequestTest, RejectsDuplicateSuperKernelEnableOption) | ||
| 297 | +{ | ||
| 298 | + const char* skOptions[] = {"--enable-super-kernel", "--enable-super-kernel"}; | ||
| 299 | + aclrtcKernelSpecRequest request = CreateValidRequest(); | ||
| 300 | + request.skOptions = skOptions; | ||
| 301 | + request.skOptionCount = 2; | ||
| 302 | + | ||
| 303 | + ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 304 | + EXPECT_EQ( | ||
| 305 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), | ||
| 306 | + ACL_ERROR_RTC_INVALID_OPTION); | ||
| 307 | +} | ||
| 308 | +} // namespace | ||
| @@ -0,0 +1,301 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | +namespace { | ||
| 31 | +namespace fs = boost::filesystem; | ||
| 32 | +using ascendc::aclrtc::CompilationCommand; | ||
| 33 | +using ascendc::aclrtc::CompilationCommandKind; | ||
| 34 | +using ascendc::aclrtc::CompilationProcessResult; | ||
| 35 | +using ascendc::aclrtc::KernelSpecializationDiagnostics; | ||
| 36 | +using ascendc::aclrtc::OutputPublicationStatus; | ||
| 37 | +using namespace mockcpp; | ||
| 38 | + | ||
| 39 | +int RejectReplayScriptPermissionChange(const char*, mode_t) | ||
| 40 | +{ | ||
| 41 | + errno = EPERM; | ||
| 42 | + return -1; | ||
| 43 | +} | ||
| 44 | + | ||
| 45 | +class ScopedCurrentDirectory final { | ||
| 46 | +public: | ||
| 47 | + explicit ScopedCurrentDirectory(const fs::path& currentDirectory) : previousDirectory_(fs::current_path()) | ||
| 48 | + { | ||
| 49 | + fs::current_path(currentDirectory); | ||
| 50 | + } | ||
| 51 | + | ||
| 52 | + ~ScopedCurrentDirectory() | ||
| 53 | + { | ||
| 54 | + boost::system::error_code ignoredError; | ||
| 55 | + fs::current_path(previousDirectory_, ignoredError); | ||
| 56 | + } | ||
| 57 | + | ||
| 58 | +private: | ||
| 59 | + fs::path previousDirectory_; | ||
| 60 | +}; | ||
| 61 | + | ||
| 62 | +std::string ReadTextFile(const fs::path& filePath) | ||
| 63 | +{ | ||
| 64 | + std::ifstream inputStream(filePath.string()); | ||
| 65 | + return std::string(std::istreambuf_iterator<char>(inputStream), std::istreambuf_iterator<char>()); | ||
| 66 | +} | ||
| 67 | + | ||
| 68 | +int ExecuteReplayScriptAndWait(const fs::path& replayScriptPath) | ||
| 69 | +{ | ||
| 70 | + const pid_t childProcessId = fork(); | ||
| 71 | + if (childProcessId == 0) { | ||
| 72 | + execl(replayScriptPath.c_str(), replayScriptPath.c_str(), static_cast<char*>(nullptr)); | ||
| 73 | + _exit(127); | ||
| 74 | + } | ||
| 75 | + if (childProcessId < 0) { | ||
| 76 | + return -1; | ||
| 77 | + } | ||
| 78 | + | ||
| 79 | + int childStatus = 0; | ||
| 80 | + pid_t waitResult; | ||
| 81 | + do { | ||
| 82 | + waitResult = waitpid(childProcessId, &childStatus, 0); | ||
| 83 | + } while (waitResult < 0 && errno == EINTR); | ||
| 84 | + if (waitResult != childProcessId || !WIFEXITED(childStatus)) { | ||
| 85 | + return -1; | ||
| 86 | + } | ||
| 87 | + return WEXITSTATUS(childStatus); | ||
| 88 | +} | ||
| 89 | + | ||
| 90 | +TEST(KernelSpecializationDiagnosticsTest, SavesOnlyReviewedDiagnosticFiles) | ||
| 91 | +{ | ||
| 92 | + static uint64_t sequence = 0U; | ||
| 93 | + const fs::path testRoot = fs::temp_directory_path() / ("aclrtc_diagnostics_test_" + std::to_string(getpid()) + "_" + | ||
| 94 | + std::to_string(sequence++)); | ||
| 95 | + fs::create_directories(testRoot); | ||
| 96 | + { | ||
| 97 | + ScopedCurrentDirectory currentDirectory(testRoot); | ||
| 98 | + KernelSpecializationDiagnostics diagnostics(testRoot, "session_1"); | ||
| 99 | + diagnostics.WriteManifestSnapshot(nlohmann::json{{"schema_version", "1.0"}}); | ||
| 100 | + | ||
| 101 | + const CompilationCommand command{ | ||
| 102 | + CompilationCommandKind::Compile, | ||
| 103 | + 0U, | ||
| 104 | + "basic/compile[0]", | ||
| 105 | + "/bin/echo", | ||
| 106 | + {"argument with space", "quote'value"}}; | ||
| 107 | + diagnostics.AppendCommandToReplayScript(command); | ||
| 108 | + const CompilationCommand workingDirectoryCheck{ | ||
| 109 | + CompilationCommandKind::Compile, | ||
| 110 | + std::nullopt, | ||
| 111 | + "basic/compile[1]", | ||
| 112 | + "/bin/sh", | ||
| 113 | + {"-c", "test \"$PWD\" = \"$1\"", "replay-working-directory-check", testRoot.string()}}; | ||
| 114 | + diagnostics.AppendCommandToReplayScript(workingDirectoryCheck); | ||
| 115 | + CompilationProcessResult processResult; | ||
| 116 | + processResult.capturedOutput = "compiler output\n"; | ||
| 117 | + diagnostics.AppendCommandResultToCompilationLog(command, processResult); | ||
| 118 | + diagnostics.WriteSpecializationResult("/tmp/kernel.elf", OutputPublicationStatus::Published); | ||
| 119 | + } | ||
| 120 | + | ||
| 121 | + const fs::path replayScriptPath = testRoot / "aclrtc_replay.sh"; | ||
| 122 | + EXPECT_TRUE(fs::is_regular_file(testRoot / "aclrtc_manifest.json")); | ||
| 123 | + EXPECT_TRUE(fs::is_regular_file(replayScriptPath)); | ||
| 124 | + EXPECT_TRUE(fs::is_regular_file(testRoot / "aclrtc_compile.log")); | ||
| 125 | + EXPECT_TRUE(fs::is_regular_file(testRoot / "aclrtc_result.json")); | ||
| 126 | + EXPECT_EQ(std::distance(fs::directory_iterator(testRoot), fs::directory_iterator()), 4); | ||
| 127 | + | ||
| 128 | + const std::string replayScript = ReadTextFile(replayScriptPath); | ||
| 129 | + EXPECT_NE(replayScript.find("'/bin/echo' 'argument with space' 'quote'\"'\"'value'"), std::string::npos); | ||
| 130 | + | ||
| 131 | + struct stat replayScriptStatus {}; | ||
| 132 | + ASSERT_EQ(stat(replayScriptPath.c_str(), &replayScriptStatus), 0); | ||
| 133 | + EXPECT_EQ(replayScriptStatus.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO), S_IRWXU); | ||
| 134 | + EXPECT_EQ(ExecuteReplayScriptAndWait(replayScriptPath), 0); | ||
| 135 | + | ||
| 136 | + const nlohmann::json result = nlohmann::json::parse(ReadTextFile(testRoot / "aclrtc_result.json")); | ||
| 137 | + EXPECT_EQ(result.at("specialization_session_id"), "session_1"); | ||
| 138 | + EXPECT_EQ(result.at("output_path"), "/tmp/kernel.elf"); | ||
| 139 | + EXPECT_TRUE(result.at("output_published")); | ||
| 140 | + EXPECT_FALSE(result.contains("result")); | ||
| 141 | + | ||
| 142 | + boost::system::error_code ignoredError; | ||
| 143 | + fs::remove_all(testRoot, ignoredError); | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +TEST(KernelSpecializationDiagnosticsTest, DisablesDiagnosticsWhenReplayScriptCannotBeCreated) | ||
| 147 | +{ | ||
| 148 | + const fs::path missingWorktreePath = | ||
| 149 | + fs::temp_directory_path() / fs::unique_path("aclrtc_missing_diagnostics_%%%%-%%%%") / "worktree"; | ||
| 150 | + KernelSpecializationDiagnostics diagnostics(missingWorktreePath, "disabled_session"); | ||
| 151 | + diagnostics.WriteManifestSnapshot(nlohmann::json{{"schema_version", "1.0"}}); | ||
| 152 | + | ||
| 153 | + const CompilationCommand command{CompilationCommandKind::Compile, std::nullopt, "compile", "/bin/true", {}}; | ||
| 154 | + diagnostics.AppendCommandToReplayScript(command); | ||
| 155 | + diagnostics.AppendCommandResultToCompilationLog(command, CompilationProcessResult{}); | ||
| 156 | + diagnostics.LogCommandFailureRecoveryHint(); | ||
| 157 | + diagnostics.WriteSpecializationResult("/tmp/kernel.elf", OutputPublicationStatus::NotPublished); | ||
| 158 | + | ||
| 159 | + EXPECT_FALSE(fs::exists(missingWorktreePath)); | ||
| 160 | +} | ||
| 161 | + | ||
| 162 | +TEST(KernelSpecializationDiagnosticsTest, RemovesReplayScriptWhenInitialWriteCannotBeFinalized) | ||
| 163 | +{ | ||
| 164 | + const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_diagnostics_write_failure_%%%%-%%%%"); | ||
| 165 | + fs::create_directories(testRoot); | ||
| 166 | + struct rlimit originalFileSizeLimit {}; | ||
| 167 | + ASSERT_EQ(getrlimit(RLIMIT_FSIZE, &originalFileSizeLimit), 0); | ||
| 168 | + struct sigaction ignoreFileSizeSignal {}; | ||
| 169 | + struct sigaction originalFileSizeSignal {}; | ||
| 170 | + ignoreFileSizeSignal.sa_handler = SIG_IGN; | ||
| 171 | + ASSERT_EQ(sigemptyset(&ignoreFileSizeSignal.sa_mask), 0); | ||
| 172 | + ASSERT_EQ(sigaction(SIGXFSZ, &ignoreFileSizeSignal, &originalFileSizeSignal), 0); | ||
| 173 | + struct rlimit zeroFileSizeLimit = originalFileSizeLimit; | ||
| 174 | + zeroFileSizeLimit.rlim_cur = 0U; | ||
| 175 | + ASSERT_EQ(setrlimit(RLIMIT_FSIZE, &zeroFileSizeLimit), 0); | ||
| 176 | + | ||
| 177 | + KernelSpecializationDiagnostics diagnostics(testRoot, "write_failure_session"); | ||
| 178 | + | ||
| 179 | + const int restoreLimitResult = setrlimit(RLIMIT_FSIZE, &originalFileSizeLimit); | ||
| 180 | + const int restoreSignalResult = sigaction(SIGXFSZ, &originalFileSizeSignal, nullptr); | ||
| 181 | + ASSERT_EQ(restoreLimitResult, 0); | ||
| 182 | + ASSERT_EQ(restoreSignalResult, 0); | ||
| 183 | + EXPECT_FALSE(fs::exists(testRoot / "aclrtc_replay.sh")); | ||
| 184 | + | ||
| 185 | + boost::system::error_code ignoredError; | ||
| 186 | + fs::remove_all(testRoot, ignoredError); | ||
| 187 | +} | ||
| 188 | + | ||
| 189 | +TEST(KernelSpecializationDiagnosticsTest, KeepsNonEmptyReplayPathWhenInitialOpenFails) | ||
| 190 | +{ | ||
| 191 | + const fs::path testRoot = | ||
| 192 | + fs::temp_directory_path() / fs::unique_path("aclrtc_diagnostics_replay_directory_%%%%-%%%%"); | ||
| 193 | + const fs::path replayScriptDirectory = testRoot / "aclrtc_replay.sh"; | ||
| 194 | + fs::create_directories(replayScriptDirectory); | ||
| 195 | + std::ofstream(replayScriptDirectory / "existing.log") << "keep this diagnostic evidence\n"; | ||
| 196 | + | ||
| 197 | + KernelSpecializationDiagnostics diagnostics(testRoot, "replay_directory_session"); | ||
| 198 | + EXPECT_TRUE(fs::is_directory(replayScriptDirectory)); | ||
| 199 | + EXPECT_TRUE(fs::is_regular_file(replayScriptDirectory / "existing.log")); | ||
| 200 | + | ||
| 201 | + boost::system::error_code ignoredError; | ||
| 202 | + fs::remove_all(testRoot, ignoredError); | ||
| 203 | +} | ||
| 204 | + | ||
| 205 | +TEST(KernelSpecializationDiagnosticsTest, DisablesDiagnosticsWhenReplayScriptCannotBeMadeExecutable) | ||
| 206 | +{ | ||
| 207 | + const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_diagnostics_chmod_failure_%%%%-%%%%"); | ||
| 208 | + fs::create_directories(testRoot); | ||
| 209 | + MOCKER(chmod, int (*)(const char*, mode_t)).expects(once()).will(invoke(RejectReplayScriptPermissionChange)); | ||
| 210 | + | ||
| 211 | + KernelSpecializationDiagnostics diagnostics(testRoot, "chmod_failure_session"); | ||
| 212 | + | ||
| 213 | + GlobalMockObject::verify(); | ||
| 214 | + EXPECT_FALSE(fs::exists(testRoot / "aclrtc_replay.sh")); | ||
| 215 | + boost::system::error_code ignoredError; | ||
| 216 | + fs::remove_all(testRoot, ignoredError); | ||
| 217 | +} | ||
| 218 | + | ||
| 219 | +TEST(KernelSpecializationDiagnosticsTest, HandlesMissingCurrentDirectoryDuringInitialization) | ||
| 220 | +{ | ||
| 221 | + const int originalDirectoryFileDescriptor = open(".", O_RDONLY | O_DIRECTORY | O_CLOEXEC); | ||
| 222 | + ASSERT_GE(originalDirectoryFileDescriptor, 0); | ||
| 223 | + const fs::path removedCurrentDirectory = | ||
| 224 | + fs::temp_directory_path() / fs::unique_path("aclrtc_diagnostics_removed_cwd_%%%%-%%%%"); | ||
| 225 | + fs::create_directories(removedCurrentDirectory); | ||
| 226 | + ASSERT_EQ(chdir(removedCurrentDirectory.c_str()), 0); | ||
| 227 | + ASSERT_EQ(rmdir(removedCurrentDirectory.c_str()), 0); | ||
| 228 | + | ||
| 229 | + KernelSpecializationDiagnostics diagnostics(removedCurrentDirectory, "removed_cwd_session"); | ||
| 230 | + | ||
| 231 | + const int restoreDirectoryResult = fchdir(originalDirectoryFileDescriptor); | ||
| 232 | + const int closeDirectoryResult = close(originalDirectoryFileDescriptor); | ||
| 233 | + ASSERT_EQ(restoreDirectoryResult, 0); | ||
| 234 | + ASSERT_EQ(closeDirectoryResult, 0); | ||
| 235 | + EXPECT_FALSE(fs::exists(removedCurrentDirectory)); | ||
| 236 | +} | ||
| 237 | + | ||
| 238 | +TEST(KernelSpecializationDiagnosticsTest, IgnoresManifestSerializationFailure) | ||
| 239 | +{ | ||
| 240 | + const fs::path testRoot = | ||
| 241 | + fs::temp_directory_path() / fs::unique_path("aclrtc_diagnostics_invalid_manifest_%%%%-%%%%"); | ||
| 242 | + fs::create_directories(testRoot); | ||
| 243 | + KernelSpecializationDiagnostics diagnostics(testRoot, "invalid_manifest_session"); | ||
| 244 | + const nlohmann::json invalidUtf8Manifest = {{"invalid_utf8", std::string(1U, static_cast<char>(0xff))}}; | ||
| 245 | + | ||
| 246 | + diagnostics.WriteManifestSnapshot(invalidUtf8Manifest); | ||
| 247 | + | ||
| 248 | + EXPECT_FALSE(fs::exists(testRoot / "aclrtc_manifest.json")); | ||
| 249 | + boost::system::error_code ignoredError; | ||
| 250 | + fs::remove_all(testRoot, ignoredError); | ||
| 251 | +} | ||
| 252 | + | ||
| 253 | +TEST(KernelSpecializationDiagnosticsTest, RecordsTerminationKindsAndUnpublishedResult) | ||
| 254 | +{ | ||
| 255 | + const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_diagnostics_states_%%%%-%%%%"); | ||
| 256 | + fs::create_directories(testRoot); | ||
| 257 | + KernelSpecializationDiagnostics diagnostics(testRoot, "session_states"); | ||
| 258 | + const CompilationCommand command{CompilationCommandKind::Compile, std::nullopt, "compile", "/bin/true", {}}; | ||
| 259 | + | ||
| 260 | + CompilationProcessResult processResult; | ||
| 261 | + processResult.termination = ascendc::aclrtc::CompilationProcessTermination::Signaled; | ||
| 262 | + processResult.terminationCode = SIGTERM; | ||
| 263 | + diagnostics.AppendCommandResultToCompilationLog(command, processResult); | ||
| 264 | + processResult.termination = ascendc::aclrtc::CompilationProcessTermination::TimedOut; | ||
| 265 | + diagnostics.AppendCommandResultToCompilationLog(command, processResult); | ||
| 266 | + processResult.termination = static_cast<ascendc::aclrtc::CompilationProcessTermination>(UINT32_MAX); | ||
| 267 | + diagnostics.AppendCommandResultToCompilationLog(command, processResult); | ||
| 268 | + diagnostics.LogCommandFailureRecoveryHint(); | ||
| 269 | + diagnostics.WriteSpecializationResult("/tmp/not_published.elf", OutputPublicationStatus::NotPublished); | ||
| 270 | + | ||
| 271 | + const std::string compilationLog = ReadTextFile(testRoot / "aclrtc_compile.log"); | ||
| 272 | + EXPECT_NE(compilationLog.find("termination=signaled"), std::string::npos); | ||
| 273 | + EXPECT_NE(compilationLog.find("termination=timed_out"), std::string::npos); | ||
| 274 | + EXPECT_NE(compilationLog.find("termination=unknown"), std::string::npos); | ||
| 275 | + const nlohmann::json result = nlohmann::json::parse(ReadTextFile(testRoot / "aclrtc_result.json")); | ||
| 276 | + EXPECT_FALSE(result.at("output_published")); | ||
| 277 | + | ||
| 278 | + boost::system::error_code ignoredError; | ||
| 279 | + fs::remove_all(testRoot, ignoredError); | ||
| 280 | +} | ||
| 281 | + | ||
| 282 | +TEST(KernelSpecializationDiagnosticsTest, IgnoresDiagnosticWriteFailuresAfterWorktreeRemoval) | ||
| 283 | +{ | ||
| 284 | + const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_diagnostics_removed_%%%%-%%%%"); | ||
| 285 | + fs::create_directories(testRoot); | ||
| 286 | + KernelSpecializationDiagnostics diagnostics(testRoot, "removed_session"); | ||
| 287 | + boost::system::error_code removeError; | ||
| 288 | + fs::remove_all(testRoot, removeError); | ||
| 289 | + ASSERT_FALSE(removeError); | ||
| 290 | + | ||
| 291 | + const CompilationCommand command{ | ||
| 292 | + CompilationCommandKind::Compile, std::nullopt, "compile", "/bin/true", {"argument"}}; | ||
| 293 | + diagnostics.WriteManifestSnapshot(nlohmann::json{{"schema_version", "1.0"}}); | ||
| 294 | + diagnostics.AppendCommandToReplayScript(command); | ||
| 295 | + diagnostics.AppendCommandResultToCompilationLog(command, CompilationProcessResult{}); | ||
| 296 | + diagnostics.WriteSpecializationResult("/tmp/kernel.elf", OutputPublicationStatus::NotPublished); | ||
| 297 | + | ||
| 298 | + EXPECT_FALSE(fs::exists(testRoot)); | ||
| 299 | +} | ||
| 300 | + | ||
| 301 | +} // namespace | ||
| @@ -0,0 +1,589 @@ | |||
| 1 | +/** | ||
| 2 | + * Copyright (c) 2026 Huawei Technologies Co., Ltd. | ||
| 3 | + * This program is free software, you can redistribute it and/or modify it under the terms and conditions of | ||
| 4 | + * CANN Open Software License Agreement Version 2.0 (the "License"). | ||
| 5 | + * Please refer to the License for details. You may not use this file except in compliance with the License. | ||
| 6 | + * THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND, EITHER EXPRESS OR IMPLIED, | ||
| 7 | + * INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. | ||
| 8 | + * See LICENSE in the root of the software repository for the full text of the License. | ||
| 9 | + */ | ||
| 10 | + | ||
| 11 | + | ||
| 12 | + | ||
| 13 | + | ||
| 14 | + | ||
| 15 | + | ||
| 16 | + | ||
| 17 | + | ||
| 18 | + | ||
| 19 | + | ||
| 20 | + | ||
| 21 | + | ||
| 22 | + | ||
| 23 | + | ||
| 24 | + | ||
| 25 | + | ||
| 26 | + | ||
| 27 | + | ||
| 28 | + | ||
| 29 | + | ||
| 30 | + | ||
| 31 | +namespace { | ||
| 32 | +namespace fs = boost::filesystem; | ||
| 33 | +using Json = nlohmann::json; | ||
| 34 | +using ascendc::aclrtc::KernelCompilationWorkspace; | ||
| 35 | +using ascendc::aclrtc::KernelSpecializationSession; | ||
| 36 | +using ascendc::aclrtc::MaterializedKernelCompilationResource; | ||
| 37 | +using ascendc::aclrtc::NormalizedKernelSpecializationRequest; | ||
| 38 | +using ascendc::aclrtc::WorktreeRetentionPolicy; | ||
| 39 | +using ascendc::specialization_compile::ResourceEntry; | ||
| 40 | +using ascendc::specialization_compile::ResourceFileData; | ||
| 41 | +using ascendc::specialization_compile::ResourceRegistry; | ||
| 42 | +using ascendc::specialization_compile::ResourceSourceType; | ||
| 43 | +using ascendc::specialization_compile::ResourceStatus; | ||
| 44 | + | ||
| 45 | +enum class FakeCompilerOutcome : uint32_t { | ||
| 46 | + Success, | ||
| 47 | + Failure, | ||
| 48 | +}; | ||
| 49 | + | ||
| 50 | +constexpr char RESOURCE_ID[] = "0123456789abcdef0123456789abcdef0123456789abcdef0123456789abcdef"; | ||
| 51 | + | ||
| 52 | +class ScopedEnvironmentVariable final { | ||
| 53 | +public: | ||
| 54 | + ScopedEnvironmentVariable(const char* name, const char* value) : name_(name) | ||
| 55 | + { | ||
| 56 | + const char* previousValue = std::getenv(name); | ||
| 57 | + if (previousValue != nullptr) { | ||
| 58 | + previousValue_ = previousValue; | ||
| 59 | + wasSet_ = true; | ||
| 60 | + } | ||
| 61 | + if (value == nullptr) { | ||
| 62 | + unsetenv(name); | ||
| 63 | + } else { | ||
| 64 | + setenv(name, value, 1); | ||
| 65 | + } | ||
| 66 | + } | ||
| 67 | + | ||
| 68 | + ~ScopedEnvironmentVariable() | ||
| 69 | + { | ||
| 70 | + if (wasSet_) { | ||
| 71 | + setenv(name_.c_str(), previousValue_.c_str(), 1); | ||
| 72 | + } else { | ||
| 73 | + unsetenv(name_.c_str()); | ||
| 74 | + } | ||
| 75 | + } | ||
| 76 | + | ||
| 77 | +private: | ||
| 78 | + std::string name_; | ||
| 79 | + std::string previousValue_; | ||
| 80 | + bool wasSet_{false}; | ||
| 81 | +}; | ||
| 82 | + | ||
| 83 | +class EndToEndTestResource final { | ||
| 84 | +public: | ||
| 85 | + explicit EndToEndTestResource(FakeCompilerOutcome compilerOutcome) | ||
| 86 | + { | ||
| 87 | + static uint64_t sequence = 0; | ||
| 88 | + rootPath_ = fs::temp_directory_path() / | ||
| 89 | + ("aclrtc_session_test_" + std::to_string(getpid()) + "_" + std::to_string(sequence++)); | ||
| 90 | + worktreePath_ = rootPath_ / "worktree"; | ||
| 91 | + sourceDirectoryPath_ = rootPath_ / "user"; | ||
| 92 | + toolchainPath_ = rootPath_ / "toolchain"; | ||
| 93 | + outputElfPath_ = rootPath_ / "output/kernel.elf"; | ||
| 94 | + fs::create_directories(worktreePath_ / "resources/include"); | ||
| 95 | + fs::create_directories(worktreePath_ / "resources/src"); | ||
| 96 | + fs::create_directories(sourceDirectoryPath_); | ||
| 97 | + fs::create_directories(toolchainPath_ / "bin"); | ||
| 98 | + fs::create_directories(outputElfPath_.parent_path()); | ||
| 99 | + WriteFile(sourceDirectoryPath_ / "add_custom.cpp", "// user source\n"); | ||
| 100 | + WriteFile(worktreePath_ / "resources/include/constants.h", "TILING=@@STATIC_TILING@@;\n"); | ||
| 101 | + WriteFile(worktreePath_ / "resources/src/basic.cpp", "// basic source\n"); | ||
| 102 | + WriteExecutable( | ||
| 103 | + toolchainPath_ / "bin/bisheng", | ||
| 104 | + compilerOutcome == FakeCompilerOutcome::Success ? CreateOutputScript("object") : "#!/bin/sh\nexit 2\n"); | ||
| 105 | + WriteExecutable(toolchainPath_ / "bin/ld.lld", CreateOutputScript("linked-elf")); | ||
| 106 | + manifest_ = CreateManifest(); | ||
| 107 | + } | ||
| 108 | + | ||
| 109 | + ~EndToEndTestResource() | ||
| 110 | + { | ||
| 111 | + boost::system::error_code ignoredError; | ||
| 112 | + fs::remove_all(rootPath_, ignoredError); | ||
| 113 | + } | ||
| 114 | + | ||
| 115 | + MaterializedKernelCompilationResource CreateMaterializedCompilationResource() const | ||
| 116 | + { | ||
| 117 | + return MaterializedKernelCompilationResource{manifest_, worktreePath_, sourceDirectoryPath_}; | ||
| 118 | + } | ||
| 119 | + | ||
| 120 | + const fs::path& ToolchainPath() const { return toolchainPath_; } | ||
| 121 | + const fs::path& WorktreePath() const { return worktreePath_; } | ||
| 122 | + const fs::path& SourceDirectoryPath() const { return sourceDirectoryPath_; } | ||
| 123 | + const fs::path& OutputElfPath() const { return outputElfPath_; } | ||
| 124 | + const Json& Manifest() const { return manifest_; } | ||
| 125 | + | ||
| 126 | +private: | ||
| 127 | + static void WriteFile(const fs::path& path, const std::string& contents) | ||
| 128 | + { | ||
| 129 | + std::ofstream stream(path.string()); | ||
| 130 | + stream << contents; | ||
| 131 | + } | ||
| 132 | + | ||
| 133 | + static void WriteExecutable(const fs::path& path, const std::string& contents) | ||
| 134 | + { | ||
| 135 | + WriteFile(path, contents); | ||
| 136 | + chmod(path.c_str(), 0755); | ||
| 137 | + } | ||
| 138 | + | ||
| 139 | + static std::string CreateOutputScript(const char* contents) | ||
| 140 | + { | ||
| 141 | + return std::string("#!/bin/sh\n") + | ||
| 142 | + "while [ \"$#\" -gt 0 ]; do\n" | ||
| 143 | + " if [ \"$1\" = \"-o\" ]; then\n" | ||
| 144 | + " shift\n" | ||
| 145 | + " printf '" + | ||
| 146 | + contents + | ||
| 147 | + "' > \"$1\"\n" | ||
| 148 | + " exit 0\n" | ||
| 149 | + " fi\n" | ||
| 150 | + " shift\n" | ||
| 151 | + "done\n" | ||
| 152 | + "exit 3\n"; | ||
| 153 | + } | ||
| 154 | + | ||
| 155 | + static Json CreateManifest() | ||
| 156 | + { | ||
| 157 | + return Json{ | ||
| 158 | + {"schema_version", "1.0"}, | ||
| 159 | + {"source_file", "add_custom.cpp"}, | ||
| 160 | + {"kernels", Json::array( | ||
| 161 | + {{{"kernel_name", "add_custom_100000"}, | ||
| 162 | + {"constant_infos", Json::array( | ||
| 163 | + {{{"parameter_index", 0}, | ||
| 164 | + {"byte_size", 2}, | ||
| 165 | + {"file", "${resource}/resources/include/constants.h"}, | ||
| 166 | + {"template", "@@STATIC_TILING@@"}}})}, | ||
| 167 | + {"link_options", Json::array({"-m", "aicorelinux", "-Ttext=0"})}, | ||
| 168 | + {"objects", Json::array( | ||
| 169 | + {{{"object_name", "basic"}, | ||
| 170 | + {"object_type", "basic"}, | ||
| 171 | + {"commands", Json::array( | ||
| 172 | + {{{"type", "compile"}, | ||
| 173 | + {"stage", 0}, | ||
| 174 | + {"cmd", Json::array( | ||
| 175 | + {"${env:ASCEND_HOME_PATH}/bin/bisheng", | ||
| 176 | + "${resource}/resources/src/basic.cpp", | ||
| 177 | + "-o", "${output}/basic.o"})}}})}, | ||
| 178 | + {"outputs", Json::array({"${output}/basic.o"})}}})}}})}}; | ||
| 179 | + } | ||
| 180 | + | ||
| 181 | + fs::path rootPath_; | ||
| 182 | + fs::path worktreePath_; | ||
| 183 | + fs::path sourceDirectoryPath_; | ||
| 184 | + fs::path toolchainPath_; | ||
| 185 | + fs::path outputElfPath_; | ||
| 186 | + Json manifest_; | ||
| 187 | +}; | ||
| 188 | + | ||
| 189 | +class ScopedRegisteredCompilationResource final { | ||
| 190 | +public: | ||
| 191 | + ScopedRegisteredCompilationResource( | ||
| 192 | + std::string resourceId, const Json& manifest, const fs::path& sourceDirectoryPath, | ||
| 193 | + std::vector<ResourceFileData> resourceFiles = {{"payload.bin", "payload.bin", {0x01U, 0x02U}}}) | ||
| 194 | + : registry_(ResourceRegistry::Instance()), | ||
| 195 | + resourceId_(std::move(resourceId)), | ||
| 196 | + previousTemporaryRoot_(registry_.temporaryRoot_), | ||
| 197 | + previousAutomaticLoadAttempted_(registry_.automaticLoadAttempted_), | ||
| 198 | + previousKeepTemporaryRoot_(registry_.keepTemporaryRoot_), | ||
| 199 | + previousAutomaticLoadStatus_(registry_.automaticLoadStatus_) | ||
| 200 | + { | ||
| 201 | + registry_.temporaryRoot_.clear(); | ||
| 202 | + registry_.automaticLoadAttempted_ = true; | ||
| 203 | + registry_.keepTemporaryRoot_ = false; | ||
| 204 | + registry_.automaticLoadStatus_ = ResourceStatus::Success; | ||
| 205 | + | ||
| 206 | + std::unique_ptr<ResourceEntry> resourceEntry(new ResourceEntry()); | ||
| 207 | + resourceEntry->data.json = manifest; | ||
| 208 | + resourceEntry->data.sourceFilePath = sourceDirectoryPath.string(); | ||
| 209 | + resourceEntry->files = std::move(resourceFiles); | ||
| 210 | + resourceEntry->sourceType = ResourceSourceType::External; | ||
| 211 | + inserted_ = registry_.externalResources_.emplace(resourceId_, std::move(resourceEntry)).second; | ||
| 212 | + } | ||
| 213 | + | ||
| 214 | + ~ScopedRegisteredCompilationResource() | ||
| 215 | + { | ||
| 216 | + if (inserted_) { | ||
| 217 | + registry_.externalResources_.erase(resourceId_); | ||
| 218 | + } | ||
| 219 | + boost::system::error_code ignoredError; | ||
| 220 | + fs::remove_all(registry_.temporaryRoot_, ignoredError); | ||
| 221 | + registry_.temporaryRoot_ = previousTemporaryRoot_; | ||
| 222 | + registry_.automaticLoadAttempted_ = previousAutomaticLoadAttempted_; | ||
| 223 | + registry_.keepTemporaryRoot_ = previousKeepTemporaryRoot_; | ||
| 224 | + registry_.automaticLoadStatus_ = previousAutomaticLoadStatus_; | ||
| 225 | + } | ||
| 226 | + | ||
| 227 | + bool WasInserted() const noexcept { return inserted_; } | ||
| 228 | + | ||
| 229 | + void SetAutomaticLoadStatus(ResourceStatus loadStatus) noexcept { registry_.automaticLoadStatus_ = loadStatus; } | ||
| 230 | + | ||
| 231 | +private: | ||
| 232 | + ResourceRegistry& registry_; | ||
| 233 | + std::string resourceId_; | ||
| 234 | + std::string previousTemporaryRoot_; | ||
| 235 | + bool previousAutomaticLoadAttempted_{false}; | ||
| 236 | + bool previousKeepTemporaryRoot_{false}; | ||
| 237 | + ResourceStatus previousAutomaticLoadStatus_{ResourceStatus::Success}; | ||
| 238 | + bool inserted_{false}; | ||
| 239 | +}; | ||
| 240 | + | ||
| 241 | +aclrtcKernelSpecRequest CreateRequest(const uint64_t* constantByteCounts, const void* const* constantAddresses) | ||
| 242 | +{ | ||
| 243 | + aclrtcKernelSpecRequest request{}; | ||
| 244 | + request.resourceId = RESOURCE_ID; | ||
| 245 | + request.kernelEntry = "add_custom_100000"; | ||
| 246 | + request.argsCount = 1; | ||
| 247 | + request.argsAddr = constantAddresses; | ||
| 248 | + request.argsBytes = constantByteCounts; | ||
| 249 | + return request; | ||
| 250 | +} | ||
| 251 | + | ||
| 252 | +NormalizedKernelSpecializationRequest NormalizeRequestForTest( | ||
| 253 | + const aclrtcKernelSpecRequest& request, const fs::path& outputElfPath) | ||
| 254 | +{ | ||
| 255 | + NormalizedKernelSpecializationRequest normalizedRequest; | ||
| 256 | + EXPECT_EQ( | ||
| 257 | + ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, outputElfPath.c_str(), normalizedRequest), | ||
| 258 | + ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 259 | + return normalizedRequest; | ||
| 260 | +} | ||
| 261 | + | ||
| 262 | +std::string ReadFile(const fs::path& path) | ||
| 263 | +{ | ||
| 264 | + std::ifstream stream(path.string()); | ||
| 265 | + return std::string(std::istreambuf_iterator<char>(stream), std::istreambuf_iterator<char>()); | ||
| 266 | +} | ||
| 267 | + | ||
| 268 | +TEST(KernelSpecializationSessionTest, RunsPatchCompileLinkPublishAndCleanupWithMaterializedResource) | ||
| 269 | +{ | ||
| 270 | + EndToEndTestResource testResource(FakeCompilerOutcome::Success); | ||
| 271 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str()); | ||
| 272 | + ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", nullptr); | ||
| 273 | + const uint8_t constantBytes[] = {0x01, 0xfe}; | ||
| 274 | + const void* constantAddresses[] = {constantBytes}; | ||
| 275 | + const uint64_t constantByteCounts[] = {sizeof(constantBytes)}; | ||
| 276 | + aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses); | ||
| 277 | + | ||
| 278 | + KernelSpecializationSession session; | ||
| 279 | + EXPECT_EQ( | ||
| 280 | + session.RunSpecializationWithMaterializedResource( | ||
| 281 | + NormalizeRequestForTest(request, testResource.OutputElfPath()), | ||
| 282 | + testResource.CreateMaterializedCompilationResource()), | ||
| 283 | + 0); | ||
| 284 | + EXPECT_EQ(ReadFile(testResource.OutputElfPath()), "linked-elf"); | ||
| 285 | + EXPECT_FALSE(fs::exists(testResource.WorktreePath())); | ||
| 286 | +} | ||
| 287 | + | ||
| 288 | +TEST(KernelSpecializationSessionTest, DoesNotReplaceExistingOutputWhenCompilationFails) | ||
| 289 | +{ | ||
| 290 | + EndToEndTestResource testResource(FakeCompilerOutcome::Failure); | ||
| 291 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str()); | ||
| 292 | + ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", nullptr); | ||
| 293 | + std::ofstream(testResource.OutputElfPath().string()) << "previous-elf"; | ||
| 294 | + const uint8_t constantBytes[] = {0x01, 0xfe}; | ||
| 295 | + const void* constantAddresses[] = {constantBytes}; | ||
| 296 | + const uint64_t constantByteCounts[] = {sizeof(constantBytes)}; | ||
| 297 | + aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses); | ||
| 298 | + | ||
| 299 | + KernelSpecializationSession session; | ||
| 300 | + EXPECT_EQ( | ||
| 301 | + session.RunSpecializationWithMaterializedResource( | ||
| 302 | + NormalizeRequestForTest(request, testResource.OutputElfPath()), | ||
| 303 | + testResource.CreateMaterializedCompilationResource()), | ||
| 304 | + ascendc::aclrtc::ACLRTC_ERROR_COMPILATION); | ||
| 305 | + EXPECT_EQ(ReadFile(testResource.OutputElfPath()), "previous-elf"); | ||
| 306 | + EXPECT_FALSE(fs::exists(testResource.WorktreePath())); | ||
| 307 | +} | ||
| 308 | + | ||
| 309 | +TEST(KernelSpecializationSessionTest, FailsWhenOutputDirectoryIsMissing) | ||
| 310 | +{ | ||
| 311 | + EndToEndTestResource testResource(FakeCompilerOutcome::Success); | ||
| 312 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str()); | ||
| 313 | + ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", nullptr); | ||
| 314 | + boost::system::error_code removeError; | ||
| 315 | + fs::remove_all(testResource.OutputElfPath().parent_path(), removeError); | ||
| 316 | + ASSERT_FALSE(removeError); | ||
| 317 | + const uint8_t constantBytes[] = {0x01, 0xfe}; | ||
| 318 | + const void* constantAddresses[] = {constantBytes}; | ||
| 319 | + const uint64_t constantByteCounts[] = {sizeof(constantBytes)}; | ||
| 320 | + aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses); | ||
| 321 | + | ||
| 322 | + KernelSpecializationSession session; | ||
| 323 | + EXPECT_EQ( | ||
| 324 | + session.RunSpecializationWithMaterializedResource( | ||
| 325 | + NormalizeRequestForTest(request, testResource.OutputElfPath()), | ||
| 326 | + testResource.CreateMaterializedCompilationResource()), | ||
| 327 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 328 | +} | ||
| 329 | + | ||
| 330 | +TEST(KernelSpecializationSessionTest, RetainsWorktreeWhenKernelMetaSavingValueHasWhitespace) | ||
| 331 | +{ | ||
| 332 | + EndToEndTestResource testResource(FakeCompilerOutcome::Success); | ||
| 333 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str()); | ||
| 334 | + ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", " \t1\r\n"); | ||
| 335 | + const uint8_t constantBytes[] = {0x01, 0xfe}; | ||
| 336 | + const void* constantAddresses[] = {constantBytes}; | ||
| 337 | + const uint64_t constantByteCounts[] = {sizeof(constantBytes)}; | ||
| 338 | + aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses); | ||
| 339 | + | ||
| 340 | + KernelSpecializationSession session; | ||
| 341 | + EXPECT_EQ( | ||
| 342 | + session.RunSpecializationWithMaterializedResource( | ||
| 343 | + NormalizeRequestForTest(request, testResource.OutputElfPath()), | ||
| 344 | + testResource.CreateMaterializedCompilationResource()), | ||
| 345 | + 0); | ||
| 346 | + EXPECT_TRUE(fs::is_directory(testResource.WorktreePath())); | ||
| 347 | + EXPECT_TRUE(fs::is_regular_file(testResource.WorktreePath() / "aclrtc_manifest.json")); | ||
| 348 | + EXPECT_TRUE(fs::is_regular_file(testResource.WorktreePath() / "aclrtc_compile.log")); | ||
| 349 | + EXPECT_TRUE(fs::is_regular_file(testResource.WorktreePath() / "aclrtc_replay.sh")); | ||
| 350 | + EXPECT_TRUE(fs::is_regular_file(testResource.WorktreePath() / "aclrtc_result.json")); | ||
| 351 | + EXPECT_NE( | ||
| 352 | + ReadFile(testResource.WorktreePath() / "aclrtc_replay.sh").find(testResource.WorktreePath().string()), | ||
| 353 | + std::string::npos); | ||
| 354 | +} | ||
| 355 | + | ||
| 356 | +TEST(KernelSpecializationSessionTest, LoadsAndCopiesFreshMaterializedPathsFromRegistry) | ||
| 357 | +{ | ||
| 358 | + const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_session_registry_%%%%-%%%%-%%%%"); | ||
| 359 | + const fs::path sourceDirectoryPath = testRoot / "source"; | ||
| 360 | + fs::create_directories(sourceDirectoryPath); | ||
| 361 | + ScopedEnvironmentVariable temporaryDirectoryEnvironment("TMPDIR", testRoot.c_str()); | ||
| 362 | + const std::string resourceId = "aclrtc-session-registry-integration"; | ||
| 363 | + const Json manifest = {{"resource_id", resourceId}, {"schema_version", "1.0"}}; | ||
| 364 | + ScopedRegisteredCompilationResource registeredResource(resourceId, manifest, sourceDirectoryPath); | ||
| 365 | + ASSERT_TRUE(registeredResource.WasInserted()); | ||
| 366 | + | ||
| 367 | + registeredResource.SetAutomaticLoadStatus(ResourceStatus::NotFound); | ||
| 368 | + MaterializedKernelCompilationResource rejectedResource; | ||
| 369 | + EXPECT_EQ( | ||
| 370 | + KernelSpecializationSession::PrepareCompilationResource(resourceId, rejectedResource), | ||
| 371 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 372 | + | ||
| 373 | + registeredResource.SetAutomaticLoadStatus(ResourceStatus::Success); | ||
| 374 | + MaterializedKernelCompilationResource firstResource; | ||
| 375 | + ASSERT_EQ( | ||
| 376 | + KernelSpecializationSession::PrepareCompilationResource(resourceId, firstResource), | ||
| 377 | + ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 378 | + EXPECT_EQ(firstResource.manifest, manifest); | ||
| 379 | + EXPECT_EQ(firstResource.externalSourceDirectoryPath, sourceDirectoryPath); | ||
| 380 | + EXPECT_TRUE(fs::is_regular_file(firstResource.ownedResourceDirectoryPath / "payload.bin")); | ||
| 381 | + | ||
| 382 | + MaterializedKernelCompilationResource secondResource; | ||
| 383 | + ASSERT_EQ( | ||
| 384 | + KernelSpecializationSession::PrepareCompilationResource(resourceId, secondResource), | ||
| 385 | + ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 386 | + EXPECT_NE(firstResource.ownedResourceDirectoryPath, secondResource.ownedResourceDirectoryPath); | ||
| 387 | + EXPECT_TRUE(fs::is_regular_file(secondResource.ownedResourceDirectoryPath / "payload.bin")); | ||
| 388 | +} | ||
| 389 | + | ||
| 390 | +TEST(KernelSpecializationApiTest, ReportsMissingRegisteredResource) | ||
| 391 | +{ | ||
| 392 | + const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_session_missing_resource_%%%%-%%%%"); | ||
| 393 | + fs::create_directories(testRoot); | ||
| 394 | + ScopedRegisteredCompilationResource registeredResource( | ||
| 395 | + "unrelated-resource", Json{{"schema_version", "1.0"}}, testRoot); | ||
| 396 | + ASSERT_TRUE(registeredResource.WasInserted()); | ||
| 397 | + const uint8_t constantBytes[] = {0x01, 0xfe}; | ||
| 398 | + const void* constantAddresses[] = {constantBytes}; | ||
| 399 | + const uint64_t constantByteCounts[] = {sizeof(constantBytes)}; | ||
| 400 | + aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses); | ||
| 401 | + request.resourceId = "missing-resource"; | ||
| 402 | + | ||
| 403 | + EXPECT_EQ( | ||
| 404 | + aclrtcKernelSpecialization(&request, (testRoot / "kernel.elf").c_str()), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 405 | + | ||
| 406 | + boost::system::error_code ignoredError; | ||
| 407 | + fs::remove_all(testRoot, ignoredError); | ||
| 408 | +} | ||
| 409 | + | ||
| 410 | +TEST(KernelSpecializationApiTest, RejectsNullPointersBeforeResourceLookup) | ||
| 411 | +{ | ||
| 412 | + aclrtcKernelSpecRequest request{}; | ||
| 413 | + EXPECT_EQ(aclrtcKernelSpecialization(nullptr, "/tmp/kernel.elf"), ascendc::aclrtc::ACLRTC_ERROR_INVALID_INPUT); | ||
| 414 | + | ||
| 415 | + EXPECT_EQ(aclrtcKernelSpecialization(&request, nullptr), ascendc::aclrtc::ACLRTC_ERROR_INVALID_INPUT); | ||
| 416 | + | ||
| 417 | + EXPECT_EQ(aclrtcKernelSpecialization(&request, "/tmp/kernel.elf"), ascendc::aclrtc::ACLRTC_ERROR_INVALID_INPUT); | ||
| 418 | +} | ||
| 419 | + | ||
| 420 | +TEST(KernelSpecializationApiTest, CompilesRegisteredResourceThroughPublicApi) | ||
| 421 | +{ | ||
| 422 | + EndToEndTestResource testResource(FakeCompilerOutcome::Success); | ||
| 423 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str()); | ||
| 424 | + ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", nullptr); | ||
| 425 | + const std::vector<ResourceFileData> resourceFiles = { | ||
| 426 | + {"constants.h", "resources/include/constants.h", {'T', 'I', 'L', 'I', 'N', 'G', '=', '@', '@', | ||
| 427 | + 'S', 'T', 'A', 'T', 'I', 'C', '_', 'T', 'I', | ||
| 428 | + 'L', 'I', 'N', 'G', '@', '@', ';', '\n'}}, | ||
| 429 | + {"basic.cpp", "resources/src/basic.cpp", {'/', '/', ' ', 'b', 'a', 's', 'i', 'c', '\n'}}, | ||
| 430 | + }; | ||
| 431 | + ScopedRegisteredCompilationResource registeredResource( | ||
| 432 | + RESOURCE_ID, testResource.Manifest(), testResource.SourceDirectoryPath(), resourceFiles); | ||
| 433 | + ASSERT_TRUE(registeredResource.WasInserted()); | ||
| 434 | + const uint8_t constantBytes[] = {0x01, 0xfe}; | ||
| 435 | + const void* constantAddresses[] = {constantBytes}; | ||
| 436 | + const uint64_t constantByteCounts[] = {sizeof(constantBytes)}; | ||
| 437 | + aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses); | ||
| 438 | + | ||
| 439 | + EXPECT_EQ( | ||
| 440 | + aclrtcKernelSpecialization(&request, testResource.OutputElfPath().c_str()), ascendc::aclrtc::ACLRTC_SUCCESS); | ||
| 441 | + EXPECT_EQ(ReadFile(testResource.OutputElfPath()), "linked-elf"); | ||
| 442 | +} | ||
| 443 | + | ||
| 444 | +TEST(KernelSpecializationSessionTest, ReportsManifestPlanFailure) | ||
| 445 | +{ | ||
| 446 | + EndToEndTestResource testResource(FakeCompilerOutcome::Success); | ||
| 447 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str()); | ||
| 448 | + ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", nullptr); | ||
| 449 | + const uint8_t constantBytes[] = {0x01, 0xfe}; | ||
| 450 | + const void* constantAddresses[] = {constantBytes}; | ||
| 451 | + const uint64_t constantByteCounts[] = {sizeof(constantBytes)}; | ||
| 452 | + const aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses); | ||
| 453 | + MaterializedKernelCompilationResource resource = testResource.CreateMaterializedCompilationResource(); | ||
| 454 | + resource.manifest["schema_version"] = "unsupported"; | ||
| 455 | + | ||
| 456 | + KernelSpecializationSession session; | ||
| 457 | + EXPECT_EQ( | ||
| 458 | + session.RunSpecializationWithMaterializedResource( | ||
| 459 | + NormalizeRequestForTest(request, testResource.OutputElfPath()), std::move(resource)), | ||
| 460 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 461 | + EXPECT_FALSE(fs::exists(testResource.OutputElfPath())); | ||
| 462 | +} | ||
| 463 | + | ||
| 464 | +TEST(KernelSpecializationSessionTest, ReportsSourcePatchFailure) | ||
| 465 | +{ | ||
| 466 | + EndToEndTestResource testResource(FakeCompilerOutcome::Success); | ||
| 467 | + ScopedEnvironmentVariable ascendHomeEnvironment("ASCEND_HOME_PATH", testResource.ToolchainPath().c_str()); | ||
| 468 | + ScopedEnvironmentVariable saveMetaEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", nullptr); | ||
| 469 | + const uint8_t constantBytes[] = {0x01, 0xfe}; | ||
| 470 | + const void* constantAddresses[] = {constantBytes}; | ||
| 471 | + const uint64_t constantByteCounts[] = {sizeof(constantBytes)}; | ||
| 472 | + const aclrtcKernelSpecRequest request = CreateRequest(constantByteCounts, constantAddresses); | ||
| 473 | + MaterializedKernelCompilationResource resource = testResource.CreateMaterializedCompilationResource(); | ||
| 474 | + resource.manifest["kernels"][0]["constant_infos"][0]["template"] = "@@MISSING_TEMPLATE@@"; | ||
| 475 | + | ||
| 476 | + KernelSpecializationSession session; | ||
| 477 | + EXPECT_EQ( | ||
| 478 | + session.RunSpecializationWithMaterializedResource( | ||
| 479 | + NormalizeRequestForTest(request, testResource.OutputElfPath()), std::move(resource)), | ||
| 480 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 481 | + EXPECT_FALSE(fs::exists(testResource.OutputElfPath())); | ||
| 482 | +} | ||
| 483 | + | ||
| 484 | +TEST(KernelSpecializationSessionTest, RejectsMissingLinkedElfDuringPublication) | ||
| 485 | +{ | ||
| 486 | + const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_publish_missing_%%%%-%%%%"); | ||
| 487 | + const fs::path worktreePath = testRoot / "worktree"; | ||
| 488 | + fs::create_directories(worktreePath); | ||
| 489 | + KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 490 | + KernelSpecializationSession session; | ||
| 491 | + | ||
| 492 | + EXPECT_EQ( | ||
| 493 | + session.PublishKernelElf(testRoot / "missing.elf", testRoot / "output.elf", workspace), | ||
| 494 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 495 | + | ||
| 496 | + boost::system::error_code ignoredError; | ||
| 497 | + fs::remove_all(testRoot, ignoredError); | ||
| 498 | +} | ||
| 499 | + | ||
| 500 | +TEST(KernelSpecializationSessionTest, RemovesTemporaryElfWhenPublicationTargetIsDirectory) | ||
| 501 | +{ | ||
| 502 | + const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_publish_directory_%%%%-%%%%"); | ||
| 503 | + const fs::path worktreePath = testRoot / "worktree"; | ||
| 504 | + const fs::path linkedElfPath = testRoot / "linked.elf"; | ||
| 505 | + const fs::path outputDirectoryPath = testRoot / "output.elf"; | ||
| 506 | + fs::create_directories(worktreePath); | ||
| 507 | + fs::create_directories(outputDirectoryPath); | ||
| 508 | + std::ofstream(linkedElfPath.string()) << "linked-elf"; | ||
| 509 | + KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 510 | + KernelSpecializationSession session; | ||
| 511 | + | ||
| 512 | + EXPECT_EQ( | ||
| 513 | + session.PublishKernelElf(linkedElfPath, outputDirectoryPath, workspace), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 514 | + EXPECT_TRUE(fs::is_directory(outputDirectoryPath)); | ||
| 515 | + EXPECT_EQ(std::distance(fs::directory_iterator(testRoot), fs::directory_iterator()), 3); | ||
| 516 | + | ||
| 517 | + boost::system::error_code ignoredError; | ||
| 518 | + fs::remove_all(testRoot, ignoredError); | ||
| 519 | +} | ||
| 520 | + | ||
| 521 | +TEST(KernelSpecializationSessionTest, RemovesTemporaryElfWhenLinkedElfCannotBeRead) | ||
| 522 | +{ | ||
| 523 | + const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_publish_read_failure_%%%%-%%%%"); | ||
| 524 | + const fs::path linkedElfDirectory = testRoot / "linked.elf"; | ||
| 525 | + fs::create_directories(linkedElfDirectory); | ||
| 526 | + KernelCompilationWorkspace workspace(testRoot / "worktree", WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 527 | + KernelSpecializationSession session; | ||
| 528 | + | ||
| 529 | + EXPECT_EQ( | ||
| 530 | + session.PublishKernelElf(linkedElfDirectory, testRoot / "output.elf", workspace), | ||
| 531 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 532 | + EXPECT_EQ(std::distance(fs::directory_iterator(testRoot), fs::directory_iterator()), 1); | ||
| 533 | + | ||
| 534 | + boost::system::error_code ignoredError; | ||
| 535 | + fs::remove_all(testRoot, ignoredError); | ||
| 536 | +} | ||
| 537 | + | ||
| 538 | +TEST(KernelSpecializationSessionTest, RemovesTemporaryElfWhenOutputWriteFails) | ||
| 539 | +{ | ||
| 540 | + const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_publish_write_failure_%%%%-%%%%"); | ||
| 541 | + fs::create_directories(testRoot); | ||
| 542 | + const fs::path linkedElfPath = testRoot / "linked.elf"; | ||
| 543 | + std::ofstream(linkedElfPath.string()) << "linked-elf"; | ||
| 544 | + KernelCompilationWorkspace workspace(testRoot / "worktree", WorktreeRetentionPolicy::RetainAfterCompilation); | ||
| 545 | + KernelSpecializationSession session; | ||
| 546 | + struct rlimit originalFileSizeLimit {}; | ||
| 547 | + ASSERT_EQ(getrlimit(RLIMIT_FSIZE, &originalFileSizeLimit), 0); | ||
| 548 | + struct sigaction ignoreFileSizeSignal {}; | ||
| 549 | + struct sigaction originalFileSizeSignal {}; | ||
| 550 | + ignoreFileSizeSignal.sa_handler = SIG_IGN; | ||
| 551 | + ASSERT_EQ(sigemptyset(&ignoreFileSizeSignal.sa_mask), 0); | ||
| 552 | + ASSERT_EQ(sigaction(SIGXFSZ, &ignoreFileSizeSignal, &originalFileSizeSignal), 0); | ||
| 553 | + struct rlimit zeroFileSizeLimit = originalFileSizeLimit; | ||
| 554 | + zeroFileSizeLimit.rlim_cur = 0U; | ||
| 555 | + ASSERT_EQ(setrlimit(RLIMIT_FSIZE, &zeroFileSizeLimit), 0); | ||
| 556 | + | ||
| 557 | + const aclError publishResult = session.PublishKernelElf(linkedElfPath, testRoot / "output.elf", workspace); | ||
| 558 | + | ||
| 559 | + const int restoreLimitResult = setrlimit(RLIMIT_FSIZE, &originalFileSizeLimit); | ||
| 560 | + const int restoreSignalResult = sigaction(SIGXFSZ, &originalFileSizeSignal, nullptr); | ||
| 561 | + ASSERT_EQ(restoreLimitResult, 0); | ||
| 562 | + ASSERT_EQ(restoreSignalResult, 0); | ||
| 563 | + EXPECT_EQ(publishResult, ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 564 | + EXPECT_FALSE(fs::exists(testRoot / "output.elf")); | ||
| 565 | + EXPECT_EQ(std::distance(fs::directory_iterator(testRoot), fs::directory_iterator()), 1); | ||
| 566 | + | ||
| 567 | + boost::system::error_code ignoredError; | ||
| 568 | + fs::remove_all(testRoot, ignoredError); | ||
| 569 | +} | ||
| 570 | + | ||
| 571 | +TEST(KernelSpecializationSessionTest, StopsPublicationWhenWorktreeCannotBeRemoved) | ||
| 572 | +{ | ||
| 573 | + const fs::path testRoot = fs::temp_directory_path() / fs::unique_path("aclrtc_publish_cleanup_failure_%%%%-%%%%"); | ||
| 574 | + fs::create_directories(testRoot); | ||
| 575 | + const fs::path linkedElfPath = testRoot / "linked.elf"; | ||
| 576 | + std::ofstream(linkedElfPath.string()) << "linked-elf"; | ||
| 577 | + KernelCompilationWorkspace workspace("/proc/self/status", WorktreeRetentionPolicy::RemoveAfterCompilation); | ||
| 578 | + KernelSpecializationSession session; | ||
| 579 | + | ||
| 580 | + EXPECT_EQ( | ||
| 581 | + session.PublishKernelElf(linkedElfPath, testRoot / "output.elf", workspace), | ||
| 582 | + ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 583 | + EXPECT_FALSE(fs::exists(testRoot / "output.elf")); | ||
| 584 | + EXPECT_EQ(std::distance(fs::directory_iterator(testRoot), fs::directory_iterator()), 1); | ||
| 585 | + | ||
| 586 | + boost::system::error_code ignoredError; | ||
| 587 | + fs::remove_all(testRoot, ignoredError); | ||
| 588 | +} | ||
| 589 | +} // namespace | ||
| @@ -12,12 +12,15 @@ | |||
| 12 | 12 | ||
| 13 | 13 | ||
| 14 | 14 | ||
| 15 | + | ||
| 15 | 16 | ||
| 16 | 17 | ||
| 17 | 18 | ||
| 18 | 19 | ||
| 19 | 20 | ||
| 20 | 21 | ||
| 22 | + | ||
| 23 | + | ||
| 21 | 24 | ||
| 22 | 25 | ||
| 23 | 26 | ||
| @@ -42,6 +45,12 @@ constexpr uint64_t TEST_MAX_EXTENSION_COUNT = 4096U; | |||
| 42 | constexpr uint64_t TEST_MAX_FILE_COUNT = 65536U; | 45 | constexpr uint64_t TEST_MAX_FILE_COUNT = 65536U; |
| 43 | constexpr uint64_t TEST_MAX_REGISTRY_FILE_COUNT = 131072U; | 46 | constexpr uint64_t TEST_MAX_REGISTRY_FILE_COUNT = 131072U; |
| 44 | 47 | ||
| 48 | +static_assert( | ||
| 49 | + std::is_same<std::underlying_type_t<ResourceStatus>, uint32_t>::value, "ResourceStatus must have a stable width"); | ||
| 50 | +static_assert( | ||
| 51 | + std::is_same<std::underlying_type_t<ResourceSourceType>, uint32_t>::value, | ||
| 52 | + "ResourceSourceType must have a stable width"); | ||
| 53 | + | ||
| 45 | const AcCompileResourceBundleHeader* gBundleHeader = nullptr; | 54 | const AcCompileResourceBundleHeader* gBundleHeader = nullptr; |
| 46 | uint32_t gDlopenCalls = 0U; | 55 | uint32_t gDlopenCalls = 0U; |
| 47 | uint32_t gDlsymCalls = 0U; | 56 | uint32_t gDlsymCalls = 0U; |
| @@ -1111,6 +1120,30 @@ TEST_F(ResourceRegistryTest, WriteMaterializedFilesWritesNestedAndEmptyPayloadsA | |||
| 1111 | EXPECT_EQ(ResourceRegistry::WriteMaterializedFiles(direct, directoryTarget.string()), ResourceStatus::IoError); | 1120 | EXPECT_EQ(ResourceRegistry::WriteMaterializedFiles(direct, directoryTarget.string()), ResourceStatus::IoError); |
| 1112 | } | 1121 | } |
| 1113 | 1122 | ||
| 1123 | +TEST_F(ResourceRegistryTest, ReportsMaterializedFileFinalizeFailure) | ||
| 1124 | +{ | ||
| 1125 | + struct rlimit originalFileSizeLimit {}; | ||
| 1126 | + ASSERT_EQ(getrlimit(RLIMIT_FSIZE, &originalFileSizeLimit), 0); | ||
| 1127 | + struct sigaction ignoreFileSizeSignal {}; | ||
| 1128 | + struct sigaction originalFileSizeSignal {}; | ||
| 1129 | + ignoreFileSizeSignal.sa_handler = SIG_IGN; | ||
| 1130 | + ASSERT_EQ(sigemptyset(&ignoreFileSizeSignal.sa_mask), 0); | ||
| 1131 | + ASSERT_EQ(sigaction(SIGXFSZ, &ignoreFileSizeSignal, &originalFileSizeSignal), 0); | ||
| 1132 | + struct rlimit zeroFileSizeLimit = originalFileSizeLimit; | ||
| 1133 | + zeroFileSizeLimit.rlim_cur = 0U; | ||
| 1134 | + ASSERT_EQ(setrlimit(RLIMIT_FSIZE, &zeroFileSizeLimit), 0); | ||
| 1135 | + | ||
| 1136 | + const fs::path output = Path("materialized-finalize-failure"); | ||
| 1137 | + const std::vector<ResourceFileData> files = {{"data.bin", "data.bin", {1U}}}; | ||
| 1138 | + const ResourceStatus writeStatus = ResourceRegistry::WriteMaterializedFiles(files, output.string()); | ||
| 1139 | + | ||
| 1140 | + const int restoreLimitResult = setrlimit(RLIMIT_FSIZE, &originalFileSizeLimit); | ||
| 1141 | + const int restoreSignalResult = sigaction(SIGXFSZ, &originalFileSizeSignal, nullptr); | ||
| 1142 | + ASSERT_EQ(restoreLimitResult, 0); | ||
| 1143 | + ASSERT_EQ(restoreSignalResult, 0); | ||
| 1144 | + EXPECT_EQ(writeStatus, ResourceStatus::IoError); | ||
| 1145 | +} | ||
| 1146 | + | ||
| 1114 | TEST_F(ResourceRegistryTest, LookupValidatesInputsAndReturnsIndependentMaterializations) | 1147 | TEST_F(ResourceRegistryTest, LookupValidatesInputsAndReturnsIndependentMaterializations) |
| 1115 | { | 1148 | { |
| 1116 | ResourceData output; | 1149 | ResourceData output; |
| @@ -8,10 +8,11 @@ | |||
| 8 | # See LICENSE in the root of the software repository for the full text of the License. | 8 | # See LICENSE in the root of the software repository for the full text of the License. |
| 9 | # ---------------------------------------------------------------------------------------------------------- | 9 | # ---------------------------------------------------------------------------------------------------------- |
| 10 | aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/ SRC) | 10 | aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/ SRC) |
| 11 | +aux_source_directory(${CMAKE_CURRENT_SOURCE_DIR}/specialization SPECIALIZATION_SRC) | ||
| 11 | 12 | ||
| 12 | add_library(acl_rtc SHARED | 13 | add_library(acl_rtc SHARED |
| 13 | ${SRC} | 14 | ${SRC} |
| 14 | - ${CMAKE_CURRENT_SOURCE_DIR}/specialization/resource_registry.cpp | 15 | + ${SPECIALIZATION_SRC} |
| 15 | ${CMAKE_CURRENT_SOURCE_DIR}/../build/common/file_utils.cpp | 16 | ${CMAKE_CURRENT_SOURCE_DIR}/../build/common/file_utils.cpp |
| 16 | ) | 17 | ) |
| 17 | 18 | ||
| @@ -58,8 +59,14 @@ target_link_libraries(acl_rtc PRIVATE | |||
| 58 | json | 59 | json |
| 59 | ) | 60 | ) |
| 60 | 61 | ||
| 62 | +target_compile_definitions(acl_rtc PRIVATE | ||
| 63 | + ACLRTC_FUNC_VISIBILITY | ||
| 64 | +) | ||
| 65 | + | ||
| 61 | target_compile_options(acl_rtc PRIVATE | 66 | target_compile_options(acl_rtc PRIVATE |
| 62 | -fPIE | 67 | -fPIE |
| 68 | + -fvisibility=hidden | ||
| 69 | + -fvisibility-inlines-hidden | ||
| 63 | -fno-strict-aliasing | 70 | -fno-strict-aliasing |
| 64 | -fno-common | 71 | -fno-common |
| 65 | -Wall | 72 | -Wall |
| @@ -74,6 +81,7 @@ target_compile_options(acl_rtc PRIVATE | |||
| 74 | target_link_options(acl_rtc PRIVATE | 81 | target_link_options(acl_rtc PRIVATE |
| 75 | -Wl,-z,relro,-z,now,-z,noexecstack | 82 | -Wl,-z,relro,-z,now,-z,noexecstack |
| 76 | -Wl,-Bsymbolic | 83 | -Wl,-Bsymbolic |
| 84 | + -Wl,--exclude-libs,ALL | ||
| 77 | -Wl,--gc-sections | 85 | -Wl,--gc-sections |
| 78 | $<$<CONFIG:Release>:-s> | 86 | $<$<CONFIG:Release>:-s> |
| 79 | ) | 87 | ) |