/** * Registers a new thread to the runtime system. This includes * initialization of the hardware performance counters */ void rec_sched_register_thread(pid_t parent, pid_t child, int flags) { struct tasklist_entry* entry = sys_malloc_zero(sizeof(*entry)); struct task* t = &entry->t; assert(child > 0 && child < MAX_TID); t->status = 0; t->rec_tid = t->tid = child; t->child_mem_fd = sys_open_child_mem(child); push_placeholder_event(t); if (parent) { struct task* parent_t = get_task(parent); struct sighandlers* parent_handlers = parent_t->sighandlers; t->syscallbuf_lib_start = parent_t->syscallbuf_lib_start; t->syscallbuf_lib_end = parent_t->syscallbuf_lib_end; t->task_group = (SHARE_TASK_GROUP & flags) ? task_group_add_and_ref(parent_t->task_group, t) : task_group_new_and_add(t); t->sighandlers = (SHARE_SIGHANDLERS & flags) ? sighandlers_ref(parent_handlers) : sighandlers_copy(parent_handlers); } else { /* After the first task is forked, we always need to * know the parent in order to initialize some task * state. */ static int is_first_task = 1; assert(is_first_task); is_first_task = 0; t->task_group = task_group_new_and_add(t); /* The very first task we fork inherits our * sighandlers (which should all be default at this * point, but ...). From there on, new tasks will * transitively inherit from this first task. */ t->sighandlers = sighandlers_new(); sighandlers_init_from_current_process(t->sighandlers); } /* These will be initialized when the syscall buffer is. */ t->desched_fd = t->desched_fd_child = -1; sys_ptrace_setup(child); init_hpc(t); start_hpc(t, rr_flags()->max_rbc); CIRCLEQ_INSERT_TAIL(&head, entry, entries); num_active_threads++; tid_to_entry[child] = entry; }
static void set_sw_breakpoint(struct context *ctx, const struct dbg_request* req) { struct breakpoint* bp = sys_malloc_zero(sizeof(*bp)); byte* orig_data_ptr; assert(sizeof(int_3_insn) == req->mem.len); bp->addr = req->mem.addr; orig_data_ptr = read_child_data(ctx, 1, bp->addr); bp->overwritten_data = *orig_data_ptr; sys_free((void**)&orig_data_ptr); write_child_data_n(ctx->child_tid, sizeof(int_3_insn), bp->addr, &int_3_insn); add_breakpoint(bp); }
struct dbg_context* dbg_await_client_connection(const char* address, short port) { struct dbg_context* dbg; int listen_fd; int reuseaddr; int autoprobe; struct sockaddr_in client_addr; int ret; socklen_t len; int flags; #ifdef REDIRECT_DEBUGLOG out = fopen("/tmp/rr.debug.log", "w"); #endif dbg = sys_malloc_zero(sizeof(*dbg)); dbg->insize = sizeof(dbg->inbuf); dbg->outsize = sizeof(dbg->outbuf); listen_fd = socket(AF_INET, SOCK_STREAM, 0); dbg->addr.sin_family = AF_INET; dbg->addr.sin_addr.s_addr = inet_addr(address); reuseaddr = 1; setsockopt(listen_fd, SOL_SOCKET, SO_REUSEADDR, &reuseaddr, sizeof(reuseaddr)); if (port <= 0) { autoprobe = 1; port = getpid(); } else { autoprobe = 0; } do { dbg->addr.sin_port = htons(port); ret = bind(listen_fd, (struct sockaddr*)&dbg->addr, sizeof(dbg->addr)); if (ret == EADDRINUSE) { continue; } else if (ret != 0) { break; } ret = listen(listen_fd, 1/*backlogged connection*/); if (ret == 0 || ret != EADDRINUSE) { break; } } while (++port, autoprobe); if (ret) { fatal("Couldn't bind to server address"); } log_info("(rr debug server listening on %s:%d)", !strcmp(address, "127.0.0.1") ? "" : address, ntohs(dbg->addr.sin_port)); /* block until debugging client connects to us */ len = sizeof(client_addr); dbg->fd = accept(listen_fd, (struct sockaddr*)&client_addr, &len); if (-1 == (flags = fcntl(dbg->fd, F_GETFD))) { fatal("Can't GETFD flags"); } if (fcntl(dbg->fd, F_SETFD, flags | FD_CLOEXEC)) { fatal("Can't make client socket CLOEXEC"); } if (fcntl(dbg->fd, F_SETFL, O_NONBLOCK)) { fatal("Can't make client socket NONBLOCK"); } return dbg; }