[sanitizers] extracted process management functions

Differential Revision: http://reviews.llvm.org/D16546

llvm-svn: 258849
This commit is contained in:
Mike Aizatsky 2016-01-26 20:10:01 +00:00
parent 26d04ef9d9
commit 4a93316c8c
8 changed files with 148 additions and 30 deletions

View File

@ -423,6 +423,10 @@ bool TemplateMatch(const char *templ, const char *str) {
static const char kPathSeparator = SANITIZER_WINDOWS ? ';' : ':';
char *FindPathToBinary(const char *name) {
if (FileExists(name)) {
return internal_strdup(name);
}
const char *path = GetEnv("PATH");
if (!path)
return nullptr;

View File

@ -279,6 +279,17 @@ const char *GetPwd();
char *FindPathToBinary(const char *name);
bool IsPathSeparator(const char c);
bool IsAbsolutePath(const char *path);
// Starts a subprocess and returs its pid.
// If *_fd parameters are not kInvalidFd their corresponding input/output
// streams will be redirect to the file. The files will always be closed
// in parent process even in case of an error.
// The child process will close all fds after STDERR_FILENO
// before passing control to a program.
pid_t StartSubprocess(const char *filename, const char *const argv[],
fd_t stdin_fd = kInvalidFd, fd_t stdout_fd = kInvalidFd,
fd_t stderr_fd = kInvalidFd);
// Checks if specified process is still running
bool IsProcessRunning(pid_t pid);
u32 GetUid();
void ReExec();
@ -748,6 +759,23 @@ void GetPcSpBp(void *context, uptr *pc, uptr *sp, uptr *bp);
void DisableReexec();
void MaybeReexec();
template <typename Fn>
class RunOnDestruction {
public:
explicit RunOnDestruction(Fn fn) : fn_(fn) {}
~RunOnDestruction() { fn_(); }
private:
Fn fn_;
};
// A simple scope guard. Usage:
// auto cleanup = at_scope_exit([]{ do_cleanup; });
template <typename Fn>
RunOnDestruction<Fn> at_scope_exit(Fn fn) {
return RunOnDestruction<Fn>(fn);
}
} // namespace __sanitizer
inline void *operator new(__sanitizer::operator_new_size_type size,

View File

@ -89,6 +89,7 @@ typedef unsigned error_t;
typedef int fd_t;
typedef int error_t;
#endif
typedef int pid_t;
// WARNING: OFF_T may be different from OS type off_t, depending on the value of
// _FILE_OFFSET_BITS. This definition of OFF_T matches the ABI of system calls

View File

@ -81,6 +81,8 @@ int my_pthread_attr_getstack(void *attr, void **addr, uptr *size);
int internal_sigaction(int signum, const void *act, void *oldact);
void internal_sigfillset(__sanitizer_sigset_t *set);
uptr internal_execve(const char *filename, char *const argv[],
char *const envp[]);
} // namespace __sanitizer
#endif // SANITIZER_POSIX_H

View File

@ -34,6 +34,7 @@
#include <sys/stat.h>
#include <sys/time.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#if SANITIZER_FREEBSD
@ -320,6 +321,68 @@ void AdjustStackSize(void *attr_) {
}
#endif // !SANITIZER_GO
pid_t StartSubprocess(const char *program, const char *const argv[],
fd_t stdin_fd, fd_t stdout_fd, fd_t stderr_fd) {
auto file_closer = at_scope_exit([&] {
if (stdin_fd != kInvalidFd) {
internal_close(stdin_fd);
}
if (stdout_fd != kInvalidFd) {
internal_close(stdout_fd);
}
if (stderr_fd != kInvalidFd) {
internal_close(stderr_fd);
}
});
int pid = internal_fork();
if (pid < 0) {
int rverrno;
if (internal_iserror(pid, &rverrno)) {
Report("WARNING: failed to fork (errno %d)\n", rverrno);
}
return pid;
}
if (pid == 0) {
// Child subprocess
if (stdin_fd != kInvalidFd) {
internal_close(STDIN_FILENO);
internal_dup2(stdin_fd, STDIN_FILENO);
internal_close(stdin_fd);
}
if (stdout_fd != kInvalidFd) {
internal_close(STDOUT_FILENO);
internal_dup2(stdout_fd, STDOUT_FILENO);
internal_close(stdout_fd);
}
if (stderr_fd != kInvalidFd) {
internal_close(STDERR_FILENO);
internal_dup2(stderr_fd, STDERR_FILENO);
internal_close(stderr_fd);
}
for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--) internal_close(fd);
internal_execve(program, const_cast<char **>(&argv[0]), nullptr);
internal__exit(1);
}
return pid;
}
bool IsProcessRunning(pid_t pid) {
int process_status;
uptr waitpid_status = internal_waitpid(pid, &process_status, WNOHANG);
int local_errno;
if (internal_iserror(waitpid_status, &local_errno)) {
VReport(1, "Waiting on the process failed (errno %d).\n", local_errno);
return false;
}
return waitpid_status == 0;
}
} // namespace __sanitizer
#endif // SANITIZER_POSIX

View File

@ -137,47 +137,23 @@ bool SymbolizerProcess::StartSymbolizerSubprocess() {
CHECK(infd);
CHECK(outfd);
// Real fork() may call user callbacks registered with pthread_atfork().
pid = internal_fork();
if (pid == -1) {
// Fork() failed.
internal_close(infd[0]);
internal_close(infd[1]);
internal_close(outfd[0]);
internal_close(outfd[1]);
Report("WARNING: failed to fork external symbolizer "
" (errno: %d)\n", errno);
return false;
} else if (pid == 0) {
// Child subprocess.
internal_close(STDOUT_FILENO);
internal_close(STDIN_FILENO);
internal_dup2(outfd[0], STDIN_FILENO);
internal_dup2(infd[1], STDOUT_FILENO);
internal_close(outfd[0]);
internal_close(outfd[1]);
internal_close(infd[0]);
internal_close(infd[1]);
for (int fd = sysconf(_SC_OPEN_MAX); fd > 2; fd--)
internal_close(fd);
const char *argv[kArgVMax];
GetArgV(path_, argv);
execv(path_, const_cast<char **>(&argv[0]));
internal__exit(1);
pid = StartSubprocess(path_, argv, /* stdin */ outfd[0],
/* stdout */ infd[1]);
if (pid < 0) {
internal_close(infd[0]);
internal_close(outfd[1]);
return false;
}
// Continue execution in parent process.
internal_close(outfd[0]);
internal_close(infd[1]);
input_fd_ = infd[0];
output_fd_ = outfd[1];
}
// Check that symbolizer subprocess started successfully.
int pid_status;
SleepForMillis(kSymbolizerStartupTimeMillis);
int exited_pid = waitpid(pid, &pid_status, WNOHANG);
if (exited_pid != 0) {
if (!IsProcessRunning(pid)) {
// Either waitpid failed, or child has already exited.
Report("WARNING: external symbolizer didn't start up correctly!\n");
return false;

View File

@ -775,6 +775,20 @@ char **GetArgv() {
return 0;
}
pid_t StartSubprocess(const char *program, const char *const argv[],
fd_t stdin_fd, fd_t stdout_fd, fd_t stderr_fd) {
// FIXME: implement on this platform
// Should be implemented based on
// SymbolizerProcess::StarAtSymbolizerSubprocess
// from lib/sanitizer_common/sanitizer_symbolizer_win.cc.
return -1;
}
bool IsProcessRunning(pid_t pid) {
// FIXME: implement on this platform.
return false;
}
} // namespace __sanitizer
#endif // _WIN32

View File

@ -263,6 +263,36 @@ TEST(SanitizerLinux, ThreadSelfTest) {
}
#endif
TEST(SanitizerCommon, StartSubprocessTest) {
int pipe_fds[2];
ASSERT_EQ(0, pipe(pipe_fds));
const char *argv[] = {"/bin/sh", "-c", "echo -n 'hello'"};
int pid = StartSubprocess("/bin/sh", const_cast<char **>(&argv[0]),
kInvalidFd /* stdin */, pipe_fds[1] /* stdout */);
ASSERT_GT(pid, 0);
// wait for process to finish.
while (IsProcessRunning(pid)) {
}
ASSERT_FALSE(IsProcessRunning(pid));
char buffer[256];
{
char *ptr = buffer;
uptr bytes_read;
while (ReadFromFile(pipe_fds[0], ptr, 256, &bytes_read)) {
if (!bytes_read) {
break;
}
ptr += bytes_read;
}
ASSERT_EQ(5, ptr - buffer);
*ptr = 0;
}
ASSERT_EQ(0, strcmp(buffer, "hello")) << "Buffer: " << buffer;
internal_close(pipe_fds[0]);
}
} // namespace __sanitizer
#endif // SANITIZER_LINUX