// File: /workspace/github_cpp_makefile_star2_O1/aleden[P]ptrace-multi-threaded-demo/ptrace_multi_threaded_demo.cpp
// Function: main
int main(int argc, char **argv) {
  fprintf(stderr, "parent: forking...\n");

  const pid_t child = fork();
  if (!child)
    return do_child(argc, argv);

  //
  // parent
  //

  //
  // Normally when a (possibly multithreaded) process receives any signal except
  // SIGKILL, the kernel selects an arbitrary thread which handles the signal.
  // (If the signal is generated with tgkill(2), the target thread can be
  // explicitly selected by the caller.)
  //
  // However, if the selected thread is traced, it enters signal-delivery-stop.
  //
  // At this point, the signal is not yet delivered to the process, and can be
  // suppressed by the tracer. If the tracer doesn't suppress the signal, it
  // passes the signal to the tracee in the next ptrace restart request.
  //

  //
  // observe the (initial) signal-delivery-stop
  //
  fprintf(stderr, "parent: waiting for initial stop of child %d...\n", child);
  int status;
  do
    waitpid(child, &status, 0);
  while (!WIFSTOPPED(status));
  fprintf(stderr, "parent: initial stop observed\n");

  //
  // select ptrace options
  //
  int ptrace_options = 0;

  // When delivering system call traps, set bit 7 in the signal number (i.e.,
  // deliver SIGTRAP|0x80). This makes it easy for the tracer to distinguish
  // normal traps from those caused by a system call. Note:
  // PTRACE_O_TRACESYSGOOD may not work on all architectures.
  ptrace_options |= PTRACE_O_TRACESYSGOOD;

  // Send a SIGKILL signal to the tracee if the tracer exits. This option is
  // useful for ptrace jailers that want to ensure that tracees can never escape
  // the tracer's control.
  ptrace_options |= PTRACE_O_EXITKILL;

  // Stop the tracee at the next clone(2) and automatically start tracing the
  // newly cloned process, which will start with a SIGSTOP, or PTRACE_EVENT_STOP
  // if PTRACE_SEIZE was used.  A waitpid(2) by the tracer will return a status
  // value such that
  //
  //  status>>8 == (SIGTRAP | (PTRACE_EVENT_CLONE<<8))
  //
  // The PID of the new process can be retrieved with PTRACE_GETEVENTMSG. This
  // option may not catch clone(2) calls in all cases.  If the tracee calls
  // clone(2) with the CLONE_VFORK flag, PTRACE_EVENT_VFORK will be delivered
  // instead if PTRACE_O_TRACEVFORK is set; otherwise if the tracee calls
  // clone(2) with the exit signal set to SIGCHLD, PTRACE_EVENT_FORK will be
  // delivered if PTRACE_O_TRACEFORK is set.
  ptrace_options |= PTRACE_O_TRACECLONE;

  //
  // set those options
  //
  fprintf(stderr, "parent: setting ptrace options...\n");
  ptrace(PTRACE_SETOPTIONS, child, 0, ptrace_options);
  fprintf(stderr, "ptrace options set!\n");

  auto wait_for_syscall_entry_or_exit = [](pid_t pid) -> pid_t {
    siginfo_t si;
    uintptr_t sig = 0;

    for (;;) {
      if (pid != -1) {
        // Restart the stopped tracee as for PTRACE_CONT, but arrange for the
        // tracee to be stopped at the next entry to or exit from a system call.
        // (The tracee will also, as usual, be stopped upon receipt of a
        // signal.) From the tracer's perspective, the tracee will appear to
        // have been stopped by receipt of a SIGTRAP. So, for PTRACE_SYSCALL,
        // the idea is to inspect the arguments to the system call at the first
        // stop, then do another PTRACE_SYSCALL and inspect the return value of
        // the system call at the second stop.
        //
        // The data argument is treated as for PTRACE_CONT; i.e. If data is
        // nonzero, it is interpreted as the number of a signal to be delivered
        // to the tracee; otherwise, no signal is delivered.  Thus, for example,
        // the tracer can control whether a signal sent to the tracee is
        // delivered or not.
        if (ptrace(PTRACE_SYSCALL, pid, 0, (void *)sig) == -1) {
          fprintf(stderr,
                  "parent: failed to ptrace(PTRACE_SYSCALL): %s\n",
                  strerror(errno));
          return -1;
        }
      }

      //
      // reset restart signal and pid
      //
      sig = 0;
      pid = -1;

      //
      // wait for a child process to stop or terminate
      //
      int status;
      pid_t child_waited = waitpid(-1, &status, __WALL);
      if (child_waited == -1) {
        fprintf(stderr, "parent: waitpid(1) failed : %s\n",
                strerror(errno));
        return -1;
      } else {
        if (WIFSTOPPED(status)) {
          //
          // the following kinds of ptrace-stops exist:
          //
          //   (1) syscall-stops
          //   (2) PTRACE_EVENT stops
          //   (3) group-stops
          //   (4) signal-delivery-stops
          //
          // they all are reported by waitpid(2) with WIFSTOPPED(status) true.
          // They may be differentiated by examining the value status>>8, and if
          // there is ambiguity in that value, by querying PTRACE_GETSIGINFO.
          // (Note: the WSTOPSIG(status) macro can't be used to perform this
          // examination, because it returns the value (status>>8) & 0xff.)
          //
          pid = child_waited;

          const int stopsig = WSTOPSIG(status);
          if (stopsig == (SIGTRAP | 0x80)) {
            //
            // (1) Syscall-enter-stop and syscall-exit-stop are observed by the
            // tracer as waitpid(2) returning with WIFSTOPPED(status) true, and-
            // if the PTRACE_O_TRACESYSGOOD option was set by the tracer- then
            // WSTOPSIG(status) will give the value (SIGTRAP | 0x80).
            //
            return child_waited;
          } else if (stopsig == SIGTRAP) {
            //
            // PTRACE_EVENT stops (2) are observed by the tracer as waitpid(2)
            // returning with WIFSTOPPED(status), and WSTOPSIG(status) returns
            // SIGTRAP.
            //
            const unsigned int event = (unsigned int)status >> 16;
            switch (event) {
            case PTRACE_EVENT_VFORK:
              fprintf(stderr, "parent: ptrace event (PTRACE_EVENT_VFORK)\n");
              break;
            case PTRACE_EVENT_FORK:
              fprintf(stderr, "parent: ptrace event (PTRACE_EVENT_FORK)\n");
              break;
            case PTRACE_EVENT_CLONE: {
              pid_t new_child;
              ptrace(PTRACE_GETEVENTMSG, child_waited, 0, &new_child);
              fprintf(stderr,
                      "parent: ptrace event (PTRACE_EVENT_CLONE) [%d]\n",
                      new_child);
              break;
            }
            case PTRACE_EVENT_VFORK_DONE:
              fprintf(stderr, "parent: ptrace event (PTRACE_EVENT_VFORK_DONE)\n");
              break;
            case PTRACE_EVENT_EXEC:
              fprintf(stderr, "parent: ptrace event (PTRACE_EVENT_EXEC)\n");
              break;
            case PTRACE_EVENT_EXIT:
              fprintf(stderr, "parent: ptrace event (PTRACE_EVENT_EXIT)\n");
              break;
            case PTRACE_EVENT_STOP:
              fprintf(stderr, "parent: ptrace event (PTRACE_EVENT_STOP)\n");
              break;
            case PTRACE_EVENT_SECCOMP:
              fprintf(stderr, "parent: ptrace event (PTRACE_EVENT_SECCOMP)\n");
              break;
            default:
              fprintf(stderr, "parent: unknown ptrace event %u\n", event);
              break;
            }
          } else if (ptrace(PTRACE_GETSIGINFO, child_waited, 0, &si) < 0) {
            //
            // (3) group-stop
            //
            fprintf(stderr, "parent: group-stop [%s]\n",
                    name_of_signal_number(stopsig));

            // When restarting a tracee from a ptrace-stop other than
            // signal-delivery-stop, recommended practice is to always pass 0 in
            // sig.
          } else {
            //
            // (4) signal-delivery-stop
            //
            fprintf(stderr, "parent: signal-delivery-stop [%s]\n",
                    name_of_signal_number(stopsig));

            // deliver it
            sig = stopsig;
          }
        } else {
          //
          // the child process terminated
          //
          fprintf(stderr, "parent: child terminated\n");
        }
      }
    }
  };

  //
  // Main loop of the parent
  //
  pid_t pid = child; /* handle initial signal-delivery-stop */
  for (;;) {
    pid = wait_for_syscall_entry_or_exit(pid);
    if (pid == -1)
      break;

    child_syscall_state_t &st = chld_sysc_map[pid];
    switch (st.st) {
    case SYSCALL_ENTERED: {
      //
      // getting the syscall number
      //
      int no;

#if defined(__i386__)
      no = ptrace(PTRACE_PEEKUSER, pid,
                  __builtin_offsetof(struct user, regs.orig_eax));
#elif defined(__x86_64__)
      no = ptrace(PTRACE_PEEKUSER, pid,
                  __builtin_offsetof(struct user, regs.orig_rax));
#elif defined(__arm__)
      no = ptrace(PTRACE_PEEKUSER, pid,
                  __builtin_offsetof(struct user, regs.uregs[7]));
#else
#error "unknown architecture"
#endif

      st.no = no;
      break;
    }

    case SYSCALL_EXITED: {
      //
      // getting the syscall return value
      //
      int res;

#if defined(__i386__)
      res = ptrace(PTRACE_PEEKUSER, pid,
                   __builtin_offsetof(struct user, regs.eax));
#elif defined(__x86_64__)
      res = ptrace(PTRACE_PEEKUSER, pid,
                   __builtin_offsetof(struct user, regs.rax));
#elif defined(__arm__)
      res = ptrace(PTRACE_PEEKUSER, pid,
                   __builtin_offsetof(struct user, regs.uregs[0]));
#else
#error "unknown architecture"
#endif

      fprintf(stderr, "parent: [%d] SYSCALL [%s] = %d\n", pid,
              name_of_syscall_number(st.no), res);
      break;
    }

    default:
      __builtin_unreachable();
    }

    toggle_syscall_state(st);
  }

  return 0;
}

// Assembly:
pushq %rbp
pushq %r15
pushq %r14
pushq %r13
pushq %r12
pushq %rbx
subq $0xa8, %rsp
movq 0x3db8(%rip), %r13      # 0x5fe0
movq (%r13), %rcx
leaq 0x1399(%rip), %rdi      # 0x35cc
pushq $0x13
popq %rsi
pushq $0x1
popq %rdx
callq 0x21f0
callq 0x2050
testl %eax, %eax
jne 0x22f7
xorl %r12d, %r12d
xorl %edi, %edi
xorl %eax, %eax
callq 0x2100
pushq $0x13
popq %rdi
callq 0x2150
xorps %xmm0, %xmm0
andq $0x0, 0x30(%rsp)
movaps %xmm0, 0x20(%rsp)
leaq 0x558(%rip), %rbx       # 0x27cc
leaq 0x10(%rsp), %r14
leaq 0xc(%rsp), %r15
leaq 0x1(%r12), %r13
movl %r13d, 0xc(%rsp)
movq %r14, %rdi
movq %rbx, %rsi
movq %r15, %rdx
callq 0x28ee
cmpq $0x0, 0x20(%rsp,%r12,8)
jne 0x265b
movq 0x10(%rsp), %rax
movq %rax, 0x20(%rsp,%r12,8)
movq %r13, %r12
cmpq $0x3, %r13
jne 0x227e
xorl %ebx, %ebx
leaq (%rsp,%rbx), %rdi
addq $0x20, %rdi
callq 0x21c0
addq $0x8, %rbx
cmpq $0x18, %rbx
jne 0x22b7
pushq $0x18
popq %rax
cmpq $0x0, 0x18(%rsp,%rax)
jne 0x265b
addq $-0x8, %rax
jne 0x22d1
xorl %eax, %eax
addq $0xa8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movl %eax, %ebx
movq (%r13), %rdi
leaq 0x12dc(%rip), %rsi      # 0x35e0
movl %eax, %edx
xorl %eax, %eax
callq 0x2180
leaq 0x1c(%rsp), %r14
movl %ebx, %edi
movq %r14, %rsi
xorl %edx, %edx
callq 0x20f0
cmpb $0x7f, 0x1c(%rsp)
jne 0x2312
movq (%r13), %rcx
leaq 0x12e1(%rip), %rdi      # 0x3611
pushq $0x1e
popq %rsi
pushq $0x1
popq %r14
movq %r14, %rdx
callq 0x21f0
movq (%r13), %rcx
leaq 0x12e6(%rip), %rdi      # 0x3630
pushq $0x22
popq %rsi
movq %r14, %rdx
callq 0x21f0
movl $0x4200, %edi           # imm = 0x4200
movl %ebx, %esi
xorl %edx, %edx
movl $0x100009, %ecx         # imm = 0x100009
xorl %eax, %eax
callq 0x2100
movq (%r13), %rcx
leaq 0x12de(%rip), %rdi      # 0x3653
pushq $0x14
popq %rsi
movq %r14, %rdx
callq 0x21f0
leaq 0x8(%rsp), %rax
movl %ebx, (%rax)
pushq $-0x1
popq %r15
movl 0x8(%rsp), %ebp
xorl %ebx, %ebx
cmpl $-0x1, %ebp
je 0x23b1
pushq $0x18
popq %rdi
movl %ebp, %esi
xorl %edx, %edx
movq %rbx, %rcx
xorl %eax, %eax
callq 0x2100
cmpq $-0x1, %rax
je 0x2588
movl %r15d, %edi
leaq 0x10(%rsp), %rsi
movl $0x40000000, %edx       # imm = 0x40000000
callq 0x20f0
cmpl $-0x1, %eax
je 0x23f1
movl 0x10(%rsp), %edx
cmpb $0x7f, %dl
jne 0x2422
movl %eax, %ebp
movzbl %dh, %ebx
cmpl $0x85, %ebx
setne %r12b
jne 0x2442
xorl %ebx, %ebx
movl %ebp, %r14d
testb %r12b, %r12b
jne 0x2391
jmp 0x25af
movq (%r13), %rbx
callq 0x2030
movl (%rax), %edi
callq 0x2070
xorl %r12d, %r12d
movq %rbx, %rdi
leaq 0x12a8(%rip), %rsi      # 0x36b6
movq %rax, %rdx
xorl %eax, %eax
callq 0x2180
xorl %ebx, %ebx
movl %r15d, %ebp
movl %r15d, %r14d
jmp 0x23e7
movq (%r13), %rcx
leaq 0x1466(%rip), %rdi      # 0x3893
pushq $0x19
popq %rsi
pushq $0x1
popq %rdx
callq 0x21f0
movb $0x1, %r12b
xorl %ebx, %ebx
movl %r15d, %ebp
jmp 0x23e7
cmpl $0x5, %ebx
jne 0x2476
shrl $0x10, %edx
leal -0x1(%rdx), %eax
cmpl $0x6, %eax
ja 0x24db
leaq 0xba7(%rip), %rcx       # 0x3004
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq (%r13), %rcx
leaq 0x1290(%rip), %rdi      # 0x3701
jmp 0x2550
movl $0x4202, %edi           # imm = 0x4202
movl %ebp, %esi
xorl %edx, %edx
leaq 0x20(%rsp), %rcx
xorl %eax, %eax
callq 0x2100
movq %r13, %rcx
movq %rax, %r13
movq (%rcx), %r12
movl %ebx, %edi
callq 0x27ac
movq %r12, %rdi
testq %r13, %r13
jns 0x24bb
leaq 0x13ad(%rip), %rsi      # 0x3857
movq %rax, %rdx
xorl %eax, %eax
callq 0x2180
movb $0x1, %r12b
xorl %ebx, %ebx
jmp 0x24cf
leaq 0x13ae(%rip), %rsi      # 0x3870
movq %rax, %rdx
xorl %eax, %eax
callq 0x2180
movb $0x1, %r12b
movq 0x3b0a(%rip), %r13      # 0x5fe0
jmp 0x23e7
cmpl $0x80, %edx
jne 0x256c
movq (%r13), %rcx
leaq 0x12ed(%rip), %rdi      # 0x37df
jmp 0x2550
movq (%r13), %rcx
leaq 0x125c(%rip), %rdi      # 0x375b
pushq $0x2f
jmp 0x2552
movl $0x4201, %edi           # imm = 0x4201
movl %ebp, %esi
xorl %edx, %edx
leaq 0xc(%rsp), %rcx
xorl %eax, %eax
callq 0x2100
movq (%r13), %rdi
movl 0xc(%rsp), %edx
leaq 0x1204(%rip), %rsi      # 0x372b
jmp 0x2577
movq (%r13), %rcx
leaq 0x1257(%rip), %rdi      # 0x378b
jmp 0x2550
movq (%r13), %rcx
leaq 0x1195(%rip), %rdi      # 0x36d6
pushq $0x2a
jmp 0x2552
movq (%r13), %rcx
leaq 0x1265(%rip), %rdi      # 0x37b5
pushq $0x29
popq %rsi
pushq $0x1
popq %rdx
callq 0x21f0
jmp 0x257e
movq (%r13), %rcx
leaq 0x12a1(%rip), %rdi      # 0x3809
pushq $0x2c
jmp 0x2552
movq (%r13), %rdi
leaq 0x12bf(%rip), %rsi      # 0x3836
xorl %eax, %eax
callq 0x2180
movb $0x1, %r12b
xorl %ebx, %ebx
jmp 0x23e7
movq (%r13), %rbx
callq 0x2030
movl (%rax), %edi
callq 0x2070
movq %rbx, %rdi
leaq 0x10e6(%rip), %rsi      # 0x3688
movq %rax, %rdx
xorl %eax, %eax
callq 0x2180
movl %r15d, %r14d
movl %r14d, 0x8(%rsp)
cmpl $-0x1, %r14d
je 0x22e3
leaq 0x3b5b(%rip), %rdi      # 0x6120
leaq 0x8(%rsp), %rsi
callq 0x29a0
movq %rax, %rbx
cmpl $0x0, (%rax)
movl 0x8(%rsp), %esi
pushq $0x3
popq %rdi
jne 0x25fa
pushq $0x78
popq %rdx
xorl %eax, %eax
callq 0x2100
movl %eax, 0x4(%rbx)
xorl %eax, %eax
cmpl $0x0, (%rbx)
sete %al
movl %eax, (%rbx)
jmp 0x238b
pushq $0x50
popq %rdx
xorl %eax, %eax
callq 0x2100
movl 0x4(%rbx), %edx
leaq 0x138d(%rip), %rcx      # 0x399b
cmpq $0x14b, %rdx            # imm = 0x14B
ja 0x2625
leaq 0xa02(%rip), %rsi       # 0x3020
movslq (%rsi,%rdx,4), %rcx
addq %rsi, %rcx
movq (%r13), %rdi
movl 0x8(%rsp), %edx
leaq 0x1034(%rip), %rsi      # 0x3668
movl %eax, %r8d
xorl %eax, %eax
callq 0x2180
jmp 0x25eb
jmp 0x2642
pushq $0x18
popq %rcx
cmpq $0x0, 0x18(%rsp,%rcx)
jne 0x265b
addq $-0x8, %rcx
jne 0x2645
movq %rax, %rdi
callq 0x21d0
callq 0x2060