出于测试目的强制“pthread_create”失败

Forcing `pthread_create` to fail for test purposes

提问人:zwol 提问时间:10/6/2023 更新时间:10/7/2023 访问量:111

问:

为了测试错误恢复,我想以可预测的方式使失败。最明显的方法是对特定进程中允许存在的线程数设置硬性上限。pthread_create

具体而言,在 Linux 上,会影响进程和线程创建;将限制设置为 1 将导致调用后任何代码都失败。Solaris 派生的 Unixes [我只测试了 OmniOS r151030] 没有RLIMIT_NPROC但确实有办法通过更高级的 setrctl 接口限制进程中的线程数。setrlimit(RLIMIT_NPROC)pthread_createsetrlimit

我还没有找到一种方法在我尝试过的任何其他 Unix 上用 C 来做到这一点。特别是,这个问题底部的测试程序将在所有(AIX 7.1、FreeBSD 12.1、NetBSD 9.3、OpenBSD 6.8)上打印 “fail: pthread_create succeeded”。

所以,问题来了:请修改测试程序,使调用在一个或多个既不是 Linux 也不是 Solaris 的 Unix 上失败。在各种 Unix 上都有效的答案是首选(例如,如果你能在所有 BSD 衍生系统上展示一种方法来做到这一点,那就太好了)。pthread_create

注意:除了实际执行测试程序的进程之外,还会对进程产生副作用的答案(例如,对在特定 uid 下运行的线程总数或整个系统施加限制)对我没有好处。


#include <pthread.h>
#include <stdio.h>
#include <string.h>
#include <sys/resource.h>

static void *threadproc(void *arg)
{
    puts("fail: pthread_create succeeded");
    return arg;
}

int main(void)
{
    struct rlimit r = { 1, 1 };
    if (setrlimit(RLIMIT_NPROC, &r)) {
        perror("error: setrlimit failed");
        return 1;
    }
    pthread_t t;
    int err = pthread_create(&t, 0, threadproc, 0);
    if (err) {
        printf("ok: pthread_create failed (%s)\n", strerror(err));
        return 0;
    }
    err = pthread_join(t, 0);
    if (err)
        printf("error: pthread_join failed (%s)\n", strerror(err));
    return 1;
}
C 测试 UNIX Pthreads POSIX

评论

0赞 Ted Lyngmo 10/6/2023
解决方案包装会起作用吗?pthread_create
2赞 zwol 10/6/2023
@TedLyngmo 不,这是在测试一个已经编译好的二进制文件(在实际的测试驱动程序中,setrlimit()或其他文件之后有一个exec()或其他什么),它可能已被静态链接(所以LD_PRELOAD也不是一个选项)。
0赞 Ted Lyngmo 10/6/2023
还行。而且,你不能单独运行这个过程(docker 等)吗?
1赞 zwol 10/6/2023
这只是众多测试之一,测试工具目前无法做到这一点,因此,并非没有大量的额外工作。

答:

1赞 Andrew Henle 10/6/2023 #1

AIX的

AIX setrlimit() 具有RLIMIT_THREADS

RLIMIT_THREADS

每个进程可以创建的最大线程数。此限制由内核和 pthread 调试库强制执行。

只需更换

setrlimit(RLIMIT_NPROC, &r)

setrlimit(RLIMIT_THREADS, &r)

(但是,我无权访问要测试的 AIX 实例)

BSD公司

BSD 似乎没有任何不依赖于每个用户或整个系统限制的等价物。

0赞 Misha T 10/6/2023 #2

您可以使用POSIX功能保护堆栈内存int mprotect(void *addr, size_t len, int prot);

像这样的东西:

   pthread_attr_t attr;
    // attr initialization here:
   int rc; 
   void  *mystack;
   size_t mystacksize = 2 * PTHREAD_STACK_MIN;
                                                                                
   if ( pthread_attr_init(&attr) ) {                                        
      perror("pthread_attr_init()");                                     
      return -1;
   }                                                                            
                                                                                
   // Get a big enough stack and align it on 4K boundary.
   mystack = malloc(PTHREAD_STACK_MIN * 3);
   if (!mystack) {
      perror("Unable to acquire storage.");
      return -1;
   }
   // Align stack, memory leak is possible
   mystack =  (((intptr_t)mystack) & 0xfff)
               ? (void*)( ( ((intptr_t)mystack) & ~0xfff) + 0x1000)
              : mystack;
                                                                                
    rc = pthread_attr_setstack(&attr, mystack, mystacksize);
    if (rc != 0) {                                           
        perror("pthread_attr_setstack()");
        return -1;
    } 

    // Protect stack memory:
    rc = memprotect(stack, mystacksize, PROT_NONE);
    if (rc) {
        perror("mprotect()");
        return -1;
    }

    pthread_t t;
    // Should fail, cause stack memory is protected
    int err = pthread_create(&t, attr, threadproc, 0);
    if (err) {
        printf("ok: pthread_create failed (%s)\n", strerror(err));
        return 0;
    }

评论

0赞 Andrew Henle 10/6/2023
它与你深思熟虑的答案无关,pthread_attr_init() 不能保证在失败时返回负值:“成功完成后,应返回值 0;否则,应返回错误号以指示错误。pthread_attr_destroy()pthread_attr_init()
0赞 zwol 10/6/2023
你测试过吗?对我来说,它使整个过程崩溃。
0赞 Andrew Henle 10/6/2023
@zwol 它可能在 BSD 上工作 - 如果无法访问堆栈,则在 BSD 上读取 ISTR 失败 - 要么是因为内存权限不正确,要么是地址不在地址空间中。当然,问题在于让被测可执行文件按照这个答案运行。pthread_create()
1赞 zwol 10/6/2023
@AndrewHenle 刚刚在 NetBSD 上尝试过,不,段错误。
1赞 zwol 10/6/2023
在 OpenBSD 和 FreeBSD 上也会崩溃。在达尔文,pthread_attr_setstack返回了EINVAL。
0赞 Craig Estey 10/7/2023 #3

我们可以使用和捕获/修改系统调用ptraceclonePTRACE_SYSCALL

以下是针对 linux (x86_64) 的,但 BSD 具有类似但略有不同的选项。

在 的“进入系统调用”时,如果我们想强制失败,一种方法 [也许是最简单的] 是将 设置为零。这是系统调用号,0 是无效的系统调用。目标程序将出现错误。cloneorig_raxENOSYS

BSD(显然)必须使用略有不同的选项,但原理是相同的。


下面是跟踪程序 ():trace.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <signal.h>
//#include <syscall.h>
#include <asm/unistd.h>
#include <sys/ptrace.h>
#include <sys/wait.h>
#include <sys/user.h>

FILE *xflog;
int mode;
int maxthr;
int clone_count;
int kicked;
struct user_regs_struct regs;

#define outf(_fmt...) \
    fprintf(xflog,_fmt)

#define REGSALL(_cmd) \
  _cmd(r15) \
  _cmd(r14) \
  _cmd(r13) \
  _cmd(r12) \
  _cmd(rbp) \
  _cmd(rbx) \
  _cmd(r11) \
  _cmd(r10) \
  _cmd(r9) \
  _cmd(r8) \
  _cmd(rax) \
  _cmd(rcx) \
  _cmd(rdx) \
  _cmd(rsi) \
  _cmd(rdi) \
  _cmd(orig_rax) \
  _cmd(rip) \
  _cmd(cs) \
  _cmd(eflags) \
  _cmd(rsp) \
  _cmd(ss) \
  _cmd(fs_base) \
  _cmd(gs_base) \
  _cmd(ds) \
  _cmd(es) \
  _cmd(fs) \
  _cmd(gs)

#define GETREG(_reg) \
    _getreg(#_reg,regs._reg);

#define MAXTHREADS          10

const char *svcname(int);
char svc_verbose[500] = {
    [__NR_clone] = 1,
};

void *
func(void *ptr)
{

    sleep(2);

    return (void *) 0;
}

void
showstop(pid_t pid,int status)
{

    outf("\n");
    outf("trace: pid=%d status=%8.8X",pid,status);

    do {
        if (WIFSIGNALED(status)) {
            outf(" WIFSIGNALED signo=%d",WTERMSIG(status));
            break;
        }

        if (WIFSTOPPED(status)) {
            outf(" WIFSTOPPED signo=%d",WSTOPSIG(status));
            break;
        }

        if (WIFEXITED(status)) {
            outf(" WIFEXITED code=%d",WEXITSTATUS(status));
            break;
        }
    } while (0);

    outf("\n");
}

void
_getreg(const char *sym,unsigned long long val)
{

    outf("getreg: %s %16.16llX %llu\n",sym,val,val);
}

void
getregs(pid_t pid,int force)
{

    int err = ptrace(PTRACE_GETREGS,pid,NULL,&regs);
    if (err < 0) {
        outf("getregs: fail -- %s\n",strerror(errno));
        exit(1);
    }

    long long svcno = regs.orig_rax;
    long long retval = regs.rax;

    outf("getregs: SYSCALL %d %s",svcno,svcname(svcno));
    outf(" mode=%d/%s ret=%lld",mode,mode ? "exit" : "enter",retval);
    if (retval < 0)
        outf(" -- %s",strerror(-retval));
    outf("\n");

    int show = svc_verbose[svcno];

    if (show || force) {
        REGSALL(GETREG)
    }
}

void
setregs(pid_t pid)
{

    int err = ptrace(PTRACE_SETREGS,pid,NULL,&regs);
    if (err < 0) {
        outf("setregs: fail -- %s\n",strerror(errno));
        exit(1);
    }

    outf("setregs: mode=%d\n",mode);

    getregs(pid,1);

    kicked = 1;
}

int
main(int argc,char **argv)
{

    --argc;
    ++argv;

    xflog = fopen("LOG","w");
    setlinebuf(xflog);

    maxthr = -1;

    for (;  argc > 0;  --argc, ++argv) {
        char *cp = *argv;
        if (*cp != '-')
            break;

        cp += 2;
        switch (cp[-1]) {
        case 'L':
            maxthr = (*cp != 0) ? atoi(cp) : 1;
            break;
        }
    }

    if (argc < 1) {
        outf("trace: need at least one arg\n");
        exit(1);
    }

    outf("trace: __NR_clone=%d\n",__NR_clone);

    int status;

    pid_t pidtop = fork();
    pid_t pidcur;

    if (pidtop == 0) {
        pidtop = getpid();
        outf("trace: child pidtop=%d\n",pidtop);
        ptrace(PTRACE_TRACEME,pidtop);
        outf("trace: child execvp\n");
        execvp(argv[0],argv);
        outf("trace: execvp failure -- %s\n",strerror(errno));
        exit(1);
    }

    outf("trace: parent pidtop=%d\n",pidtop);
    pidcur = waitpid(pidtop,&status,0);
    showstop(pidcur,status);
    getregs(pidcur,1);

    int err = ptrace(PTRACE_SYSCALL,pidcur,NULL,NULL);
    if (err < 0) {
        outf("trace: PTRACE_SYSCALL/1 fail pidcur=%d -- %s\n",
            pidcur,strerror(errno));
        //break;
    }

    while (1) {
        pidcur = waitpid(-1,&status,0);
        if (pidcur < 0)
            break;

        showstop(pidcur,status);

        getregs(pidcur,kicked);
        kicked = 0;

        if (regs.orig_rax == __NR_clone) {
            if (mode == 0) {
                ++clone_count;

                // if we're over the limit, zap the syscall number to force
                // target to see EINVAL
                if ((maxthr >= 0) && (clone_count > maxthr)) {
                    regs.orig_rax = 0;
                    setregs(pidcur);
                }
            }
        }

        mode = ! mode;

        if (WIFSTOPPED(status) && (WSTOPSIG(status) == SIGSTOP)) {
        }

        int err = ptrace(PTRACE_SYSCALL,pidcur,NULL,NULL);
        if (err < 0) {
            outf("trace: PTRACE_SYSCALL/2 fail pidcur=%d -- %s\n",
                pidcur,strerror(errno));
            //break;
        }
    }

    outf("trace: done\n");
    fclose(xflog);

    return 0;
}
#define NRALL(_cmd) \
    _cmd(read,0) \
    _cmd(write,1) \
    _cmd(open,2) \
    _cmd(close,3) \
    _cmd(stat,4) \
    _cmd(fstat,5) \
    _cmd(lstat,6) \
    _cmd(poll,7) \
    _cmd(lseek,8) \
    _cmd(mmap,9) \
    _cmd(mprotect,10) \
    _cmd(munmap,11) \
    _cmd(brk,12) \
    _cmd(rt_sigaction,13) \
    _cmd(rt_sigprocmask,14) \
    _cmd(rt_sigreturn,15) \
    _cmd(ioctl,16) \
    _cmd(pread64,17) \
    _cmd(pwrite64,18) \
    _cmd(readv,19) \
    _cmd(writev,20) \
    _cmd(access,21) \
    _cmd(pipe,22) \
    _cmd(select,23) \
    _cmd(sched_yield,24) \
    _cmd(mremap,25) \
    _cmd(msync,26) \
    _cmd(mincore,27) \
    _cmd(madvise,28) \
    _cmd(shmget,29) \
    _cmd(shmat,30) \
    _cmd(shmctl,31) \
    _cmd(dup,32) \
    _cmd(dup2,33) \
    _cmd(pause,34) \
    _cmd(nanosleep,35) \
    _cmd(getitimer,36) \
    _cmd(alarm,37) \
    _cmd(setitimer,38) \
    _cmd(getpid,39) \
    _cmd(sendfile,40) \
    _cmd(socket,41) \
    _cmd(connect,42) \
    _cmd(accept,43) \
    _cmd(sendto,44) \
    _cmd(recvfrom,45) \
    _cmd(sendmsg,46) \
    _cmd(recvmsg,47) \
    _cmd(shutdown,48) \
    _cmd(bind,49) \
    _cmd(listen,50) \
    _cmd(getsockname,51) \
    _cmd(getpeername,52) \
    _cmd(socketpair,53) \
    _cmd(setsockopt,54) \
    _cmd(getsockopt,55) \
    _cmd(clone,56) \
    _cmd(fork,57) \
    _cmd(vfork,58) \
    _cmd(execve,59) \
    _cmd(exit,60) \
    _cmd(wait4,61) \
    _cmd(kill,62) \
    _cmd(uname,63) \
    _cmd(semget,64) \
    _cmd(semop,65) \
    _cmd(semctl,66) \
    _cmd(shmdt,67) \
    _cmd(msgget,68) \
    _cmd(msgsnd,69) \
    _cmd(msgrcv,70) \
    _cmd(msgctl,71) \
    _cmd(fcntl,72) \
    _cmd(flock,73) \
    _cmd(fsync,74) \
    _cmd(fdatasync,75) \
    _cmd(truncate,76) \
    _cmd(ftruncate,77) \
    _cmd(getdents,78) \
    _cmd(getcwd,79) \
    _cmd(chdir,80) \
    _cmd(fchdir,81) \
    _cmd(rename,82) \
    _cmd(mkdir,83) \
    _cmd(rmdir,84) \
    _cmd(creat,85) \
    _cmd(link,86) \
    _cmd(unlink,87) \
    _cmd(symlink,88) \
    _cmd(readlink,89) \
    _cmd(chmod,90) \
    _cmd(fchmod,91) \
    _cmd(chown,92) \
    _cmd(fchown,93) \
    _cmd(lchown,94) \
    _cmd(umask,95) \
    _cmd(gettimeofday,96) \
    _cmd(getrlimit,97) \
    _cmd(getrusage,98) \
    _cmd(sysinfo,99) \
    _cmd(times,100) \
    _cmd(ptrace,101) \
    _cmd(getuid,102) \
    _cmd(syslog,103) \
    _cmd(getgid,104) \
    _cmd(setuid,105) \
    _cmd(setgid,106) \
    _cmd(geteuid,107) \
    _cmd(getegid,108) \
    _cmd(setpgid,109) \
    _cmd(getppid,110) \
    _cmd(getpgrp,111) \
    _cmd(setsid,112) \
    _cmd(setreuid,113) \
    _cmd(setregid,114) \
    _cmd(getgroups,115) \
    _cmd(setgroups,116) \
    _cmd(setresuid,117) \
    _cmd(getresuid,118) \
    _cmd(setresgid,119) \
    _cmd(getresgid,120) \
    _cmd(getpgid,121) \
    _cmd(setfsuid,122) \
    _cmd(setfsgid,123) \
    _cmd(getsid,124) \
    _cmd(capget,125) \
    _cmd(capset,126) \
    _cmd(rt_sigpending,127) \
    _cmd(rt_sigtimedwait,128) \
    _cmd(rt_sigqueueinfo,129) \
    _cmd(rt_sigsuspend,130) \
    _cmd(sigaltstack,131) \
    _cmd(utime,132) \
    _cmd(mknod,133) \
    _cmd(uselib,134) \
    _cmd(personality,135) \
    _cmd(ustat,136) \
    _cmd(statfs,137) \
    _cmd(fstatfs,138) \
    _cmd(sysfs,139) \
    _cmd(getpriority,140) \
    _cmd(setpriority,141) \
    _cmd(sched_setparam,142) \
    _cmd(sched_getparam,143) \
    _cmd(sched_setscheduler,144) \
    _cmd(sched_getscheduler,145) \
    _cmd(sched_get_priority_max,146) \
    _cmd(sched_get_priority_min,147) \
    _cmd(sched_rr_get_interval,148) \
    _cmd(mlock,149) \
    _cmd(munlock,150) \
    _cmd(mlockall,151) \
    _cmd(munlockall,152) \
    _cmd(vhangup,153) \
    _cmd(modify_ldt,154) \
    _cmd(pivot_root,155) \
    _cmd(_sysctl,156) \
    _cmd(prctl,157) \
    _cmd(arch_prctl,158) \
    _cmd(adjtimex,159) \
    _cmd(setrlimit,160) \
    _cmd(chroot,161) \
    _cmd(sync,162) \
    _cmd(acct,163) \
    _cmd(settimeofday,164) \
    _cmd(mount,165) \
    _cmd(umount2,166) \
    _cmd(swapon,167) \
    _cmd(swapoff,168) \
    _cmd(reboot,169) \
    _cmd(sethostname,170) \
    _cmd(setdomainname,171) \
    _cmd(iopl,172) \
    _cmd(ioperm,173) \
    _cmd(create_module,174) \
    _cmd(init_module,175) \
    _cmd(delete_module,176) \
    _cmd(get_kernel_syms,177) \
    _cmd(query_module,178) \
    _cmd(quotactl,179) \
    _cmd(nfsservctl,180) \
    _cmd(getpmsg,181) \
    _cmd(putpmsg,182) \
    _cmd(afs_syscall,183) \
    _cmd(tuxcall,184) \
    _cmd(security,185) \
    _cmd(gettid,186) \
    _cmd(readahead,187) \
    _cmd(setxattr,188) \
    _cmd(lsetxattr,189) \
    _cmd(fsetxattr,190) \
    _cmd(getxattr,191) \
    _cmd(lgetxattr,192) \
    _cmd(fgetxattr,193) \
    _cmd(listxattr,194) \
    _cmd(llistxattr,195) \
    _cmd(flistxattr,196) \
    _cmd(removexattr,197) \
    _cmd(lremovexattr,198) \
    _cmd(fremovexattr,199) \
    _cmd(tkill,200) \
    _cmd(time,201) \
    _cmd(futex,202) \
    _cmd(sched_setaffinity,203) \
    _cmd(sched_getaffinity,204) \
    _cmd(set_thread_area,205) \
    _cmd(io_setup,206) \
    _cmd(io_destroy,207) \
    _cmd(io_getevents,208) \
    _cmd(io_submit,209) \
    _cmd(io_cancel,210) \
    _cmd(get_thread_area,211) \
    _cmd(lookup_dcookie,212) \
    _cmd(epoll_create,213) \
    _cmd(epoll_ctl_old,214) \
    _cmd(epoll_wait_old,215) \
    _cmd(remap_file_pages,216) \
    _cmd(getdents64,217) \
    _cmd(set_tid_address,218) \
    _cmd(restart_syscall,219) \
    _cmd(semtimedop,220) \
    _cmd(fadvise64,221) \
    _cmd(timer_create,222) \
    _cmd(timer_settime,223) \
    _cmd(timer_gettime,224) \
    _cmd(timer_getoverrun,225) \
    _cmd(timer_delete,226) \
    _cmd(clock_settime,227) \
    _cmd(clock_gettime,228) \
    _cmd(clock_getres,229) \
    _cmd(clock_nanosleep,230) \
    _cmd(exit_group,231) \
    _cmd(epoll_wait,232) \
    _cmd(epoll_ctl,233) \
    _cmd(tgkill,234) \
    _cmd(utimes,235) \
    _cmd(vserver,236) \
    _cmd(mbind,237) \
    _cmd(set_mempolicy,238) \
    _cmd(get_mempolicy,239) \
    _cmd(mq_open,240) \
    _cmd(mq_unlink,241) \
    _cmd(mq_timedsend,242) \
    _cmd(mq_timedreceive,243) \
    _cmd(mq_notify,244) \
    _cmd(mq_getsetattr,245) \
    _cmd(kexec_load,246) \
    _cmd(waitid,247) \
    _cmd(add_key,248) \
    _cmd(request_key,249) \
    _cmd(keyctl,250) \
    _cmd(ioprio_set,251) \
    _cmd(ioprio_get,252) \
    _cmd(inotify_init,253) \
    _cmd(inotify_add_watch,254) \
    _cmd(inotify_rm_watch,255) \
    _cmd(migrate_pages,256) \
    _cmd(openat,257) \
    _cmd(mkdirat,258) \
    _cmd(mknodat,259) \
    _cmd(fchownat,260) \
    _cmd(futimesat,261) \
    _cmd(newfstatat,262) \
    _cmd(unlinkat,263) \
    _cmd(renameat,264) \
    _cmd(linkat,265) \
    _cmd(symlinkat,266) \
    _cmd(readlinkat,267) \
    _cmd(fchmodat,268) \
    _cmd(faccessat,269) \
    _cmd(pselect6,270) \
    _cmd(ppoll,271) \
    _cmd(unshare,272) \
    _cmd(set_robust_list,273) \
    _cmd(get_robust_list,274) \
    _cmd(splice,275) \
    _cmd(tee,276) \
    _cmd(sync_file_range,277) \
    _cmd(vmsplice,278) \
    _cmd(move_pages,279) \
    _cmd(utimensat,280) \
    _cmd(epoll_pwait,281) \
    _cmd(signalfd,282) \
    _cmd(timerfd_create,283) \
    _cmd(eventfd,284) \
    _cmd(fallocate,285) \
    _cmd(timerfd_settime,286) \
    _cmd(timerfd_gettime,287) \
    _cmd(accept4,288) \
    _cmd(signalfd4,289) \
    _cmd(eventfd2,290) \
    _cmd(epoll_create1,291) \
    _cmd(dup3,292) \
    _cmd(pipe2,293) \
    _cmd(inotify_init1,294) \
    _cmd(preadv,295) \
    _cmd(pwritev,296) \
    _cmd(rt_tgsigqueueinfo,297) \
    _cmd(perf_event_open,298) \
    _cmd(recvmmsg,299) \
    _cmd(fanotify_init,300) \
    _cmd(fanotify_mark,301) \
    _cmd(prlimit64,302) \
    _cmd(name_to_handle_at,303) \
    _cmd(open_by_handle_at,304) \
    _cmd(clock_adjtime,305) \
    _cmd(syncfs,306) \
    _cmd(sendmmsg,307) \
    _cmd(setns,308) \
    _cmd(getcpu,309) \
    _cmd(process_vm_readv,310) \
    _cmd(process_vm_writev,311) \
    _cmd(kcmp,312) \
    _cmd(finit_module,313) \
    _cmd(sched_setattr,314) \
    _cmd(sched_getattr,315) \
    _cmd(renameat2,316) \
    _cmd(seccomp,317) \
    _cmd(getrandom,318) \
    _cmd(memfd_create,319) \
    _cmd(kexec_file_load,320) \
    _cmd(bpf,321) \
    _cmd(execveat,322) \
    _cmd(userfaultfd,323) \
    _cmd(membarrier,324) \
    _cmd(mlock2,325) \
    _cmd(copy_file_range,326) \
    _cmd(preadv2,327) \
    _cmd(pwritev2,328) \
    _cmd(pkey_mprotect,329) \
    _cmd(pkey_alloc,330) \
    _cmd(pkey_free,331) \
    _cmd(statx,332) \
    _cmd(io_pgetevents,333) \
    _cmd(rseq,334) \
    _cmd(pidfd_send_signal,424) \
    _cmd(io_uring_setup,425) \
    _cmd(io_uring_enter,426) \
    _cmd(io_uring_register,427) \
    _cmd(open_tree,428) \
    _cmd(move_mount,429) \
    _cmd(fsopen,430) \
    _cmd(fsconfig,431) \
    _cmd(fsmount,432) \
    _cmd(fspick,433) \
    _cmd(pidfd_open,434) \
    _cmd(clone3,435)

#define SVCDEF(_sym,_val) \
    [__NR_ ## _sym] = #_sym,

const char *svcdef[500] = {
    NRALL(SVCDEF)
};

const char *
svcname(int svcno)
{

    return svcdef[svcno];
}

这是目标/测试程序():target.c

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <pthread.h>

#define MAXTHREADS          5

void *
func(void *ptr)
{

    sleep(2);

    return (void *) 0;
}

int
main(void)
{

    pthread_t tid[MAXTHREADS];

    int err;
    int count = 0;

    for (int idx = 0;  idx < MAXTHREADS;  ++idx, ++count) {
        err = pthread_create(&tid[idx],NULL,func,NULL);
        if (err != 0) {
            printf("main: idx=%d -- %s\n",idx,strerror(err));
            break;
        }
        printf("main: start idx=%d\n",idx);
    }

    for (int idx = 0;  idx < count;  ++idx) {
        pthread_join(tid[idx],NULL);
        printf("main: joined idx=%d\n",idx);
    }

    printf("main: done\n");

    return 0;
}

编译时使用:

cc -o trace trace.c
cc -o target target.c -lpthread

这是正常输出 ():./target

main: start idx=0
main: start idx=1
main: start idx=2
main: start idx=3
main: start idx=4
main: joined idx=0
main: joined idx=1
main: joined idx=2
main: joined idx=3
main: joined idx=4
main: done

以下是 的输出。请注意,跟踪程序将调试/跟踪输出放在文件中:./trace -L2 ./target./LOG

main: start idx=0
main: start idx=1
main: idx=2 -- Bad file descriptor
main: joined idx=0
main: joined idx=1
main: done