-
Notifications
You must be signed in to change notification settings - Fork 4.8k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Merge-on-Red] - Implement Test Process Watcher #78742
Changes from 10 commits
be9867e
e79ae66
b17eef2
d40ad9e
6cc9e9e
fcd9f1a
1c7f46c
ec90d1e
b157904
36a75f0
c4f2009
e8b13a5
b476ad2
f74fb2e
958dc29
67e25fe
7ef09b2
226ea36
5fbd787
0ff6b8d
8f21b9c
c7fc541
4f658e2
a88e48c
8fceb44
064b9b0
2cb9270
450eba9
eba2bcd
d68d16c
998965c
7cb4e89
2ba916a
a712352
0cc8875
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,26 @@ | ||
cmake_minimum_required(VERSION 3.6.2) | ||
|
||
if (CLR_CMAKE_TARGET_IOS OR CLR_CMAKE_TARGET_TVOS) | ||
# CMake 3.14.5 contains bug fixes for iOS | ||
cmake_minimum_required(VERSION 3.14.5) | ||
elseif (CLR_CMAKE_TARGET_MACCATALYST) | ||
# CMake 3.18.1 properly generates MacCatalyst C compiler | ||
cmake_minimum_required(VERSION 3.18.1) | ||
endif () | ||
|
||
if (WIN32) | ||
cmake_policy(SET CMP0091 NEW) | ||
else () | ||
cmake_policy(SET CMP0042 NEW) | ||
endif () | ||
|
||
project(watchdog) | ||
|
||
include(../../../eng/native/configurepaths.cmake) | ||
include(${CLR_ENG_NATIVE_DIR}/configurecompiler.cmake) | ||
|
||
set(CMAKE_INCLUDE_CURRENT_DIR ON) | ||
set(CMAKE_CXX_STANDARD 11) | ||
|
||
add_executable(watchdog watchdog.cpp) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
#!/usr/bin/env bash | ||
|
||
echo "Called build script successfully!" | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,41 @@ | ||
<Project Sdk="Microsoft.Build.Traversal"> | ||
|
||
<PropertyGroup> | ||
<AssemblyName>.NET Tests Watchdog</AssemblyName> | ||
<_BuildGeneralArgs>-arch $(TargetArchitecture) -c $(Configuration) -os $(TargetOS)</_BuildGeneralArgs> | ||
</PropertyGroup> | ||
|
||
|
||
<!-- Instructions to build on Unix. --> | ||
|
||
<Target Name="BuildForUnix" | ||
Condition="!$([MSBuild]::IsOsPlatform(Windows))"> | ||
|
||
<PropertyGroup> | ||
<_CMakeArgs Condition="'$(CMakeArgs)' != ''"> $(CMakeArgs)</_CMakeArgs> | ||
<_BuildUnixArgs>$(_BuildGeneralArgs)$(_CMakeArgs)</_BuildUnixArgs> | ||
</PropertyGroup> | ||
|
||
<Message Text="$(MSBuildThisFileDirectory)build-watchdog.sh $(_BuildUnixArgs)" Importance="High" /> | ||
<Exec Command=""$(MSBuildThisFileDirectory)build-watchdog.sh" $(_BuildUnixArgs)" /> | ||
|
||
</Target> | ||
|
||
|
||
<!-- Instructions to build on Windows. --> | ||
|
||
<Target Name="BuildForWindows" | ||
Condition="$([MSBuild]::IsOsPlatform(Windows))"> | ||
|
||
<PropertyGroup> | ||
<_BuildWindowsArgs>$(_BuildGeneralArgs)</_BuildWindowsArgs> | ||
<_BuildWindowsArgs Condition="'$(Ninja)' == 'false'">$(_BuildWindowsArgs) msbuild</_BuildWindowsArgs> | ||
</PropertyGroup> | ||
|
||
<Message Text="$(MSBuildThisFileDirectory)build-watchdog.cmd $(_BuildWindowsArgs)" Importance="High" /> | ||
<Exec Command=""$(MSBuildThisFileDirectory)build-watchdog.cmd" $(_BuildWindowsArgs)" /> | ||
|
||
</Target> | ||
|
||
</Project> | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,128 @@ | ||
#include <cstdio> | ||
#include <cstdlib> | ||
#include <cstdarg> | ||
#include <errno.h> | ||
#include <signal.h> | ||
|
||
#ifdef _WIN32 | ||
#include <windows.h> | ||
#include <string> | ||
#else | ||
#include <unistd.h> | ||
#include <sys/wait.h> | ||
#endif | ||
|
||
int run_timed_process(const long, const int, const char *[]); | ||
|
||
int main(const int argc, const char *argv[]) | ||
{ | ||
if (argc < 3) | ||
{ | ||
printf("There are missing arguments. Got %d instead of 3+ :(\n", argc); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
const long timeout_ms = strtol(argv[1], nullptr, 10); | ||
int exit_code = run_timed_process(timeout_ms, argc-2, &argv[2]); | ||
|
||
printf("App Exit Code: %d\n", exit_code); | ||
return EXIT_SUCCESS; | ||
} | ||
|
||
int run_timed_process(const long timeout, const int exe_argc, const char *exe_path_and_argv[]) | ||
{ | ||
#ifdef _WIN32 | ||
std::string cmdline(exe_path_and_argv[0]); | ||
|
||
for (int i = 1; i < exe_argc; i++) | ||
{ | ||
cmdline.append(" "); | ||
cmdline.append(exe_path_and_argv[i]); | ||
} | ||
|
||
STARTUPINFO startup_info; | ||
PROCESS_INFORMATION proc_info; | ||
int exit_code; | ||
|
||
ZeroMemory(&startup_info, sizeof(startup_info)); | ||
startup_info.cb = sizeof(startup_info); | ||
ZeroMemory(&proc_info, sizeof(proc_info)); | ||
|
||
if (!CreateProcess(NULL, &cmdline[0], NULL, NULL, FALSE, 0, NULL, NULL, | ||
&startup_info, &proc_info)) | ||
{ | ||
int error_code = GetLastError(); | ||
printf("Process creation failed... Code %d.\n", error_code); | ||
return error_code; | ||
} | ||
|
||
WaitForSingleObject(proc_info.hProcess, timeout); | ||
GetExitCodeProcess(proc_info.hProcess, &exit_code); | ||
|
||
CloseHandle(proc_info.hProcess); | ||
CloseHandle(proc_info.hThread); | ||
return exit_code; | ||
|
||
#else | ||
const int check_interval = 1000; | ||
int check_count = 0; | ||
char **args = new char *[exe_argc]; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Consider a vector here. You forgot to delete There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I forgot the deletion, thanks for pointing it out Andy. The reason we're using a There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That might work as well. I'll try it. |
||
|
||
pid_t child_pid; | ||
int child_status; | ||
int wait_code; | ||
|
||
for (int i = 0; i < exe_argc; i++) | ||
{ | ||
args[i] = (char *) exe_path_and_argv[i]; | ||
} | ||
|
||
// This is just for development. Will remove it when it's ready to be submitted :) | ||
for (int j = 0; j < exe_argc; j++) | ||
{ | ||
printf("[%d]: %s\n", j, args[j]); | ||
} | ||
|
||
child_pid = fork(); | ||
|
||
if (child_pid < 0) | ||
{ | ||
printf("Fork failed... No memory available.\n"); | ||
return ENOMEM; | ||
} | ||
else if (child_pid == 0) | ||
{ | ||
printf("Running child process...\n"); | ||
execv(args[0], &args[0]); | ||
} | ||
else | ||
{ | ||
do | ||
{ | ||
wait_code = waitpid(child_pid, &child_status, WNOHANG); | ||
|
||
// Something went terribly wrong. | ||
if (wait_code == -1) | ||
return EINVAL; | ||
|
||
// Passing ms * 1000 because usleep() receives its parameter in microseconds. | ||
usleep(check_interval * 1000); | ||
|
||
if (wait_code) | ||
{ | ||
if (WIFEXITED(child_status)) | ||
{ | ||
printf("Child process exited successfully with status %d.\n", | ||
WEXITSTATUS(child_status)); | ||
return WEXITSTATUS(child_status); | ||
} | ||
} | ||
} while (check_count++ < (timeout / check_interval)); | ||
} | ||
|
||
printf("Child process took too long and timed out... Exiting it...\n"); | ||
kill(child_pid, SIGKILL); | ||
#endif | ||
return ETIMEDOUT; | ||
} | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Nit: Add license header