static u8 run_target(char
*
*
argv, u32 timeout) {
static struct itimerval it;
static u32 prev_timed_out
=
0
;
static u64 exec_ms
=
0
;
int
status
=
0
;
u32 tb4;
child_timed_out
=
0
;
/
*
After this memset, trace_bits[] are effectively volatile, so we
must prevent
any
earlier operations
from
venturing into that
territory.
*
/
memset(trace_bits,
0
, MAP_SIZE);
/
/
将trace_bits全部置
0
,清空共享内存
MEM_BARRIER();
/
*
If we
're running in "dumb" mode, we can'
t rely on the fork server
logic compiled into the target program, so we will just keep calling
execve(). There
is
a bit of code duplication between here
and
init_fork server(), but c'est la vie.
*
/
if
(dumb_mode
=
=
1
|| no_fork server) {
/
/
如果是dumb_mode模式且没有fork server
child_pid
=
fork();
/
/
直接fork出一个子进程
if
(child_pid <
0
) PFATAL(
"fork() failed"
);
if
(!child_pid) {
... ...
/
*
Isolate the process
and
configure standard descriptors. If out_file
is
specified, stdin
is
/
dev
/
null; otherwise, out_fd
is
cloned instead.
*
/
setsid();
dup2(dev_null_fd,
1
);
dup2(dev_null_fd,
2
);
if
(out_file) {
dup2(dev_null_fd,
0
);
}
else
{
dup2(out_fd,
0
);
close(out_fd);
}
/
*
On Linux, would be faster to use O_CLOEXEC. Maybe TODO.
*
/
close(dev_null_fd);
close(out_dir_fd);
close(dev_urandom_fd);
close(fileno(plot_file));
/
*
Set
sane defaults
for
ASAN
if
nothing
else
specified.
*
/
setenv(
"ASAN_OPTIONS"
,
"abort_on_error=1:"
"detect_leaks=0:"
"symbolize=0:"
"allocator_may_return_null=1"
,
0
);
setenv(
"MSAN_OPTIONS"
,
"exit_code="
STRINGIFY(MSAN_ERROR)
":"
"symbolize=0:"
"msan_track_origins=0"
,
0
);
execv(target_path, argv);
/
/
让子进程execv执行目标程序
/
*
Use a distinctive bitmap value to tell the parent about execv()
falling through.
*
/
*
(u32
*
)trace_bits
=
EXEC_FAIL_SIG;
/
/
execv执行失败,写入 EXEC_FAIL_SIG
exit(
0
);
}
}
else
{
s32 res;
/
*
In non
-
dumb mode, we have the fork server up
and
running, so simply
tell it to have at it,
and
then read back PID.
*
/
/
/
如果并不是处在dumb_mode模式,说明fork server已经启动了,我们只需要进行
/
/
控制管道的写和状态管道的读即可
if
((res
=
write(fsrv_ctl_fd, &prev_timed_out,
4
)) !
=
4
) {
if
(stop_soon)
return
0
;
RPFATAL(res,
"Unable to request new process from fork server (OOM?)"
);
}
if
((res
=
read(fsrv_st_fd, &child_pid,
4
)) !
=
4
) {
if
(stop_soon)
return
0
;
RPFATAL(res,
"Unable to request new process from fork server (OOM?)"
);
}
if
(child_pid <
=
0
) FATAL(
"Fork server is misbehaving (OOM?)"
);
}
/
*
Configure timeout, as requested by user, then wait
for
child to terminate.
*
/
/
/
配置超时,等待子进程结束
it.it_value.tv_sec
=
(timeout
/
1000
);
it.it_value.tv_usec
=
(timeout
%
1000
)
*
1000
;
setitimer(ITIMER_REAL, &it, NULL);
/
*
The SIGALRM handler simply kills the child_pid
and
sets child_timed_out.
*
/
if
(dumb_mode
=
=
1
|| no_fork server) {
if
(waitpid(child_pid, &status,
0
) <
=
0
) PFATAL(
"waitpid() failed"
);
}
else
{
s32 res;
if
((res
=
read(fsrv_st_fd, &status,
4
)) !
=
4
) {
if
(stop_soon)
return
0
;
RPFATAL(res,
"Unable to communicate with fork server (OOM?)"
);
}
}
if
(!WIFSTOPPED(status)) child_pid
=
0
;
getitimer(ITIMER_REAL, &it);
exec_ms
=
(u64) timeout
-
(it.it_value.tv_sec
*
1000
+
it.it_value.tv_usec
/
1000
);
/
/
计算执行时间
it.it_value.tv_sec
=
0
;
it.it_value.tv_usec
=
0
;
setitimer(ITIMER_REAL, &it, NULL);
total_execs
+
+
;
/
*
Any
subsequent operations on trace_bits must
not
be moved by the
compiler below this point. Past this location, trace_bits[] behave
very normally
and
do
not
have to be treated as volatile.
*
/
MEM_BARRIER();
tb4
=
*
(u32
*
)trace_bits;
/
/
分别执行
64
和
32
位下的classify_counts,设置trace_bits所在的mem
classify_counts((u64
*
)trace_bits);
classify_counts((u32
*
)trace_bits);
prev_timed_out
=
child_timed_out;
/
*
Report outcome to caller.
*
/
if
(WIFSIGNALED(status) && !stop_soon) {
kill_signal
=
WTERMSIG(status);
if
(child_timed_out && kill_signal
=
=
SIGKILL)
return
FAULT_TMOUT;
return
FAULT_CRASH;
}
/
*
A somewhat nasty hack
for
MSAN, which doesn't support abort_on_error
and
must use a special exit code.
*
/
if
(uses_asan && WEXITSTATUS(status)
=
=
MSAN_ERROR) {
kill_signal
=
0
;
return
FAULT_CRASH;
}
if
((dumb_mode
=
=
1
|| no_fork server) && tb4
=
=
EXEC_FAIL_SIG)
return
FAULT_ERROR;
/
*
It makes sense to account
for
the slowest units only
if
the testcase was run
under the user defined timeout.
*
/
if
(!(timeout > exec_tmout) && (slowest_exec_ms < exec_ms)) {
slowest_exec_ms
=
exec_ms;
}
return
FAULT_NONE;
}