// File: /workspace/github_cpp_makefile_star2_O2/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 0x3c7c(%rip), %r12      # 0x5fe0
movq (%r12), %rcx
leaq 0x125d(%rip), %rdi      # 0x35cc
pushq $0x13
popq %rsi
pushq $0x1
popq %rdx
callq 0x21f0
callq 0x2050
testl %eax, %eax
jne 0x242d
xorl %edi, %edi
xorl %eax, %eax
callq 0x2100
pushq $0x13
popq %rdi
callq 0x2150
xorps %xmm0, %xmm0
leaq 0x20(%rsp), %rbx
movaps %xmm0, (%rbx)
andq $0x0, 0x10(%rbx)
pushq $-0x3
popq %r13
leaq 0x3c3(%rip), %r14       # 0x2776
leaq 0x18(%rsp), %r15
leaq 0x10(%rsp), %r12
testq %r13, %r13
je 0x23f4
leal 0x4(%r13), %eax
movl %eax, 0x10(%rsp)
movq %r15, %rdi
movq %r14, %rsi
movq %r12, %rdx
callq 0x28b0
movq %rbx, %rdi
movq %r15, %rsi
callq 0x2928
movq %r15, %rdi
callq 0x2942
addq $0x8, %rbx
incq %r13
jmp 0x23bd
xorl %ebx, %ebx
cmpq $0x18, %rbx
je 0x240f
leaq (%rsp,%rbx), %rdi
addq $0x20, %rdi
callq 0x21c0
addq $0x8, %rbx
jmp 0x23f6
leaq 0x20(%rsp), %rdi
callq 0x2950
xorl %eax, %eax
addq $0xa8, %rsp
popq %rbx
popq %r12
popq %r13
popq %r14
popq %r15
popq %rbp
retq
movl %eax, %ebp
movq (%r12), %rdi
leaq 0x11a6(%rip), %rsi      # 0x35e0
movl %eax, %edx
xorl %eax, %eax
callq 0x2180
leaq 0x14(%rsp), %rbx
movl %ebp, %edi
movq %rbx, %rsi
xorl %edx, %edx
callq 0x20f0
cmpb $0x7f, 0x14(%rsp)
jne 0x2448
movq (%r12), %rcx
leaq 0x11ab(%rip), %rdi      # 0x3611
pushq $0x1e
popq %rsi
pushq $0x1
popq %rbx
movq %rbx, %rdx
callq 0x21f0
movq (%r12), %rcx
leaq 0x11b1(%rip), %rdi      # 0x3630
pushq $0x22
popq %rsi
movq %rbx, %rdx
callq 0x21f0
movl $0x4200, %edi           # imm = 0x4200
movl %ebp, %esi
xorl %edx, %edx
movl $0x100009, %ecx         # imm = 0x100009
xorl %eax, %eax
callq 0x2100
movq (%r12), %rcx
leaq 0x11a9(%rip), %rdi      # 0x3653
pushq $0x14
popq %rsi
movq %rbx, %rdx
callq 0x21f0
leaq 0xc(%rsp), %rax
movl %ebp, (%rax)
pushq $-0x1
popq %r14
leaq 0x18(%rsp), %r15
xorl %ebx, %ebx
cmpl $-0x1, %ebp
je 0x24e7
pushq $0x18
popq %rdi
movl %ebp, %esi
xorl %edx, %edx
movq %rbx, %rcx
xorl %eax, %eax
callq 0x2100
cmpq $-0x1, %rax
je 0x2736
movl %r14d, %edi
movq %r15, %rsi
movl $0x40000000, %edx       # imm = 0x40000000
callq 0x20f0
cmpl $-0x1, %eax
je 0x271d
movl 0x18(%rsp), %edx
cmpb $0x7f, %dl
jne 0x2583
movl %eax, %ebp
movl %edx, %r13d
shrl $0x8, %r13d
cmpb $0x5, %r13b
je 0x25a3
movzbl %r13b, %ebx
cmpl $0x85, %ebx
je 0x25ce
movl $0x4202, %edi           # imm = 0x4202
movl %ebp, %esi
xorl %edx, %edx
leaq 0x20(%rsp), %rcx
xorl %eax, %eax
callq 0x2100
decb %r13b
leaq 0x1448(%rip), %rdx      # 0x3993
cmpb $0x1e, %r13b
ja 0x2563
movzbl %r13b, %ecx
leaq 0xac4(%rip), %rsi       # 0x3020
movslq (%rsi,%rcx,4), %rdx
addq %rsi, %rdx
movq (%r12), %rdi
testq %rax, %rax
js 0x2616
leaq 0x12f9(%rip), %rsi      # 0x3870
xorl %eax, %eax
callq 0x2180
jmp 0x24c7
movq (%r12), %rcx
leaq 0x1305(%rip), %rdi      # 0x3893
pushq $0x19
popq %rsi
pushq $0x1
popq %rdx
callq 0x21f0
xorl %ebx, %ebx
movl %r14d, %ebp
jmp 0x24c7
shrl $0x10, %edx
leal -0x1(%rdx), %eax
cmpl $0x6, %eax
ja 0x2629
leaq 0xa4f(%rip), %rcx       # 0x3004
movslq (%rcx,%rax,4), %rax
addq %rcx, %rax
jmpq *%rax
movq (%r12), %rcx
leaq 0x1138(%rip), %rdi      # 0x3701
jmp 0x269e
movl %ebp, 0xc(%rsp)
leaq 0x3b47(%rip), %rdi      # 0x6120
leaq 0xc(%rsp), %rsi
callq 0x29aa
movq %rax, %rbx
cmpl $0x0, (%rax)
movl 0xc(%rsp), %esi
pushq $0x3
popq %rdi
jne 0x26d4
pushq $0x78
popq %rdx
xorl %eax, %eax
callq 0x2100
movl %eax, 0x4(%rbx)
xorl %eax, %eax
cmpl $0x0, (%rbx)
sete %al
movl %eax, (%rbx)
movl 0xc(%rsp), %ebp
jmp 0x24c5
leaq 0x123a(%rip), %rsi      # 0x3857
xorl %eax, %eax
callq 0x2180
jmp 0x24c5
cmpl $0x80, %edx
jne 0x26bd
movq (%r12), %rcx
leaq 0x119f(%rip), %rdi      # 0x37df
jmp 0x269e
movq (%r12), %rcx
leaq 0x110e(%rip), %rdi      # 0x375b
pushq $0x2f
jmp 0x26a0
movl $0x4201, %edi           # imm = 0x4201
movl %ebp, %esi
xorl %edx, %edx
leaq 0x10(%rsp), %rcx
xorl %eax, %eax
callq 0x2100
movq (%r12), %rdi
movl 0x10(%rsp), %edx
leaq 0x10b6(%rip), %rsi      # 0x372b
jmp 0x26c8
movq (%r12), %rcx
leaq 0x1109(%rip), %rdi      # 0x378b
jmp 0x269e
movq (%r12), %rcx
leaq 0x1047(%rip), %rdi      # 0x36d6
pushq $0x2a
jmp 0x26a0
movq (%r12), %rcx
leaq 0x1117(%rip), %rdi      # 0x37b5
pushq $0x29
popq %rsi
pushq $0x1
popq %rdx
callq 0x21f0
jmp 0x24c5
movq (%r12), %rcx
leaq 0x1150(%rip), %rdi      # 0x3809
pushq $0x2c
jmp 0x26a0
movq (%r12), %rdi
leaq 0x116e(%rip), %rsi      # 0x3836
xorl %eax, %eax
callq 0x2180
jmp 0x24c5
pushq $0x50
popq %rdx
xorl %eax, %eax
callq 0x2100
movl 0x4(%rbx), %edx
leaq 0x12ab(%rip), %rcx      # 0x3993
cmpq $0x14b, %rdx            # imm = 0x14B
ja 0x26ff
leaq 0x9a4(%rip), %rsi       # 0x309c
movslq (%rsi,%rdx,4), %rcx
addq %rsi, %rcx
movq (%r12), %rdi
movl 0xc(%rsp), %edx
leaq 0xf5a(%rip), %rsi       # 0x3668
movl %eax, %r8d
xorl %eax, %eax
callq 0x2180
jmp 0x2603
movq (%r12), %rbx
callq 0x2030
movl (%rax), %edi
callq 0x2070
leaq 0xf82(%rip), %rsi       # 0x36b6
jmp 0x274d
movq (%r12), %rbx
callq 0x2030
movl (%rax), %edi
callq 0x2070
leaq 0xf3b(%rip), %rsi       # 0x3688
movq %rbx, %rdi
movq %rax, %rdx
xorl %eax, %eax
callq 0x2180
jmp 0x2419
jmp 0x2761
movq %rax, %rbx
leaq 0x20(%rsp), %rdi
callq 0x2950
movq %rbx, %rdi
callq 0x21d0