Merged
refactor(build): consolidate common build infrastructure #5247
XuebinYangAdd on Aug 21
refactor(build): consolidate common build infrastructure #5247
Merged
Total: 36 files changed+1675-1371
| @@ -19,8 +19,10 @@ file(GLOB ACLRTC_SRC_FILES | |||
| 19 | ${ASCENDC_DIR}/tools/aclrtc/specialization/*.cpp | 19 | ${ASCENDC_DIR}/tools/aclrtc/specialization/*.cpp |
| 20 | ) | 20 | ) |
| 21 | 21 | ||
| 22 | -set(ACLRTC_RESOURCE_REGISTRY_DEPENDENCY_SRC_FILES | 22 | +set(ACLRTC_COMMON_DEPENDENCY_SRC_FILES |
| 23 | ${ASCENDC_DIR}/tools/build/common/file_utils.cpp | 23 | ${ASCENDC_DIR}/tools/build/common/file_utils.cpp |
| 24 | + ${ASCENDC_DIR}/tools/build/common/process_executor.cpp | ||
| 25 | + ${ASCENDC_DIR}/tools/build/common/directory_cleanup_guard.cpp | ||
| 24 | ) | 26 | ) |
| 25 | 27 | ||
| 26 | file(GLOB TEST_ACLRTC_SRC_FILES | 28 | file(GLOB TEST_ACLRTC_SRC_FILES |
| @@ -31,7 +33,7 @@ file(GLOB TEST_ACLRTC_SRC_FILES | |||
| 31 | 33 | ||
| 32 | add_executable(ascendc_ut_aclrtc | 34 | add_executable(ascendc_ut_aclrtc |
| 33 | ${ACLRTC_SRC_FILES} | 35 | ${ACLRTC_SRC_FILES} |
| 34 | - ${ACLRTC_RESOURCE_REGISTRY_DEPENDENCY_SRC_FILES} | 36 | + ${ACLRTC_COMMON_DEPENDENCY_SRC_FILES} |
| 35 | ${TEST_ACLRTC_SRC_FILES} | 37 | ${TEST_ACLRTC_SRC_FILES} |
| 36 | ) | 38 | ) |
| 37 | 39 | ||
| @@ -33,10 +33,8 @@ using ascendc::aclrtc::CompilationCommand; | |||
| 33 | using ascendc::aclrtc::CompilationCommandExecutionLimits; | 33 | using ascendc::aclrtc::CompilationCommandExecutionLimits; |
| 34 | using ascendc::aclrtc::CompilationCommandKind; | 34 | using ascendc::aclrtc::CompilationCommandKind; |
| 35 | using ascendc::aclrtc::CompilationPlanExecutor; | 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; | 36 | using ascendc::aclrtc::KernelCompilationPlan; |
| 37 | +using ascendc::aclrtc::KernelCompilationVariant; | ||
| 40 | using ascendc::aclrtc::KernelSpecializationDiagnostics; | 38 | using ascendc::aclrtc::KernelSpecializationDiagnostics; |
| 41 | using namespace mockcpp; | 39 | using namespace mockcpp; |
| 42 | 40 | ||
| @@ -46,28 +44,36 @@ int FailCommandOutputPoll(pollfd*, nfds_t, int) | |||
| 46 | return -1; | 44 | return -1; |
| 47 | } | 45 | } |
| 48 | 46 | ||
| 49 | -int FailCommandLeaderInspection(idtype_t, id_t, siginfo_t*, int) | 47 | +pid_t FailCommandWait(pid_t, int*, int) |
| 50 | { | 48 | { |
| 51 | errno = ECHILD; | 49 | errno = ECHILD; |
| 52 | return -1; | 50 | return -1; |
| 53 | } | 51 | } |
| 54 | 52 | ||
| 53 | +uint32_t COMMAND_OUTPUT_READ_CALL_COUNT = 0U; | ||
| 54 | + | ||
| 55 | +ssize_t FailFinalCommandOutputRead(int, void*, size_t) | ||
| 56 | +{ | ||
| 57 | + ++COMMAND_OUTPUT_READ_CALL_COUNT; | ||
| 58 | + errno = COMMAND_OUTPUT_READ_CALL_COUNT == 3U ? EIO : EAGAIN; | ||
| 59 | + return -1; | ||
| 60 | +} | ||
| 61 | + | ||
| 62 | +pid_t ReapCommandProcess(pid_t processId, int* waitStatus, int) | ||
| 63 | +{ | ||
| 64 | + pid_t waitResult; | ||
| 65 | + do { | ||
| 66 | + waitResult = wait4(processId, waitStatus, 0, nullptr); | ||
| 67 | + } while (waitResult < 0 && errno == EINTR); | ||
| 68 | + return waitResult; | ||
| 69 | +} | ||
| 70 | + | ||
| 55 | static_assert( | 71 | static_assert( |
| 56 | - std::is_same<std::underlying_type_t<CompilationVariant>, uint32_t>::value, | 72 | + std::is_same<std::underlying_type_t<KernelCompilationVariant>, uint32_t>::value, |
| 57 | - "CompilationVariant must have a stable width"); | 73 | + "KernelCompilationVariant must have a stable width"); |
| 58 | static_assert( | 74 | static_assert( |
| 59 | std::is_same<std::underlying_type_t<CompilationCommandKind>, uint32_t>::value, | 75 | std::is_same<std::underlying_type_t<CompilationCommandKind>, uint32_t>::value, |
| 60 | "CompilationCommandKind must have a stable width"); | 76 | "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( | 77 | CompilationCommand CreateCommand( |
| 72 | CompilationCommandKind commandKind, const char* executable, std::vector<std::string> arguments) | 78 | CompilationCommandKind commandKind, const char* executable, std::vector<std::string> arguments) |
| 73 | { | 79 | { |
| @@ -108,7 +114,6 @@ TEST(CompilationPlanExecutorTest, ProductionLimitsMatchReviewedPerCommandTimeout | |||
| 108 | EXPECT_EQ(limits.compileCommandTimeout, 5min); | 114 | EXPECT_EQ(limits.compileCommandTimeout, 5min); |
| 109 | EXPECT_EQ(limits.objectCopyAndLinkCommandTimeout, 1min); | 115 | EXPECT_EQ(limits.objectCopyAndLinkCommandTimeout, 1min); |
| 110 | EXPECT_EQ(limits.terminationGracePeriod, 5s); | 116 | EXPECT_EQ(limits.terminationGracePeriod, 5s); |
| 111 | - EXPECT_EQ(limits.capturedOutputByteLimit, 4U * 1024U * 1024U); | ||
| 112 | } | 117 | } |
| 113 | 118 | ||
| 114 | TEST(CompilationPlanExecutorTest, ExecutesCommandsWithoutShellParsing) | 119 | TEST(CompilationPlanExecutorTest, ExecutesCommandsWithoutShellParsing) |
| @@ -167,7 +172,6 @@ TEST(CompilationPlanExecutorTest, UsesInjectedShortTimeoutInUnitTests) | |||
| 167 | limits.compileCommandTimeout = 20ms; | 172 | limits.compileCommandTimeout = 20ms; |
| 168 | limits.objectCopyAndLinkCommandTimeout = 20ms; | 173 | limits.objectCopyAndLinkCommandTimeout = 20ms; |
| 169 | limits.terminationGracePeriod = 20ms; | 174 | limits.terminationGracePeriod = 20ms; |
| 170 | - limits.capturedOutputByteLimit = 1024U; | ||
| 171 | CompilationPlanExecutor executor(diagnostics, limits); | 175 | CompilationPlanExecutor executor(diagnostics, limits); |
| 172 | KernelCompilationPlan plan; | 176 | KernelCompilationPlan plan; |
| 173 | plan.compilationCommands.push_back(CreateCommand(CompilationCommandKind::Compile, "/bin/sleep", {"10"})); | 177 | plan.compilationCommands.push_back(CreateCommand(CompilationCommandKind::Compile, "/bin/sleep", {"10"})); |
| @@ -180,7 +184,7 @@ TEST(CompilationPlanExecutorTest, UsesInjectedShortTimeoutInUnitTests) | |||
| 180 | TEST(CompilationPlanExecutorTest, EnforcesTimeoutWhileCommandContinuouslyProducesOutput) | 184 | TEST(CompilationPlanExecutorTest, EnforcesTimeoutWhileCommandContinuouslyProducesOutput) |
| 181 | { | 185 | { |
| 182 | KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_continuous_output"); | 186 | KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_continuous_output"); |
| 183 | - CompilationCommandExecutionLimits limits{20ms, 20ms, 20ms, 1024U}; | 187 | + CompilationCommandExecutionLimits limits{20ms, 20ms, 20ms}; |
| 184 | CompilationPlanExecutor executor(diagnostics, limits); | 188 | CompilationPlanExecutor executor(diagnostics, limits); |
| 185 | KernelCompilationPlan plan; | 189 | KernelCompilationPlan plan; |
| 186 | plan.compilationCommands.push_back(CreateCommand( | 190 | plan.compilationCommands.push_back(CreateCommand( |
| @@ -194,7 +198,7 @@ TEST(CompilationPlanExecutorTest, EnforcesTimeoutWhileCommandContinuouslyProduce | |||
| 194 | TEST(CompilationPlanExecutorTest, WaitsWithoutBusyPollingAfterCommandOutputCloses) | 198 | TEST(CompilationPlanExecutorTest, WaitsWithoutBusyPollingAfterCommandOutputCloses) |
| 195 | { | 199 | { |
| 196 | KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_closed_output"); | 200 | KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_closed_output"); |
| 197 | - CompilationCommandExecutionLimits limits{1s, 1s, 20ms, 1024U}; | 201 | + CompilationCommandExecutionLimits limits{1s, 1s, 20ms}; |
| 198 | CompilationPlanExecutor executor(diagnostics, limits); | 202 | CompilationPlanExecutor executor(diagnostics, limits); |
| 199 | KernelCompilationPlan plan; | 203 | KernelCompilationPlan plan; |
| 200 | plan.compilationCommands.push_back( | 204 | plan.compilationCommands.push_back( |
| @@ -211,10 +215,10 @@ TEST(CompilationPlanExecutorTest, WaitsWithoutBusyPollingAfterCommandOutputClose | |||
| 211 | EXPECT_LT(consumedCpuNanoseconds, 100000000LL); | 215 | EXPECT_LT(consumedCpuNanoseconds, 100000000LL); |
| 212 | } | 216 | } |
| 213 | 217 | ||
| 214 | -TEST(CompilationPlanExecutorTest, WaitsForOutputPipeClosureAfterCommandLeaderExits) | 218 | +TEST(CompilationPlanExecutorTest, CompletesWhenSynchronousCommandLeaderExits) |
| 215 | { | 219 | { |
| 216 | KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_leader_exit"); | 220 | KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_leader_exit"); |
| 217 | - CompilationCommandExecutionLimits limits{500ms, 500ms, 20ms, 1024U}; | 221 | + CompilationCommandExecutionLimits limits{500ms, 500ms, 20ms}; |
| 218 | CompilationPlanExecutor executor(diagnostics, limits); | 222 | CompilationPlanExecutor executor(diagnostics, limits); |
| 219 | KernelCompilationPlan plan; | 223 | KernelCompilationPlan plan; |
| 220 | plan.compilationCommands.push_back( | 224 | plan.compilationCommands.push_back( |
| @@ -222,22 +226,21 @@ TEST(CompilationPlanExecutorTest, WaitsForOutputPipeClosureAfterCommandLeaderExi | |||
| 222 | 226 | ||
| 223 | const auto startedAt = std::chrono::steady_clock::now(); | 227 | const auto startedAt = std::chrono::steady_clock::now(); |
| 224 | EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_SUCCESS); | 228 | EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_SUCCESS); |
| 225 | - EXPECT_GE(std::chrono::steady_clock::now() - startedAt, 200ms); | 229 | + EXPECT_LT(std::chrono::steady_clock::now() - startedAt, 200ms); |
| 226 | } | 230 | } |
| 227 | 231 | ||
| 228 | -TEST(CompilationPlanExecutorTest, TimeoutTerminatesDescendantsAfterCommandLeaderExits) | 232 | +TEST(CompilationPlanExecutorTest, TimeoutTerminatesActiveProcessGroup) |
| 229 | { | 233 | { |
| 230 | static uint64_t sequence = 0U; | 234 | static uint64_t sequence = 0U; |
| 231 | const fs::path descendantMarkerPath = | 235 | const fs::path descendantMarkerPath = |
| 232 | fs::temp_directory_path() / | 236 | fs::temp_directory_path() / |
| 233 | ("aclrtc_descendant_marker_" + std::to_string(getpid()) + "_" + std::to_string(sequence++)); | 237 | ("aclrtc_descendant_marker_" + std::to_string(getpid()) + "_" + std::to_string(sequence++)); |
| 234 | KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_process_group"); | 238 | KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_process_group"); |
| 235 | - CompilationCommandExecutionLimits limits{20ms, 20ms, 20ms, 1024U}; | 239 | + CompilationCommandExecutionLimits limits{20ms, 20ms, 20ms}; |
| 236 | CompilationPlanExecutor executor(diagnostics, limits); | 240 | CompilationPlanExecutor executor(diagnostics, limits); |
| 237 | KernelCompilationPlan plan; | 241 | KernelCompilationPlan plan; |
| 238 | const std::string commandScript = | 242 | const std::string commandScript = |
| 239 | - "(trap '' TERM; count=0; while [ $count -lt 100 ]; do printf x >> '" + descendantMarkerPath.string() + | 243 | + "(trap '' TERM; while true; do printf x >> '" + descendantMarkerPath.string() + "'; sleep 0.01; done) & wait"; |
| 240 | - "'; count=$((count + 1)); sleep 0.01; done; rm -f '" + descendantMarkerPath.string() + "') & exec /bin/true"; | ||
| 241 | plan.compilationCommands.push_back( | 244 | plan.compilationCommands.push_back( |
| 242 | CreateCommand(CompilationCommandKind::Compile, "/bin/sh", {"-c", commandScript})); | 245 | CreateCommand(CompilationCommandKind::Compile, "/bin/sh", {"-c", commandScript})); |
| 243 | 246 | ||
| @@ -254,7 +257,7 @@ TEST(CompilationPlanExecutorTest, TimeoutTerminatesDescendantsAfterCommandLeader | |||
| 254 | fs::remove(descendantMarkerPath, ignoredError); | 257 | fs::remove(descendantMarkerPath, ignoredError); |
| 255 | } | 258 | } |
| 256 | 259 | ||
| 257 | -TEST(CompilationPlanExecutorTest, RecordsSpawnFailureAsInfrastructureFailure) | 260 | +TEST(CompilationPlanExecutorTest, RecordsSpawnFailureAsExecutorFailure) |
| 258 | { | 261 | { |
| 259 | ScopedDiagnosticCurrentDirectory diagnosticCurrentDirectory; | 262 | ScopedDiagnosticCurrentDirectory diagnosticCurrentDirectory; |
| 260 | const std::string specializationSessionId = "executor_spawn_failure"; | 263 | const std::string specializationSessionId = "executor_spawn_failure"; |
| @@ -265,27 +268,25 @@ TEST(CompilationPlanExecutorTest, RecordsSpawnFailureAsInfrastructureFailure) | |||
| 265 | EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | 268 | EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); |
| 266 | 269 | ||
| 267 | const std::string compilationLog = ReadTextFile(fs::current_path() / "aclrtc_compile.log"); | 270 | const std::string compilationLog = ReadTextFile(fs::current_path() / "aclrtc_compile.log"); |
| 268 | - EXPECT_NE(compilationLog.find("termination=infrastructure_failure"), std::string::npos); | 271 | + EXPECT_NE(compilationLog.find("outcome=executor_failure"), std::string::npos); |
| 269 | EXPECT_EQ(compilationLog.find("termination=exited code=0"), std::string::npos); | 272 | EXPECT_EQ(compilationLog.find("termination=exited code=0"), std::string::npos); |
| 270 | } | 273 | } |
| 271 | 274 | ||
| 272 | -TEST(CompilationPlanExecutorTest, BoundsCapturedProcessOutput) | 275 | +TEST(CompilationPlanExecutorTest, MirrorsCompleteProcessOutputWhenDiagnosticsAreEnabled) |
| 273 | { | 276 | { |
| 274 | ScopedDiagnosticCurrentDirectory diagnosticCurrentDirectory; | 277 | ScopedDiagnosticCurrentDirectory diagnosticCurrentDirectory; |
| 275 | const std::string specializationSessionId = "executor_output_limit"; | 278 | const std::string specializationSessionId = "executor_output_limit"; |
| 276 | KernelSpecializationDiagnostics diagnostics(fs::current_path(), specializationSessionId); | 279 | KernelSpecializationDiagnostics diagnostics(fs::current_path(), specializationSessionId); |
| 277 | - CompilationCommandExecutionLimits limits = CompilationCommandExecutionLimits::ProductionDefaults(); | 280 | + CompilationPlanExecutor executor(diagnostics, CompilationCommandExecutionLimits::ProductionDefaults()); |
| 278 | - limits.capturedOutputByteLimit = 8U; | ||
| 279 | - CompilationPlanExecutor executor(diagnostics, limits); | ||
| 280 | KernelCompilationPlan plan; | 281 | KernelCompilationPlan plan; |
| 281 | plan.compilationCommands.push_back( | 282 | plan.compilationCommands.push_back( |
| 282 | CreateCommand(CompilationCommandKind::Compile, "/usr/bin/printf", {"0123456789abcdef"})); | 283 | CreateCommand(CompilationCommandKind::Compile, "/usr/bin/printf", {"0123456789abcdef"})); |
| 283 | EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_SUCCESS); | 284 | EXPECT_EQ(executor.ExecuteCompilationPlan(plan), ascendc::aclrtc::ACLRTC_SUCCESS); |
| 284 | 285 | ||
| 285 | const std::string compilationLog = ReadTextFile(fs::current_path() / "aclrtc_compile.log"); | 286 | 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("[test-command] output:\n"), std::string::npos); |
| 287 | - EXPECT_NE(compilationLog.find("01234567"), std::string::npos); | 288 | + EXPECT_NE(compilationLog.find("0123456789abcdef"), std::string::npos); |
| 288 | - EXPECT_EQ(compilationLog.find("89abcdef"), std::string::npos); | 289 | + EXPECT_NE(compilationLog.find("\n[test-command] outcome=exited code=0"), std::string::npos); |
| 289 | } | 290 | } |
| 290 | 291 | ||
| 291 | TEST(CompilationPlanExecutorTest, ReportsToolProcessTerminatedBySignal) | 292 | TEST(CompilationPlanExecutorTest, ReportsToolProcessTerminatedBySignal) |
| @@ -311,40 +312,57 @@ TEST(CompilationPlanExecutorTest, ReportsPipeCreationFailureWhenFileDescriptorsA | |||
| 311 | struct rlimit exhaustedFileDescriptorLimit = originalFileDescriptorLimit; | 312 | struct rlimit exhaustedFileDescriptorLimit = originalFileDescriptorLimit; |
| 312 | exhaustedFileDescriptorLimit.rlim_cur = 0U; | 313 | exhaustedFileDescriptorLimit.rlim_cur = 0U; |
| 313 | ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &exhaustedFileDescriptorLimit), 0); | 314 | ASSERT_EQ(setrlimit(RLIMIT_NOFILE, &exhaustedFileDescriptorLimit), 0); |
| 314 | - const aclError executionResult = executor.ExecuteCompilationPlan(plan); | 315 | + const aclError planExecutionStatus = executor.ExecuteCompilationPlan(plan); |
| 315 | const int restoreLimitResult = setrlimit(RLIMIT_NOFILE, &originalFileDescriptorLimit); | 316 | const int restoreLimitResult = setrlimit(RLIMIT_NOFILE, &originalFileDescriptorLimit); |
| 316 | 317 | ||
| 317 | ASSERT_EQ(restoreLimitResult, 0); | 318 | ASSERT_EQ(restoreLimitResult, 0); |
| 318 | - EXPECT_EQ(executionResult, ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | 319 | + EXPECT_EQ(planExecutionStatus, ascendc::aclrtc::ACLRTC_ERROR_FAILURE); |
| 319 | } | 320 | } |
| 320 | 321 | ||
| 321 | TEST(CompilationPlanExecutorTest, ReportsCommandOutputPollFailure) | 322 | TEST(CompilationPlanExecutorTest, ReportsCommandOutputPollFailure) |
| 322 | { | 323 | { |
| 323 | KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_poll_failure"); | 324 | KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_poll_failure"); |
| 324 | - CompilationCommandExecutionLimits limits{1s, 1s, 20ms, 1024U}; | 325 | + CompilationCommandExecutionLimits limits{1s, 1s, 20ms}; |
| 325 | CompilationPlanExecutor executor(diagnostics, limits); | 326 | CompilationPlanExecutor executor(diagnostics, limits); |
| 326 | KernelCompilationPlan plan; | 327 | KernelCompilationPlan plan; |
| 327 | plan.compilationCommands.push_back(CreateCommand(CompilationCommandKind::Compile, "/bin/sleep", {"1"})); | 328 | plan.compilationCommands.push_back(CreateCommand(CompilationCommandKind::Compile, "/bin/sleep", {"1"})); |
| 328 | MOCKER(poll, int (*)(pollfd*, nfds_t, int)).expects(once()).will(invoke(FailCommandOutputPoll)); | 329 | MOCKER(poll, int (*)(pollfd*, nfds_t, int)).expects(once()).will(invoke(FailCommandOutputPoll)); |
| 329 | 330 | ||
| 330 | - const aclError executionResult = executor.ExecuteCompilationPlan(plan); | 331 | + const aclError planExecutionStatus = executor.ExecuteCompilationPlan(plan); |
| 331 | 332 | ||
| 332 | GlobalMockObject::verify(); | 333 | GlobalMockObject::verify(); |
| 333 | - EXPECT_EQ(executionResult, ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | 334 | + EXPECT_EQ(planExecutionStatus, ascendc::aclrtc::ACLRTC_ERROR_FAILURE); |
| 334 | } | 335 | } |
| 335 | 336 | ||
| 336 | -TEST(CompilationPlanExecutorTest, ReportsCommandLeaderInspectionFailure) | 337 | +TEST(CompilationPlanExecutorTest, ReportsCommandWaitFailure) |
| 337 | { | 338 | { |
| 338 | KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_waitid_failure"); | 339 | KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_waitid_failure"); |
| 339 | - CompilationCommandExecutionLimits limits{1s, 1s, 20ms, 1024U}; | 340 | + CompilationCommandExecutionLimits limits{1s, 1s, 20ms}; |
| 340 | CompilationPlanExecutor executor(diagnostics, limits); | 341 | CompilationPlanExecutor executor(diagnostics, limits); |
| 341 | KernelCompilationPlan plan; | 342 | KernelCompilationPlan plan; |
| 342 | plan.compilationCommands.push_back(CreateCommand(CompilationCommandKind::Compile, "/bin/sleep", {"1"})); | 343 | 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 | + MOCKER(waitpid, pid_t(*)(pid_t, int*, int)).expects(once()).will(invoke(FailCommandWait)); |
| 344 | 345 | ||
| 345 | - const aclError executionResult = executor.ExecuteCompilationPlan(plan); | 346 | + const aclError planExecutionStatus = executor.ExecuteCompilationPlan(plan); |
| 346 | 347 | ||
| 347 | GlobalMockObject::verify(); | 348 | GlobalMockObject::verify(); |
| 348 | - EXPECT_EQ(executionResult, ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | 349 | + EXPECT_EQ(planExecutionStatus, ascendc::aclrtc::ACLRTC_ERROR_FAILURE); |
| 349 | } | 350 | } |
| 351 | + | ||
| 352 | +TEST(CompilationPlanExecutorTest, ReportsFinalCommandOutputReadFailure) | ||
| 353 | +{ | ||
| 354 | + KernelSpecializationDiagnostics diagnostics(fs::path(), "executor_final_output_read_failure"); | ||
| 355 | + CompilationPlanExecutor executor(diagnostics, CompilationCommandExecutionLimits::ProductionDefaults()); | ||
| 356 | + KernelCompilationPlan plan; | ||
| 357 | + plan.compilationCommands.push_back(CreateCommand(CompilationCommandKind::Compile, "/bin/true", {})); | ||
| 358 | + COMMAND_OUTPUT_READ_CALL_COUNT = 0U; | ||
| 359 | + MOCKER(read, ssize_t(*)(int, void*, size_t)).expects(exactly(4)).will(invoke(FailFinalCommandOutputRead)); | ||
| 360 | + MOCKER(waitpid, pid_t(*)(pid_t, int*, int)).expects(once()).will(invoke(ReapCommandProcess)); | ||
| 361 | + | ||
| 362 | + const aclError planExecutionStatus = executor.ExecuteCompilationPlan(plan); | ||
| 363 | + | ||
| 364 | + GlobalMockObject::verify(); | ||
| 365 | + EXPECT_EQ(planExecutionStatus, ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | ||
| 366 | +} | ||
| 367 | + | ||
| 350 | } // namespace | 368 | } // namespace |
| @@ -26,9 +26,9 @@ namespace { | |||
| 26 | namespace fs = boost::filesystem; | 26 | namespace fs = boost::filesystem; |
| 27 | using Json = nlohmann::json; | 27 | using Json = nlohmann::json; |
| 28 | using ascendc::aclrtc::CompilationCommandKind; | 28 | using ascendc::aclrtc::CompilationCommandKind; |
| 29 | -using ascendc::aclrtc::CompilationVariant; | ||
| 30 | using ascendc::aclrtc::KernelCompilationPlan; | 29 | using ascendc::aclrtc::KernelCompilationPlan; |
| 31 | using ascendc::aclrtc::KernelCompilationPlanBuilder; | 30 | using ascendc::aclrtc::KernelCompilationPlanBuilder; |
| 31 | +using ascendc::aclrtc::KernelCompilationVariant; | ||
| 32 | using ascendc::aclrtc::NormalizedKernelSpecializationRequest; | 32 | using ascendc::aclrtc::NormalizedKernelSpecializationRequest; |
| 33 | 33 | ||
| 34 | class ScopedEnvironmentVariable final { | 34 | class ScopedEnvironmentVariable final { |
| @@ -300,7 +300,7 @@ TEST(KernelCompilationPlanBuilderTest, PreservesExplicitStagesAndManifestCommand | |||
| 300 | const void* argumentAddresses[] = {tilingBytes}; | 300 | const void* argumentAddresses[] = {tilingBytes}; |
| 301 | const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | 301 | const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; |
| 302 | NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); | 302 | NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); |
| 303 | - request.compilationVariant = CompilationVariant::BasicWithSuperKernel; | 303 | + request.compilationVariant = KernelCompilationVariant::BasicWithSuperKernel; |
| 304 | request.compilerOptions.superKernelOptions = {"-g"}; | 304 | request.compilerOptions.superKernelOptions = {"-g"}; |
| 305 | 305 | ||
| 306 | KernelCompilationPlan plan; | 306 | KernelCompilationPlan plan; |
| @@ -335,7 +335,7 @@ TEST(KernelCompilationPlanBuilderTest, SelectsAllRequestedObjectsInManifestOrder | |||
| 335 | const void* argumentAddresses[] = {tilingBytes}; | 335 | const void* argumentAddresses[] = {tilingBytes}; |
| 336 | const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; | 336 | const uint64_t argumentByteCounts[] = {sizeof(tilingBytes)}; |
| 337 | NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); | 337 | NormalizedKernelSpecializationRequest request = CreateRequest(workspace, argumentAddresses, argumentByteCounts); |
| 338 | - request.compilationVariant = CompilationVariant::BasicWithSuperKernel; | 338 | + request.compilationVariant = KernelCompilationVariant::BasicWithSuperKernel; |
| 339 | 339 | ||
| 340 | Json manifest = CreateManifest(); | 340 | Json manifest = CreateManifest(); |
| 341 | Json secondBasicObject = manifest["kernels"][1]["objects"][0]; | 341 | Json secondBasicObject = manifest["kernels"][1]["objects"][0]; |
| @@ -398,7 +398,7 @@ TEST(KernelCompilationPlanBuilderTest, RequiresOnlyEnvironmentUsedBySelectedObje | |||
| 398 | .BuildCompilationPlan(plan), | 398 | .BuildCompilationPlan(plan), |
| 399 | 0); | 399 | 0); |
| 400 | 400 | ||
| 401 | - request.compilationVariant = CompilationVariant::BasicWithSuperKernel; | 401 | + request.compilationVariant = KernelCompilationVariant::BasicWithSuperKernel; |
| 402 | EXPECT_EQ( | 402 | EXPECT_EQ( |
| 403 | KernelCompilationPlanBuilder( | 403 | KernelCompilationPlanBuilder( |
| 404 | request, CreateManifest(), workspace.ResourcePath(), workspace.SourceDirectoryPath()) | 404 | request, CreateManifest(), workspace.ResourcePath(), workspace.SourceDirectoryPath()) |
| @@ -673,7 +673,7 @@ TEST(KernelCompilationPlanBuilderTest, RequiresBasicAndRequestedSuperKernelObjec | |||
| 673 | .BuildCompilationPlan(plan), | 673 | .BuildCompilationPlan(plan), |
| 674 | ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | 674 | ascendc::aclrtc::ACLRTC_ERROR_FAILURE); |
| 675 | 675 | ||
| 676 | - request.compilationVariant = CompilationVariant::BasicWithSuperKernel; | 676 | + request.compilationVariant = KernelCompilationVariant::BasicWithSuperKernel; |
| 677 | Json manifestWithoutSuperKernel = CreateManifest(); | 677 | Json manifestWithoutSuperKernel = CreateManifest(); |
| 678 | manifestWithoutSuperKernel["kernels"][1]["objects"].erase(1); | 678 | manifestWithoutSuperKernel["kernels"][1]["objects"].erase(1); |
| 679 | EXPECT_EQ( | 679 | EXPECT_EQ( |
| @@ -227,7 +227,7 @@ TEST(KernelCompilationWorkspaceTest, RemovesWorktreeBeforeOutputPublicationWhenN | |||
| 227 | { | 227 | { |
| 228 | const fs::path worktreePath = CreateWorkspacePath("aclrtc_workspace_prepublish_cleanup"); | 228 | const fs::path worktreePath = CreateWorkspacePath("aclrtc_workspace_prepublish_cleanup"); |
| 229 | KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RemoveAfterCompilation); | 229 | KernelCompilationWorkspace workspace(worktreePath, WorktreeRetentionPolicy::RemoveAfterCompilation); |
| 230 | - EXPECT_EQ(workspace.RemoveWorktreeBeforePublishingElfIfNeeded(), 0); | 230 | + EXPECT_EQ(workspace.CleanupWorktreeBeforeElfPublication(), 0); |
| 231 | EXPECT_FALSE(fs::exists(worktreePath)); | 231 | EXPECT_FALSE(fs::exists(worktreePath)); |
| 232 | } | 232 | } |
| 233 | 233 | ||
| @@ -266,11 +266,11 @@ TEST(KernelCompilationWorkspaceTest, PreservesSourceWhenTemporaryPatchFilenameIs | |||
| 266 | TEST(KernelCompilationWorkspaceTest, PrepublicationCleanupNoOpsForEmptyAndRetainedWorktrees) | 266 | TEST(KernelCompilationWorkspaceTest, PrepublicationCleanupNoOpsForEmptyAndRetainedWorktrees) |
| 267 | { | 267 | { |
| 268 | KernelCompilationWorkspace emptyWorkspace({}, WorktreeRetentionPolicy::RemoveAfterCompilation); | 268 | KernelCompilationWorkspace emptyWorkspace({}, WorktreeRetentionPolicy::RemoveAfterCompilation); |
| 269 | - EXPECT_EQ(emptyWorkspace.RemoveWorktreeBeforePublishingElfIfNeeded(), ascendc::aclrtc::ACLRTC_SUCCESS); | 269 | + EXPECT_EQ(emptyWorkspace.CleanupWorktreeBeforeElfPublication(), ascendc::aclrtc::ACLRTC_SUCCESS); |
| 270 | 270 | ||
| 271 | const fs::path retainedWorktreePath = CreateWorkspacePath("aclrtc_workspace_retained_prepublish"); | 271 | const fs::path retainedWorktreePath = CreateWorkspacePath("aclrtc_workspace_retained_prepublish"); |
| 272 | KernelCompilationWorkspace retainedWorkspace(retainedWorktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); | 272 | KernelCompilationWorkspace retainedWorkspace(retainedWorktreePath, WorktreeRetentionPolicy::RetainAfterCompilation); |
| 273 | - EXPECT_EQ(retainedWorkspace.RemoveWorktreeBeforePublishingElfIfNeeded(), ascendc::aclrtc::ACLRTC_SUCCESS); | 273 | + EXPECT_EQ(retainedWorkspace.CleanupWorktreeBeforeElfPublication(), ascendc::aclrtc::ACLRTC_SUCCESS); |
| 274 | EXPECT_TRUE(fs::is_directory(retainedWorktreePath)); | 274 | EXPECT_TRUE(fs::is_directory(retainedWorktreePath)); |
| 275 | 275 | ||
| 276 | boost::system::error_code ignoredError; | 276 | boost::system::error_code ignoredError; |
| @@ -280,7 +280,7 @@ TEST(KernelCompilationWorkspaceTest, PrepublicationCleanupNoOpsForEmptyAndRetain | |||
| 280 | TEST(KernelCompilationWorkspaceTest, ReportsWorktreeRemovalFailure) | 280 | TEST(KernelCompilationWorkspaceTest, ReportsWorktreeRemovalFailure) |
| 281 | { | 281 | { |
| 282 | KernelCompilationWorkspace workspace("/proc/self/status", WorktreeRetentionPolicy::RemoveAfterCompilation); | 282 | KernelCompilationWorkspace workspace("/proc/self/status", WorktreeRetentionPolicy::RemoveAfterCompilation); |
| 283 | - EXPECT_EQ(workspace.RemoveWorktreeBeforePublishingElfIfNeeded(), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | 283 | + EXPECT_EQ(workspace.CleanupWorktreeBeforeElfPublication(), ascendc::aclrtc::ACLRTC_ERROR_FAILURE); |
| 284 | EXPECT_EQ(workspace.GetWorktreePath(), "/proc/self/status"); | 284 | EXPECT_EQ(workspace.GetWorktreePath(), "/proc/self/status"); |
| 285 | } | 285 | } |
| 286 | } // namespace | 286 | } // namespace |
| @@ -70,7 +70,7 @@ TEST(KernelSpecializationRequestTest, NormalizesBasicRequestAndCopiesOptions) | |||
| 70 | EXPECT_EQ(normalizedRequest.resourceId, RESOURCE_ID); | 70 | EXPECT_EQ(normalizedRequest.resourceId, RESOURCE_ID); |
| 71 | EXPECT_EQ(normalizedRequest.kernelName, "add_custom_100000"); | 71 | EXPECT_EQ(normalizedRequest.kernelName, "add_custom_100000"); |
| 72 | EXPECT_EQ(normalizedRequest.outputElfPath, "/tmp/kernel.elf"); | 72 | EXPECT_EQ(normalizedRequest.outputElfPath, "/tmp/kernel.elf"); |
| 73 | - EXPECT_EQ(normalizedRequest.compilationVariant, ascendc::aclrtc::CompilationVariant::Basic); | 73 | + EXPECT_EQ(normalizedRequest.compilationVariant, ascendc::aclrtc::KernelCompilationVariant::Basic); |
| 74 | EXPECT_EQ(normalizedRequest.compilerOptions.basicOptions, (std::vector<std::string>{"-g", "-DVALUE=1"})); | 74 | EXPECT_EQ(normalizedRequest.compilerOptions.basicOptions, (std::vector<std::string>{"-g", "-DVALUE=1"})); |
| 75 | EXPECT_TRUE(normalizedRequest.compilerOptions.superKernelOptions.empty()); | 75 | EXPECT_TRUE(normalizedRequest.compilerOptions.superKernelOptions.empty()); |
| 76 | } | 76 | } |
| @@ -96,7 +96,7 @@ TEST(KernelSpecializationRequestTest, EnablesSuperKernelAndRemovesControlOption) | |||
| 96 | 96 | ||
| 97 | ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | 97 | ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; |
| 98 | EXPECT_EQ(ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), 0); | 98 | EXPECT_EQ(ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), 0); |
| 99 | - EXPECT_EQ(normalizedRequest.compilationVariant, ascendc::aclrtc::CompilationVariant::BasicWithSuperKernel); | 99 | + EXPECT_EQ(normalizedRequest.compilationVariant, ascendc::aclrtc::KernelCompilationVariant::BasicWithSuperKernel); |
| 100 | EXPECT_EQ(normalizedRequest.compilerOptions.superKernelOptions, (std::vector<std::string>{"-g", "-DDCCI_OFF"})); | 100 | EXPECT_EQ(normalizedRequest.compilerOptions.superKernelOptions, (std::vector<std::string>{"-g", "-DDCCI_OFF"})); |
| 101 | } | 101 | } |
| 102 | 102 | ||
| @@ -109,7 +109,7 @@ TEST(KernelSpecializationRequestTest, IgnoresSuperKernelOptionsWithoutEnable) | |||
| 109 | 109 | ||
| 110 | ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; | 110 | ascendc::aclrtc::NormalizedKernelSpecializationRequest normalizedRequest; |
| 111 | EXPECT_EQ(ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), 0); | 111 | EXPECT_EQ(ascendc::aclrtc::NormalizeKernelSpecializationRequest(request, "/tmp/kernel.elf", normalizedRequest), 0); |
| 112 | - EXPECT_EQ(normalizedRequest.compilationVariant, ascendc::aclrtc::CompilationVariant::Basic); | 112 | + EXPECT_EQ(normalizedRequest.compilationVariant, ascendc::aclrtc::KernelCompilationVariant::Basic); |
| 113 | EXPECT_TRUE(normalizedRequest.compilerOptions.superKernelOptions.empty()); | 113 | EXPECT_TRUE(normalizedRequest.compilerOptions.superKernelOptions.empty()); |
| 114 | } | 114 | } |
| 115 | 115 | ||
| @@ -9,6 +9,7 @@ | |||
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | #include "specialization/kernel_specialization_diagnostics.h" | 11 | #include "specialization/kernel_specialization_diagnostics.h" |
| 12 | +#include "process_executor.h" | ||
| 12 | 13 | ||
| 13 | #include <gtest/gtest.h> | 14 | #include <gtest/gtest.h> |
| 14 | #include <mockcpp/mockcpp.hpp> | 15 | #include <mockcpp/mockcpp.hpp> |
| @@ -29,11 +30,12 @@ | |||
| 29 | 30 | ||
| 30 | namespace { | 31 | namespace { |
| 31 | namespace fs = boost::filesystem; | 32 | namespace fs = boost::filesystem; |
| 33 | +using ascendc::ProcessExecutorResult; | ||
| 34 | +using ascendc::ProcessOutcome; | ||
| 32 | using ascendc::aclrtc::CompilationCommand; | 35 | using ascendc::aclrtc::CompilationCommand; |
| 33 | using ascendc::aclrtc::CompilationCommandKind; | 36 | using ascendc::aclrtc::CompilationCommandKind; |
| 34 | -using ascendc::aclrtc::CompilationProcessResult; | 37 | +using ascendc::aclrtc::KernelElfPublicationStatus; |
| 35 | using ascendc::aclrtc::KernelSpecializationDiagnostics; | 38 | using ascendc::aclrtc::KernelSpecializationDiagnostics; |
| 36 | -using ascendc::aclrtc::OutputPublicationStatus; | ||
| 37 | using namespace mockcpp; | 39 | using namespace mockcpp; |
| 38 | 40 | ||
| 39 | int RejectReplayScriptPermissionChange(const char*, mode_t) | 41 | int RejectReplayScriptPermissionChange(const char*, mode_t) |
| @@ -112,10 +114,9 @@ TEST(KernelSpecializationDiagnosticsTest, SavesOnlyReviewedDiagnosticFiles) | |||
| 112 | "/bin/sh", | 114 | "/bin/sh", |
| 113 | {"-c", "test \"$PWD\" = \"$1\"", "replay-working-directory-check", testRoot.string()}}; | 115 | {"-c", "test \"$PWD\" = \"$1\"", "replay-working-directory-check", testRoot.string()}}; |
| 114 | diagnostics.AppendCommandToReplayScript(workingDirectoryCheck); | 116 | diagnostics.AppendCommandToReplayScript(workingDirectoryCheck); |
| 115 | - CompilationProcessResult processResult; | 117 | + ProcessExecutorResult executorResult; |
| 116 | - processResult.capturedOutput = "compiler output\n"; | 118 | + diagnostics.AppendCommandExecutionResultToLog(command, executorResult); |
| 117 | - diagnostics.AppendCommandResultToCompilationLog(command, processResult); | 119 | + diagnostics.WriteSpecializationResult("/tmp/kernel.elf", KernelElfPublicationStatus::Published); |
| 118 | - diagnostics.WriteSpecializationResult("/tmp/kernel.elf", OutputPublicationStatus::Published); | ||
| 119 | } | 120 | } |
| 120 | 121 | ||
| 121 | const fs::path replayScriptPath = testRoot / "aclrtc_replay.sh"; | 122 | const fs::path replayScriptPath = testRoot / "aclrtc_replay.sh"; |
| @@ -152,9 +153,9 @@ TEST(KernelSpecializationDiagnosticsTest, DisablesDiagnosticsWhenReplayScriptCan | |||
| 152 | 153 | ||
| 153 | const CompilationCommand command{CompilationCommandKind::Compile, std::nullopt, "compile", "/bin/true", {}}; | 154 | const CompilationCommand command{CompilationCommandKind::Compile, std::nullopt, "compile", "/bin/true", {}}; |
| 154 | diagnostics.AppendCommandToReplayScript(command); | 155 | diagnostics.AppendCommandToReplayScript(command); |
| 155 | - diagnostics.AppendCommandResultToCompilationLog(command, CompilationProcessResult{}); | 156 | + diagnostics.AppendCommandExecutionResultToLog(command, ProcessExecutorResult{}); |
| 156 | diagnostics.LogCommandFailureRecoveryHint(); | 157 | diagnostics.LogCommandFailureRecoveryHint(); |
| 157 | - diagnostics.WriteSpecializationResult("/tmp/kernel.elf", OutputPublicationStatus::NotPublished); | 158 | + diagnostics.WriteSpecializationResult("/tmp/kernel.elf", KernelElfPublicationStatus::NotPublished); |
| 158 | 159 | ||
| 159 | EXPECT_FALSE(fs::exists(missingWorktreePath)); | 160 | EXPECT_FALSE(fs::exists(missingWorktreePath)); |
| 160 | } | 161 | } |
| @@ -257,21 +258,21 @@ TEST(KernelSpecializationDiagnosticsTest, RecordsTerminationKindsAndUnpublishedR | |||
| 257 | KernelSpecializationDiagnostics diagnostics(testRoot, "session_states"); | 258 | KernelSpecializationDiagnostics diagnostics(testRoot, "session_states"); |
| 258 | const CompilationCommand command{CompilationCommandKind::Compile, std::nullopt, "compile", "/bin/true", {}}; | 259 | const CompilationCommand command{CompilationCommandKind::Compile, std::nullopt, "compile", "/bin/true", {}}; |
| 259 | 260 | ||
| 260 | - CompilationProcessResult processResult; | 261 | + ProcessExecutorResult executorResult; |
| 261 | - processResult.termination = ascendc::aclrtc::CompilationProcessTermination::Signaled; | 262 | + executorResult.outcome = ProcessOutcome::Signaled; |
| 262 | - processResult.terminationCode = SIGTERM; | 263 | + executorResult.terminationCode = SIGTERM; |
| 263 | - diagnostics.AppendCommandResultToCompilationLog(command, processResult); | 264 | + diagnostics.AppendCommandExecutionResultToLog(command, executorResult); |
| 264 | - processResult.termination = ascendc::aclrtc::CompilationProcessTermination::TimedOut; | 265 | + executorResult.outcome = ProcessOutcome::TimedOut; |
| 265 | - diagnostics.AppendCommandResultToCompilationLog(command, processResult); | 266 | + diagnostics.AppendCommandExecutionResultToLog(command, executorResult); |
| 266 | - processResult.termination = static_cast<ascendc::aclrtc::CompilationProcessTermination>(UINT32_MAX); | 267 | + executorResult.outcome = static_cast<ProcessOutcome>(UINT32_MAX); |
| 267 | - diagnostics.AppendCommandResultToCompilationLog(command, processResult); | 268 | + diagnostics.AppendCommandExecutionResultToLog(command, executorResult); |
| 268 | diagnostics.LogCommandFailureRecoveryHint(); | 269 | diagnostics.LogCommandFailureRecoveryHint(); |
| 269 | - diagnostics.WriteSpecializationResult("/tmp/not_published.elf", OutputPublicationStatus::NotPublished); | 270 | + diagnostics.WriteSpecializationResult("/tmp/not_published.elf", KernelElfPublicationStatus::NotPublished); |
| 270 | 271 | ||
| 271 | const std::string compilationLog = ReadTextFile(testRoot / "aclrtc_compile.log"); | 272 | const std::string compilationLog = ReadTextFile(testRoot / "aclrtc_compile.log"); |
| 272 | - EXPECT_NE(compilationLog.find("termination=signaled"), std::string::npos); | 273 | + EXPECT_NE(compilationLog.find("outcome=signaled"), std::string::npos); |
| 273 | - EXPECT_NE(compilationLog.find("termination=timed_out"), std::string::npos); | 274 | + EXPECT_NE(compilationLog.find("outcome=timed_out"), std::string::npos); |
| 274 | - EXPECT_NE(compilationLog.find("termination=unknown"), std::string::npos); | 275 | + EXPECT_NE(compilationLog.find("outcome=unknown"), std::string::npos); |
| 275 | const nlohmann::json result = nlohmann::json::parse(ReadTextFile(testRoot / "aclrtc_result.json")); | 276 | const nlohmann::json result = nlohmann::json::parse(ReadTextFile(testRoot / "aclrtc_result.json")); |
| 276 | EXPECT_FALSE(result.at("output_published")); | 277 | EXPECT_FALSE(result.at("output_published")); |
| 277 | 278 | ||
| @@ -292,8 +293,8 @@ TEST(KernelSpecializationDiagnosticsTest, IgnoresDiagnosticWriteFailuresAfterWor | |||
| 292 | CompilationCommandKind::Compile, std::nullopt, "compile", "/bin/true", {"argument"}}; | 293 | CompilationCommandKind::Compile, std::nullopt, "compile", "/bin/true", {"argument"}}; |
| 293 | diagnostics.WriteManifestSnapshot(nlohmann::json{{"schema_version", "1.0"}}); | 294 | diagnostics.WriteManifestSnapshot(nlohmann::json{{"schema_version", "1.0"}}); |
| 294 | diagnostics.AppendCommandToReplayScript(command); | 295 | diagnostics.AppendCommandToReplayScript(command); |
| 295 | - diagnostics.AppendCommandResultToCompilationLog(command, CompilationProcessResult{}); | 296 | + diagnostics.AppendCommandExecutionResultToLog(command, ProcessExecutorResult{}); |
| 296 | - diagnostics.WriteSpecializationResult("/tmp/kernel.elf", OutputPublicationStatus::NotPublished); | 297 | + diagnostics.WriteSpecializationResult("/tmp/kernel.elf", KernelElfPublicationStatus::NotPublished); |
| 297 | 298 | ||
| 298 | EXPECT_FALSE(fs::exists(testRoot)); | 299 | EXPECT_FALSE(fs::exists(testRoot)); |
| 299 | } | 300 | } |
| @@ -31,6 +31,7 @@ | |||
| 31 | namespace { | 31 | namespace { |
| 32 | namespace fs = boost::filesystem; | 32 | namespace fs = boost::filesystem; |
| 33 | using Json = nlohmann::json; | 33 | using Json = nlohmann::json; |
| 34 | +using ascendc::DirectoryCleanupGuard; | ||
| 34 | using ascendc::aclrtc::KernelCompilationWorkspace; | 35 | using ascendc::aclrtc::KernelCompilationWorkspace; |
| 35 | using ascendc::aclrtc::KernelSpecializationSession; | 36 | using ascendc::aclrtc::KernelSpecializationSession; |
| 36 | using ascendc::aclrtc::MaterializedKernelCompilationResource; | 37 | using ascendc::aclrtc::MaterializedKernelCompilationResource; |
| @@ -193,14 +194,13 @@ public: | |||
| 193 | std::vector<ResourceFileData> resourceFiles = {{"payload.bin", "payload.bin", {0x01U, 0x02U}}}) | 194 | std::vector<ResourceFileData> resourceFiles = {{"payload.bin", "payload.bin", {0x01U, 0x02U}}}) |
| 194 | : registry_(ResourceRegistry::Instance()), | 195 | : registry_(ResourceRegistry::Instance()), |
| 195 | resourceId_(std::move(resourceId)), | 196 | resourceId_(std::move(resourceId)), |
| 196 | - previousTemporaryRoot_(registry_.temporaryRoot_), | 197 | + previousOwnedMaterializationRoot_(std::move(registry_.ownedMaterializationRoot_)), |
| 197 | previousAutomaticLoadAttempted_(registry_.automaticLoadAttempted_), | 198 | previousAutomaticLoadAttempted_(registry_.automaticLoadAttempted_), |
| 198 | - previousKeepTemporaryRoot_(registry_.keepTemporaryRoot_), | 199 | + previousRetainMaterializedDirectories_(registry_.retainMaterializedDirectories_), |
| 199 | previousAutomaticLoadStatus_(registry_.automaticLoadStatus_) | 200 | previousAutomaticLoadStatus_(registry_.automaticLoadStatus_) |
| 200 | { | 201 | { |
| 201 | - registry_.temporaryRoot_.clear(); | ||
| 202 | registry_.automaticLoadAttempted_ = true; | 202 | registry_.automaticLoadAttempted_ = true; |
| 203 | - registry_.keepTemporaryRoot_ = false; | 203 | + registry_.retainMaterializedDirectories_ = false; |
| 204 | registry_.automaticLoadStatus_ = ResourceStatus::Success; | 204 | registry_.automaticLoadStatus_ = ResourceStatus::Success; |
| 205 | 205 | ||
| 206 | std::unique_ptr<ResourceEntry> resourceEntry(new ResourceEntry()); | 206 | std::unique_ptr<ResourceEntry> resourceEntry(new ResourceEntry()); |
| @@ -216,11 +216,9 @@ public: | |||
| 216 | if (inserted_) { | 216 | if (inserted_) { |
| 217 | registry_.externalResources_.erase(resourceId_); | 217 | registry_.externalResources_.erase(resourceId_); |
| 218 | } | 218 | } |
| 219 | - boost::system::error_code ignoredError; | 219 | + registry_.ownedMaterializationRoot_ = std::move(previousOwnedMaterializationRoot_); |
| 220 | - fs::remove_all(registry_.temporaryRoot_, ignoredError); | ||
| 221 | - registry_.temporaryRoot_ = previousTemporaryRoot_; | ||
| 222 | registry_.automaticLoadAttempted_ = previousAutomaticLoadAttempted_; | 220 | registry_.automaticLoadAttempted_ = previousAutomaticLoadAttempted_; |
| 223 | - registry_.keepTemporaryRoot_ = previousKeepTemporaryRoot_; | 221 | + registry_.retainMaterializedDirectories_ = previousRetainMaterializedDirectories_; |
| 224 | registry_.automaticLoadStatus_ = previousAutomaticLoadStatus_; | 222 | registry_.automaticLoadStatus_ = previousAutomaticLoadStatus_; |
| 225 | } | 223 | } |
| 226 | 224 | ||
| @@ -231,9 +229,9 @@ public: | |||
| 231 | private: | 229 | private: |
| 232 | ResourceRegistry& registry_; | 230 | ResourceRegistry& registry_; |
| 233 | std::string resourceId_; | 231 | std::string resourceId_; |
| 234 | - std::string previousTemporaryRoot_; | 232 | + DirectoryCleanupGuard previousOwnedMaterializationRoot_; |
| 235 | bool previousAutomaticLoadAttempted_{false}; | 233 | bool previousAutomaticLoadAttempted_{false}; |
| 236 | - bool previousKeepTemporaryRoot_{false}; | 234 | + bool previousRetainMaterializedDirectories_{false}; |
| 237 | ResourceStatus previousAutomaticLoadStatus_{ResourceStatus::Success}; | 235 | ResourceStatus previousAutomaticLoadStatus_{ResourceStatus::Success}; |
| 238 | bool inserted_{false}; | 236 | bool inserted_{false}; |
| 239 | }; | 237 | }; |
| @@ -367,13 +365,13 @@ TEST(KernelSpecializationSessionTest, LoadsAndCopiesFreshMaterializedPathsFromRe | |||
| 367 | registeredResource.SetAutomaticLoadStatus(ResourceStatus::NotFound); | 365 | registeredResource.SetAutomaticLoadStatus(ResourceStatus::NotFound); |
| 368 | MaterializedKernelCompilationResource rejectedResource; | 366 | MaterializedKernelCompilationResource rejectedResource; |
| 369 | EXPECT_EQ( | 367 | EXPECT_EQ( |
| 370 | - KernelSpecializationSession::PrepareCompilationResource(resourceId, rejectedResource), | 368 | + KernelSpecializationSession::LoadAndMaterializeCompilationResource(resourceId, rejectedResource), |
| 371 | ascendc::aclrtc::ACLRTC_ERROR_FAILURE); | 369 | ascendc::aclrtc::ACLRTC_ERROR_FAILURE); |
| 372 | 370 | ||
| 373 | registeredResource.SetAutomaticLoadStatus(ResourceStatus::Success); | 371 | registeredResource.SetAutomaticLoadStatus(ResourceStatus::Success); |
| 374 | MaterializedKernelCompilationResource firstResource; | 372 | MaterializedKernelCompilationResource firstResource; |
| 375 | ASSERT_EQ( | 373 | ASSERT_EQ( |
| 376 | - KernelSpecializationSession::PrepareCompilationResource(resourceId, firstResource), | 374 | + KernelSpecializationSession::LoadAndMaterializeCompilationResource(resourceId, firstResource), |
| 377 | ascendc::aclrtc::ACLRTC_SUCCESS); | 375 | ascendc::aclrtc::ACLRTC_SUCCESS); |
| 378 | EXPECT_EQ(firstResource.manifest, manifest); | 376 | EXPECT_EQ(firstResource.manifest, manifest); |
| 379 | EXPECT_EQ(firstResource.externalSourceDirectoryPath, sourceDirectoryPath); | 377 | EXPECT_EQ(firstResource.externalSourceDirectoryPath, sourceDirectoryPath); |
| @@ -381,7 +379,7 @@ TEST(KernelSpecializationSessionTest, LoadsAndCopiesFreshMaterializedPathsFromRe | |||
| 381 | 379 | ||
| 382 | MaterializedKernelCompilationResource secondResource; | 380 | MaterializedKernelCompilationResource secondResource; |
| 383 | ASSERT_EQ( | 381 | ASSERT_EQ( |
| 384 | - KernelSpecializationSession::PrepareCompilationResource(resourceId, secondResource), | 382 | + KernelSpecializationSession::LoadAndMaterializeCompilationResource(resourceId, secondResource), |
| 385 | ascendc::aclrtc::ACLRTC_SUCCESS); | 383 | ascendc::aclrtc::ACLRTC_SUCCESS); |
| 386 | EXPECT_NE(firstResource.ownedResourceDirectoryPath, secondResource.ownedResourceDirectoryPath); | 384 | EXPECT_NE(firstResource.ownedResourceDirectoryPath, secondResource.ownedResourceDirectoryPath); |
| 387 | EXPECT_TRUE(fs::is_regular_file(secondResource.ownedResourceDirectoryPath / "payload.bin")); | 385 | EXPECT_TRUE(fs::is_regular_file(secondResource.ownedResourceDirectoryPath / "payload.bin")); |
| @@ -1202,8 +1202,8 @@ TEST_F(ResourceRegistryTest, LookupReportsTemporaryRootAndMaterializationFailure | |||
| 1202 | EXPECT_EQ(registry_->Lookup("bad-temp", output), ResourceStatus::IoError); | 1202 | EXPECT_EQ(registry_->Lookup("bad-temp", output), ResourceStatus::IoError); |
| 1203 | EXPECT_TRUE(output.resourceDir.empty()); | 1203 | EXPECT_TRUE(output.resourceDir.empty()); |
| 1204 | 1204 | ||
| 1205 | - registry_->temporaryRoot_ = Path("forced-materialize").string(); | 1205 | + ASSERT_TRUE(registry_->ownedMaterializationRoot_.RegisterDirectoryForCleanup(Path("forced-materialize").string())); |
| 1206 | - WriteFile(fs::path(registry_->temporaryRoot_) / "external", "blocking file"); | 1206 | + WriteFile(fs::path(registry_->ownedMaterializationRoot_.GetDirectoryPath()) / "external", "blocking file"); |
| 1207 | output.resourceDir = "stale"; | 1207 | output.resourceDir = "stale"; |
| 1208 | EXPECT_EQ(registry_->Lookup("bad-temp", output), ResourceStatus::IoError); | 1208 | EXPECT_EQ(registry_->Lookup("bad-temp", output), ResourceStatus::IoError); |
| 1209 | EXPECT_TRUE(output.resourceDir.empty()); | 1209 | EXPECT_TRUE(output.resourceDir.empty()); |
| @@ -1216,7 +1216,7 @@ TEST_F(ResourceRegistryTest, DestructorHonorsSaveKernelMetaAtTemporaryRootCreati | |||
| 1216 | registry_->externalResources_.emplace("keep-temp", MakeEntry(ResourceSourceType::External)); | 1216 | registry_->externalResources_.emplace("keep-temp", MakeEntry(ResourceSourceType::External)); |
| 1217 | ResourceData output; | 1217 | ResourceData output; |
| 1218 | EXPECT_EQ(registry_->Lookup("keep-temp", output), ResourceStatus::Success); | 1218 | EXPECT_EQ(registry_->Lookup("keep-temp", output), ResourceStatus::Success); |
| 1219 | - const std::string retainedRoot = registry_->temporaryRoot_; | 1219 | + const std::string retainedRoot = registry_->ownedMaterializationRoot_.GetDirectoryPath(); |
| 1220 | ASSERT_TRUE(fs::is_directory(retainedRoot)); | 1220 | ASSERT_TRUE(fs::is_directory(retainedRoot)); |
| 1221 | registry_.reset(); | 1221 | registry_.reset(); |
| 1222 | EXPECT_TRUE(fs::is_directory(retainedRoot)); | 1222 | EXPECT_TRUE(fs::is_directory(retainedRoot)); |
| @@ -1225,7 +1225,7 @@ TEST_F(ResourceRegistryTest, DestructorHonorsSaveKernelMetaAtTemporaryRootCreati | |||
| 1225 | SetEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", "true"); | 1225 | SetEnvironment("ASCEND_OP_COMPILE_SAVE_KERNEL_META", "true"); |
| 1226 | registry_->externalResources_.emplace("remove-temp", MakeEntry(ResourceSourceType::External)); | 1226 | registry_->externalResources_.emplace("remove-temp", MakeEntry(ResourceSourceType::External)); |
| 1227 | EXPECT_EQ(registry_->Lookup("remove-temp", output), ResourceStatus::Success); | 1227 | EXPECT_EQ(registry_->Lookup("remove-temp", output), ResourceStatus::Success); |
| 1228 | - const std::string removedRoot = registry_->temporaryRoot_; | 1228 | + const std::string removedRoot = registry_->ownedMaterializationRoot_.GetDirectoryPath(); |
| 1229 | ASSERT_TRUE(fs::is_directory(removedRoot)); | 1229 | ASSERT_TRUE(fs::is_directory(removedRoot)); |
| 1230 | registry_.reset(); | 1230 | registry_.reset(); |
| 1231 | EXPECT_FALSE(fs::exists(removedRoot)); | 1231 | EXPECT_FALSE(fs::exists(removedRoot)); |
| @@ -1241,8 +1241,8 @@ TEST_F(ResourceRegistryTest, SaveKernelMetaTrimsWhitespaceAndKeepsFailedMaterial | |||
| 1241 | ResourceData output; | 1241 | ResourceData output; |
| 1242 | EXPECT_EQ(registry_->Lookup("failed-materialization", output), ResourceStatus::IoError); | 1242 | EXPECT_EQ(registry_->Lookup("failed-materialization", output), ResourceStatus::IoError); |
| 1243 | EXPECT_TRUE(output.resourceDir.empty()); | 1243 | EXPECT_TRUE(output.resourceDir.empty()); |
| 1244 | - EXPECT_TRUE(registry_->keepTemporaryRoot_); | 1244 | + EXPECT_TRUE(registry_->retainMaterializedDirectories_); |
| 1245 | - const fs::path retainedRoot(registry_->temporaryRoot_); | 1245 | + const fs::path retainedRoot(registry_->ownedMaterializationRoot_.GetDirectoryPath()); |
| 1246 | const fs::path categoryParent = retainedRoot / "external"; | 1246 | const fs::path categoryParent = retainedRoot / "external"; |
| 1247 | std::vector<fs::path> retainedCategories; | 1247 | std::vector<fs::path> retainedCategories; |
| 1248 | for (fs::directory_iterator current(categoryParent), end; current != end; ++current) { | 1248 | for (fs::directory_iterator current(categoryParent), end; current != end; ++current) { |
| @@ -202,21 +202,21 @@ add_executable(ascendc_ut_asc_compile_exporter | |||
| 202 | ${ASCENDC_DIR}/tests/main.cpp | 202 | ${ASCENDC_DIR}/tests/main.cpp |
| 203 | asc_compile_exporter/test_asc_compile_exporter_unit.cpp | 203 | asc_compile_exporter/test_asc_compile_exporter_unit.cpp |
| 204 | asc_compile_exporter/test_asc_compile_exporter.cpp | 204 | asc_compile_exporter/test_asc_compile_exporter.cpp |
| 205 | - asc_compile_exporter/test_build_workspace.cpp | ||
| 206 | asc_compile_exporter/test_bundle_source_generator.cpp | 205 | asc_compile_exporter/test_bundle_source_generator.cpp |
| 207 | asc_compile_exporter/test_collected_manifest_repository.cpp | 206 | asc_compile_exporter/test_collected_manifest_repository.cpp |
| 208 | asc_compile_exporter/test_file_utils.cpp | 207 | asc_compile_exporter/test_file_utils.cpp |
| 209 | asc_compile_exporter/test_manifest_bundle_types.cpp | 208 | asc_compile_exporter/test_manifest_bundle_types.cpp |
| 210 | - asc_compile_exporter/test_process_runner.cpp | 209 | + common/test_process_executor.cpp |
| 211 | asc_compile_exporter/test_resource_manifest_validator.cpp | 210 | asc_compile_exporter/test_resource_manifest_validator.cpp |
| 211 | + common/test_directory_cleanup_guard.cpp | ||
| 212 | asc_compile_exporter/test_ascendc_tool_log.cpp | 212 | asc_compile_exporter/test_ascendc_tool_log.cpp |
| 213 | - ${ASCENDC_DIR}/tools/build/asc_compile_exporter/build_workspace.cpp | ||
| 214 | ${ASCENDC_DIR}/tools/build/asc_compile_exporter/bundle_source_generator.cpp | 213 | ${ASCENDC_DIR}/tools/build/asc_compile_exporter/bundle_source_generator.cpp |
| 215 | ${ASCENDC_DIR}/tools/build/asc_compile_exporter/collected_manifest_repository.cpp | 214 | ${ASCENDC_DIR}/tools/build/asc_compile_exporter/collected_manifest_repository.cpp |
| 216 | ${ASCENDC_DIR}/tools/build/asc_compile_exporter/manifest_bundle_compiler.cpp | 215 | ${ASCENDC_DIR}/tools/build/asc_compile_exporter/manifest_bundle_compiler.cpp |
| 217 | ${ASCENDC_DIR}/tools/build/asc_compile_exporter/resource_manifest_validator.cpp | 216 | ${ASCENDC_DIR}/tools/build/asc_compile_exporter/resource_manifest_validator.cpp |
| 218 | ${ASCENDC_DIR}/tools/build/common/file_utils.cpp | 217 | ${ASCENDC_DIR}/tools/build/common/file_utils.cpp |
| 219 | - ${ASCENDC_DIR}/tools/build/common/process_runner.cpp | 218 | + ${ASCENDC_DIR}/tools/build/common/process_executor.cpp |
| 219 | + ${ASCENDC_DIR}/tools/build/common/directory_cleanup_guard.cpp | ||
| 220 | ) | 220 | ) |
| 221 | 221 | ||
| 222 | function(configure_asc_compile_exporter_test target_name) | 222 | function(configure_asc_compile_exporter_test target_name) |
| @@ -235,9 +235,7 @@ function(configure_asc_compile_exporter_test target_name) | |||
| 235 | ) | 235 | ) |
| 236 | 236 | ||
| 237 | target_link_options(${target_name} PRIVATE | 237 | target_link_options(${target_name} PRIVATE |
| 238 | - -Wl,--wrap=fork | ||
| 239 | -Wl,--wrap=rename | 238 | -Wl,--wrap=rename |
| 240 | - -Wl,--wrap=waitpid | ||
| 241 | ) | 239 | ) |
| 242 | 240 | ||
| 243 | target_link_libraries(${target_name} PRIVATE | 241 | target_link_libraries(${target_name} PRIVATE |
| @@ -15,6 +15,7 @@ | |||
| 15 | #include <unistd.h> | 15 | #include <unistd.h> |
| 16 | 16 | ||
| 17 | #include <cerrno> | 17 | #include <cerrno> |
| 18 | +#include <chrono> | ||
| 18 | #include <cstdlib> | 19 | #include <cstdlib> |
| 19 | #include <cstring> | 20 | #include <cstring> |
| 20 | #include <fstream> | 21 | #include <fstream> |
| @@ -28,13 +29,14 @@ | |||
| 28 | 29 | ||
| 29 | #include "ascendc_manifest_abi.h" | 30 | #include "ascendc_manifest_abi.h" |
| 30 | #include "manifest_bundle_compiler.h" | 31 | #include "manifest_bundle_compiler.h" |
| 31 | -#include "process_runner.h" | 32 | +#include "process_executor.h" |
| 32 | #include "test_support.h" | 33 | #include "test_support.h" |
| 33 | 34 | ||
| 34 | namespace { | 35 | namespace { |
| 35 | 36 | ||
| 36 | using asc_compile_exporter_test::ScopedEnvironment; | 37 | using asc_compile_exporter_test::ScopedEnvironment; |
| 37 | -using ascendc::ProcessRunner; | 38 | +using ascendc::ProcessExecutor; |
| 39 | +using ascendc::ProcessExecutorRequest; | ||
| 38 | using ascendc::manifest_generator::BuildCollectedBundleRequest; | 40 | using ascendc::manifest_generator::BuildCollectedBundleRequest; |
| 39 | using ascendc::manifest_generator::BundleOutputKind; | 41 | using ascendc::manifest_generator::BundleOutputKind; |
| 40 | using ascendc::manifest_generator::ManifestBundleCompiler; | 42 | using ascendc::manifest_generator::ManifestBundleCompiler; |
| @@ -371,6 +373,7 @@ TEST_F(AscCompileExporterTest, BuildsLoadableBundleAndCleansTemporaryWorkspace) | |||
| 371 | { | 373 | { |
| 372 | ScopedEnvironment temporary("TMPDIR", root_.c_str()); | 374 | ScopedEnvironment temporary("TMPDIR", root_.c_str()); |
| 373 | ScopedEnvironment cannHome("ASCEND_HOME_PATH", nullptr); | 375 | ScopedEnvironment cannHome("ASCEND_HOME_PATH", nullptr); |
| 376 | + ScopedEnvironment inheritedMakeFlags("MAKEFLAGS", "--definitely-invalid-option"); | ||
| 374 | const std::string first = JoinTestPath(root_, "collection/nested/First"); | 377 | const std::string first = JoinTestPath(root_, "collection/nested/First"); |
| 375 | const std::string second = JoinTestPath(root_, "collection/Second"); | 378 | const std::string second = JoinTestPath(root_, "collection/Second"); |
| 376 | WriteFile(JoinTestPath(first, "resources/kernel.cpp"), "first source"); | 379 | WriteFile(JoinTestPath(first, "resources/kernel.cpp"), "first source"); |
| @@ -450,7 +453,11 @@ TEST_F(AscCompileExporterTest, BuildsRelocatableObjectThatCanBeLinkedIntoSharedL | |||
| 450 | EXPECT_EQ(elfType, ET_REL); | 453 | EXPECT_EQ(elfType, ET_REL); |
| 451 | 454 | ||
| 452 | const std::string linkedBundle = JoinTestPath(root_, "output/linked-bundle.so"); | 455 | const std::string linkedBundle = JoinTestPath(root_, "output/linked-bundle.so"); |
| 453 | - ASSERT_TRUE(ProcessRunner::Run({request.cxxCompiler, "-shared", request.outputPath, "-o", linkedBundle}, {})); | 456 | + ProcessExecutorRequest linkRequest; |
| 457 | + linkRequest.arguments = {request.cxxCompiler, "-shared", request.outputPath, "-o", linkedBundle}; | ||
| 458 | + linkRequest.executionTimeout = std::chrono::minutes(5); | ||
| 459 | + linkRequest.terminationGracePeriod = std::chrono::seconds(1); | ||
| 460 | + ASSERT_TRUE(ProcessExecutor::Execute(linkRequest).HasSuccessfulExit()); | ||
| 454 | 461 | ||
| 455 | void* handle = dlopen(linkedBundle.c_str(), RTLD_NOW | RTLD_LOCAL); | 462 | void* handle = dlopen(linkedBundle.c_str(), RTLD_NOW | RTLD_LOCAL); |
| 456 | const char* loadError = handle == nullptr ? dlerror() : nullptr; | 463 | const char* loadError = handle == nullptr ? dlerror() : nullptr; |
| @@ -1,70 +0,0 @@ | |||
| 1 | -/** | ||
| 2 | - * Copyright (c) 2025 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 | -#include <gtest/gtest.h> | ||
| 12 | - | ||
| 13 | -#include "build_workspace.h" | ||
| 14 | -#include "file_utils.h" | ||
| 15 | -#include "test_support.h" | ||
| 16 | - | ||
| 17 | -namespace ascendc { | ||
| 18 | -namespace manifest_generator { | ||
| 19 | -namespace { | ||
| 20 | - | ||
| 21 | -using asc_compile_exporter_test::LstatPathExists; | ||
| 22 | -using asc_compile_exporter_test::ModuleTest; | ||
| 23 | -using asc_compile_exporter_test::ScopedEnvironment; | ||
| 24 | -using asc_compile_exporter_test::WriteTestFile; | ||
| 25 | - | ||
| 26 | -TEST_F(ModuleTest, BuildWorkspaceHonorsCleanupPolicy) | ||
| 27 | -{ | ||
| 28 | - const std::string parent = FileUtils::JoinPath(root_, "sessions"); | ||
| 29 | - std::string removedSession; | ||
| 30 | - { | ||
| 31 | - BuildWorkspace workspace; | ||
| 32 | - ASSERT_TRUE(workspace.Create(parent, false)); | ||
| 33 | - removedSession = workspace.Session(); | ||
| 34 | - EXPECT_TRUE(FileUtils::IsDirectory(removedSession)); | ||
| 35 | - EXPECT_FALSE(workspace.Create(parent, false)); | ||
| 36 | - EXPECT_EQ(workspace.Session(), removedSession); | ||
| 37 | - } | ||
| 38 | - EXPECT_FALSE(LstatPathExists(removedSession)); | ||
| 39 | - | ||
| 40 | - std::string keptSession; | ||
| 41 | - { | ||
| 42 | - BuildWorkspace workspace; | ||
| 43 | - ASSERT_TRUE(workspace.Create(parent, true)); | ||
| 44 | - keptSession = workspace.Session(); | ||
| 45 | - } | ||
| 46 | - EXPECT_TRUE(FileUtils::IsDirectory(keptSession)); | ||
| 47 | - | ||
| 48 | - const std::string regularFile = FileUtils::JoinPath(root_, "not-a-directory"); | ||
| 49 | - WriteTestFile(regularFile, "file"); | ||
| 50 | - BuildWorkspace failedWorkspace; | ||
| 51 | - EXPECT_FALSE(failedWorkspace.Create(regularFile, false)); | ||
| 52 | - EXPECT_FALSE(failedWorkspace.Create("/proc", false)); | ||
| 53 | -} | ||
| 54 | - | ||
| 55 | -TEST_F(ModuleTest, BuildWorkspaceUsesTmpDir) | ||
| 56 | -{ | ||
| 57 | - ScopedEnvironment temporary("TMPDIR", root_.c_str()); | ||
| 58 | - std::string session; | ||
| 59 | - { | ||
| 60 | - BuildWorkspace workspace; | ||
| 61 | - ASSERT_TRUE(workspace.Create("", false)); | ||
| 62 | - session = workspace.Session(); | ||
| 63 | - EXPECT_TRUE(FileUtils::IsPathWithin(session, root_)); | ||
| 64 | - } | ||
| 65 | - EXPECT_FALSE(LstatPathExists(session)); | ||
| 66 | -} | ||
| 67 | - | ||
| 68 | -} // namespace | ||
| 69 | -} // namespace manifest_generator | ||
| 70 | -} // namespace ascendc | ||
| @@ -1,112 +0,0 @@ | |||
| 1 | -/** | ||
| 2 | - * Copyright (c) 2025 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 | -#include <gtest/gtest.h> | ||
| 12 | - | ||
| 13 | -#include <cerrno> | ||
| 14 | -#include <chrono> | ||
| 15 | -#include <cstdlib> | ||
| 16 | - | ||
| 17 | -#include <signal.h> | ||
| 18 | -#include <sys/wait.h> | ||
| 19 | - | ||
| 20 | -#include "file_utils.h" | ||
| 21 | -#include "process_runner.h" | ||
| 22 | -#include "test_support.h" | ||
| 23 | - | ||
| 24 | -namespace { | ||
| 25 | - | ||
| 26 | -bool g_failFork = false; | ||
| 27 | -bool g_failNextWaitpid = false; | ||
| 28 | - | ||
| 29 | -} // namespace | ||
| 30 | - | ||
| 31 | -extern "C" pid_t __real_fork(void); | ||
| 32 | -extern "C" pid_t __real_waitpid(pid_t pid, int* status, int options); | ||
| 33 | - | ||
| 34 | -extern "C" pid_t __wrap_fork(void) | ||
| 35 | -{ | ||
| 36 | - if (g_failFork) { | ||
| 37 | - errno = EAGAIN; | ||
| 38 | - return -1; | ||
| 39 | - } | ||
| 40 | - return __real_fork(); | ||
| 41 | -} | ||
| 42 | - | ||
| 43 | -extern "C" pid_t __wrap_waitpid(pid_t pid, int* status, int options) | ||
| 44 | -{ | ||
| 45 | - if (g_failNextWaitpid) { | ||
| 46 | - g_failNextWaitpid = false; | ||
| 47 | - errno = EIO; | ||
| 48 | - return -1; | ||
| 49 | - } | ||
| 50 | - return __real_waitpid(pid, status, options); | ||
| 51 | -} | ||
| 52 | - | ||
| 53 | -namespace ascendc { | ||
| 54 | -namespace { | ||
| 55 | - | ||
| 56 | -using asc_compile_exporter_test::ModuleTest; | ||
| 57 | -using asc_compile_exporter_test::ReadTestFile; | ||
| 58 | -using asc_compile_exporter_test::ScopedEnvironment; | ||
| 59 | - | ||
| 60 | -TEST_F(ModuleTest, ProcessRunnerValidatesArgumentsAndPropagatesEnvironment) | ||
| 61 | -{ | ||
| 62 | - EXPECT_FALSE(ProcessRunner::Run({}, {})); | ||
| 63 | - EXPECT_FALSE(ProcessRunner::Run({"/bin/true"}, {}, std::chrono::milliseconds(0U))); | ||
| 64 | - EXPECT_FALSE(ProcessRunner::Run({"/bin/sh", "-c", "exit 7"}, {})); | ||
| 65 | - | ||
| 66 | - const std::string output = FileUtils::JoinPath(root_, "environment.txt"); | ||
| 67 | - ScopedEnvironment makeFlags("MAKEFLAGS", "must-not-leak"); | ||
| 68 | - ASSERT_TRUE(ProcessRunner::Run( | ||
| 69 | - {"/bin/sh", "-c", | ||
| 70 | - "printf '%s|%s' \"$ASC_COMPILE_EXPORTER_UNIT_VALUE\" \"${MAKEFLAGS-unset}\" > " | ||
| 71 | - "\"$ASC_COMPILE_EXPORTER_UNIT_OUTPUT\""}, | ||
| 72 | - {{"ASC_COMPILE_EXPORTER_UNIT_VALUE", "visible"}, {"ASC_COMPILE_EXPORTER_UNIT_OUTPUT", output}})); | ||
| 73 | - EXPECT_EQ(ReadTestFile(output), "visible|unset"); | ||
| 74 | -} | ||
| 75 | - | ||
| 76 | -TEST_F(ModuleTest, ProcessRunnerReportsExecAndSignalFailures) | ||
| 77 | -{ | ||
| 78 | - g_failFork = true; | ||
| 79 | - EXPECT_FALSE(ProcessRunner::Run({"/bin/true"}, {})); | ||
| 80 | - g_failFork = false; | ||
| 81 | - EXPECT_FALSE(ProcessRunner::Run({FileUtils::JoinPath(root_, "missing-command")}, {})); | ||
| 82 | - EXPECT_FALSE(ProcessRunner::Run({"/bin/sh", "-c", "kill -TERM $$"}, {})); | ||
| 83 | -} | ||
| 84 | - | ||
| 85 | -TEST_F(ModuleTest, ProcessRunnerTimesOutAndReapsProcessGroup) | ||
| 86 | -{ | ||
| 87 | - const std::string pidFile = FileUtils::JoinPath(root_, "timed-out.pid"); | ||
| 88 | - const std::string terminationFile = FileUtils::JoinPath(root_, "termination.txt"); | ||
| 89 | - EXPECT_FALSE(ProcessRunner::Run( | ||
| 90 | - {"/bin/sh", "-c", | ||
| 91 | - "trap 'printf terminated > \"$ASC_COMPILE_EXPORTER_TERMINATION_FILE\"; exit 0' TERM; " | ||
| 92 | - "printf '%s' \"$$\" > \"$ASC_COMPILE_EXPORTER_PID_FILE\"; sleep 10"}, | ||
| 93 | - {{"ASC_COMPILE_EXPORTER_PID_FILE", pidFile}, {"ASC_COMPILE_EXPORTER_TERMINATION_FILE", terminationFile}}, | ||
| 94 | - std::chrono::milliseconds(100U))); | ||
| 95 | - | ||
| 96 | - EXPECT_EQ(ReadTestFile(terminationFile), "terminated"); | ||
| 97 | - const pid_t child = static_cast<pid_t>(std::strtol(ReadTestFile(pidFile).c_str(), nullptr, 10)); | ||
| 98 | - ASSERT_GT(child, 0); | ||
| 99 | - errno = 0; | ||
| 100 | - EXPECT_EQ(kill(child, 0), -1); | ||
| 101 | - EXPECT_EQ(errno, ESRCH); | ||
| 102 | -} | ||
| 103 | - | ||
| 104 | -TEST_F(ModuleTest, ProcessRunnerReportsWaitFailureAndReapsChild) | ||
| 105 | -{ | ||
| 106 | - g_failNextWaitpid = true; | ||
| 107 | - EXPECT_FALSE(ProcessRunner::Run({"/bin/true"}, {}, std::chrono::seconds(1U))); | ||
| 108 | - EXPECT_FALSE(g_failNextWaitpid); | ||
| 109 | -} | ||
| 110 | - | ||
| 111 | -} // namespace | ||
| 112 | -} // namespace ascendc | ||
| @@ -0,0 +1,172 @@ | |||
| 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 | +#include <gtest/gtest.h> | ||
| 12 | + | ||
| 13 | +#include "file_utils.h" | ||
| 14 | +#include "directory_cleanup_guard.h" | ||
| 15 | + | ||
| 16 | +#include <cstdlib> | ||
| 17 | +#include <string> | ||
| 18 | +#include <sys/stat.h> | ||
| 19 | +#include <utility> | ||
| 20 | +#include <vector> | ||
| 21 | + | ||
| 22 | +namespace ascendc { | ||
| 23 | +namespace { | ||
| 24 | + | ||
| 25 | +bool PathExistsWithoutFollowingSymlinks(const std::string& path) | ||
| 26 | +{ | ||
| 27 | + struct stat pathStatus {}; | ||
| 28 | + return lstat(path.c_str(), &pathStatus) == 0; | ||
| 29 | +} | ||
| 30 | + | ||
| 31 | +class DirectoryCleanupGuardTest : public testing::Test { | ||
| 32 | +protected: | ||
| 33 | + void SetUp() override | ||
| 34 | + { | ||
| 35 | + const char* configuredTemporaryDirectory = std::getenv("TMPDIR"); | ||
| 36 | + const std::string parentDirectoryPath = | ||
| 37 | + configuredTemporaryDirectory == nullptr || configuredTemporaryDirectory[0] == '\0' ? | ||
| 38 | + "/tmp" : | ||
| 39 | + configuredTemporaryDirectory; | ||
| 40 | + std::string rootDirectoryPattern = | ||
| 41 | + FileUtils::JoinPath(parentDirectoryPath, "directory_cleanup_guard_ut_XXXXXX"); | ||
| 42 | + std::vector<char> writablePattern(rootDirectoryPattern.begin(), rootDirectoryPattern.end()); | ||
| 43 | + writablePattern.push_back('\0'); | ||
| 44 | + char* createdDirectoryPath = mkdtemp(writablePattern.data()); | ||
| 45 | + ASSERT_NE(createdDirectoryPath, nullptr); | ||
| 46 | + rootDirectoryPath_ = createdDirectoryPath; | ||
| 47 | + } | ||
| 48 | + | ||
| 49 | + void TearDown() override { (void)FileUtils::RemoveAll(rootDirectoryPath_); } | ||
| 50 | + | ||
| 51 | + std::string rootDirectoryPath_; | ||
| 52 | +}; | ||
| 53 | + | ||
| 54 | +TEST_F(DirectoryCleanupGuardTest, CreatesUniqueDirectoryAndRemovesItOnDestruction) | ||
| 55 | +{ | ||
| 56 | + const std::string parentPath = FileUtils::JoinPath(rootDirectoryPath_, "temporary-directories"); | ||
| 57 | + ASSERT_TRUE(FileUtils::CreateDirectories(parentPath)); | ||
| 58 | + std::string removedDirectoryPath; | ||
| 59 | + { | ||
| 60 | + DirectoryCleanupGuard directory; | ||
| 61 | + ASSERT_TRUE(directory.CreateUniqueSubdirectory(parentPath, "test")); | ||
| 62 | + removedDirectoryPath = directory.GetDirectoryPath(); | ||
| 63 | + EXPECT_TRUE(FileUtils::IsDirectory(removedDirectoryPath)); | ||
| 64 | + EXPECT_TRUE(FileUtils::IsPathWithin(removedDirectoryPath, parentPath)); | ||
| 65 | + struct stat directoryStatus {}; | ||
| 66 | + ASSERT_EQ(stat(removedDirectoryPath.c_str(), &directoryStatus), 0); | ||
| 67 | + constexpr mode_t permissionBits = S_IRWXU | S_IRWXG | S_IRWXO; | ||
| 68 | + EXPECT_EQ(directoryStatus.st_mode & permissionBits, S_IRWXU); | ||
| 69 | + EXPECT_FALSE(directory.CreateUniqueSubdirectory(parentPath, "second")); | ||
| 70 | + } | ||
| 71 | + EXPECT_FALSE(PathExistsWithoutFollowingSymlinks(removedDirectoryPath)); | ||
| 72 | +} | ||
| 73 | + | ||
| 74 | +TEST_F(DirectoryCleanupGuardTest, PreservesCreatedOrRegisteredDirectory) | ||
| 75 | +{ | ||
| 76 | + const std::string createdParentPath = FileUtils::JoinPath(rootDirectoryPath_, "retained-created"); | ||
| 77 | + ASSERT_TRUE(FileUtils::CreateDirectories(createdParentPath)); | ||
| 78 | + std::string retainedCreatedPath; | ||
| 79 | + { | ||
| 80 | + DirectoryCleanupGuard directory; | ||
| 81 | + ASSERT_TRUE(directory.CreateUniqueSubdirectory(createdParentPath, "created")); | ||
| 82 | + retainedCreatedPath = directory.GetDirectoryPath(); | ||
| 83 | + directory.PreserveDirectory(); | ||
| 84 | + } | ||
| 85 | + EXPECT_TRUE(FileUtils::IsDirectory(retainedCreatedPath)); | ||
| 86 | + | ||
| 87 | + const std::string retainedRegisteredPath = FileUtils::JoinPath(rootDirectoryPath_, "retained-registered"); | ||
| 88 | + ASSERT_TRUE(FileUtils::CreateDirectories(retainedRegisteredPath)); | ||
| 89 | + { | ||
| 90 | + DirectoryCleanupGuard directory; | ||
| 91 | + ASSERT_TRUE(directory.RegisterDirectoryForCleanup(retainedRegisteredPath)); | ||
| 92 | + directory.PreserveDirectory(); | ||
| 93 | + } | ||
| 94 | + EXPECT_TRUE(FileUtils::IsDirectory(retainedRegisteredPath)); | ||
| 95 | +} | ||
| 96 | + | ||
| 97 | +TEST_F(DirectoryCleanupGuardTest, MovesDirectoryCleanupResponsibilityExactlyOnce) | ||
| 98 | +{ | ||
| 99 | + const std::string parentPath = FileUtils::JoinPath(rootDirectoryPath_, "moved"); | ||
| 100 | + ASSERT_TRUE(FileUtils::CreateDirectories(parentPath)); | ||
| 101 | + std::string movedDirectoryPath; | ||
| 102 | + { | ||
| 103 | + DirectoryCleanupGuard original; | ||
| 104 | + ASSERT_TRUE(original.CreateUniqueSubdirectory(parentPath, "original")); | ||
| 105 | + movedDirectoryPath = original.GetDirectoryPath(); | ||
| 106 | + | ||
| 107 | + DirectoryCleanupGuard moved(std::move(original)); | ||
| 108 | + EXPECT_TRUE(original.GetDirectoryPath().empty()); | ||
| 109 | + EXPECT_EQ(moved.GetDirectoryPath(), movedDirectoryPath); | ||
| 110 | + | ||
| 111 | + DirectoryCleanupGuard assigned; | ||
| 112 | + assigned = std::move(moved); | ||
| 113 | + EXPECT_TRUE(moved.GetDirectoryPath().empty()); | ||
| 114 | + EXPECT_EQ(assigned.GetDirectoryPath(), movedDirectoryPath); | ||
| 115 | + } | ||
| 116 | + EXPECT_FALSE(PathExistsWithoutFollowingSymlinks(movedDirectoryPath)); | ||
| 117 | +} | ||
| 118 | + | ||
| 119 | +TEST_F(DirectoryCleanupGuardTest, RemovesRegisteredDirectoryOnRequest) | ||
| 120 | +{ | ||
| 121 | + const std::string registeredPath = FileUtils::JoinPath(rootDirectoryPath_, "registered"); | ||
| 122 | + ASSERT_TRUE(FileUtils::CreateDirectories(registeredPath)); | ||
| 123 | + DirectoryCleanupGuard directory; | ||
| 124 | + ASSERT_TRUE(directory.RegisterDirectoryForCleanup(registeredPath)); | ||
| 125 | + EXPECT_TRUE(directory.RemoveDirectory()); | ||
| 126 | + EXPECT_TRUE(directory.GetDirectoryPath().empty()); | ||
| 127 | + EXPECT_FALSE(PathExistsWithoutFollowingSymlinks(registeredPath)); | ||
| 128 | + EXPECT_TRUE(directory.RemoveDirectory()); | ||
| 129 | +} | ||
| 130 | + | ||
| 131 | +TEST_F(DirectoryCleanupGuardTest, MoveAssignmentRemovesPreviouslyRegisteredDirectoryAndEmptiesSource) | ||
| 132 | +{ | ||
| 133 | + const std::string sourceParentPath = FileUtils::JoinPath(rootDirectoryPath_, "move-source"); | ||
| 134 | + const std::string destinationParentPath = FileUtils::JoinPath(rootDirectoryPath_, "move-destination"); | ||
| 135 | + ASSERT_TRUE(FileUtils::CreateDirectories(sourceParentPath)); | ||
| 136 | + ASSERT_TRUE(FileUtils::CreateDirectories(destinationParentPath)); | ||
| 137 | + std::string sourceDirectoryPath; | ||
| 138 | + std::string destinationDirectoryPath; | ||
| 139 | + { | ||
| 140 | + DirectoryCleanupGuard source; | ||
| 141 | + ASSERT_TRUE(source.CreateUniqueSubdirectory(sourceParentPath, "source")); | ||
| 142 | + sourceDirectoryPath = source.GetDirectoryPath(); | ||
| 143 | + | ||
| 144 | + DirectoryCleanupGuard destination; | ||
| 145 | + ASSERT_TRUE(destination.CreateUniqueSubdirectory(destinationParentPath, "destination")); | ||
| 146 | + destinationDirectoryPath = destination.GetDirectoryPath(); | ||
| 147 | + destination = std::move(source); | ||
| 148 | + | ||
| 149 | + EXPECT_EQ(destination.GetDirectoryPath(), sourceDirectoryPath); | ||
| 150 | + EXPECT_TRUE(source.GetDirectoryPath().empty()); | ||
| 151 | + EXPECT_FALSE(PathExistsWithoutFollowingSymlinks(destinationDirectoryPath)); | ||
| 152 | + } | ||
| 153 | + EXPECT_FALSE(PathExistsWithoutFollowingSymlinks(sourceDirectoryPath)); | ||
| 154 | + EXPECT_FALSE(PathExistsWithoutFollowingSymlinks(destinationDirectoryPath)); | ||
| 155 | +} | ||
| 156 | + | ||
| 157 | +TEST_F(DirectoryCleanupGuardTest, RejectsInvalidCreationInputsAndReportsRemovalFailure) | ||
| 158 | +{ | ||
| 159 | + DirectoryCleanupGuard directory; | ||
| 160 | + EXPECT_FALSE(directory.CreateUniqueSubdirectory(FileUtils::JoinPath(rootDirectoryPath_, "missing"), "test")); | ||
| 161 | + EXPECT_FALSE(directory.CreateUniqueSubdirectory(rootDirectoryPath_, "nested/test")); | ||
| 162 | + EXPECT_FALSE(directory.CreateUniqueSubdirectory(rootDirectoryPath_, "invalid%prefix")); | ||
| 163 | + EXPECT_FALSE(directory.RegisterDirectoryForCleanup(std::string())); | ||
| 164 | + | ||
| 165 | + ASSERT_TRUE(directory.RegisterDirectoryForCleanup("/proc/self/status")); | ||
| 166 | + EXPECT_FALSE(directory.RemoveDirectory()); | ||
| 167 | + EXPECT_EQ(directory.GetDirectoryPath(), "/proc/self/status"); | ||
| 168 | + directory.PreserveDirectory(); | ||
| 169 | +} | ||
| 170 | + | ||
| 171 | +} // namespace | ||
| 172 | +} // namespace ascendc | ||
| @@ -0,0 +1,305 @@ | |||
| 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 | +#include <gtest/gtest.h> | ||
| 12 | + | ||
| 13 | +#include "process_executor.h" | ||
| 14 | + | ||
| 15 | +#include <boost/filesystem.hpp> | ||
| 16 | +#include <boost/system/error_code.hpp> | ||
| 17 | +#include <chrono> | ||
| 18 | +#include <csignal> | ||
| 19 | +#include <cstdlib> | ||
| 20 | +#include <fstream> | ||
| 21 | +#include <string> | ||
| 22 | +#include <thread> | ||
| 23 | +#include <type_traits> | ||
| 24 | +#include <utility> | ||
| 25 | +#include <unistd.h> | ||
| 26 | +#include <vector> | ||
| 27 | + | ||
| 28 | +namespace ascendc { | ||
| 29 | +namespace { | ||
| 30 | +namespace fs = boost::filesystem; | ||
| 31 | +using namespace std::chrono_literals; | ||
| 32 | + | ||
| 33 | +static_assert( | ||
| 34 | + std::is_same<std::underlying_type_t<ProcessOutcome>, uint32_t>::value, "ProcessOutcome must have a stable width"); | ||
| 35 | +static_assert( | ||
| 36 | + std::is_same<decltype(ProcessExecutorResult::terminationCode), int32_t>::value, | ||
| 37 | + "terminationCode must have a stable width"); | ||
| 38 | + | ||
| 39 | +ProcessExecutorRequest CreateProcessRequest(std::vector<std::string> arguments) | ||
| 40 | +{ | ||
| 41 | + ProcessExecutorRequest request; | ||
| 42 | + request.arguments = std::move(arguments); | ||
| 43 | + request.executionTimeout = 1s; | ||
| 44 | + request.terminationGracePeriod = 20ms; | ||
| 45 | + return request; | ||
| 46 | +} | ||
| 47 | + | ||
| 48 | +std::string ReadTextFile(const fs::path& filePath) | ||
| 49 | +{ | ||
| 50 | + std::ifstream inputStream(filePath.string()); | ||
| 51 | + return {std::istreambuf_iterator<char>(inputStream), std::istreambuf_iterator<char>()}; | ||
| 52 | +} | ||
| 53 | + | ||
| 54 | +class TemporarilyClosedStandardOutputStreams final { | ||
| 55 | +public: | ||
| 56 | + ~TemporarilyClosedStandardOutputStreams() { (void)Restore(); } | ||
| 57 | + | ||
| 58 | + bool Close() | ||
| 59 | + { | ||
| 60 | + savedStandardOutput_ = dup(STDOUT_FILENO); | ||
| 61 | + savedStandardError_ = dup(STDERR_FILENO); | ||
| 62 | + if (savedStandardOutput_ < 0 || savedStandardError_ < 0) { | ||
| 63 | + return false; | ||
| 64 | + } | ||
| 65 | + const bool standardOutputClosed = close(STDOUT_FILENO) == 0; | ||
| 66 | + const bool standardErrorClosed = close(STDERR_FILENO) == 0; | ||
| 67 | + return standardOutputClosed && standardErrorClosed; | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | + bool Restore() | ||
| 71 | + { | ||
| 72 | + bool restored = true; | ||
| 73 | + if (savedStandardOutput_ >= 0) { | ||
| 74 | + restored = dup2(savedStandardOutput_, STDOUT_FILENO) == STDOUT_FILENO && restored; | ||
| 75 | + restored = close(savedStandardOutput_) == 0 && restored; | ||
| 76 | + savedStandardOutput_ = -1; | ||
| 77 | + } | ||
| 78 | + if (savedStandardError_ >= 0) { | ||
| 79 | + restored = dup2(savedStandardError_, STDERR_FILENO) == STDERR_FILENO && restored; | ||
| 80 | + restored = close(savedStandardError_) == 0 && restored; | ||
| 81 | + savedStandardError_ = -1; | ||
| 82 | + } | ||
| 83 | + return restored; | ||
| 84 | + } | ||
| 85 | + | ||
| 86 | +private: | ||
| 87 | + int savedStandardOutput_{-1}; | ||
| 88 | + int savedStandardError_{-1}; | ||
| 89 | +}; | ||
| 90 | + | ||
| 91 | +TEST(ProcessExecutorTest, RejectsInvalidRequestsBeforeSpawning) | ||
| 92 | +{ | ||
| 93 | + ProcessExecutorRequest emptyArgumentsRequest; | ||
| 94 | + emptyArgumentsRequest.executionTimeout = 1s; | ||
| 95 | + EXPECT_EQ(ProcessExecutor::Execute(emptyArgumentsRequest).outcome, ProcessOutcome::ExecutorFailure); | ||
| 96 | + | ||
| 97 | + ProcessExecutorRequest zeroTimeoutRequest = CreateProcessRequest({"/bin/true"}); | ||
| 98 | + zeroTimeoutRequest.executionTimeout = 0ms; | ||
| 99 | + EXPECT_EQ(ProcessExecutor::Execute(zeroTimeoutRequest).outcome, ProcessOutcome::ExecutorFailure); | ||
| 100 | + | ||
| 101 | + ProcessExecutorRequest negativeGracePeriodRequest = CreateProcessRequest({"/bin/true"}); | ||
| 102 | + negativeGracePeriodRequest.terminationGracePeriod = -1ms; | ||
| 103 | + EXPECT_EQ(ProcessExecutor::Execute(negativeGracePeriodRequest).outcome, ProcessOutcome::ExecutorFailure); | ||
| 104 | + | ||
| 105 | + ProcessExecutorRequest embeddedNullRequest = CreateProcessRequest({std::string("/bin/true\0invalid", 17U)}); | ||
| 106 | + EXPECT_EQ(ProcessExecutor::Execute(embeddedNullRequest).outcome, ProcessOutcome::ExecutorFailure); | ||
| 107 | + | ||
| 108 | + ProcessExecutorRequest invalidOverrideNameRequest = CreateProcessRequest({"/bin/true"}); | ||
| 109 | + invalidOverrideNameRequest.environmentOverrides.emplace_back("INVALID=NAME", "value"); | ||
| 110 | + EXPECT_EQ(ProcessExecutor::Execute(invalidOverrideNameRequest).outcome, ProcessOutcome::ExecutorFailure); | ||
| 111 | + | ||
| 112 | + ProcessExecutorRequest embeddedNullOverrideValueRequest = CreateProcessRequest({"/bin/true"}); | ||
| 113 | + embeddedNullOverrideValueRequest.environmentOverrides.emplace_back("VALID_NAME", std::string("value\0suffix", 12U)); | ||
| 114 | + EXPECT_EQ(ProcessExecutor::Execute(embeddedNullOverrideValueRequest).outcome, ProcessOutcome::ExecutorFailure); | ||
| 115 | + | ||
| 116 | + ProcessExecutorRequest invalidRemovalNameRequest = CreateProcessRequest({"/bin/true"}); | ||
| 117 | + invalidRemovalNameRequest.removedEnvironmentVariables.emplace_back("INVALID=NAME"); | ||
| 118 | + EXPECT_EQ(ProcessExecutor::Execute(invalidRemovalNameRequest).outcome, ProcessOutcome::ExecutorFailure); | ||
| 119 | +} | ||
| 120 | + | ||
| 121 | +TEST(ProcessExecutorTest, ExecutesArgumentsWithoutShellParsing) | ||
| 122 | +{ | ||
| 123 | + const fs::path unexpectedFilePath = | ||
| 124 | + fs::temp_directory_path() / ("process_executor_shell_" + std::to_string(getpid())); | ||
| 125 | + boost::system::error_code ignoredError; | ||
| 126 | + fs::remove(unexpectedFilePath, ignoredError); | ||
| 127 | + | ||
| 128 | + const fs::path outputLogPath = fs::temp_directory_path() / fs::unique_path("process_executor_output_%%%%-%%%%"); | ||
| 129 | + ProcessExecutorRequest request = | ||
| 130 | + CreateProcessRequest({"/usr/bin/printf", "$(touch " + unexpectedFilePath.string() + ")"}); | ||
| 131 | + request.mirroredOutputLogFilePath = outputLogPath.string(); | ||
| 132 | + const ProcessExecutorResult result = ProcessExecutor::Execute(request); | ||
| 133 | + | ||
| 134 | + EXPECT_TRUE(result.HasSuccessfulExit()); | ||
| 135 | + EXPECT_EQ(ReadTextFile(outputLogPath), "$(touch " + unexpectedFilePath.string() + ")"); | ||
| 136 | + EXPECT_FALSE(fs::exists(unexpectedFilePath)); | ||
| 137 | + fs::remove(outputLogPath, ignoredError); | ||
| 138 | +} | ||
| 139 | + | ||
| 140 | +TEST(ProcessExecutorTest, AppliesEnvironmentOverridesAndRemovals) | ||
| 141 | +{ | ||
| 142 | + ASSERT_EQ(setenv("PROCESS_EXECUTOR_REMOVE_ME", "inherited", 1), 0); | ||
| 143 | + const fs::path outputLogPath = fs::temp_directory_path() / fs::unique_path("process_executor_env_%%%%-%%%%"); | ||
| 144 | + ProcessExecutorRequest request = CreateProcessRequest( | ||
| 145 | + {"/bin/sh", "-c", "printf '%s|%s' \"$PROCESS_EXECUTOR_VALUE\" \"${PROCESS_EXECUTOR_REMOVE_ME-unset}\""}); | ||
| 146 | + request.environmentOverrides.emplace_back("PROCESS_EXECUTOR_VALUE", "visible"); | ||
| 147 | + request.removedEnvironmentVariables.emplace_back("PROCESS_EXECUTOR_REMOVE_ME"); | ||
| 148 | + request.mirroredOutputLogFilePath = outputLogPath.string(); | ||
| 149 | + | ||
| 150 | + const ProcessExecutorResult result = ProcessExecutor::Execute(request); | ||
| 151 | + EXPECT_TRUE(result.HasSuccessfulExit()); | ||
| 152 | + EXPECT_EQ(ReadTextFile(outputLogPath), "visible|unset"); | ||
| 153 | + EXPECT_EQ(unsetenv("PROCESS_EXECUTOR_REMOVE_ME"), 0); | ||
| 154 | + boost::system::error_code ignoredError; | ||
| 155 | + fs::remove(outputLogPath, ignoredError); | ||
| 156 | +} | ||
| 157 | + | ||
| 158 | +TEST(ProcessExecutorTest, EnvironmentOverrideTakesPrecedenceOverRemoval) | ||
| 159 | +{ | ||
| 160 | + const fs::path outputLogPath = fs::temp_directory_path() / fs::unique_path("process_executor_precedence_%%%%-%%%%"); | ||
| 161 | + ProcessExecutorRequest request = | ||
| 162 | + CreateProcessRequest({"/bin/sh", "-c", "printf '%s' \"$PROCESS_EXECUTOR_PRECEDENCE\""}); | ||
| 163 | + request.removedEnvironmentVariables.emplace_back("PROCESS_EXECUTOR_PRECEDENCE"); | ||
| 164 | + request.environmentOverrides.emplace_back("PROCESS_EXECUTOR_PRECEDENCE", "overridden"); | ||
| 165 | + request.mirroredOutputLogFilePath = outputLogPath.string(); | ||
| 166 | + | ||
| 167 | + const ProcessExecutorResult result = ProcessExecutor::Execute(request); | ||
| 168 | + EXPECT_TRUE(result.HasSuccessfulExit()); | ||
| 169 | + EXPECT_EQ(ReadTextFile(outputLogPath), "overridden"); | ||
| 170 | + boost::system::error_code ignoredError; | ||
| 171 | + fs::remove(outputLogPath, ignoredError); | ||
| 172 | +} | ||
| 173 | + | ||
| 174 | +TEST(ProcessExecutorTest, ReportsExitSignalAndSpawnFailureSeparately) | ||
| 175 | +{ | ||
| 176 | + const ProcessExecutorResult nonzeroExit = | ||
| 177 | + ProcessExecutor::Execute(CreateProcessRequest({"/bin/sh", "-c", "exit 7"})); | ||
| 178 | + EXPECT_EQ(nonzeroExit.outcome, ProcessOutcome::Exited); | ||
| 179 | + EXPECT_EQ(nonzeroExit.terminationCode, 7); | ||
| 180 | + | ||
| 181 | + const ProcessExecutorResult signalExit = | ||
| 182 | + ProcessExecutor::Execute(CreateProcessRequest({"/bin/sh", "-c", "kill -TERM $$"})); | ||
| 183 | + EXPECT_EQ(signalExit.outcome, ProcessOutcome::Signaled); | ||
| 184 | + EXPECT_EQ(signalExit.terminationCode, SIGTERM); | ||
| 185 | + | ||
| 186 | + const ProcessExecutorResult spawnFailure = | ||
| 187 | + ProcessExecutor::Execute(CreateProcessRequest({"/path/that/does/not/exist"})); | ||
| 188 | + EXPECT_EQ(spawnFailure.outcome, ProcessOutcome::ExecutorFailure); | ||
| 189 | +} | ||
| 190 | + | ||
| 191 | +TEST(ProcessExecutorTest, RedirectsChildOutputWhenParentStandardOutputStreamsAreClosed) | ||
| 192 | +{ | ||
| 193 | + const fs::path outputLogPath = | ||
| 194 | + fs::temp_directory_path() / fs::unique_path("process_executor_closed_stdio_%%%%-%%%%"); | ||
| 195 | + ProcessExecutorRequest request = CreateProcessRequest({"/bin/sh", "-c", "printf stdout; printf stderr >&2"}); | ||
| 196 | + request.mirroredOutputLogFilePath = outputLogPath.string(); | ||
| 197 | + TemporarilyClosedStandardOutputStreams standardOutputStreams; | ||
| 198 | + const bool streamsClosed = standardOutputStreams.Close(); | ||
| 199 | + ProcessExecutorResult result; | ||
| 200 | + if (streamsClosed) { | ||
| 201 | + result = ProcessExecutor::Execute(request); | ||
| 202 | + } | ||
| 203 | + const bool streamsRestored = standardOutputStreams.Restore(); | ||
| 204 | + | ||
| 205 | + ASSERT_TRUE(streamsClosed); | ||
| 206 | + ASSERT_TRUE(streamsRestored); | ||
| 207 | + EXPECT_TRUE(result.HasSuccessfulExit()); | ||
| 208 | + const std::string mirroredOutput = ReadTextFile(outputLogPath); | ||
| 209 | + EXPECT_NE(mirroredOutput.find("stdout"), std::string::npos); | ||
| 210 | + EXPECT_NE(mirroredOutput.find("stderr"), std::string::npos); | ||
| 211 | + boost::system::error_code ignoredError; | ||
| 212 | + fs::remove(outputLogPath, ignoredError); | ||
| 213 | +} | ||
| 214 | + | ||
| 215 | +TEST(ProcessExecutorTest, MirrorsStandardOutputAndStandardErrorToOneLogFile) | ||
| 216 | +{ | ||
| 217 | + const fs::path outputLogPath = fs::temp_directory_path() / fs::unique_path("process_executor_streams_%%%%-%%%%"); | ||
| 218 | + ProcessExecutorRequest request = | ||
| 219 | + CreateProcessRequest({"/bin/sh", "-c", "printf 'stdout-line\\n'; printf 'stderr-line\\n' >&2"}); | ||
| 220 | + request.mirroredOutputLogFilePath = outputLogPath.string(); | ||
| 221 | + | ||
| 222 | + const ProcessExecutorResult result = ProcessExecutor::Execute(request); | ||
| 223 | + EXPECT_TRUE(result.HasSuccessfulExit()); | ||
| 224 | + const std::string mirroredOutput = ReadTextFile(outputLogPath); | ||
| 225 | + EXPECT_NE(mirroredOutput.find("stdout-line\n"), std::string::npos); | ||
| 226 | + EXPECT_NE(mirroredOutput.find("stderr-line\n"), std::string::npos); | ||
| 227 | + boost::system::error_code ignoredError; | ||
| 228 | + fs::remove(outputLogPath, ignoredError); | ||
| 229 | +} | ||
| 230 | + | ||
| 231 | +TEST(ProcessExecutorTest, DrainsProcessOutputLargerThanPipeCapacity) | ||
| 232 | +{ | ||
| 233 | + constexpr uintmax_t outputByteCount = 200000U; | ||
| 234 | + const fs::path outputLogPath = fs::temp_directory_path() / fs::unique_path("process_executor_large_%%%%-%%%%"); | ||
| 235 | + ProcessExecutorRequest request = CreateProcessRequest({"/bin/sh", "-c", "head -c 200000 /dev/zero | tr '\\0' x"}); | ||
| 236 | + request.mirroredOutputLogFilePath = outputLogPath.string(); | ||
| 237 | + | ||
| 238 | + EXPECT_TRUE(ProcessExecutor::Execute(request).HasSuccessfulExit()); | ||
| 239 | + EXPECT_EQ(fs::file_size(outputLogPath), outputByteCount); | ||
| 240 | + boost::system::error_code ignoredError; | ||
| 241 | + fs::remove(outputLogPath, ignoredError); | ||
| 242 | +} | ||
| 243 | + | ||
| 244 | +TEST(ProcessExecutorTest, DiagnosticLogFailureDoesNotFailCommand) | ||
| 245 | +{ | ||
| 246 | + ProcessExecutorRequest request = CreateProcessRequest({"/usr/bin/printf", "still-runs"}); | ||
| 247 | + request.mirroredOutputLogFilePath = fs::temp_directory_path().string(); | ||
| 248 | + | ||
| 249 | + EXPECT_TRUE(ProcessExecutor::Execute(request).HasSuccessfulExit()); | ||
| 250 | +} | ||
| 251 | + | ||
| 252 | +TEST(ProcessExecutorTest, RejectsIncompatibleSigchldDispositionBeforeSpawning) | ||
| 253 | +{ | ||
| 254 | + const fs::path spawnMarkerPath = fs::temp_directory_path() / fs::unique_path("process_executor_sigchld_%%%%-%%%%"); | ||
| 255 | + struct sigaction ignoredSigchld {}; | ||
| 256 | + struct sigaction originalSigchld {}; | ||
| 257 | + ignoredSigchld.sa_handler = SIG_IGN; | ||
| 258 | + ASSERT_EQ(sigemptyset(&ignoredSigchld.sa_mask), 0); | ||
| 259 | + ASSERT_EQ(sigaction(SIGCHLD, &ignoredSigchld, &originalSigchld), 0); | ||
| 260 | + | ||
| 261 | + const ProcessExecutorResult result = | ||
| 262 | + ProcessExecutor::Execute(CreateProcessRequest({"/usr/bin/touch", spawnMarkerPath.string()})); | ||
| 263 | + | ||
| 264 | + ASSERT_EQ(sigaction(SIGCHLD, &originalSigchld, nullptr), 0); | ||
| 265 | + EXPECT_EQ(result.outcome, ProcessOutcome::ExecutorFailure); | ||
| 266 | + EXPECT_FALSE(fs::exists(spawnMarkerPath)); | ||
| 267 | + boost::system::error_code ignoredError; | ||
| 268 | + fs::remove(spawnMarkerPath, ignoredError); | ||
| 269 | +} | ||
| 270 | + | ||
| 271 | +TEST(ProcessExecutorTest, CompletesWhenSynchronousCommandLeaderExits) | ||
| 272 | +{ | ||
| 273 | + ProcessExecutorRequest request = CreateProcessRequest({"/bin/sh", "-c", "sleep 0.3 & exec /bin/true"}); | ||
| 274 | + | ||
| 275 | + const auto startedAt = std::chrono::steady_clock::now(); | ||
| 276 | + const ProcessExecutorResult result = ProcessExecutor::Execute(request); | ||
| 277 | + EXPECT_TRUE(result.HasSuccessfulExit()); | ||
| 278 | + EXPECT_LT(std::chrono::steady_clock::now() - startedAt, 200ms); | ||
| 279 | +} | ||
| 280 | + | ||
| 281 | +TEST(ProcessExecutorTest, TimeoutTerminatesActiveProcessGroup) | ||
| 282 | +{ | ||
| 283 | + static uint64_t sequence = 0U; | ||
| 284 | + const fs::path descendantMarkerPath = | ||
| 285 | + fs::temp_directory_path() / | ||
| 286 | + ("process_executor_descendant_" + std::to_string(getpid()) + "_" + std::to_string(sequence++)); | ||
| 287 | + const std::string commandScript = | ||
| 288 | + "(trap '' TERM; while true; do printf x >> '" + descendantMarkerPath.string() + "'; sleep 0.01; done) & wait"; | ||
| 289 | + ProcessExecutorRequest request = CreateProcessRequest({"/bin/sh", "-c", commandScript}); | ||
| 290 | + request.executionTimeout = 20ms; | ||
| 291 | + | ||
| 292 | + const ProcessExecutorResult result = ProcessExecutor::Execute(request); | ||
| 293 | + EXPECT_EQ(result.outcome, ProcessOutcome::TimedOut); | ||
| 294 | + | ||
| 295 | + const uintmax_t markerSizeAfterTimeout = | ||
| 296 | + fs::exists(descendantMarkerPath) ? fs::file_size(descendantMarkerPath) : 0U; | ||
| 297 | + std::this_thread::sleep_for(100ms); | ||
| 298 | + const uintmax_t markerSizeAfterWait = fs::exists(descendantMarkerPath) ? fs::file_size(descendantMarkerPath) : 0U; | ||
| 299 | + EXPECT_EQ(markerSizeAfterWait, markerSizeAfterTimeout); | ||
| 300 | + boost::system::error_code ignoredError; | ||
| 301 | + fs::remove(descendantMarkerPath, ignoredError); | ||
| 302 | +} | ||
| 303 | + | ||
| 304 | +} // namespace | ||
| 305 | +} // namespace ascendc | ||
| @@ -14,6 +14,8 @@ add_library(acl_rtc SHARED | |||
| 14 | ${SRC} | 14 | ${SRC} |
| 15 | ${SPECIALIZATION_SRC} | 15 | ${SPECIALIZATION_SRC} |
| 16 | ${CMAKE_CURRENT_SOURCE_DIR}/../build/common/file_utils.cpp | 16 | ${CMAKE_CURRENT_SOURCE_DIR}/../build/common/file_utils.cpp |
| 17 | + ${CMAKE_CURRENT_SOURCE_DIR}/../build/common/process_executor.cpp | ||
| 18 | + ${CMAKE_CURRENT_SOURCE_DIR}/../build/common/directory_cleanup_guard.cpp | ||
| 17 | ) | 19 | ) |
| 18 | 20 | ||
| 19 | if(BUILD_OPEN_PROJECT) | 21 | if(BUILD_OPEN_PROJECT) |
| @@ -5,570 +5,29 @@ | |||
| 5 | * Please refer to the License for details. You may not use this file except in compliance with 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, | 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. | 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. | 8 | + * See LICENSE in the root of the software repository for the full text of the LICENSE. |
| 9 | */ | 9 | */ |
| 10 | 10 | ||
| 11 | #include "compilation_plan_executor.h" | 11 | #include "compilation_plan_executor.h" |
| 12 | 12 | ||
| 13 | -#include "kernel_specialization_diagnostics.h" | ||
| 14 | #include "ascendc_tool_log.h" | 13 | #include "ascendc_tool_log.h" |
| 14 | +#include "kernel_specialization_diagnostics.h" | ||
| 15 | +#include "process_executor.h" | ||
| 15 | 16 | ||
| 16 | -#include <algorithm> | ||
| 17 | -#include <cerrno> | ||
| 18 | -#include <csignal> | ||
| 19 | -#include <cstring> | ||
| 20 | #include <exception> | 17 | #include <exception> |
| 21 | -#include <fcntl.h> | ||
| 22 | #include <new> | 18 | #include <new> |
| 23 | -#include <poll.h> | ||
| 24 | -#include <spawn.h> | ||
| 25 | -#include <sys/wait.h> | ||
| 26 | -#include <thread> | ||
| 27 | -#include <unistd.h> | ||
| 28 | 19 | ||
| 29 | namespace ascendc { | 20 | namespace ascendc { |
| 30 | namespace aclrtc { | 21 | namespace aclrtc { |
| 31 | -namespace { | ||
| 32 | -using SteadyClock = std::chrono::steady_clock; | ||
| 33 | -constexpr uint64_t MAX_PROCESS_OUTPUT_READ_BYTES_PER_WAIT_ITERATION = 64U * 1024U; | ||
| 34 | -constexpr int64_t MAX_OUTPUT_WAIT_INTERVAL_MILLISECONDS = 50; | ||
| 35 | -constexpr int64_t MAX_PROCESS_CHECK_INTERVAL_AFTER_OUTPUT_EOF_MILLISECONDS = 5; | ||
| 36 | - | ||
| 37 | -enum class ProcessOutputDrainStatus : uint32_t { | ||
| 38 | - NoDataAvailable, | ||
| 39 | - EndOfFile, | ||
| 40 | - ReadBudgetExhausted, | ||
| 41 | - Failed, | ||
| 42 | -}; | ||
| 43 | - | ||
| 44 | -class ScopedFileDescriptor final { | ||
| 45 | -public: | ||
| 46 | - explicit ScopedFileDescriptor(int fileDescriptor) noexcept : fileDescriptor_(fileDescriptor) {} | ||
| 47 | - | ||
| 48 | - ~ScopedFileDescriptor() noexcept { Close(); } | ||
| 49 | - | ||
| 50 | - ScopedFileDescriptor(const ScopedFileDescriptor&) = delete; | ||
| 51 | - ScopedFileDescriptor& operator=(const ScopedFileDescriptor&) = delete; | ||
| 52 | - | ||
| 53 | - int GetFileDescriptor() const noexcept { return fileDescriptor_; } | ||
| 54 | - | ||
| 55 | - void TakeOwnership(int fileDescriptor) noexcept | ||
| 56 | - { | ||
| 57 | - Close(); | ||
| 58 | - fileDescriptor_ = fileDescriptor; | ||
| 59 | - } | ||
| 60 | - | ||
| 61 | - void Close() noexcept | ||
| 62 | - { | ||
| 63 | - if (fileDescriptor_ < 0) { | ||
| 64 | - return; | ||
| 65 | - } | ||
| 66 | - const int closingFileDescriptor = fileDescriptor_; | ||
| 67 | - fileDescriptor_ = -1; | ||
| 68 | - if (close(closingFileDescriptor) != 0) { | ||
| 69 | - const int closeError = errno; | ||
| 70 | - ASCENDLOGW( | ||
| 71 | - "Failed to close command pipe descriptor: fd=%d errno=%d message=%s", closingFileDescriptor, closeError, | ||
| 72 | - std::strerror(closeError)); | ||
| 73 | - } | ||
| 74 | - } | ||
| 75 | - | ||
| 76 | -private: | ||
| 77 | - int fileDescriptor_{-1}; | ||
| 78 | -}; | ||
| 79 | - | ||
| 80 | -void CloseFileDescriptorBestEffort(int fileDescriptor) noexcept | ||
| 81 | -{ | ||
| 82 | - if (fileDescriptor >= 0 && close(fileDescriptor) != 0) { | ||
| 83 | - const int closeError = errno; | ||
| 84 | - ASCENDLOGW( | ||
| 85 | - "Failed to close command pipe descriptor: fd=%d errno=%d message=%s", fileDescriptor, closeError, | ||
| 86 | - std::strerror(closeError)); | ||
| 87 | - } | ||
| 88 | -} | ||
| 89 | - | ||
| 90 | -class CommandSpawnConfiguration final { | ||
| 91 | -public: | ||
| 92 | - CommandSpawnConfiguration() = default; | ||
| 93 | - | ||
| 94 | - ~CommandSpawnConfiguration() noexcept | ||
| 95 | - { | ||
| 96 | - if (spawnAttributesInitialized_) { | ||
| 97 | - const int destroyResult = posix_spawnattr_destroy(&spawnAttributes_); | ||
| 98 | - if (destroyResult != 0) { | ||
| 99 | - ASCENDLOGW( | ||
| 100 | - "Failed to destroy spawn attributes: error=%d message=%s", destroyResult, | ||
| 101 | - std::strerror(destroyResult)); | ||
| 102 | - } | ||
| @@ -15,8 +15,12 @@ | |||
| 15 | 15 | ||
| 16 | #include <chrono> | 16 | #include <chrono> |
| 17 | #include <cstddef> | 17 | #include <cstddef> |
| 18 | +#include <cstdint> | ||
| 18 | 19 | ||
| 19 | namespace ascendc { | 20 | namespace ascendc { |
| 21 | + | ||
| 22 | +struct ProcessExecutorResult; | ||
| 23 | + | ||
| 20 | namespace aclrtc { | 24 | namespace aclrtc { |
| 21 | 25 | ||
| 22 | class KernelSpecializationDiagnostics; | 26 | class KernelSpecializationDiagnostics; |
| @@ -25,7 +29,6 @@ struct CompilationCommandExecutionLimits { | |||
| 25 | std::chrono::milliseconds compileCommandTimeout; | 29 | std::chrono::milliseconds compileCommandTimeout; |
| 26 | std::chrono::milliseconds objectCopyAndLinkCommandTimeout; | 30 | std::chrono::milliseconds objectCopyAndLinkCommandTimeout; |
| 27 | std::chrono::milliseconds terminationGracePeriod; | 31 | std::chrono::milliseconds terminationGracePeriod; |
| 28 | - uint64_t capturedOutputByteLimit; | ||
| 29 | 32 | ||
| 30 | static CompilationCommandExecutionLimits ProductionDefaults() noexcept; | 33 | static CompilationCommandExecutionLimits ProductionDefaults() noexcept; |
| 31 | }; | 34 | }; |
| @@ -39,11 +42,10 @@ public: | |||
| 39 | 42 | ||
| 40 | private: | 43 | private: |
| 41 | aclError ExecuteCompilationCommand( | 44 | aclError ExecuteCompilationCommand( |
| 42 | - const CompilationCommand& compilationCommand, CompilationProcessResult& processResult) const; | 45 | + const CompilationCommand& compilationCommand, ProcessExecutorResult& executorResult) const; |
| 43 | - std::chrono::milliseconds GetCommandExecutionTimeout(CompilationCommandKind commandKind) const noexcept; | ||
| 44 | 46 | ||
| 45 | KernelSpecializationDiagnostics& specializationDiagnostics_; | 47 | KernelSpecializationDiagnostics& specializationDiagnostics_; |
| 46 | - CompilationCommandExecutionLimits executionLimits_; | 48 | + CompilationCommandExecutionLimits commandExecutionLimits_; |
| 47 | }; | 49 | }; |
| 48 | 50 | ||
| 49 | } // namespace aclrtc | 51 | } // namespace aclrtc |
| @@ -354,7 +354,7 @@ aclError BuildManifestCommand( | |||
| 354 | } | 354 | } |
| 355 | 355 | ||
| 356 | aclError SelectManifestObjectsForCompilation( | 356 | aclError SelectManifestObjectsForCompilation( |
| 357 | - const Json& selectedKernel, CompilationVariant compilationVariant, | 357 | + const Json& selectedKernel, KernelCompilationVariant compilationVariant, |
| 358 | std::vector<SelectedManifestObject>& selectedObjects) | 358 | std::vector<SelectedManifestObject>& selectedObjects) |
| 359 | { | 359 | { |
| 360 | bool foundBasicObject = false; | 360 | bool foundBasicObject = false; |
| @@ -366,7 +366,7 @@ aclError SelectManifestObjectsForCompilation( | |||
| 366 | selectedObjects.push_back({&manifestObject, ManifestObjectKind::Basic}); | 366 | selectedObjects.push_back({&manifestObject, ManifestObjectKind::Basic}); |
| 367 | } else if (objectType == "sk") { | 367 | } else if (objectType == "sk") { |
| 368 | foundSuperKernelObject = true; | 368 | foundSuperKernelObject = true; |
| 369 | - if (compilationVariant == CompilationVariant::BasicWithSuperKernel) { | 369 | + if (compilationVariant == KernelCompilationVariant::BasicWithSuperKernel) { |
| 370 | selectedObjects.push_back({&manifestObject, ManifestObjectKind::SuperKernel}); | 370 | selectedObjects.push_back({&manifestObject, ManifestObjectKind::SuperKernel}); |
| 371 | } | 371 | } |
| 372 | } | 372 | } |
| @@ -375,7 +375,7 @@ aclError SelectManifestObjectsForCompilation( | |||
| 375 | ASCENDLOGE("Selected kernel does not contain a basic object; regenerate the JIT resource"); | 375 | ASCENDLOGE("Selected kernel does not contain a basic object; regenerate the JIT resource"); |
| 376 | return ACLRTC_ERROR_FAILURE; | 376 | return ACLRTC_ERROR_FAILURE; |
| 377 | } | 377 | } |
| 378 | - if (compilationVariant == CompilationVariant::BasicWithSuperKernel && !foundSuperKernelObject) { | 378 | + if (compilationVariant == KernelCompilationVariant::BasicWithSuperKernel && !foundSuperKernelObject) { |
| 379 | ASCENDLOGE("Super-kernel compilation was requested but the selected kernel has no sk object; remove " | 379 | ASCENDLOGE("Super-kernel compilation was requested but the selected kernel has no sk object; remove " |
| 380 | "--enable-super-kernel or use a JIT resource that contains an sk object"); | 380 | "--enable-super-kernel or use a JIT resource that contains an sk object"); |
| 381 | return ACLRTC_ERROR_FAILURE; | 381 | return ACLRTC_ERROR_FAILURE; |
| @@ -117,25 +117,20 @@ bool WritePatchedSourceFileAtomically(const fs::path& sourceFilePath, const std: | |||
| 117 | } // namespace | 117 | } // namespace |
| 118 | 118 | ||
| 119 | KernelCompilationWorkspace::KernelCompilationWorkspace(fs::path worktreePath, WorktreeRetentionPolicy retentionPolicy) | 119 | KernelCompilationWorkspace::KernelCompilationWorkspace(fs::path worktreePath, WorktreeRetentionPolicy retentionPolicy) |
| 120 | - : worktreePath_(std::move(worktreePath)), retentionPolicy_(retentionPolicy) | 120 | + : retentionPolicy_(retentionPolicy) |
| 121 | -{} | ||
| 122 | - | ||
| 123 | -KernelCompilationWorkspace::~KernelCompilationWorkspace() | ||
| 124 | { | 121 | { |
| 125 | - if (retentionPolicy_ == WorktreeRetentionPolicy::RetainAfterCompilation || worktreePath_.empty()) { | 122 | + if (!worktreePath.empty()) { |
| 126 | - return; | 123 | + (void)worktreeCleanupGuard_.RegisterDirectoryForCleanup(worktreePath.string()); |
| 127 | } | 124 | } |
| 128 | - boost::system::error_code cleanupError; | 125 | + if (retentionPolicy_ == WorktreeRetentionPolicy::RetainAfterCompilation) { |
| 129 | - fs::remove_all(worktreePath_, cleanupError); | 126 | + worktreeCleanupGuard_.PreserveDirectory(); |
| 130 | - if (cleanupError) { | ||
| 131 | - ASCENDLOGW( | ||
| 132 | - "Failed to clean resource worktree: path=%s error=%d message=%s; remove the worktree manually if it " | ||
| 133 | - "is no longer needed", | ||
| 134 | - worktreePath_.c_str(), cleanupError.value(), cleanupError.message().c_str()); | ||
| 135 | } | 127 | } |
| 136 | } | 128 | } |
| 137 | 129 | ||
| 138 | -const fs::path& KernelCompilationWorkspace::GetWorktreePath() const noexcept { return worktreePath_; } | 130 | +fs::path KernelCompilationWorkspace::GetWorktreePath() const |
| 131 | +{ | ||
| 132 | + return fs::path(worktreeCleanupGuard_.GetDirectoryPath()); | ||
| 133 | +} | ||
| 139 | 134 | ||
| 140 | aclError KernelCompilationWorkspace::CreateOutputDirectoriesAndApplySourcePatches( | 135 | aclError KernelCompilationWorkspace::CreateOutputDirectoriesAndApplySourcePatches( |
| 141 | const KernelCompilationPlan& compilationPlan) | 136 | const KernelCompilationPlan& compilationPlan) |
| @@ -178,24 +173,21 @@ aclError KernelCompilationWorkspace::CreateOutputDirectoriesAndApplySourcePatche | |||
| 178 | return ACLRTC_SUCCESS; | 173 | return ACLRTC_SUCCESS; |
| 179 | } | 174 | } |
| 180 | 175 | ||
| 181 | -aclError KernelCompilationWorkspace::RemoveWorktreeBeforePublishingElfIfNeeded() | 176 | +aclError KernelCompilationWorkspace::CleanupWorktreeBeforeElfPublication() |
| 182 | { | 177 | { |
| 183 | - if (worktreePath_.empty()) { | 178 | + if (worktreeCleanupGuard_.GetDirectoryPath().empty()) { |
| 184 | return ACLRTC_SUCCESS; | 179 | return ACLRTC_SUCCESS; |
| 185 | } | 180 | } |
| 186 | if (retentionPolicy_ == WorktreeRetentionPolicy::RetainAfterCompilation) { | 181 | if (retentionPolicy_ == WorktreeRetentionPolicy::RetainAfterCompilation) { |
| 187 | return ACLRTC_SUCCESS; | 182 | return ACLRTC_SUCCESS; |
| 188 | } | 183 | } |
| 189 | - boost::system::error_code cleanupError; | 184 | + const std::string worktreePath = worktreeCleanupGuard_.GetDirectoryPath(); |
| 190 | - fs::remove_all(worktreePath_, cleanupError); | 185 | + if (!worktreeCleanupGuard_.RemoveDirectory()) { |
| 191 | - if (cleanupError) { | ||
| 192 | ASCENDLOGE( | 186 | ASCENDLOGE( |
| 193 | - "Failed to clean resource worktree before publishing ELF: path=%s error=%d message=%s; ensure the " | 187 | + "Failed to clean resource worktree before publishing ELF: path=%s; ensure the path is removable and retry", |
| 194 | - "path is removable and retry", | 188 | + worktreePath.c_str()); |
| 195 | - worktreePath_.c_str(), cleanupError.value(), cleanupError.message().c_str()); | ||
| 196 | return ACLRTC_ERROR_FAILURE; | 189 | return ACLRTC_ERROR_FAILURE; |
| 197 | } | 190 | } |
| 198 | - worktreePath_.clear(); | ||
| 199 | return ACLRTC_SUCCESS; | 191 | return ACLRTC_SUCCESS; |
| 200 | } | 192 | } |
| 201 | 193 | ||
| @@ -12,6 +12,7 @@ | |||
| 12 | #define ASCENDC_ACLRTC_KERNEL_COMPILATION_WORKSPACE_H | 12 | #define ASCENDC_ACLRTC_KERNEL_COMPILATION_WORKSPACE_H |
| 13 | 13 | ||
| 14 | #include "kernel_specialization_types.h" | 14 | #include "kernel_specialization_types.h" |
| 15 | +#include "directory_cleanup_guard.h" | ||
| 15 | 16 | ||
| 16 | #include <boost/filesystem/path.hpp> | 17 | #include <boost/filesystem/path.hpp> |
| 17 | #include <cstdint> | 18 | #include <cstdint> |
| @@ -27,17 +28,17 @@ enum class WorktreeRetentionPolicy : uint32_t { | |||
| 27 | class KernelCompilationWorkspace final { | 28 | class KernelCompilationWorkspace final { |
| 28 | public: | 29 | public: |
| 29 | KernelCompilationWorkspace(boost::filesystem::path worktreePath, WorktreeRetentionPolicy retentionPolicy); | 30 | KernelCompilationWorkspace(boost::filesystem::path worktreePath, WorktreeRetentionPolicy retentionPolicy); |
| 30 | - ~KernelCompilationWorkspace(); | 31 | + ~KernelCompilationWorkspace() = default; |
| 31 | 32 | ||
| 32 | KernelCompilationWorkspace(const KernelCompilationWorkspace&) = delete; | 33 | KernelCompilationWorkspace(const KernelCompilationWorkspace&) = delete; |
| 33 | KernelCompilationWorkspace& operator=(const KernelCompilationWorkspace&) = delete; | 34 | KernelCompilationWorkspace& operator=(const KernelCompilationWorkspace&) = delete; |
| 34 | 35 | ||
| 35 | - const boost::filesystem::path& GetWorktreePath() const noexcept; | 36 | + boost::filesystem::path GetWorktreePath() const; |
| 36 | aclError CreateOutputDirectoriesAndApplySourcePatches(const KernelCompilationPlan& compilationPlan); | 37 | aclError CreateOutputDirectoriesAndApplySourcePatches(const KernelCompilationPlan& compilationPlan); |
| 37 | - aclError RemoveWorktreeBeforePublishingElfIfNeeded(); | 38 | + aclError CleanupWorktreeBeforeElfPublication(); |
| 38 | 39 | ||
| 39 | private: | 40 | private: |
| 40 | - boost::filesystem::path worktreePath_; | 41 | + DirectoryCleanupGuard worktreeCleanupGuard_; |
| 41 | WorktreeRetentionPolicy retentionPolicy_{WorktreeRetentionPolicy::RemoveAfterCompilation}; | 42 | WorktreeRetentionPolicy retentionPolicy_{WorktreeRetentionPolicy::RemoveAfterCompilation}; |
| 42 | }; | 43 | }; |
| 43 | 44 | ||
| @@ -117,7 +117,7 @@ void RemoveTemporaryOutputFileBestEffort(const fs::path& temporaryOutputPath) | |||
| 117 | } | 117 | } |
| 118 | } // namespace | 118 | } // namespace |
| 119 | 119 | ||
| 120 | -aclError KernelSpecializationSession::PrepareCompilationResource( | 120 | +aclError KernelSpecializationSession::LoadAndMaterializeCompilationResource( |
| 121 | const std::string& resourceId, MaterializedKernelCompilationResource& compilationResource) | 121 | const std::string& resourceId, MaterializedKernelCompilationResource& compilationResource) |
| 122 | { | 122 | { |
| 123 | ResourceRegistry& resourceRegistry = ResourceRegistry::Instance(); | 123 | ResourceRegistry& resourceRegistry = ResourceRegistry::Instance(); |
| @@ -146,20 +146,20 @@ aclError KernelSpecializationSession::PrepareCompilationResource( | |||
| 146 | return ACLRTC_SUCCESS; | 146 | return ACLRTC_SUCCESS; |
| 147 | } | 147 | } |
| 148 | 148 | ||
| 149 | -aclError KernelSpecializationSession::FinalizeSpecializationResult( | 149 | +aclError KernelSpecializationSession::ReportSpecializationStatus( |
| 150 | const NormalizedKernelSpecializationRequest& specializationRequest, KernelSpecializationDiagnostics& diagnostics, | 150 | const NormalizedKernelSpecializationRequest& specializationRequest, KernelSpecializationDiagnostics& diagnostics, |
| 151 | - aclError specializationResult) noexcept | 151 | + aclError specializationStatus) noexcept |
| 152 | { | 152 | { |
| 153 | - const OutputPublicationStatus publicationStatus = specializationResult == ACLRTC_SUCCESS ? | 153 | + const KernelElfPublicationStatus publicationStatus = specializationStatus == ACLRTC_SUCCESS ? |
| 154 | - OutputPublicationStatus::Published : | 154 | + KernelElfPublicationStatus::Published : |
| 155 | - OutputPublicationStatus::NotPublished; | 155 | + KernelElfPublicationStatus::NotPublished; |
| 156 | diagnostics.WriteSpecializationResult(specializationRequest.outputElfPath, publicationStatus); | 156 | diagnostics.WriteSpecializationResult(specializationRequest.outputElfPath, publicationStatus); |
| 157 | - if (specializationResult == ACLRTC_SUCCESS) { | 157 | + if (specializationStatus == ACLRTC_SUCCESS) { |
| 158 | ASCENDLOGI( | 158 | ASCENDLOGI( |
| 159 | "Kernel %s specialization completed: output=%s", specializationRequest.kernelName.c_str(), | 159 | "Kernel %s specialization completed: output=%s", specializationRequest.kernelName.c_str(), |
| 160 | specializationRequest.outputElfPath.c_str()); | 160 | specializationRequest.outputElfPath.c_str()); |
| 161 | } | 161 | } |
| 162 | - return specializationResult; | 162 | + return specializationStatus; |
| 163 | } | 163 | } |
| 164 | 164 | ||
| 165 | aclError KernelSpecializationSession::RunKernelSpecialization( | 165 | aclError KernelSpecializationSession::RunKernelSpecialization( |
| @@ -175,16 +175,16 @@ aclError KernelSpecializationSession::RunKernelSpecialization( | |||
| 175 | } | 175 | } |
| 176 | 176 | ||
| 177 | NormalizedKernelSpecializationRequest specializationRequest; | 177 | NormalizedKernelSpecializationRequest specializationRequest; |
| 178 | - aclError specializationResult = | 178 | + aclError specializationStatus = |
| 179 | NormalizeKernelSpecializationRequest(*apiRequest, outputElfPath, specializationRequest); | 179 | NormalizeKernelSpecializationRequest(*apiRequest, outputElfPath, specializationRequest); |
| 180 | - if (specializationResult != ACLRTC_SUCCESS) { | 180 | + if (specializationStatus != ACLRTC_SUCCESS) { |
| 181 | - return specializationResult; | 181 | + return specializationStatus; |
| 182 | } | 182 | } |
| 183 | 183 | ||
| 184 | MaterializedKernelCompilationResource compilationResource; | 184 | MaterializedKernelCompilationResource compilationResource; |
| 185 | - specializationResult = PrepareCompilationResource(specializationRequest.resourceId, compilationResource); | 185 | + specializationStatus = LoadAndMaterializeCompilationResource(specializationRequest.resourceId, compilationResource); |
| 186 | - if (specializationResult != ACLRTC_SUCCESS) { | 186 | + if (specializationStatus != ACLRTC_SUCCESS) { |
| 187 | - return specializationResult; | 187 | + return specializationStatus; |
| 188 | } | 188 | } |
| 189 | return RunSpecializationWithMaterializedResource(specializationRequest, std::move(compilationResource)); | 189 | return RunSpecializationWithMaterializedResource(specializationRequest, std::move(compilationResource)); |
| 190 | } | 190 | } |
| @@ -204,28 +204,28 @@ aclError KernelSpecializationSession::RunSpecializationWithMaterializedResource( | |||
| 204 | diagnostics.WriteManifestSnapshot(compilationResource.manifest); | 204 | diagnostics.WriteManifestSnapshot(compilationResource.manifest); |
| 205 | 205 | ||
| 206 | KernelCompilationPlan compilationPlan; | 206 | KernelCompilationPlan compilationPlan; |
| 207 | - aclError specializationResult = | 207 | + aclError specializationStatus = |
| 208 | KernelCompilationPlanBuilder( | 208 | KernelCompilationPlanBuilder( |
| 209 | specializationRequest, compilationResource.manifest, compilationWorkspace.GetWorktreePath(), | 209 | specializationRequest, compilationResource.manifest, compilationWorkspace.GetWorktreePath(), |
| 210 | compilationResource.externalSourceDirectoryPath) | 210 | compilationResource.externalSourceDirectoryPath) |
| 211 | .BuildCompilationPlan(compilationPlan); | 211 | .BuildCompilationPlan(compilationPlan); |
| 212 | - if (specializationResult != ACLRTC_SUCCESS) { | 212 | + if (specializationStatus != ACLRTC_SUCCESS) { |
| 213 | - return FinalizeSpecializationResult(specializationRequest, diagnostics, specializationResult); | 213 | + return ReportSpecializationStatus(specializationRequest, diagnostics, specializationStatus); |
| 214 | } | 214 | } |
| 215 | - specializationResult = compilationWorkspace.CreateOutputDirectoriesAndApplySourcePatches(compilationPlan); | 215 | + specializationStatus = compilationWorkspace.CreateOutputDirectoriesAndApplySourcePatches(compilationPlan); |
| 216 | - if (specializationResult != ACLRTC_SUCCESS) { | 216 | + if (specializationStatus != ACLRTC_SUCCESS) { |
| 217 | - return FinalizeSpecializationResult(specializationRequest, diagnostics, specializationResult); | 217 | + return ReportSpecializationStatus(specializationRequest, diagnostics, specializationStatus); |
| 218 | } | 218 | } |
| 219 | 219 | ||
| 220 | CompilationPlanExecutor planExecutor(diagnostics, CompilationCommandExecutionLimits::ProductionDefaults()); | 220 | CompilationPlanExecutor planExecutor(diagnostics, CompilationCommandExecutionLimits::ProductionDefaults()); |
| 221 | - specializationResult = planExecutor.ExecuteCompilationPlan(compilationPlan); | 221 | + specializationStatus = planExecutor.ExecuteCompilationPlan(compilationPlan); |
| 222 | - if (specializationResult != ACLRTC_SUCCESS) { | 222 | + if (specializationStatus != ACLRTC_SUCCESS) { |
| 223 | - return FinalizeSpecializationResult(specializationRequest, diagnostics, specializationResult); | 223 | + return ReportSpecializationStatus(specializationRequest, diagnostics, specializationStatus); |
| 224 | } | 224 | } |
| 225 | 225 | ||
| 226 | - specializationResult = PublishKernelElf( | 226 | + specializationStatus = PublishKernelElf( |
| 227 | compilationPlan.linkedKernelElfPath, specializationRequest.outputElfPath, compilationWorkspace); | 227 | compilationPlan.linkedKernelElfPath, specializationRequest.outputElfPath, compilationWorkspace); |
| 228 | - return FinalizeSpecializationResult(specializationRequest, diagnostics, specializationResult); | 228 | + return ReportSpecializationStatus(specializationRequest, diagnostics, specializationStatus); |
| 229 | } | 229 | } |
| 230 | 230 | ||
| 231 | aclError KernelSpecializationSession::PublishKernelElf( | 231 | aclError KernelSpecializationSession::PublishKernelElf( |
| @@ -274,10 +274,10 @@ aclError KernelSpecializationSession::PublishKernelElf( | |||
| 274 | return ACLRTC_ERROR_FAILURE; | 274 | return ACLRTC_ERROR_FAILURE; |
| 275 | } | 275 | } |
| 276 | 276 | ||
| 277 | - const aclError worktreeRemovalResult = compilationWorkspace.RemoveWorktreeBeforePublishingElfIfNeeded(); | 277 | + const aclError worktreeCleanupStatus = compilationWorkspace.CleanupWorktreeBeforeElfPublication(); |
| 278 | - if (worktreeRemovalResult != ACLRTC_SUCCESS) { | 278 | + if (worktreeCleanupStatus != ACLRTC_SUCCESS) { |
| 279 | RemoveTemporaryOutputFileBestEffort(temporaryOutputPath); | 279 | RemoveTemporaryOutputFileBestEffort(temporaryOutputPath); |
| 280 | - return worktreeRemovalResult; | 280 | + return worktreeCleanupStatus; |
| 281 | } | 281 | } |
| 282 | 282 | ||
| 283 | boost::system::error_code renameError; | 283 | boost::system::error_code renameError; |
| @@ -32,16 +32,16 @@ public: | |||
| 32 | aclError RunKernelSpecialization(const aclrtcKernelSpecRequest* apiRequest, const char* outputElfPath); | 32 | aclError RunKernelSpecialization(const aclrtcKernelSpecRequest* apiRequest, const char* outputElfPath); |
| 33 | 33 | ||
| 34 | private: | 34 | private: |
| 35 | - static aclError PrepareCompilationResource( | 35 | + static aclError LoadAndMaterializeCompilationResource( |
| 36 | const std::string& resourceId, MaterializedKernelCompilationResource& compilationResource); | 36 | const std::string& resourceId, MaterializedKernelCompilationResource& compilationResource); |
| 37 | 37 | ||
| 38 | aclError RunSpecializationWithMaterializedResource( | 38 | aclError RunSpecializationWithMaterializedResource( |
| 39 | const NormalizedKernelSpecializationRequest& specializationRequest, | 39 | const NormalizedKernelSpecializationRequest& specializationRequest, |
| 40 | MaterializedKernelCompilationResource compilationResource); | 40 | MaterializedKernelCompilationResource compilationResource); |
| 41 | 41 | ||
| 42 | - static aclError FinalizeSpecializationResult( | 42 | + static aclError ReportSpecializationStatus( |
| 43 | const NormalizedKernelSpecializationRequest& specializationRequest, | 43 | const NormalizedKernelSpecializationRequest& specializationRequest, |
| 44 | - KernelSpecializationDiagnostics& diagnostics, aclError specializationResult) noexcept; | 44 | + KernelSpecializationDiagnostics& diagnostics, aclError specializationStatus) noexcept; |
| 45 | 45 | ||
| 46 | aclError PublishKernelElf( | 46 | aclError PublishKernelElf( |
| 47 | const boost::filesystem::path& linkedKernelElfPath, const boost::filesystem::path& outputElfPath, | 47 | const boost::filesystem::path& linkedKernelElfPath, const boost::filesystem::path& outputElfPath, |
| @@ -11,6 +11,7 @@ | |||
| 11 | #include "kernel_specialization_diagnostics.h" | 11 | #include "kernel_specialization_diagnostics.h" |
| 12 | 12 | ||
| 13 | #include "ascendc_tool_log.h" | 13 | #include "ascendc_tool_log.h" |
| 14 | +#include "process_executor.h" | ||
| 14 | 15 | ||
| 15 | #include <boost/filesystem.hpp> | 16 | #include <boost/filesystem.hpp> |
| 16 | #include <cerrno> | 17 | #include <cerrno> |
| @@ -43,22 +44,6 @@ std::string QuoteArgumentForReplayScript(const std::string& argument) | |||
| 43 | return quotedArgument; | 44 | return quotedArgument; |
| 44 | } | 45 | } |
| 45 | 46 | ||
| 46 | -const char* GetProcessTerminationName(CompilationProcessTermination termination) | ||
| 47 | -{ | ||
| 48 | - switch (termination) { | ||
| 49 | - case CompilationProcessTermination::InfrastructureFailure: | ||
| 50 | - return "infrastructure_failure"; | ||
| 51 | - case CompilationProcessTermination::Exited: | ||
| 52 | - return "exited"; | ||
| 53 | - case CompilationProcessTermination::Signaled: | ||
| 54 | - return "signaled"; | ||
| 55 | - case CompilationProcessTermination::TimedOut: | ||
| 56 | - return "timed_out"; | ||
| 57 | - default: | ||
| 58 | - return "unknown"; | ||
| 59 | - } | ||
| 60 | -} | ||
| 61 | - | ||
| 62 | bool WriteDiagnosticTextFile(const fs::path& filePath, const std::string& text, std::ios::openmode writeMode) | 47 | bool WriteDiagnosticTextFile(const fs::path& filePath, const std::string& text, std::ios::openmode writeMode) |
| 63 | { | 48 | { |
| 64 | std::ofstream outputStream(filePath.string(), std::ios::out | writeMode); | 49 | std::ofstream outputStream(filePath.string(), std::ios::out | writeMode); |
| @@ -175,22 +160,40 @@ void KernelSpecializationDiagnostics::AppendCommandToReplayScript(const Compilat | |||
| 175 | } | 160 | } |
| 176 | } | 161 | } |
| 177 | 162 | ||
| 178 | -void KernelSpecializationDiagnostics::AppendCommandResultToCompilationLog( | 163 | +std::string KernelSpecializationDiagnostics::GetCompilationLogFilePath() const |
| 179 | - const CompilationCommand& compilationCommand, const CompilationProcessResult& processResult) noexcept | 164 | +{ |
| 165 | + return diagnosticsEnabled_ ? (resourceWorktreePath_ / DIAGNOSTIC_COMPILE_LOG_FILE_NAME).string() : std::string(); | ||
| 166 | +} | ||
| 167 | + | ||
| 168 | +void KernelSpecializationDiagnostics::AppendCommandExecutionHeaderToLog( | ||
| 169 | + const CompilationCommand& compilationCommand) noexcept | ||
| 170 | +{ | ||
| 171 | + if (!diagnosticsEnabled_) { | ||
| 172 | + return; | ||
| 173 | + } | ||
| 174 | + try { | ||
| 175 | + AppendTextToDiagnosticFile( | ||
| 176 | + resourceWorktreePath_ / DIAGNOSTIC_COMPILE_LOG_FILE_NAME, | ||
| 177 | + "[" + compilationCommand.diagnosticLabel + "] output:\n"); | ||
| 178 | + } catch (...) { | ||
| 179 | + ASCENDLOGW( | ||
| 180 | + "Failed to record command output header: command=%s resource_worktree=%s", | ||
| 181 | + compilationCommand.diagnosticLabel.c_str(), resourceWorktreePath_.c_str()); | ||
| 182 | + } | ||
| 183 | +} | ||
| 184 | + | ||
| 185 | +void KernelSpecializationDiagnostics::AppendCommandExecutionResultToLog( | ||
| 186 | + const CompilationCommand& compilationCommand, const ProcessExecutorResult& executorResult) noexcept | ||
| 180 | { | 187 | { |
| 181 | if (!diagnosticsEnabled_) { | 188 | if (!diagnosticsEnabled_) { |
| 182 | return; | 189 | return; |
| 183 | } | 190 | } |
| 184 | try { | 191 | try { |
| 185 | std::ostringstream resultText; | 192 | std::ostringstream resultText; |
| 186 | - resultText << '[' << compilationCommand.diagnosticLabel | 193 | + resultText << '\n' |
| 187 | - << "] termination=" << GetProcessTerminationName(processResult.termination) | 194 | + << '[' << compilationCommand.diagnosticLabel << "] outcome=" << executorResult.GetOutcomeName() |
| 188 | - << " code=" << processResult.terminationCode << " elapsed_ms=" << processResult.elapsedTime.count() | 195 | + << " code=" << executorResult.terminationCode << " elapsed_ms=" << executorResult.elapsedTime.count() |
| 189 | - << " output_truncated=" << (processResult.capturedOutputTruncated ? "true" : "false") << '\n' | 196 | + << '\n'; |
| 190 | - << processResult.capturedOutput; | ||
| 191 | - if (!processResult.capturedOutput.empty() && processResult.capturedOutput.back() != '\n') { | ||
| 192 | - resultText << '\n'; | ||
| 193 | - } | ||
| 194 | AppendTextToDiagnosticFile(resourceWorktreePath_ / DIAGNOSTIC_COMPILE_LOG_FILE_NAME, resultText.str()); | 197 | AppendTextToDiagnosticFile(resourceWorktreePath_ / DIAGNOSTIC_COMPILE_LOG_FILE_NAME, resultText.str()); |
| 195 | } catch (...) { | 198 | } catch (...) { |
| 196 | ASCENDLOGW( | 199 | ASCENDLOGW( |
| @@ -220,7 +223,7 @@ void KernelSpecializationDiagnostics::LogCommandFailureRecoveryHint() const noex | |||
| 220 | } | 223 | } |
| 221 | 224 | ||
| 222 | void KernelSpecializationDiagnostics::WriteSpecializationResult( | 225 | void KernelSpecializationDiagnostics::WriteSpecializationResult( |
| 223 | - const fs::path& outputElfPath, OutputPublicationStatus publicationStatus) noexcept | 226 | + const fs::path& outputElfPath, KernelElfPublicationStatus publicationStatus) noexcept |
| 224 | { | 227 | { |
| 225 | if (!diagnosticsEnabled_) { | 228 | if (!diagnosticsEnabled_) { |
| 226 | return; | 229 | return; |
| @@ -230,7 +233,7 @@ void KernelSpecializationDiagnostics::WriteSpecializationResult( | |||
| 230 | nlohmann::json resultJson{ | 233 | nlohmann::json resultJson{ |
| 231 | {"specialization_session_id", specializationSessionId_}, | 234 | {"specialization_session_id", specializationSessionId_}, |
| 232 | {"output_path", outputElfPath.string()}, | 235 | {"output_path", outputElfPath.string()}, |
| 233 | - {"output_published", publicationStatus == OutputPublicationStatus::Published}, | 236 | + {"output_published", publicationStatus == KernelElfPublicationStatus::Published}, |
| 234 | }; | 237 | }; |
| 235 | const fs::path resultPath = resourceWorktreePath_ / DIAGNOSTIC_RESULT_FILE_NAME; | 238 | const fs::path resultPath = resourceWorktreePath_ / DIAGNOSTIC_RESULT_FILE_NAME; |
| 236 | WriteDiagnosticTextFile(resultPath, resultJson.dump(diagnosticJsonIndentationWidth) + '\n', std::ios::trunc); | 239 | WriteDiagnosticTextFile(resultPath, resultJson.dump(diagnosticJsonIndentationWidth) + '\n', std::ios::trunc); |
| @@ -19,9 +19,12 @@ | |||
| 19 | #include <string> | 19 | #include <string> |
| 20 | 20 | ||
| 21 | namespace ascendc { | 21 | namespace ascendc { |
| 22 | + | ||
| 23 | +struct ProcessExecutorResult; | ||
| 24 | + | ||
| 22 | namespace aclrtc { | 25 | namespace aclrtc { |
| 23 | 26 | ||
| 24 | -enum class OutputPublicationStatus : uint32_t { | 27 | +enum class KernelElfPublicationStatus : uint32_t { |
| 25 | NotPublished, | 28 | NotPublished, |
| 26 | Published, | 29 | Published, |
| 27 | }; | 30 | }; |
| @@ -32,11 +35,13 @@ public: | |||
| 32 | 35 | ||
| 33 | void WriteManifestSnapshot(const nlohmann::json& resourceManifest) noexcept; | 36 | void WriteManifestSnapshot(const nlohmann::json& resourceManifest) noexcept; |
| 34 | void AppendCommandToReplayScript(const CompilationCommand& compilationCommand) noexcept; | 37 | void AppendCommandToReplayScript(const CompilationCommand& compilationCommand) noexcept; |
| 35 | - void AppendCommandResultToCompilationLog( | 38 | + void AppendCommandExecutionHeaderToLog(const CompilationCommand& compilationCommand) noexcept; |
| 36 | - const CompilationCommand& compilationCommand, const CompilationProcessResult& processResult) noexcept; | 39 | + std::string GetCompilationLogFilePath() const; |
| 40 | + void AppendCommandExecutionResultToLog( | ||
| 41 | + const CompilationCommand& compilationCommand, const ProcessExecutorResult& executorResult) noexcept; | ||
| 37 | void LogCommandFailureRecoveryHint() const noexcept; | 42 | void LogCommandFailureRecoveryHint() const noexcept; |
| 38 | void WriteSpecializationResult( | 43 | void WriteSpecializationResult( |
| 39 | - const boost::filesystem::path& outputElfPath, OutputPublicationStatus publicationStatus) noexcept; | 44 | + const boost::filesystem::path& outputElfPath, KernelElfPublicationStatus publicationStatus) noexcept; |
| 40 | 45 | ||
| 41 | private: | 46 | private: |
| 42 | void AppendTextToDiagnosticFile(const boost::filesystem::path& filePath, const std::string& text) noexcept; | 47 | void AppendTextToDiagnosticFile(const boost::filesystem::path& filePath, const std::string& text) noexcept; |
| @@ -198,7 +198,7 @@ aclError NormalizeSuperKernelCompilerOptions( | |||
| 198 | return ACLRTC_SUCCESS; | 198 | return ACLRTC_SUCCESS; |
| 199 | } | 199 | } |
| 200 | 200 | ||
| 201 | - normalizedRequest.compilationVariant = CompilationVariant::BasicWithSuperKernel; | 201 | + normalizedRequest.compilationVariant = KernelCompilationVariant::BasicWithSuperKernel; |
| 202 | for (size_t optionIndex = 0U; optionIndex < copiedSuperKernelOptions.size(); ++optionIndex) { | 202 | for (size_t optionIndex = 0U; optionIndex < copiedSuperKernelOptions.size(); ++optionIndex) { |
| 203 | std::string& superKernelOption = copiedSuperKernelOptions[optionIndex]; | 203 | std::string& superKernelOption = copiedSuperKernelOptions[optionIndex]; |
| 204 | if (superKernelOption == SUPER_KERNEL_ENABLE_OPTION) { | 204 | if (superKernelOption == SUPER_KERNEL_ENABLE_OPTION) { |
| @@ -13,7 +13,6 @@ | |||
| 13 | 13 | ||
| 14 | #include "acl/acl_rt_compile.h" | 14 | #include "acl/acl_rt_compile.h" |
| 15 | 15 | ||
| 16 | -#include <chrono> | ||
| 17 | #include <boost/filesystem/path.hpp> | 16 | #include <boost/filesystem/path.hpp> |
| 18 | #include <cstdint> | 17 | #include <cstdint> |
| 19 | #include <optional> | 18 | #include <optional> |
| @@ -31,7 +30,7 @@ constexpr aclError ACLRTC_ERROR_LINKING = 176004; | |||
| 31 | constexpr aclError ACLRTC_ERROR_OUT_OF_MEMORY = 276001; | 30 | constexpr aclError ACLRTC_ERROR_OUT_OF_MEMORY = 276001; |
| 32 | constexpr aclError ACLRTC_ERROR_FAILURE = 576000; | 31 | constexpr aclError ACLRTC_ERROR_FAILURE = 576000; |
| 33 | 32 | ||
| 34 | -enum class CompilationVariant : uint32_t { | 33 | +enum class KernelCompilationVariant : uint32_t { |
| 35 | Basic, | 34 | Basic, |
| 36 | BasicWithSuperKernel, | 35 | BasicWithSuperKernel, |
| 37 | }; | 36 | }; |
| @@ -42,13 +41,6 @@ enum class CompilationCommandKind : uint32_t { | |||
| 42 | Link, | 41 | Link, |
| 43 | }; | 42 | }; |
| 44 | 43 | ||
| 45 | -enum class CompilationProcessTermination : uint32_t { | ||
| 46 | - InfrastructureFailure, | ||
| 47 | - Exited, | ||
| 48 | - Signaled, | ||
| 49 | - TimedOut, | ||
| 50 | -}; | ||
| 51 | - | ||
| 52 | struct KernelCompilerOptions { | 44 | struct KernelCompilerOptions { |
| 53 | std::vector<std::string> basicOptions; | 45 | std::vector<std::string> basicOptions; |
| 54 | std::vector<std::string> superKernelOptions; | 46 | std::vector<std::string> superKernelOptions; |
| @@ -59,7 +51,7 @@ struct NormalizedKernelSpecializationRequest { | |||
| 59 | std::string resourceId; | 51 | std::string resourceId; |
| 60 | std::string kernelName; | 52 | std::string kernelName; |
| 61 | boost::filesystem::path outputElfPath; | 53 | boost::filesystem::path outputElfPath; |
| 62 | - CompilationVariant compilationVariant{CompilationVariant::Basic}; | 54 | + KernelCompilationVariant compilationVariant{KernelCompilationVariant::Basic}; |
| 63 | KernelCompilerOptions compilerOptions; | 55 | KernelCompilerOptions compilerOptions; |
| 64 | uint64_t kernelArgumentCount{0}; | 56 | uint64_t kernelArgumentCount{0}; |
| 65 | const void* const* borrowedKernelArgumentDataPointers{nullptr}; | 57 | const void* const* borrowedKernelArgumentDataPointers{nullptr}; |
| @@ -87,14 +79,6 @@ struct KernelCompilationPlan { | |||
| 87 | boost::filesystem::path linkedKernelElfPath; | 79 | boost::filesystem::path linkedKernelElfPath; |
| 88 | }; | 80 | }; |
| 89 | 81 | ||
| 90 | -struct CompilationProcessResult { | ||
| 91 | - CompilationProcessTermination termination{CompilationProcessTermination::InfrastructureFailure}; | ||
| 92 | - int32_t terminationCode{0}; | ||
| 93 | - bool capturedOutputTruncated{false}; | ||
| 94 | - std::chrono::milliseconds elapsedTime{0}; | ||
| 95 | - std::string capturedOutput; | ||
| 96 | -}; | ||
| 97 | - | ||
| 98 | aclError NormalizeKernelSpecializationRequest( | 82 | aclError NormalizeKernelSpecializationRequest( |
| 99 | const aclrtcKernelSpecRequest& apiRequest, const char* outputElfPath, | 83 | const aclrtcKernelSpecRequest& apiRequest, const char* outputElfPath, |
| 100 | NormalizedKernelSpecializationRequest& normalizedRequest); | 84 | NormalizedKernelSpecializationRequest& normalizedRequest); |
| @@ -113,60 +113,16 @@ const char* ResourceStatusName(ResourceStatus status) noexcept | |||
| 113 | return "unknown"; | 113 | return "unknown"; |
| 114 | } | 114 | } |
| 115 | 115 | ||
| 116 | -void CleanupPath(const std::string& path) noexcept | 116 | +ResourceStatus CreateCompileResourceMaterializationRoot(DirectoryCleanupGuard& materializationRootDirectory) |
| 117 | -{ | ||
| 118 | - if (path.empty()) { | ||
| 119 | - return; | ||
| 120 | - } | ||
| 121 | - if (!FileUtils::RemoveAll(path)) { | ||
| 122 | - ASCENDLOGW("Failed to remove compile resource path: path=%s reason=recursive removal failed", path.c_str()); | ||
| 123 | - } | ||
| 124 | -} | ||
| 125 | - | ||
| 126 | -ResourceStatus CreateTemporaryRoot(std::string& temporaryRoot) | ||
| 127 | { | 117 | { |
| 128 | const char* environment = std::getenv("TMPDIR"); | 118 | const char* environment = std::getenv("TMPDIR"); |
| 129 | - const fs::path configured = environment == nullptr || *environment == '\0' ? "/tmp" : environment; | 119 | + const std::string parentDirectoryPath = environment == nullptr || *environment == '\0' ? "/tmp" : environment; |
| 130 | - boost::system::error_code error; | 120 | + if (!materializationRootDirectory.CreateUniqueSubdirectory(parentDirectoryPath, "aclrtc-resource")) { |
| 131 | - const fs::path parent = fs::canonical(configured, error); | 121 | + ASCENDLOGE("Failed to create compile resource temporary root: parent=%s", parentDirectoryPath.c_str()); |
| 132 | - if (error) { | ||
| 133 | - ASCENDLOGE( | ||
| 134 | - "Failed to resolve compile resource temporary parent: configured=%s error=%s", configured.c_str(), | ||
| 135 | - error.message().c_str()); | ||
| 136 | return ResourceStatus::IoError; | 122 | return ResourceStatus::IoError; |
| 137 | } | 123 | } |
| 138 | - if (!fs::is_directory(parent, error)) { | 124 | + ASCENDLOGI( |
| 139 | - ASCENDLOGE( | 125 | + "Created compile resource temporary root: path=%s", materializationRootDirectory.GetDirectoryPath().c_str()); |
| 140 | - "Compile resource temporary parent is not a directory: configured=%s resolved=%s error=%s", | ||
| 141 | - configured.c_str(), parent.c_str(), error ? error.message().c_str() : "none"); | ||
| 142 | - return ResourceStatus::IoError; | ||
| 143 | - } | ||
| 144 | - const std::string pattern = (parent / "aclrtc-resource-XXXXXX").string(); | ||
| 145 | - std::vector<char> writable(pattern.begin(), pattern.end()); | ||
| 146 | - writable.push_back('\0'); | ||
| 147 | - char* created = mkdtemp(writable.data()); | ||
| 148 | - if (created == nullptr) { | ||
| 149 | - ASCENDLOGE( | ||
| 150 | - "Failed to create compile resource temporary root: parent=%s error=%s", parent.c_str(), | ||
| 151 | - std::strerror(errno)); | ||
| 152 | - return ResourceStatus::IoError; | ||
| 153 | - } | ||
| 154 | - const fs::path canonical = fs::canonical(created, error); | ||
| 155 | - if (error) { | ||
| 156 | - ASCENDLOGE( | ||
| 157 | - "Failed to resolve created compile resource temporary root: path=%s error=%s", created, | ||
| 158 | - error.message().c_str()); | ||
| 159 | - CleanupPath(created); | ||
| 160 | - return ResourceStatus::IoError; | ||
| 161 | - } | ||
| 162 | - if (canonical != fs::path(created).lexically_normal()) { | ||
| 163 | - ASCENDLOGE( | ||
| 164 | - "Compile resource temporary root identity mismatch: expected=%s actual=%s", created, canonical.c_str()); | ||
| 165 | - CleanupPath(created); | ||
| 166 | - return ResourceStatus::IoError; | ||
| 167 | - } | ||
| 168 | - temporaryRoot = canonical.string(); | ||
| 169 | - ASCENDLOGI("Created compile resource temporary root: path=%s", temporaryRoot.c_str()); | ||
| 170 | return ResourceStatus::Success; | 126 | return ResourceStatus::Success; |
| 171 | } | 127 | } |
| 172 | 128 | ||
| @@ -428,42 +384,18 @@ ResourceStatus ParseManifestOwnership( | |||
| 428 | return ParseSourceFile(ownership.document, spec, manifestIndex, ownership); | 384 | return ParseSourceFile(ownership.document, spec, manifestIndex, ownership); |
| 429 | } | 385 | } |
| 430 | 386 | ||
| 431 | -ResourceStatus PrepareMaterializationRoot( | 387 | +ResourceStatus CreateUniqueResourceMaterializationDirectory( |
| 432 | - const std::string& temporaryRoot, const std::string& resourceId, ResourceSourceType sourceType, | 388 | + const std::string& materializationRootPath, const std::string& resourceId, ResourceSourceType sourceType, |
| 433 | - std::string& categoryRoot, std::string& canonicalRoot) | 389 | + DirectoryCleanupGuard& materializedResourceDirectory) |
| 434 | { | 390 | { |
| 435 | - boost::system::error_code error; | 391 | + const std::string categoryParent = FileUtils::JoinPath(materializationRootPath, SourceTypeName(sourceType)); |
| 436 | - const fs::path randomComponent = fs::unique_path("materialize-%%%%%%%%%%%%%%%%", error); | 392 | + if (!FileUtils::CreateDirectories(categoryParent) || |
| 437 | - if (error || randomComponent.empty() || randomComponent.has_parent_path()) { | 393 | + !materializedResourceDirectory.CreateUniqueSubdirectory(categoryParent, "materialize")) { |
| 438 | ASCENDLOGE( | 394 | ASCENDLOGE( |
| 439 | - "Failed to generate compile resource materialization path: resource_id=%s source_type=%s error=%s", | 395 | + "Failed to create compile resource materialization directory: resource_id=%s source_type=%s parent=%s", |
| 440 | - resourceId.c_str(), SourceTypeName(sourceType), error ? error.message().c_str() : "invalid random path"); | 396 | + resourceId.c_str(), SourceTypeName(sourceType), categoryParent.c_str()); |
| 441 | return ResourceStatus::IoError; | 397 | return ResourceStatus::IoError; |
| 442 | } | 398 | } |
| 443 | - const std::string categoryParent = FileUtils::JoinPath(temporaryRoot, SourceTypeName(sourceType)); | ||
| 444 | - categoryRoot = FileUtils::JoinPath(categoryParent, randomComponent.string()); | ||
| 445 | - if (!FileUtils::CreateDirectories(categoryRoot)) { | ||
| 446 | - ASCENDLOGE( | ||
| 447 | - "Failed to create compile resource materialization directory: resource_id=%s source_type=%s path=%s", | ||
| 448 | - resourceId.c_str(), SourceTypeName(sourceType), categoryRoot.c_str()); | ||
| 449 | - return ResourceStatus::IoError; | ||
| 450 | - } | ||
| 451 | - const fs::path canonical = fs::canonical(categoryRoot, error); | ||
| 452 | - if (error) { | ||
| 453 | - ASCENDLOGE( | ||
| 454 | - "Failed to resolve compile resource materialization directory: resource_id=%s source_type=%s path=%s " | ||
| 455 | - "error=%s", | ||
| 456 | - resourceId.c_str(), SourceTypeName(sourceType), categoryRoot.c_str(), error.message().c_str()); | ||
| 457 | - return ResourceStatus::IoError; | ||
| 458 | - } | ||
| 459 | - if (canonical != fs::path(categoryRoot).lexically_normal()) { | ||
| 460 | - ASCENDLOGE( | ||
| 461 | - "Compile resource materialization directory identity mismatch: resource_id=%s source_type=%s expected=%s " | ||
| 462 | - "actual=%s", | ||
| 463 | - resourceId.c_str(), SourceTypeName(sourceType), categoryRoot.c_str(), canonical.c_str()); | ||
| 464 | - return ResourceStatus::IoError; | ||
| 465 | - } | ||
| 466 | - canonicalRoot = canonical.string(); | ||
| 467 | return ResourceStatus::Success; | 399 | return ResourceStatus::Success; |
| 468 | } | 400 | } |
| 469 | 401 | ||
| @@ -1267,12 +1199,7 @@ ResourceRegistry& ResourceRegistry::Instance() | |||
| 1267 | 1199 | ||
| 1268 | ResourceRegistry::ResourceRegistry() = default; | 1200 | ResourceRegistry::ResourceRegistry() = default; |
| 1269 | 1201 | ||
| 1270 | -ResourceRegistry::~ResourceRegistry() | 1202 | +ResourceRegistry::~ResourceRegistry() = default; |
| 1271 | -{ | ||
| 1272 | - if (!keepTemporaryRoot_) { | ||
| 1273 | - CleanupPath(temporaryRoot_); | ||
| 1274 | - } | ||
| 1275 | -} | ||
| 1276 | 1203 | ||
| 1277 | ResourceEntry* ResourceRegistry::FindResource(const std::string& resourceId) noexcept | 1204 | ResourceEntry* ResourceRegistry::FindResource(const std::string& resourceId) noexcept |
| 1278 | { | 1205 | { |
| @@ -1408,38 +1335,42 @@ ResourceStatus ResourceRegistry::Materialize( | |||
| 1408 | ASCENDLOGI( | 1335 | ASCENDLOGI( |
| 1409 | "Materializing compile resource: resource_id=%s source_type=%s so=%s files=%zu", resourceId.c_str(), | 1336 | "Materializing compile resource: resource_id=%s source_type=%s so=%s files=%zu", resourceId.c_str(), |
| 1410 | SourceTypeName(entry.sourceType), entry.sourceSoPath.c_str(), entry.files.size()); | 1337 | SourceTypeName(entry.sourceType), entry.sourceSoPath.c_str(), entry.files.size()); |
| 1411 | - std::string temporaryRoot; | 1338 | + std::string materializationRootPath; |
| 1412 | { | 1339 | { |
| 1413 | std::lock_guard<std::mutex> lock(registryMutex_); | 1340 | std::lock_guard<std::mutex> lock(registryMutex_); |
| 1414 | - if (temporaryRoot_.empty()) { | 1341 | + if (ownedMaterializationRoot_.GetDirectoryPath().empty()) { |
| 1415 | - keepTemporaryRoot_ = IsKernelMetaSavingEnabled(); | 1342 | + retainMaterializedDirectories_ = IsKernelMetaSavingEnabled(); |
| 1416 | - const ResourceStatus status = CreateTemporaryRoot(temporaryRoot_); | 1343 | + const ResourceStatus status = CreateCompileResourceMaterializationRoot(ownedMaterializationRoot_); |
| 1417 | if (status != ResourceStatus::Success) { | 1344 | if (status != ResourceStatus::Success) { |
| 1418 | return status; | 1345 | return status; |
| 1419 | } | 1346 | } |
| 1347 | + if (retainMaterializedDirectories_) { | ||
| 1348 | + ownedMaterializationRoot_.PreserveDirectory(); | ||
| 1349 | + } | ||
| 1420 | } | 1350 | } |
| 1421 | - temporaryRoot = temporaryRoot_; | 1351 | + materializationRootPath = ownedMaterializationRoot_.GetDirectoryPath(); |
| 1422 | } | 1352 | } |
| 1423 | ASCENDLOGD( | 1353 | ASCENDLOGD( |
| 1424 | - "Compile resource temporary root is ready: resource_id=%s path=%s", resourceId.c_str(), temporaryRoot.c_str()); | 1354 | + "Compile resource temporary root is ready: resource_id=%s path=%s", resourceId.c_str(), |
| 1425 | - std::string categoryRoot; | 1355 | + materializationRootPath.c_str()); |
| 1426 | - std::string canonicalRoot; | 1356 | + DirectoryCleanupGuard materializedResourceDirectory; |
| 1427 | - ResourceStatus status = | 1357 | + ResourceStatus status = CreateUniqueResourceMaterializationDirectory( |
| 1428 | - PrepareMaterializationRoot(temporaryRoot, resourceId, entry.sourceType, categoryRoot, canonicalRoot); | 1358 | + materializationRootPath, resourceId, entry.sourceType, materializedResourceDirectory); |
| 1429 | if (status == ResourceStatus::Success) { | 1359 | if (status == ResourceStatus::Success) { |
| 1430 | ASCENDLOGD( | 1360 | ASCENDLOGD( |
| 1431 | "Compile resource materialization directory is ready: resource_id=%s path=%s", resourceId.c_str(), | 1361 | "Compile resource materialization directory is ready: resource_id=%s path=%s", resourceId.c_str(), |
| 1432 | - canonicalRoot.c_str()); | 1362 | + materializedResourceDirectory.GetDirectoryPath().c_str()); |
| 1433 | - status = WriteMaterializedFiles(entry.files, canonicalRoot); | 1363 | + status = WriteMaterializedFiles(entry.files, materializedResourceDirectory.GetDirectoryPath()); |
| 1434 | } | 1364 | } |
| 1435 | if (status != ResourceStatus::Success) { | 1365 | if (status != ResourceStatus::Success) { |
| 1436 | - if (!keepTemporaryRoot_) { | 1366 | + if (retainMaterializedDirectories_) { |
| 1437 | - CleanupPath(categoryRoot); | 1367 | + materializedResourceDirectory.PreserveDirectory(); |
| 1438 | } | 1368 | } |
| 1439 | resource.resourceDir.clear(); | 1369 | resource.resourceDir.clear(); |
| 1440 | return status; | 1370 | return status; |
| 1441 | } | 1371 | } |
| 1442 | - resource.resourceDir = canonicalRoot; | 1372 | + resource.resourceDir = materializedResourceDirectory.GetDirectoryPath(); |
| 1373 | + materializedResourceDirectory.PreserveDirectory(); | ||
| 1443 | ASCENDLOGI( | 1374 | ASCENDLOGI( |
| 1444 | "Compile resource materialized: resource_id=%s source_type=%s so=%s files=%zu directory=%s", resourceId.c_str(), | 1375 | "Compile resource materialized: resource_id=%s source_type=%s so=%s files=%zu directory=%s", resourceId.c_str(), |
| 1445 | SourceTypeName(entry.sourceType), entry.sourceSoPath.c_str(), entry.files.size(), resource.resourceDir.c_str()); | 1376 | SourceTypeName(entry.sourceType), entry.sourceSoPath.c_str(), entry.files.size(), resource.resourceDir.c_str()); |
| @@ -21,6 +21,7 @@ | |||
| 21 | 21 | ||
| 22 | #include "ascendc_manifest_abi.h" | 22 | #include "ascendc_manifest_abi.h" |
| 23 | #include "nlohmann/json.hpp" | 23 | #include "nlohmann/json.hpp" |
| 24 | +#include "directory_cleanup_guard.h" | ||
| 24 | 25 | ||
| 25 | namespace boost { | 26 | namespace boost { |
| 26 | namespace filesystem { | 27 | namespace filesystem { |
| @@ -175,14 +176,14 @@ private: | |||
| 175 | ResourceStatus Commit(StageState& stage); | 176 | ResourceStatus Commit(StageState& stage); |
| 176 | bool HasCommitConflict(const ResourceStore& incoming, const ResourceStore& committed) const; | 177 | bool HasCommitConflict(const ResourceStore& incoming, const ResourceStore& committed) const; |
| 177 | 178 | ||
| 178 | - std::string temporaryRoot_; | 179 | + DirectoryCleanupGuard ownedMaterializationRoot_; |
| 179 | ResourceStore externalResources_; | 180 | ResourceStore externalResources_; |
| 180 | ResourceStore customResources_; | 181 | ResourceStore customResources_; |
| 181 | ResourceStore builtInResources_; | 182 | ResourceStore builtInResources_; |
| 182 | uint64_t resourceBytes_ = 0U; | 183 | uint64_t resourceBytes_ = 0U; |
| 183 | uint64_t resourceFileCount_ = 0U; | 184 | uint64_t resourceFileCount_ = 0U; |
| 184 | bool automaticLoadAttempted_ = false; | 185 | bool automaticLoadAttempted_ = false; |
| 185 | - bool keepTemporaryRoot_ = false; | 186 | + bool retainMaterializedDirectories_ = false; |
| 186 | ResourceStatus automaticLoadStatus_ = ResourceStatus::Success; | 187 | ResourceStatus automaticLoadStatus_ = ResourceStatus::Success; |
| 187 | std::mutex loadMutex_; | 188 | std::mutex loadMutex_; |
| 188 | std::mutex registryMutex_; | 189 | std::mutex registryMutex_; |
| @@ -34,13 +34,13 @@ configure_file( | |||
| 34 | 34 | ||
| 35 | add_executable(asc_compile_exporter | 35 | add_executable(asc_compile_exporter |
| 36 | asc_compile_exporter.cpp | 36 | asc_compile_exporter.cpp |
| 37 | - build_workspace.cpp | ||
| 38 | bundle_source_generator.cpp | 37 | bundle_source_generator.cpp |
| 39 | collected_manifest_repository.cpp | 38 | collected_manifest_repository.cpp |
| 40 | manifest_bundle_compiler.cpp | 39 | manifest_bundle_compiler.cpp |
| 41 | resource_manifest_validator.cpp | 40 | resource_manifest_validator.cpp |
| 42 | ../common/file_utils.cpp | 41 | ../common/file_utils.cpp |
| 43 | - ../common/process_runner.cpp | 42 | + ../common/process_executor.cpp |
| 43 | + ../common/directory_cleanup_guard.cpp | ||
| 44 | ) | 44 | ) |
| 45 | 45 | ||
| 46 | set_target_properties(asc_compile_exporter PROPERTIES | 46 | set_target_properties(asc_compile_exporter PROPERTIES |
| @@ -1,76 +0,0 @@ | |||
| 1 | -/** | ||
| 2 | - * Copyright (c) 2025 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 | -#include "build_workspace.h" | ||
| 12 | - | ||
| 13 | -#include <cstdlib> | ||
| 14 | - | ||
| 15 | -#include <boost/filesystem.hpp> | ||
| 16 | -#include <boost/system/error_code.hpp> | ||
| 17 | - | ||
| 18 | -#include "ascendc_tool_log.h" | ||
| 19 | -#include "file_utils.h" | ||
| 20 | - | ||
| 21 | -namespace ascendc { | ||
| 22 | -namespace manifest_generator { | ||
| 23 | - | ||
| 24 | -bool BuildWorkspace::Create(const std::string& requestedParent, bool keepTemp) | ||
| 25 | -{ | ||
| 26 | - if (!session_.empty()) { | ||
| 27 | - ASCENDLOGE("Build workspace is already initialized: %s", session_.c_str()); | ||
| 28 | - return false; | ||
| 29 | - } | ||
| 30 | - const char* temporary = std::getenv("TMPDIR"); | ||
| 31 | - const std::string requested = | ||
| 32 | - requestedParent.empty() ? (temporary == nullptr || temporary[0] == '\0' ? "/tmp" : temporary) : requestedParent; | ||
| 33 | - std::string absolute; | ||
| 34 | - if (!FileUtils::MakeAbsolutePath(requested, absolute)) { | ||
| 35 | - ASCENDLOGE("Failed to resolve bundle work directory: %s", requested.c_str()); | ||
| 36 | - return false; | ||
| 37 | - } | ||
| 38 | - if (!FileUtils::CreateDirectories(absolute)) { | ||
| 39 | - ASCENDLOGE("Failed to create bundle work directory: %s", absolute.c_str()); | ||
| 40 | - return false; | ||
| 41 | - } | ||
| 42 | - std::string parent; | ||
| 43 | - if (!FileUtils::ResolveDirectory(absolute, parent)) { | ||
| 44 | - ASCENDLOGE("Bundle work directory is unavailable: %s", absolute.c_str()); | ||
| 45 | - return false; | ||
| 46 | - } | ||
| 47 | - boost::system::error_code error; | ||
| 48 | - const boost::filesystem::path candidate = | ||
| 49 | - boost::filesystem::path(parent) / boost::filesystem::unique_path(".manifest-generator-%%%%%%%%", error); | ||
| 50 | - if (error || !boost::filesystem::create_directory(candidate, error)) { | ||
| 51 | - ASCENDLOGE( | ||
| 52 | - "Failed to create private bundle work directory: parent=%s error=%s", parent.c_str(), | ||
| 53 | - error ? error.message().c_str() : "path already exists"); | ||
| 54 | - return false; | ||
| 55 | - } | ||
| 56 | - ASCENDLOGD("Created private bundle work directory %s", candidate.c_str()); | ||
| 57 | - session_ = candidate.string(); | ||
| 58 | - keepTemp_ = keepTemp; | ||
| 59 | - return true; | ||
| 60 | -} | ||
| 61 | - | ||
| 62 | -BuildWorkspace::~BuildWorkspace() | ||
| 63 | -{ | ||
| 64 | - if (!session_.empty() && !keepTemp_) { | ||
| 65 | - if (FileUtils::RemoveAll(session_)) { | ||
| 66 | - ASCENDLOGD("Removed private bundle work directory %s", session_.c_str()); | ||
| 67 | - } else { | ||
| 68 | - ASCENDLOGW("Failed to completely remove private bundle work directory %s", session_.c_str()); | ||
| 69 | - } | ||
| 70 | - } | ||
| 71 | -} | ||
| 72 | - | ||
| 73 | -const std::string& BuildWorkspace::Session() const noexcept { return session_; } | ||
| 74 | - | ||
| 75 | -} // namespace manifest_generator | ||
| 76 | -} // namespace ascendc | ||
| @@ -10,6 +10,9 @@ | |||
| 10 | 10 | ||
| 11 | #include "manifest_bundle_compiler.h" | 11 | #include "manifest_bundle_compiler.h" |
| 12 | 12 | ||
| 13 | +#include <chrono> | ||
| 14 | +#include <cstdint> | ||
| 15 | +#include <cstdlib> | ||
| 13 | #include <utility> | 16 | #include <utility> |
| 14 | #include <vector> | 17 | #include <vector> |
| 15 | 18 | ||
| @@ -17,15 +20,40 @@ | |||
| 17 | #include <boost/system/error_code.hpp> | 20 | #include <boost/system/error_code.hpp> |
| 18 | 21 | ||
| 19 | #include "ascendc_tool_log.h" | 22 | #include "ascendc_tool_log.h" |
| 20 | -#include "build_workspace.h" | ||
| 21 | #include "bundle_source_generator.h" | 23 | #include "bundle_source_generator.h" |
| 22 | #include "collected_manifest_repository.h" | 24 | #include "collected_manifest_repository.h" |
| 23 | #include "file_utils.h" | 25 | #include "file_utils.h" |
| 24 | #include "manifest_bundle_types.h" | 26 | #include "manifest_bundle_types.h" |
| 25 | -#include "process_runner.h" | 27 | +#include "process_executor.h" |
| 28 | +#include "directory_cleanup_guard.h" | ||
| 26 | 29 | ||
| 27 | namespace ascendc { | 30 | namespace ascendc { |
| 28 | namespace manifest_generator { | 31 | namespace manifest_generator { |
| 32 | +namespace { | ||
| 33 | + | ||
| 34 | +constexpr uint32_t BUNDLE_BUILD_TIMEOUT_MINUTES = 30U; | ||
| 35 | +constexpr uint32_t BUNDLE_BUILD_TERMINATION_GRACE_SECONDS = 5U; | ||
| 36 | + | ||
| 37 | +bool CreatePrivateBundleWorkspaceDirectory( | ||
| 38 | + const std::string& requestedParentDirectoryPath, DirectoryCleanupGuard& bundleWorkspace) | ||
| 39 | +{ | ||
| 40 | + const char* configuredTemporaryDirectory = std::getenv("TMPDIR"); | ||
| 41 | + const std::string parentDirectoryPath = | ||
| 42 | + requestedParentDirectoryPath.empty() ? | ||
| 43 | + (configuredTemporaryDirectory == nullptr || configuredTemporaryDirectory[0] == '\0' ? | ||
| 44 | + "/tmp" : | ||
| 45 | + configuredTemporaryDirectory) : | ||
| 46 | + requestedParentDirectoryPath; | ||
| 47 | + std::string absoluteParentDirectoryPath; | ||
| 48 | + if (!FileUtils::MakeAbsolutePath(parentDirectoryPath, absoluteParentDirectoryPath) || | ||
| 49 | + !FileUtils::CreateDirectories(absoluteParentDirectoryPath)) { | ||
| 50 | + ASCENDLOGE("Failed to prepare bundle work directory parent: path=%s", parentDirectoryPath.c_str()); | ||
| 51 | + return false; | ||
| 52 | + } | ||
| 53 | + return bundleWorkspace.CreateUniqueSubdirectory(absoluteParentDirectoryPath, ".manifest-generator"); | ||
| 54 | +} | ||
| 55 | + | ||
| 56 | +} // namespace | ||
| 29 | 57 | ||
| 30 | ManifestBundleCompiler::ManifestBundleCompiler(BuildCollectedBundleRequest request) noexcept | 58 | ManifestBundleCompiler::ManifestBundleCompiler(BuildCollectedBundleRequest request) noexcept |
| 31 | : request_(std::move(request)) | 59 | : request_(std::move(request)) |
| @@ -36,9 +64,20 @@ bool ManifestBundleCompiler::BuildBundle(const std::string& workDir, const std:: | |||
| 36 | ASCENDLOGD( | 64 | ASCENDLOGD( |
| 37 | "Building bundle with make=%s compiler=%s jobs=%u", request_.makeExecutable.c_str(), | 65 | "Building bundle with make=%s compiler=%s jobs=%u", request_.makeExecutable.c_str(), |
| 38 | request_.cxxCompiler.c_str(), request_.jobs); | 66 | request_.cxxCompiler.c_str(), request_.jobs); |
| 39 | - if (!ProcessRunner::Run( | 67 | + ProcessExecutorRequest executionRequest; |
| 40 | - {request_.makeExecutable, "-C", workDir, "-f", "Makefile", "-B", "-j" + std::to_string(request_.jobs)}, | 68 | + executionRequest.arguments = { |
| 41 | - {{"ASCENDC_BUNDLE_CXX", request_.cxxCompiler}, {"ASCENDC_BUNDLE_OUTPUT", output}})) { | 69 | + request_.makeExecutable, "-C", workDir, "-f", "Makefile", "-B", "-j" + std::to_string(request_.jobs)}; |
| 70 | + executionRequest.environmentOverrides = { | ||
| 71 | + {"ASCENDC_BUNDLE_CXX", request_.cxxCompiler}, {"ASCENDC_BUNDLE_OUTPUT", output}}; | ||
| 72 | + executionRequest.removedEnvironmentVariables = {"MAKEFLAGS", "MFLAGS", "MAKEFILES", "GNUMAKEFLAGS"}; | ||
| 73 | + executionRequest.executionTimeout = std::chrono::minutes(BUNDLE_BUILD_TIMEOUT_MINUTES); | ||
| 74 | + executionRequest.terminationGracePeriod = std::chrono::seconds(BUNDLE_BUILD_TERMINATION_GRACE_SECONDS); | ||
| 75 | + const ProcessExecutorResult executionResult = ProcessExecutor::Execute(executionRequest); | ||
| 76 | + if (!executionResult.HasSuccessfulExit()) { | ||
| 77 | + ASCENDLOGE( | ||
| 78 | + "Failed to build compile resource bundle: termination=%s code=%d elapsed_ms=%lld", | ||
| 79 | + executionResult.GetOutcomeName(), static_cast<int>(executionResult.terminationCode), | ||
| 80 | + static_cast<long long>(executionResult.elapsedTime.count())); | ||
| 42 | return false; | 81 | return false; |
| 43 | } | 82 | } |
| 44 | ASCENDLOGI("Built staged compile resource bundle %s", output.c_str()); | 83 | ASCENDLOGI("Built staged compile resource bundle %s", output.c_str()); |
| @@ -71,11 +110,11 @@ bool ManifestBundleCompiler::PublishBundleAcrossFileSystems(const std::string& s | |||
| 71 | { | 110 | { |
| 72 | ASCENDLOGD("Copying staged bundle across filesystems: %s -> %s", staged.c_str(), request_.outputPath.c_str()); | 111 | ASCENDLOGD("Copying staged bundle across filesystems: %s -> %s", staged.c_str(), request_.outputPath.c_str()); |
| 73 | // A private workspace under the output parent places the copied file on the output filesystem. | 112 | // A private workspace under the output parent places the copied file on the output filesystem. |
| 74 | - BuildWorkspace publishWorkspace; | 113 | + DirectoryCleanupGuard publishWorkspace; |
| 75 | - if (!publishWorkspace.Create(FileUtils::ParentPath(request_.outputPath), false)) { | 114 | + if (!CreatePrivateBundleWorkspaceDirectory(FileUtils::ParentPath(request_.outputPath), publishWorkspace)) { |
| 76 | return false; | 115 | return false; |
| 77 | } | 116 | } |
| 78 | - const std::string copied = FileUtils::JoinPath(publishWorkspace.Session(), FileUtils::FileName(staged)); | 117 | + const std::string copied = FileUtils::JoinPath(publishWorkspace.GetDirectoryPath(), FileUtils::FileName(staged)); |
| 79 | if (!FileUtils::CopyFile(staged, copied)) { | 118 | if (!FileUtils::CopyFile(staged, copied)) { |
| 80 | ASCENDLOGE("Failed to copy staged bundle across filesystems: %s", staged.c_str()); | 119 | ASCENDLOGE("Failed to copy staged bundle across filesystems: %s", staged.c_str()); |
| 81 | return false; | 120 | return false; |
| @@ -103,23 +142,24 @@ bool ManifestBundleCompiler::Compile() const | |||
| 103 | if (!CollectedManifestRepository(request_.manifestSearchRoot).Load(units)) { | 142 | if (!CollectedManifestRepository(request_.manifestSearchRoot).Load(units)) { |
| 104 | return false; | 143 | return false; |
| 105 | } | 144 | } |
| 106 | - BuildWorkspace workspace; | 145 | + DirectoryCleanupGuard buildWorkspace; |
| 107 | - if (!workspace.Create(std::string(), request_.keepTemp)) { | 146 | + if (!CreatePrivateBundleWorkspaceDirectory(std::string(), buildWorkspace)) { |
| 108 | return false; | 147 | return false; |
| 109 | } | 148 | } |
| 110 | if (request_.keepTemp) { | 149 | if (request_.keepTemp) { |
| 111 | - ASCENDLOGI("Keeping temporary bundle directory %s", workspace.Session().c_str()); | 150 | + buildWorkspace.PreserveDirectory(); |
| 151 | + ASCENDLOGI("Keeping temporary bundle directory %s", buildWorkspace.GetDirectoryPath().c_str()); | ||
| 112 | } | 152 | } |
| 113 | 153 | ||
| 114 | // Generate isolated translation units and compile them into a staged output. | 154 | // Generate isolated translation units and compile them into a staged output. |
| 115 | - if (!BundleSourceGenerator(workspace.Session(), units, request_.outputKind).Generate()) { | 155 | + if (!BundleSourceGenerator(buildWorkspace.GetDirectoryPath(), units, request_.outputKind).Generate()) { |
| 116 | return false; | 156 | return false; |
| 117 | } | 157 | } |
| 118 | const std::string stagedName = request_.outputKind == BundleOutputKind::kSharedObject ? | 158 | const std::string stagedName = request_.outputKind == BundleOutputKind::kSharedObject ? |
| 119 | "compile_resource_bundle.so" : | 159 | "compile_resource_bundle.so" : |
| 120 | "compile_resource_bundle_relocatable.o"; | 160 | "compile_resource_bundle_relocatable.o"; |
| 121 | - const std::string stagedOutput = FileUtils::JoinPath(workspace.Session(), stagedName); | 161 | + const std::string stagedOutput = FileUtils::JoinPath(buildWorkspace.GetDirectoryPath(), stagedName); |
| 122 | - if (!BuildBundle(workspace.Session(), stagedOutput)) { | 162 | + if (!BuildBundle(buildWorkspace.GetDirectoryPath(), stagedOutput)) { |
| 123 | return false; | 163 | return false; |
| 124 | } | 164 | } |
| 125 | 165 | ||
| @@ -0,0 +1,158 @@ | |||
| 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 | +#include "directory_cleanup_guard.h" | ||
| 12 | + | ||
| 13 | +#include "ascendc_tool_log.h" | ||
| 14 | +#include "file_utils.h" | ||
| 15 | + | ||
| 16 | +#include <boost/filesystem.hpp> | ||
| 17 | +#include <boost/system/error_code.hpp> | ||
| 18 | +#include <cerrno> | ||
| 19 | +#include <cstring> | ||
| 20 | +#include <sys/stat.h> | ||
| 21 | +#include <utility> | ||
| 22 | + | ||
| 23 | +namespace ascendc { | ||
| 24 | +namespace { | ||
| 25 | +namespace fs = boost::filesystem; | ||
| 26 | + | ||
| 27 | +bool IsValidDirectoryNamePrefix(const std::string& directoryNamePrefix) | ||
| 28 | +{ | ||
| 29 | + if (directoryNamePrefix.empty() || directoryNamePrefix.find('%') != std::string::npos || | ||
| 30 | + directoryNamePrefix.find('\0') != std::string::npos || directoryNamePrefix.find('\\') != std::string::npos) { | ||
| 31 | + return false; | ||
| 32 | + } | ||
| 33 | + const fs::path prefixPath(directoryNamePrefix); | ||
| 34 | + return prefixPath.is_relative() && !prefixPath.has_parent_path(); | ||
| 35 | +} | ||
| 36 | + | ||
| 37 | +} // namespace | ||
| 38 | + | ||
| 39 | +DirectoryCleanupGuard::~DirectoryCleanupGuard() noexcept | ||
| 40 | +{ | ||
| 41 | + if (removeDirectoryOnDestruction_) { | ||
| 42 | + (void)RemoveDirectory(); | ||
| 43 | + } | ||
| 44 | +} | ||
| 45 | + | ||
| 46 | +DirectoryCleanupGuard::DirectoryCleanupGuard(DirectoryCleanupGuard&& other) noexcept { MoveStateFrom(other); } | ||
| 47 | + | ||
| 48 | +DirectoryCleanupGuard& DirectoryCleanupGuard::operator=(DirectoryCleanupGuard&& other) noexcept | ||
| 49 | +{ | ||
| 50 | + if (this == &other) { | ||
| 51 | + return *this; | ||
| 52 | + } | ||
| 53 | + DirectoryCleanupGuard previousDirectory(std::move(*this)); | ||
| 54 | + MoveStateFrom(other); | ||
| 55 | + return *this; | ||
| 56 | +} | ||
| 57 | + | ||
| 58 | +bool DirectoryCleanupGuard::CreateUniqueSubdirectory( | ||
| 59 | + const std::string& parentDirectoryPath, const std::string& directoryNamePrefix) | ||
| 60 | +{ | ||
| 61 | + if (!directoryPath_.empty()) { | ||
| 62 | + ASCENDLOGE("Directory cleanup guard already manages a path: path=%s", directoryPath_.c_str()); | ||
| 63 | + return false; | ||
| 64 | + } | ||
| 65 | + if (!IsValidDirectoryNamePrefix(directoryNamePrefix)) { | ||
| 66 | + ASCENDLOGE( | ||
| 67 | + "Temporary directory name prefix must be one path component without %% characters: prefix=%s", | ||
| 68 | + directoryNamePrefix.c_str()); | ||
| 69 | + return false; | ||
| 70 | + } | ||
| 71 | + | ||
| 72 | + std::string resolvedParentDirectoryPath; | ||
| 73 | + if (!FileUtils::ResolveDirectory(parentDirectoryPath, resolvedParentDirectoryPath)) { | ||
| 74 | + ASCENDLOGE("Temporary directory parent is unavailable: parent=%s", parentDirectoryPath.c_str()); | ||
| 75 | + return false; | ||
| 76 | + } | ||
| 77 | + boost::system::error_code filesystemError; | ||
| 78 | + const fs::path uniqueName = fs::unique_path(directoryNamePrefix + "-%%%%%%%%%%%%%%%%", filesystemError); | ||
| 79 | + if (filesystemError || uniqueName.empty() || uniqueName.has_parent_path()) { | ||
| 80 | + ASCENDLOGE( | ||
| 81 | + "Failed to generate temporary directory name: parent=%s prefix=%s error=%s", | ||
| 82 | + resolvedParentDirectoryPath.c_str(), directoryNamePrefix.c_str(), | ||
| 83 | + filesystemError ? filesystemError.message().c_str() : "invalid generated name"); | ||
| 84 | + return false; | ||
| 85 | + } | ||
| 86 | + const fs::path createdDirectoryPath = fs::path(resolvedParentDirectoryPath) / uniqueName; | ||
| 87 | + constexpr mode_t privateDirectoryPermissions = S_IRWXU; | ||
| 88 | + if (mkdir(createdDirectoryPath.c_str(), privateDirectoryPermissions) != 0) { | ||
| 89 | + const int createError = errno; | ||
| 90 | + ASCENDLOGE( | ||
| 91 | + "Failed to create temporary directory: path=%s errno=%d message=%s", createdDirectoryPath.c_str(), | ||
| 92 | + createError, std::strerror(createError)); | ||
| 93 | + return false; | ||
| 94 | + } | ||
| 95 | + | ||
| 96 | + if (chmod(createdDirectoryPath.c_str(), privateDirectoryPermissions) != 0) { | ||
| 97 | + const int permissionError = errno; | ||
| 98 | + ASCENDLOGE( | ||
| 99 | + "Failed to make temporary directory private: path=%s errno=%d message=%s", createdDirectoryPath.c_str(), | ||
| 100 | + permissionError, std::strerror(permissionError)); | ||
| 101 | + (void)FileUtils::RemoveAll(createdDirectoryPath.string()); | ||
| 102 | + return false; | ||
| 103 | + } | ||
| 104 | + | ||
| 105 | + std::string resolvedCreatedDirectoryPath; | ||
| 106 | + if (!FileUtils::ResolveDirectory(createdDirectoryPath.string(), resolvedCreatedDirectoryPath) || | ||
| 107 | + fs::path(resolvedCreatedDirectoryPath) != createdDirectoryPath.lexically_normal()) { | ||
| 108 | + ASCENDLOGE("Created temporary directory has an unexpected identity: path=%s", createdDirectoryPath.c_str()); | ||
| 109 | + (void)FileUtils::RemoveAll(createdDirectoryPath.string()); | ||
| 110 | + return false; | ||
| 111 | + } | ||
| 112 | + directoryPath_ = std::move(resolvedCreatedDirectoryPath); | ||
| 113 | + removeDirectoryOnDestruction_ = true; | ||
| 114 | + ASCENDLOGD("Created temporary directory: path=%s", directoryPath_.c_str()); | ||
| 115 | + return true; | ||
| 116 | +} | ||
| 117 | + | ||
| 118 | +bool DirectoryCleanupGuard::RegisterDirectoryForCleanup(std::string directoryPath) | ||
| 119 | +{ | ||
| 120 | + if (directoryPath.empty()) { | ||
| 121 | + ASCENDLOGE("Cannot register an empty directory path for cleanup"); | ||
| 122 | + return false; | ||
| 123 | + } | ||
| 124 | + if (!directoryPath_.empty()) { | ||
| 125 | + ASCENDLOGE("Directory cleanup guard already manages a path: path=%s", directoryPath_.c_str()); | ||
| 126 | + return false; | ||
| 127 | + } | ||
| 128 | + directoryPath_ = std::move(directoryPath); | ||
| 129 | + removeDirectoryOnDestruction_ = true; | ||
| 130 | + return true; | ||
| 131 | +} | ||
| 132 | + | ||
| 133 | +bool DirectoryCleanupGuard::RemoveDirectory() noexcept | ||
| 134 | +{ | ||
| 135 | + if (directoryPath_.empty()) { | ||
| 136 | + return true; | ||
| 137 | + } | ||
| 138 | + if (!FileUtils::RemoveAll(directoryPath_)) { | ||
| 139 | + ASCENDLOGW("Failed to remove directory registered for cleanup: path=%s", directoryPath_.c_str()); | ||
| 140 | + return false; | ||
| 141 | + } | ||
| 142 | + directoryPath_.clear(); | ||
| 143 | + return true; | ||
| 144 | +} | ||
| 145 | + | ||
| 146 | +void DirectoryCleanupGuard::PreserveDirectory() noexcept { removeDirectoryOnDestruction_ = false; } | ||
| 147 | + | ||
| 148 | +const std::string& DirectoryCleanupGuard::GetDirectoryPath() const noexcept { return directoryPath_; } | ||
| 149 | + | ||
| 150 | +void DirectoryCleanupGuard::MoveStateFrom(DirectoryCleanupGuard& other) noexcept | ||
| 151 | +{ | ||
| 152 | + directoryPath_ = std::move(other.directoryPath_); | ||
| 153 | + removeDirectoryOnDestruction_ = other.removeDirectoryOnDestruction_; | ||
| 154 | + other.directoryPath_.clear(); | ||
| 155 | + other.removeDirectoryOnDestruction_ = false; | ||
| 156 | +} | ||
| 157 | + | ||
| 158 | +} // namespace ascendc | ||
Rtools/build/asc_compile_exporter/build_workspace.h→tools/build/common/directory_cleanup_guard.h+21-15
| @@ -1,5 +1,5 @@ | |||
| 1 | /** | 1 | /** |
| 2 | - * Copyright (c) 2025 Huawei Technologies Co., Ltd. | 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 | 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"). | 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. | 5 | * Please refer to the License for details. You may not use this file except in compliance with the License. |
| @@ -8,32 +8,38 @@ | |||
| 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 | 10 | ||
| 11 | -#ifndef ASCENDC_BUILD_WORKSPACE_H | 11 | +#ifndef ASCENDC_COMMON_DIRECTORY_CLEANUP_GUARD_H |
| 12 | -#define ASCENDC_BUILD_WORKSPACE_H | 12 | +#define ASCENDC_COMMON_DIRECTORY_CLEANUP_GUARD_H |
| 13 | 13 | ||
| 14 | #include <string> | 14 | #include <string> |
| 15 | 15 | ||
| 16 | namespace ascendc { | 16 | namespace ascendc { |
| 17 | -namespace manifest_generator { | ||
| 18 | 17 | ||
| 19 | -class BuildWorkspace final { | 18 | +class DirectoryCleanupGuard final { |
| 20 | public: | 19 | public: |
| 21 | - BuildWorkspace() = default; | 20 | + DirectoryCleanupGuard() = default; |
| 21 | + ~DirectoryCleanupGuard() noexcept; | ||
| 22 | 22 | ||
| 23 | - bool Create(const std::string& requestedParent, bool keepTemp); | 23 | + DirectoryCleanupGuard(DirectoryCleanupGuard&& other) noexcept; |
| 24 | + DirectoryCleanupGuard& operator=(DirectoryCleanupGuard&& other) noexcept; | ||
| 24 | 25 | ||
| 25 | - BuildWorkspace(const BuildWorkspace&) = delete; | 26 | + DirectoryCleanupGuard(const DirectoryCleanupGuard&) = delete; |
| 26 | - BuildWorkspace& operator=(const BuildWorkspace&) = delete; | 27 | + DirectoryCleanupGuard& operator=(const DirectoryCleanupGuard&) = delete; |
| 27 | - ~BuildWorkspace(); | ||
| 28 | 28 | ||
| 29 | - const std::string& Session() const noexcept; | 29 | + bool CreateUniqueSubdirectory(const std::string& parentDirectoryPath, const std::string& directoryNamePrefix); |
| 30 | + bool RegisterDirectoryForCleanup(std::string directoryPath); | ||
| 31 | + bool RemoveDirectory() noexcept; | ||
| 32 | + void PreserveDirectory() noexcept; | ||
| 33 | + | ||
| 34 | + const std::string& GetDirectoryPath() const noexcept; | ||
| 30 | 35 | ||
| 31 | private: | 36 | private: |
| 32 | - std::string session_; | 37 | + void MoveStateFrom(DirectoryCleanupGuard& other) noexcept; |
| 33 | - bool keepTemp_ = false; | 38 | + |
| 39 | + std::string directoryPath_; | ||
| 40 | + bool removeDirectoryOnDestruction_{true}; | ||
| 34 | }; | 41 | }; |
| 35 | 42 | ||
| 36 | -} // namespace manifest_generator | ||
| 37 | } // namespace ascendc | 43 | } // namespace ascendc |
| 38 | 44 | ||
| 39 | -#endif // ASCENDC_BUILD_WORKSPACE_H | 45 | +#endif // ASCENDC_COMMON_DIRECTORY_CLEANUP_GUARD_H |
| @@ -0,0 +1,620 @@ | |||
| 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 | +#include "process_executor.h" | ||
| 12 | + | ||
| 13 | +#include "ascendc_tool_log.h" | ||
| 14 | + | ||
| 15 | +#include <algorithm> | ||
| 16 | +#include <array> | ||
| 17 | +#include <cerrno> | ||
| 18 | +#include <csignal> | ||
| 19 | +#include <cstring> | ||
| 20 | +#include <exception> | ||
| 21 | +#include <fcntl.h> | ||
| 22 | +#include <fstream> | ||
| 23 | +#include <map> | ||
| 24 | +#include <new> | ||
| 25 | +#include <poll.h> | ||
| 26 | +#include <spawn.h> | ||
| 27 | +#include <sys/wait.h> | ||
| 28 | +#include <thread> | ||
| 29 | +#include <unistd.h> | ||
| 30 | +#include <utility> | ||
| 31 | + | ||
| 32 | +namespace ascendc { | ||
| 33 | +namespace { | ||
| 34 | +using SteadyClock = std::chrono::steady_clock; | ||
| 35 | + | ||
| 36 | +constexpr uint64_t MAX_OUTPUT_READ_BYTES_PER_ITERATION = 64U * 1024U; | ||
| 37 | +constexpr int64_t MAX_PROCESS_WAIT_MILLISECONDS = 5; | ||
| 38 | + | ||
| 39 | +class FileDescriptor final { | ||
| 40 | +public: | ||
| 41 | + explicit FileDescriptor(int value = -1) noexcept : value_(value) {} | ||
| 42 | + ~FileDescriptor() noexcept { Close(); } | ||
| 43 | + | ||
| 44 | + FileDescriptor(const FileDescriptor&) = delete; | ||
| 45 | + FileDescriptor& operator=(const FileDescriptor&) = delete; | ||
| 46 | + | ||
| 47 | + int Get() const noexcept { return value_; } | ||
| 48 | + | ||
| 49 | + void Reset(int value = -1) noexcept | ||
| 50 | + { | ||
| 51 | + Close(); | ||
| 52 | + value_ = value; | ||
| 53 | + } | ||
| 54 | + | ||
| 55 | + void Close() noexcept | ||
| 56 | + { | ||
| 57 | + if (value_ < 0) { | ||
| 58 | + return; | ||
| 59 | + } | ||
| 60 | + const int valueToClose = value_; | ||
| 61 | + value_ = -1; | ||
| 62 | + if (close(valueToClose) != 0) { | ||
| 63 | + const int closeError = errno; | ||
| 64 | + ASCENDLOGW( | ||
| 65 | + "Failed to close process file descriptor: fd=%d errno=%d message=%s", valueToClose, closeError, | ||
| 66 | + std::strerror(closeError)); | ||
| 67 | + } | ||
| 68 | + } | ||
| 69 | + | ||
| 70 | +private: | ||
| 71 | + int value_; | ||
| 72 | +}; | ||
| 73 | + | ||
| 74 | +bool RelocateFileDescriptorAboveStandardStreams(FileDescriptor& fileDescriptor) noexcept | ||
| 75 | +{ | ||
| 76 | + if (fileDescriptor.Get() > STDERR_FILENO) { | ||
| 77 | + return true; | ||
| 78 | + } | ||
| 79 | + const int relocatedFileDescriptor = fcntl(fileDescriptor.Get(), F_DUPFD_CLOEXEC, STDERR_FILENO + 1); | ||
| 80 | + if (relocatedFileDescriptor < 0) { | ||
| 81 | + const int duplicateError = errno; | ||
| 82 | + ASCENDLOGE( | ||
| 83 | + "Failed to relocate process pipe file descriptor: fd=%d errno=%d message=%s", fileDescriptor.Get(), | ||
| 84 | + duplicateError, std::strerror(duplicateError)); | ||
| 85 | + return false; | ||
| 86 | + } | ||
| 87 | + fileDescriptor.Reset(relocatedFileDescriptor); | ||
| 88 | + return true; | ||
| 89 | +} | ||
| 90 | + | ||
| 91 | +struct PosixSpawnResources { | ||
| 92 | + ~PosixSpawnResources() noexcept | ||
| 93 | + { | ||
| 94 | + if (attributesInitialized_) { | ||
| 95 | + (void)posix_spawnattr_destroy(&attributes_); | ||
| 96 | + } | ||
| 97 | + if (fileActionsInitialized_) { | ||
| 98 | + (void)posix_spawn_file_actions_destroy(&fileActions_); | ||
| 99 | + } | ||
| 100 | + } | ||
| @@ -1,5 +1,5 @@ | |||
| 1 | /** | 1 | /** |
| 2 | - * Copyright (c) 2025 Huawei Technologies Co., Ltd. | 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 | 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"). | 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. | 5 | * Please refer to the License for details. You may not use this file except in compliance with the License. |
| @@ -8,31 +8,49 @@ | |||
| 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 | 10 | ||
| 11 | -#ifndef ASCENDC_COMMON_PROCESS_RUNNER_H | 11 | +#ifndef ASCENDC_COMMON_PROCESS_EXECUTOR_H |
| 12 | -#define ASCENDC_COMMON_PROCESS_RUNNER_H | 12 | +#define ASCENDC_COMMON_PROCESS_EXECUTOR_H |
| 13 | 13 | ||
| 14 | #include <chrono> | 14 | #include <chrono> |
| 15 | +#include <cstdint> | ||
| 15 | #include <string> | 16 | #include <string> |
| 17 | +#include <utility> | ||
| 16 | #include <vector> | 18 | #include <vector> |
| 17 | 19 | ||
| 18 | -#include <sys/types.h> | ||
| 19 | - | ||
| 20 | namespace ascendc { | 20 | namespace ascendc { |
| 21 | 21 | ||
| 22 | -class ProcessRunner final { | 22 | +enum class ProcessOutcome : uint32_t { |
| 23 | + ExecutorFailure, | ||
| 24 | + Exited, | ||
| 25 | + Signaled, | ||
| 26 | + TimedOut, | ||
| 27 | +}; | ||
| 28 | + | ||
| 29 | +struct ProcessExecutorRequest { | ||
| 30 | + std::vector<std::string> arguments; | ||
| 31 | + std::vector<std::pair<std::string, std::string>> environmentOverrides; | ||
| 32 | + std::vector<std::string> removedEnvironmentVariables; | ||
| 33 | + std::string mirroredOutputLogFilePath; | ||
| 34 | + std::chrono::milliseconds executionTimeout{0}; | ||
| 35 | + std::chrono::milliseconds terminationGracePeriod{0}; | ||
| 36 | +}; | ||
| 37 | + | ||
| 38 | +struct ProcessExecutorResult { | ||
| 39 | + ProcessOutcome outcome{ProcessOutcome::ExecutorFailure}; | ||
| 40 | + int32_t terminationCode{0}; | ||
| 41 | + std::chrono::milliseconds elapsedTime{0}; | ||
| 42 | + | ||
| 43 | + bool HasSuccessfulExit() const noexcept; | ||
| 44 | + const char* GetOutcomeName() const noexcept; | ||
| 45 | +}; | ||
| 46 | + | ||
| 47 | +class ProcessExecutor final { | ||
| 23 | public: | 48 | public: |
| 24 | - ProcessRunner() = delete; | 49 | + ProcessExecutor() = delete; |
| 25 | 50 | ||
| 26 | - static bool Run( | 51 | + static ProcessExecutorResult Execute(const ProcessExecutorRequest& request); |
| 27 | - const std::vector<std::string>& arguments, const std::vector<std::pair<std::string, std::string>>& environment); | ||
| 28 | - static bool Run( | ||
| 29 | - const std::vector<std::string>& arguments, const std::vector<std::pair<std::string, std::string>>& environment, | ||
| 30 | - std::chrono::milliseconds timeout); | ||
| 31 | - | ||
| 32 | -private: | ||
| 33 | - static void TerminateAndReap(pid_t child) noexcept; | ||
| 34 | }; | 52 | }; |
| 35 | 53 | ||
| 36 | } // namespace ascendc | 54 | } // namespace ascendc |
| 37 | 55 | ||
| 38 | -#endif // ASCENDC_COMMON_PROCESS_RUNNER_H | 56 | +#endif // ASCENDC_COMMON_PROCESS_EXECUTOR_H |