-
Notifications
You must be signed in to change notification settings - Fork 4.7k
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 30 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,4 @@ | ||
add_executable_clr(watchdog ${CMAKE_CURRENT_LIST_DIR}/watchdog.cpp) | ||
install_clr(TARGETS watchdog DESTINATIONS . COMPONENT hosts) | ||
install_clr(TARGETS watchdog DESTINATIONS . COMPONENT nativeaot) | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,139 @@ | ||
// Licensed to the .NET Foundation under one or more agreements. | ||
// The .NET Foundation licenses this file to you under the MIT license. | ||
|
||
#include <cstdio> | ||
#include <cstdlib> | ||
#include <errno.h> | ||
#include <signal.h> | ||
|
||
#ifdef TARGET_WINDOWS | ||
|
||
#include <windows.h> | ||
#include <string> | ||
|
||
#else // !TARGET_WINDOWS | ||
|
||
#include <chrono> | ||
#include <sys/wait.h> | ||
#include <thread> | ||
#include <unistd.h> | ||
#include <vector> | ||
|
||
#endif // TARGET_WINDOWS | ||
|
||
int run_timed_process(const long, const int, const char *[]); | ||
|
||
#ifdef TARGET_X86 | ||
int __cdecl main(const int argc, const char *argv[]) | ||
#else | ||
int main(const int argc, const char *argv[]) | ||
#endif | ||
{ | ||
if (argc < 3) | ||
{ | ||
printf("There are missing arguments. Got %d instead of 3+ :(\n", argc); | ||
return EXIT_FAILURE; | ||
} | ||
|
||
const long timeout_sec = strtol(argv[1], nullptr, 10); | ||
int exit_code = run_timed_process(timeout_sec * 1000L, argc-2, &argv[2]); | ||
|
||
printf("App Exit Code: %d\n", exit_code); | ||
return exit_code; | ||
} | ||
|
||
int run_timed_process(const long timeout_ms, const int proc_argc, const char *proc_argv[]) | ||
{ | ||
#ifdef TARGET_WINDOWS | ||
std::string cmdline(proc_argv[0]); | ||
|
||
for (int i = 1; i < proc_argc; i++) | ||
{ | ||
cmdline.append(" "); | ||
cmdline.append(proc_argv[i]); | ||
} | ||
|
||
STARTUPINFOA startup_info; | ||
PROCESS_INFORMATION proc_info; | ||
unsigned long exit_code; | ||
|
||
ZeroMemory(&startup_info, sizeof(startup_info)); | ||
startup_info.cb = sizeof(startup_info); | ||
ZeroMemory(&proc_info, sizeof(proc_info)); | ||
|
||
if (!CreateProcessA(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_ms); | ||
GetExitCodeProcess(proc_info.hProcess, &exit_code); | ||
|
||
CloseHandle(proc_info.hProcess); | ||
CloseHandle(proc_info.hThread); | ||
return exit_code; | ||
|
||
#else // !TARGET_WINDOWS | ||
|
||
// TODO: Describe what the 'ms_factor' is, and why it's being used here. | ||
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. Address this TODO? 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. Oops forgot that. Thanks for the catch. 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 actually removed it altogether. I needed it because originally, I was dealing with some C stuff that combined microseconds and milliseconds. After switching to C++'s |
||
const int ms_factor = 40; | ||
const int check_interval = 1000 / ms_factor; | ||
|
||
int check_count = 0; | ||
std::vector<const char*> args; | ||
|
||
pid_t child_pid; | ||
int child_status; | ||
int wait_code; | ||
|
||
for (int i = 0; i < proc_argc; i++) | ||
{ | ||
args.push_back(proc_argv[i]); | ||
} | ||
args.push_back(NULL); | ||
|
||
child_pid = fork(); | ||
|
||
if (child_pid < 0) | ||
{ | ||
// Fork failed. No memory remaining available :( | ||
printf("Fork failed... Returning ENOMEM.\n"); | ||
return ENOMEM; | ||
} | ||
else if (child_pid == 0) | ||
{ | ||
// Instructions for child process! | ||
execv(args[0], const_cast<char* const*>(args.data())); | ||
} | ||
else | ||
{ | ||
do | ||
{ | ||
// Instructions for the parent process! | ||
wait_code = waitpid(child_pid, &child_status, WNOHANG); | ||
|
||
if (wait_code == -1) | ||
return EINVAL; | ||
|
||
std::this_thread::sleep_for(std::chrono::milliseconds(check_interval)); | ||
|
||
if (wait_code) | ||
{ | ||
if (WIFEXITED(child_status)) | ||
return WEXITSTATUS(child_status); | ||
} | ||
check_count += ms_factor; | ||
|
||
} while (check_count < ((timeout_ms / check_interval) * ms_factor)); | ||
} | ||
|
||
printf("Child process took too long. Timed out... Exiting...\n"); | ||
kill(child_pid, SIGKILL); | ||
|
||
#endif // TARGET_WINDOWS | ||
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