static void write_context(OutputStream * out, int pid) { Context * ctx = context_find_from_pid(pid); ChildProcess * prs = find_process(pid); write_stream(out, '{'); json_write_string(out, "Name"); write_stream(out, ':'); json_write_string(out, prs ? prs->name : pid2id(pid, 0)); write_stream(out, ','); json_write_string(out, "CanTerminate"); write_stream(out, ':'); json_write_boolean(out, 1); write_stream(out, ','); if (ctx != NULL) { json_write_string(out, "Attached"); write_stream(out, ':'); json_write_boolean(out, 1); write_stream(out, ','); } if (prs != NULL) { if (*prs->inp_id) { json_write_string(out, "StdInID"); write_stream(out, ':'); json_write_string(out, prs->inp_id); write_stream(out, ','); } if (*prs->out_id) { json_write_string(out, "StdOutID"); write_stream(out, ':'); json_write_string(out, prs->out_id); write_stream(out, ','); } if (*prs->err_id) { json_write_string(out, "StdErrID"); write_stream(out, ':'); json_write_string(out, prs->err_id); write_stream(out, ','); } } json_write_string(out, "ID"); write_stream(out, ':'); json_write_string(out, pid2id(pid, 0)); write_stream(out, '}'); }
int context_attach(pid_t pid, ContextAttachCallBack * done, void * data, int mode) { Context * ctx = NULL; assert(done != NULL); trace(LOG_CONTEXT, "context: attaching pid %d", pid); if ((mode & CONTEXT_ATTACH_SELF) == 0 && ptrace(PT_ATTACH, pid, 0, 0) < 0) { int err = errno; trace(LOG_ALWAYS, "error: ptrace(PT_ATTACH) failed: pid %d, error %d %s", pid, err, errno_to_str(err)); errno = err; return -1; } add_waitpid_process(pid); ctx = create_context(pid2id(pid, 0)); ctx->mem = ctx; ctx->mem_access |= MEM_ACCESS_INSTRUCTION; ctx->mem_access |= MEM_ACCESS_DATA; ctx->mem_access |= MEM_ACCESS_USER; ctx->big_endian = big_endian_host(); EXT(ctx)->pid = pid; EXT(ctx)->attach_callback = done; EXT(ctx)->attach_data = data; list_add_first(&ctx->ctxl, &pending_list); /* TODO: context_attach works only for main task in a process */ return 0; }
static void event_attach_done(void * x) { AttachDoneArgs * args = (AttachDoneArgs *)x; if (context_find_from_pid(args->pid, 0) != NULL) { args->done(ERR_ALREADY_ATTACHED, NULL, args->data); } else { Context * ctx = NULL; if (parent_ctx == NULL) { pid_t pid = taskIdSelf(); parent_ctx = create_context(pid2id(pid, 0)); EXT(parent_ctx)->pid = pid; parent_ctx->mem = parent_ctx; parent_ctx->mem_access |= MEM_ACCESS_INSTRUCTION; parent_ctx->mem_access |= MEM_ACCESS_DATA; parent_ctx->big_endian = big_endian_host(); link_context(parent_ctx); send_context_created_event(parent_ctx); } assert(parent_ctx->ref_count > 0); ctx = create_context(pid2id(args->pid, EXT(parent_ctx)->pid)); EXT(ctx)->pid = args->pid; EXT(ctx)->regs = (REG_SET *)loc_alloc(sizeof(REG_SET)); ctx->mem = parent_ctx; ctx->big_endian = parent_ctx->big_endian; (ctx->parent = parent_ctx)->ref_count++; list_add_last(&ctx->cldl, &parent_ctx->children); link_context(ctx); trace(LOG_CONTEXT, "context: attached: ctx %#lx, id %#x", ctx, EXT(ctx)->pid); send_context_created_event(ctx); args->done(0, ctx, args->data); if (taskIsStopped(args->pid)) { struct event_info * info; ctx->pending_intercept = 1; info = event_info_alloc(EVENT_HOOK_STOP); if (info != NULL) { info->stopped_ctx.ctxId = args->pid; event_info_post(info); } } } loc_free(x); }
static void write_process_input(ChildProcess * prs) { ProcessInput * inp = prs->inp_struct = loc_alloc_zero(sizeof(ProcessInput)); inp->prs = prs; inp->req.client_data = inp; inp->req.done = write_process_input_done; inp->req.type = AsyncReqWrite; inp->req.u.fio.fd = prs->inp; virtual_stream_create(PROCESSES, pid2id(prs->pid, 0), 0x1000, VS_ENABLE_REMOTE_WRITE, process_input_streams_callback, inp, &inp->vstream); virtual_stream_get_id(inp->vstream, prs->inp_id, sizeof(prs->inp_id)); }
static void send_event_process_exited(OutputStream * out, ChildProcess * prs) { write_stringz(out, "E"); write_stringz(out, PROCESSES); write_stringz(out, "exited"); json_write_string(out, pid2id(prs->pid, 0)); write_stream(out, 0); json_write_long(out, prs->exit_code); write_stream(out, 0); write_stream(out, MARKER_EOM); }
static ProcessOutput * read_process_output(ChildProcess * prs, int fd, char * id, size_t id_size) { ProcessOutput * out = loc_alloc_zero(sizeof(ProcessOutput)); out->prs = prs; out->req.client_data = out; out->req.done = read_process_output_done; out->req.type = AsyncReqRead; out->req.u.fio.bufp = out->buf; out->req.u.fio.bufsz = sizeof(out->buf); out->req.u.fio.fd = fd; virtual_stream_create(PROCESSES, pid2id(prs->pid, 0), 0x1000, VS_ENABLE_REMOTE_READ, process_output_streams_callback, out, &out->vstream); virtual_stream_get_id(out->vstream, id, id_size); out->req_posted = 1; async_req_post(&out->req); return out; }
static void write_context(OutputStream * out, int tid) { Terminal * term = find_terminal(tid); const char * id = NULL; write_stream(out, '{'); if (term != NULL) { unsigned ws_col = 0; unsigned ws_row = 0; get_process_tty_win_size(term->prs, &ws_col, &ws_row); json_write_string(out, "ProcessID"); write_stream(out, ':'); json_write_string(out, pid2id(get_process_pid(term->prs), 0)); write_stream(out, ','); if (*term->pty_type) { json_write_string(out, "PtyType"); write_stream(out, ':'); json_write_string(out, term->pty_type); write_stream(out, ','); } if (*term->encoding) { json_write_string(out, "Encoding"); write_stream(out, ':'); json_write_string(out, term->encoding); write_stream(out, ','); } json_write_string(out, "Width"); write_stream(out, ':'); json_write_ulong(out, ws_col); write_stream(out, ','); json_write_string(out, "Height"); write_stream(out, ':'); json_write_ulong(out, ws_row); write_stream(out, ','); id = get_process_stream_id(term->prs, 0); if (id) { json_write_string(out, "StdInID"); write_stream(out, ':'); json_write_string(out, id); write_stream(out, ','); } id = get_process_stream_id(term->prs, 1); if (id) { json_write_string(out, "StdOutID"); write_stream(out, ':'); json_write_string(out, id); write_stream(out, ','); } id = get_process_stream_id(term->prs, 2); if (id) { json_write_string(out, "StdErrID"); write_stream(out, ':'); json_write_string(out, id); write_stream(out, ','); } } json_write_string(out, "ID"); write_stream(out, ':'); json_write_string(out, tid2id(tid)); write_stream(out, '}'); }
static void event_handler(void * arg) { struct event_info * info = (struct event_info *)arg; Context * current_ctx = context_find_from_pid(info->current_ctx.ctxId, 1); Context * stopped_ctx = context_find_from_pid(info->stopped_ctx.ctxId, 1); switch (info->event) { case EVENT_HOOK_BREAKPOINT: if (stopped_ctx == NULL) break; assert(!stopped_ctx->stopped); assert(!EXT(stopped_ctx)->regs_dirty); if (EXT(stopped_ctx)->regs_error) { release_error_report(EXT(stopped_ctx)->regs_error); EXT(stopped_ctx)->regs_error = NULL; } memcpy(EXT(stopped_ctx)->regs, &info->regs, sizeof(REG_SET)); EXT(stopped_ctx)->event = 0; stopped_ctx->signal = SIGTRAP; stopped_ctx->stopped = 1; stopped_ctx->stopped_by_bp = info->bp_info_ok; stopped_ctx->stopped_by_exception = 0; assert(get_regs_PC(stopped_ctx) == info->addr); if (stopped_ctx->stopped_by_bp && !is_breakpoint_address(stopped_ctx, info->addr)) { /* Break instruction that is not planted by us */ stopped_ctx->stopped_by_bp = 0; stopped_ctx->pending_intercept = 1; } EXT(stopped_ctx)->bp_info = info->bp_info; if (current_ctx != NULL) EXT(stopped_ctx)->bp_pid = EXT(current_ctx)->pid; assert(taskIsStopped(EXT(stopped_ctx)->pid)); trace(LOG_CONTEXT, "context: stopped by breakpoint: ctx %#lx, id %#x", stopped_ctx, EXT(stopped_ctx)->pid); send_context_stopped_event(stopped_ctx); break; case EVENT_HOOK_STEP_DONE: if (current_ctx == NULL) break; assert(!current_ctx->stopped); assert(!EXT(current_ctx)->regs_dirty); if (EXT(current_ctx)->regs_error) { release_error_report(EXT(current_ctx)->regs_error); EXT(current_ctx)->regs_error = NULL; } memcpy(EXT(current_ctx)->regs, &info->regs, sizeof(REG_SET)); EXT(current_ctx)->event = TRACE_EVENT_STEP; current_ctx->signal = SIGTRAP; current_ctx->stopped = 1; current_ctx->stopped_by_bp = 0; current_ctx->stopped_by_exception = 0; assert(taskIsStopped(EXT(current_ctx)->pid)); trace(LOG_CONTEXT, "context: stopped by end of step: ctx %#lx, id %#x", current_ctx, EXT(current_ctx)->pid); send_context_stopped_event(current_ctx); break; case EVENT_HOOK_STOP: if (stopped_ctx == NULL) break; assert(!stopped_ctx->exited); if (stopped_ctx->stopped) break; if (EXT(stopped_ctx)->regs_error) { release_error_report(EXT(stopped_ctx)->regs_error); EXT(stopped_ctx)->regs_error = NULL; } if (taskRegsGet(EXT(stopped_ctx)->pid, EXT(stopped_ctx)->regs) != OK) { EXT(stopped_ctx)->regs_error = get_error_report(errno); assert(EXT(stopped_ctx)->regs_error != NULL); } EXT(stopped_ctx)->event = 0; stopped_ctx->signal = SIGSTOP; stopped_ctx->stopped = 1; stopped_ctx->stopped_by_bp = 0; stopped_ctx->stopped_by_exception = 0; assert(taskIsStopped(EXT(stopped_ctx)->pid)); trace(LOG_CONTEXT, "context: stopped by sofware request: ctx %#lx, id %#x", stopped_ctx, EXT(stopped_ctx)->pid); send_context_stopped_event(stopped_ctx); break; case EVENT_HOOK_TASK_ADD: if (current_ctx == NULL) break; assert(stopped_ctx == NULL); stopped_ctx = create_context(pid2id((pid_t)info->stopped_ctx.ctxId, EXT(current_ctx->parent)->pid)); EXT(stopped_ctx)->pid = (pid_t)info->stopped_ctx.ctxId; EXT(stopped_ctx)->regs = (REG_SET *)loc_alloc(sizeof(REG_SET)); stopped_ctx->mem = current_ctx->mem; stopped_ctx->big_endian = current_ctx->mem->big_endian; (stopped_ctx->creator = current_ctx)->ref_count++; (stopped_ctx->parent = current_ctx->parent)->ref_count++; assert(stopped_ctx->mem == stopped_ctx->parent->mem); list_add_last(&stopped_ctx->cldl, &stopped_ctx->parent->children); link_context(stopped_ctx); trace(LOG_CONTEXT, "context: created: ctx %#lx, id %#x", stopped_ctx, EXT(stopped_ctx)->pid); send_context_created_event(stopped_ctx); break; default: assert(0); break; } loc_free(info); SPIN_LOCK_ISR_TAKE(&events_lock); events_cnt--; SPIN_LOCK_ISR_GIVE(&events_lock); }
static void event_pid_stopped(pid_t pid, int signal, int event, int syscall) { int stopped_by_exception = 0; unsigned long msg = 0; Context * ctx = NULL; Context * ctx2 = NULL; trace(LOG_EVENTS, "event: pid %d stopped, signal %d", pid, signal); ctx = context_find_from_pid(pid, 1); if (ctx == NULL) { ctx = find_pending(pid); if (ctx != NULL) { Context * prs = ctx; assert(prs->ref_count == 0); ctx = create_context(pid2id(pid, pid)); EXT(ctx)->pid = pid; EXT(ctx)->regs = (REG_SET *)loc_alloc(sizeof(REG_SET)); ctx->pending_intercept = 1; ctx->mem = prs; ctx->parent = prs; ctx->big_endian = prs->big_endian; prs->ref_count++; list_add_last(&ctx->cldl, &prs->children); link_context(prs); link_context(ctx); send_context_created_event(prs); send_context_created_event(ctx); if (EXT(prs)->attach_callback) { EXT(prs)->attach_callback(0, prs, EXT(prs)->attach_data); EXT(prs)->attach_callback = NULL; EXT(prs)->attach_data = NULL; } } } if (ctx == NULL) return; assert(!ctx->exited); assert(!EXT(ctx)->attach_callback); if (signal != SIGSTOP && signal != SIGTRAP) { sigset_set(&ctx->pending_signals, signal, 1); if (sigset_get(&ctx->sig_dont_stop, signal) == 0) { ctx->pending_intercept = 1; stopped_by_exception = 1; } } if (ctx->stopped) { send_context_changed_event(ctx); } else { thread_state_t state; unsigned int state_count; ContextAddress pc0 = 0; ContextAddress pc1 = 0; assert(!EXT(ctx)->regs_dirty); EXT(ctx)->end_of_step = 0; EXT(ctx)->ptrace_event = event; ctx->signal = signal; ctx->stopped_by_bp = 0; ctx->stopped_by_exception = stopped_by_exception; ctx->stopped = 1; if (EXT(ctx)->regs_error) { release_error_report(EXT(ctx)->regs_error); EXT(ctx)->regs_error = NULL; } else { pc0 = get_regs_PC(ctx); } if (thread_get_state(EXT(ctx)->pid, x86_THREAD_STATE32, EXT(ctx)->regs, &state_count) != KERN_SUCCESS) { assert(errno != 0); EXT(ctx)->regs_error = get_error_report(errno); trace(LOG_ALWAYS, "error: thread_get_state failed; id %s, error %d %s", ctx->id, errno, errno_to_str(errno)); } else { pc1 = get_regs_PC(ctx); } if (!EXT(ctx)->syscall_enter || EXT(ctx)->regs_error || pc0 != pc1) { EXT(ctx)->syscall_enter = 0; EXT(ctx)->syscall_exit = 0; EXT(ctx)->syscall_id = 0; EXT(ctx)->syscall_pc = 0; } trace(LOG_EVENTS, "event: pid %d stopped at PC = %#lx", pid, pc1); if (signal == SIGTRAP && event == 0 && !syscall) { size_t break_size = 0; get_break_instruction(ctx, &break_size); ctx->stopped_by_bp = !EXT(ctx)->regs_error && is_breakpoint_address(ctx, pc1 - break_size); EXT(ctx)->end_of_step = !ctx->stopped_by_bp && EXT(ctx)->pending_step; if (ctx->stopped_by_bp) set_regs_PC(ctx, pc1 - break_size); } EXT(ctx)->pending_step = 0; send_context_stopped_event(ctx); } }
static void write_context(OutputStream * out, Context * ctx, int is_thread) { assert(!ctx->exited); write_stream(out, '{'); json_write_string(out, "ID"); write_stream(out, ':'); json_write_string(out, is_thread ? thread_id(ctx) : container_id(ctx)); if (is_thread) { write_stream(out, ','); json_write_string(out, "ParentID"); write_stream(out, ':'); json_write_string(out, container_id(ctx)); } #if !defined(_WRS_KERNEL) write_stream(out, ','); json_write_string(out, "ProcessID"); write_stream(out, ':'); json_write_string(out, pid2id(ctx->mem, 0)); #endif #if !defined(WIN32) && !defined(_WRS_KERNEL) if (!ctx->exiting && !is_thread) { write_stream(out, ','); json_write_string(out, "File"); write_stream(out, ':'); json_write_string(out, get_executable(ctx->pid)); } #endif if (is_thread) { write_stream(out, ','); json_write_string(out, "CanSuspend"); write_stream(out, ':'); json_write_boolean(out, 1); write_stream(out, ','); json_write_string(out, "CanResume"); write_stream(out, ':'); json_write_long(out, (1 << RM_RESUME) | (1 << RM_STEP_INTO)); write_stream(out, ','); json_write_string(out, "HasState"); write_stream(out, ':'); json_write_boolean(out, 1); } #ifdef WIN32 if (!is_thread) #endif { write_stream(out, ','); json_write_string(out, "CanTerminate"); write_stream(out, ':'); json_write_boolean(out, 1); } write_stream(out, '}'); }
static void command_get_children(char * token, Channel * c) { char id[256]; int attached_only; json_read_string(&c->inp, id, sizeof(id)); if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); attached_only = json_read_boolean(&c->inp); if (read_stream(&c->inp) != 0) exception(ERR_JSON_SYNTAX); if (read_stream(&c->inp) != MARKER_EOM) exception(ERR_JSON_SYNTAX); write_stringz(&c->out, "R"); write_stringz(&c->out, token); if (id[0] != 0) { write_errno(&c->out, 0); write_stringz(&c->out, "null"); } else { #if defined(WIN32) DWORD err = 0; HANDLE snapshot; PROCESSENTRY32 pe32; snapshot = CreateToolhelp32Snapshot(TH32CS_SNAPPROCESS, 0); if (snapshot == INVALID_HANDLE_VALUE) err = set_win32_errno(GetLastError()); memset(&pe32, 0, sizeof(pe32)); pe32.dwSize = sizeof(PROCESSENTRY32); if (!err && !Process32First(snapshot, &pe32)) { err = set_win32_errno(GetLastError()); CloseHandle(snapshot); } write_errno(&c->out, err); if (err) { write_stringz(&c->out, "null"); } else { int cnt = 0; write_stream(&c->out, '['); do { if (!attached_only || context_find_from_pid(pe32.th32ProcessID) != NULL) { if (cnt > 0) write_stream(&c->out, ','); json_write_string(&c->out, pid2id(pe32.th32ProcessID, 0)); cnt++; } } while (Process32Next(snapshot, &pe32)); write_stream(&c->out, ']'); write_stream(&c->out, 0); } if (snapshot != INVALID_HANDLE_VALUE) CloseHandle(snapshot); #elif defined(_WRS_KERNEL) int i = 0; int cnt = 0; int ids_cnt = 0; int ids_max = 500; int * ids = (int *)loc_alloc(ids_max * sizeof(int)); for (;;) { ids_cnt = taskIdListGet(ids, ids_max); if (ids_cnt < ids_max) break; loc_free(ids); ids_max *= 2; ids = (int *)loc_alloc(ids_max * sizeof(int)); } write_errno(&c->out, 0); write_stream(&c->out, '['); for (i = 0; i < ids_cnt; i++) { if (!attached_only || context_find_from_pid(ids[i]) != NULL) { if (cnt > 0) write_stream(&c->out, ','); json_write_string(&c->out, pid2id(ids[i], 0)); cnt++; } } write_stream(&c->out, ']'); write_stream(&c->out, 0); #elif defined(__APPLE__) #else DIR * proc = opendir("/proc"); if (proc == NULL) { write_errno(&c->out, errno); write_stringz(&c->out, "null"); } else { int cnt = 0; write_errno(&c->out, 0); write_stream(&c->out, '['); for (;;) { struct dirent * ent = readdir(proc); if (ent == NULL) break; if (ent->d_name[0] >= '1' && ent->d_name[0] <= '9') { pid_t pid = atol(ent->d_name); if (!attached_only || context_find_from_pid(pid) != NULL) { if (cnt > 0) write_stream(&c->out, ','); json_write_string(&c->out, pid2id(pid, 0)); cnt++; } } } write_stream(&c->out, ']'); write_stream(&c->out, 0); closedir(proc); } #endif } write_stream(&c->out, MARKER_EOM); }
static void event_pid_stopped(pid_t pid, int signal, int event, int syscall) { int stopped_by_exception = 0; Context * ctx = NULL; trace(LOG_EVENTS, "event: pid %d stopped, signal %d, event %s", pid, signal, event_name(event)); ctx = context_find_from_pid(pid, 1); if (ctx == NULL) { ctx = find_pending(pid); if (ctx != NULL) { Context * prs = ctx; assert(prs->ref_count == 0); ctx = create_context(pid2id(pid, pid)); EXT(ctx)->pid = pid; EXT(ctx)->regs = (REG_SET *)loc_alloc(sizeof(REG_SET)); ctx->pending_intercept = 1; ctx->mem = prs; ctx->parent = prs; ctx->big_endian = prs->big_endian; prs->ref_count++; list_add_last(&ctx->cldl, &prs->children); link_context(prs); link_context(ctx); send_context_created_event(prs); send_context_created_event(ctx); if (EXT(prs)->attach_callback) { EXT(prs)->attach_callback(0, prs, EXT(prs)->attach_data); EXT(prs)->attach_callback = NULL; EXT(prs)->attach_data = NULL; } } } if (ctx == NULL) return; assert(!ctx->exited); assert(!EXT(ctx)->attach_callback); if (signal != SIGSTOP && signal != SIGTRAP) { assert(signal < 32); ctx->pending_signals |= 1 << signal; if ((ctx->sig_dont_stop & (1 << signal)) == 0) { ctx->pending_intercept = 1; stopped_by_exception = 1; } } if (ctx->stopped) { send_context_changed_event(ctx); } else { ContextAddress pc0 = 0; ContextAddress pc1 = 0; assert(!EXT(ctx)->regs_dirty); EXT(ctx)->end_of_step = 0; EXT(ctx)->ptrace_event = event; ctx->signal = signal; ctx->stopped_by_bp = 0; ctx->stopped_by_exception = stopped_by_exception; ctx->stopped = 1; if (EXT(ctx)->regs_error) { release_error_report(EXT(ctx)->regs_error); EXT(ctx)->regs_error = NULL; } else { pc0 = get_regs_PC(ctx); } if (ptrace(PTRACE_GETREGS, EXT(ctx)->pid, 0, (int)EXT(ctx)->regs) < 0) { assert(errno != 0); if (errno == ESRCH) { /* Racing condition: somebody resumed this context while we are handling stop event. * * One possible cause: main thread has exited forcing children to exit too. * I beleive it is a bug in PTRACE implementation - PTRACE should delay exiting of * a context while it is stopped, but it does not, which causes a nasty racing. * * Workaround: Ignore current event, assume context is running. */ ctx->stopped = 0; return; } EXT(ctx)->regs_error = get_error_report(errno); trace(LOG_ALWAYS, "error: ptrace(PTRACE_GETREGS) failed; id %s, error %d %s", ctx->id, errno, errno_to_str(errno)); } else { pc1 = get_regs_PC(ctx); } trace(LOG_EVENTS, "event: pid %d stopped at PC = %#lx", pid, pc1); if (signal == SIGTRAP && event == 0 && !syscall) { size_t break_size = 0; get_break_instruction(ctx, &break_size); ctx->stopped_by_bp = !EXT(ctx)->regs_error && is_breakpoint_address(ctx, pc1 - break_size); EXT(ctx)->end_of_step = !ctx->stopped_by_bp && EXT(ctx)->pending_step; if (ctx->stopped_by_bp) set_regs_PC(ctx, pc1 - break_size); } EXT(ctx)->pending_step = 0; send_context_stopped_event(ctx); } }