static int kplib_table_new(ktap_state *ks) { ktap_tab *h; int narr = 0, nrec = 0; if (kp_arg_nr(ks) >= 1) { kp_arg_check(ks, 1, KTAP_TYPE_NUMBER); narr = nvalue(kp_arg(ks, 1)); } if (kp_arg_nr(ks) >= 2) { kp_arg_check(ks, 2, KTAP_TYPE_NUMBER); nrec = nvalue(kp_arg(ks, 2)); } h = kp_tab_new(ks, narr, nrec); if (!h) { set_nil(ks->top); } else { set_table(ks->top, h); } incr_top(ks); return 1; }
static int kplib_ffi_new(ktap_state *ks) { int n = kp_arg_nr(ks), array_size; csymbol_id cs_id; ktap_cdata *cd; if (unlikely(n != 2)) { /* this is not likely to happen since ffi.new arguments are * generated by compiler */ set_nil(ks->top++); kp_error(ks, "wrong number of arguments\n"); return 1; } kp_arg_check(ks, 1, KTAP_TYPE_NUMBER); kp_arg_check(ks, 2, KTAP_TYPE_NUMBER); cs_id = nvalue(kp_arg(ks, 1)); array_size = nvalue(kp_arg(ks, 2)); if (unlikely(cs_id > max_csym_id(ks))) kp_error(ks, "invalid csymbol id\n"); kp_verbose_printf(ks, "ffi.new symbol %s with length %d\n", id_to_csym(ks, cs_id)->name, array_size); cd = kp_cdata_new_ptr(ks, NULL, array_size, cs_id, 1); set_cdata(ks->top, cd); incr_top(ks); return 1; }
static int kplib_sort_pairs(ktap_state *ks) { ktap_value *v = kp_arg(ks, 1); ktap_closure *cmp_func = NULL; ktap_tab *t; if (is_table(v)) { t = hvalue(v); } else if (is_ptable(v)) { t = kp_ptab_synthesis(ks, phvalue(v)); } else if (is_nil(v)) { kp_error(ks, "table is nil in pairs\n"); return 0; } else { kp_error(ks, "wrong argument for pairs\n"); return 0; } if (kp_arg_nr(ks) > 1) { kp_arg_check(ks, 2, KTAP_TYPE_FUNCTION); cmp_func = clvalue(kp_arg(ks, 2)); } kp_tab_sort(ks, t, cmp_func); set_cfunction(ks->top++, table_sort_iter_next); set_table(ks->top++, t); set_nil(ks->top++); return 3; }
static int kplib_backtrace(ktap_state *ks) { struct stack_trace trace; int skip = 10, max_entries = 10; int n = kp_arg_nr(ks); ktap_btrace *bt; if (n >= 1) { kp_arg_check(ks, 1, KTAP_TYPE_NUMBER); skip = nvalue(kp_arg(ks, 1)); } if (n >= 2) { kp_arg_check(ks, 2, KTAP_TYPE_NUMBER); max_entries = nvalue(kp_arg(ks, 2)); max_entries = min(max_entries, KTAP_MAX_STACK_ENTRIES); } bt = kp_percpu_data(ks, KTAP_PERCPU_DATA_BTRACE); trace.nr_entries = 0; trace.skip = skip; trace.max_entries = max_entries; trace.entries = (unsigned long *)(bt + 1); save_stack_trace(&trace); bt->nr_entries = trace.nr_entries; set_btrace(ks->top, bt); incr_top(ks); return 1; }
static int kplib_ffi_cast(ktap_state *ks) { int n = kp_arg_nr(ks); unsigned long addr; csymbol_id cs_id; ktap_cdata *cd; if (unlikely(n != 2)) { /* this is not likely to happen since ffi.cast arguments are * generated by compiler */ set_nil(ks->top++); kp_error(ks, "wrong number of arguments\n"); return 1; } kp_arg_check(ks, 1, KTAP_TYPE_NUMBER); kp_arg_check(ks, 2, KTAP_TYPE_NUMBER); cs_id = nvalue(kp_arg(ks, 1)); addr = nvalue(kp_arg(ks, 2)); if (unlikely(cs_id > max_csym_id(ks))) kp_error(ks, "invalid csymbol id\n"); cd = kp_cdata_new_record(ks, (void *)addr, cs_id); set_cdata(ks->top, cd); incr_top(ks); return 1; }
static int kplib_ffi_new(ktap_state_t *ks) { int n = kp_arg_nr(ks); csymbol_id cs_id = kp_arg_checknumber(ks, 1); int array_size = kp_arg_checknumber(ks, 2); int is_array = kp_arg_checknumber(ks, 3); ktap_cdata_t *cd; if (unlikely(n != 3)) { /* this is not likely to happen since ffi.new arguments are * generated by compiler */ set_nil(ks->top++); kp_error(ks, "wrong number of arguments\n"); return 1; } if (unlikely(cs_id > max_csym_id(ks))) kp_error(ks, "invalid csymbol id\n"); kp_verbose_printf(ks, "ffi.new symbol %s with length %d\n", id_to_csym(ks, cs_id)->name, array_size); if (is_array) cd = kp_cdata_new_ptr(ks, NULL, array_size, cs_id, 1); else cd = kp_cdata_new_by_id(ks, NULL, cs_id); set_cdata(ks->top, cd); incr_top(ks); return 1; }
static int kplib_ptable(ktap_state *ks) { ktap_ptab *ph; int narr = 0, nrec = 0; if (kp_arg_nr(ks) >= 1) { kp_arg_check(ks, 1, KTAP_TYPE_NUMBER); narr = nvalue(kp_arg(ks, 1)); } if (kp_arg_nr(ks) >= 2) { kp_arg_check(ks, 2, KTAP_TYPE_NUMBER); nrec = nvalue(kp_arg(ks, 2)); } ph = kp_ptab_new(ks, narr, nrec); set_ptable(ks->top, ph); incr_top(ks); return 1; }
static int kplib_print(ktap_state_t *ks) { int i; int n = kp_arg_nr(ks); for (i = 1; i <= n; i++) { ktap_val_t *arg = kp_arg(ks, i); if (i > 1) kp_puts(ks, "\t"); kp_obj_show(ks, arg); } kp_puts(ks, "\n"); return 0; }
static int kplib_print_backtrace(ktap_state *ks) { int skip = 10, max_entries = 10; int n = kp_arg_nr(ks); if (n >= 1) { kp_arg_check(ks, 1, KTAP_TYPE_NUMBER); skip = nvalue(kp_arg(ks, 1)); } if (n >= 2) { kp_arg_check(ks, 2, KTAP_TYPE_NUMBER); max_entries = nvalue(kp_arg(ks, 2)); max_entries = min(max_entries, KTAP_MAX_STACK_ENTRIES); } kp_transport_print_backtrace(ks, skip, max_entries); return 0; }
static int ktap_lib_table_count(ktap_state *ks) { ktap_table *tbl; ktap_value *k = kp_arg(ks, 2); int n; kp_arg_check(ks, 1, KTAP_TTABLE); tbl = hvalue(kp_arg(ks, 1)); if (kp_arg_nr(ks) > 2) n = nvalue(kp_arg(ks, 3)); else n = 1; kp_table_atomic_inc(ks, tbl, k, n); return 0; }
/* * use gdb to get field offset of struct task_struct, for example: * * gdb vmlinux * (gdb)p &(((struct task_struct *)0).prio) */ static int kplib_curr_taskinfo(ktap_state *ks) { int offset; int fetch_bytes; kp_arg_check(ks, 1, KTAP_TYPE_NUMBER); offset = nvalue(kp_arg(ks, 1)); if (kp_arg_nr(ks) == 1) fetch_bytes = 4; /* default fetch 4 bytes*/ else { kp_arg_check(ks, 2, KTAP_TYPE_NUMBER); fetch_bytes = nvalue(kp_arg(ks, 2)); } if (offset >= sizeof(struct task_struct)) { set_nil(ks->top++); kp_error(ks, "access out of bound value of task_struct\n"); return 1; } #define RET_VALUE ((unsigned long)current + offset) switch (fetch_bytes) { case 4: set_number(ks->top, *(unsigned int *)RET_VALUE); break; case 8: set_number(ks->top, *(unsigned long *)RET_VALUE); break; default: kp_error(ks, "unsupported fetch bytes in curr_task_info\n"); set_nil(ks->top); break; } #undef RET_VALUE incr_top(ks); return 1; }
static int kplib_histogram(ktap_state *ks) { ktap_value *v = kp_arg(ks, 1); int n = HISTOGRAM_DEFAULT_TOP_NUM; if (kp_arg_nr(ks) >= 2) { kp_arg_check(ks, 2, KTAP_TYPE_NUMBER); n = nvalue(kp_arg(ks, 2)); if (n > 1000) n = 1000; } n = max(n, HISTOGRAM_DEFAULT_TOP_NUM); if (is_table(v)) kp_tab_histogram(ks, hvalue(v), n); else if (is_ptable(v)) kp_ptab_histogram(ks, phvalue(v), n); return 0; }
static int kplib_ffi_free(ktap_state *ks) { int n = kp_arg_nr(ks); ktap_cdata *cd; if (n != 1) { set_nil(ks->top++); kp_error(ks, "wrong number of arguments\n"); return 1; } kp_arg_check(ks, 1, KTAP_TYPE_CDATA); cd = cdvalue(kp_arg(ks, 1)); if (cd_type(ks, cd) != FFI_PTR) kp_error(ks, "could free pointer cdata only\n"); kp_cdata_free_ptr(ks, cd); return 0; }
/* * Call C into function * First argument should be function symbol address, argument types * and return type. * Left arguments should be arguments for calling the C function. * Types between Ktap and C are converted automatically. * Only support x86_64 function call by now */ int ffi_call(ktap_state_t *ks, csymbol_func *csf) { int i; int expected_arg_nr, arg_nr; ktap_closure_t *cl; void *rvalue; expected_arg_nr = csymf_arg_nr(csf); arg_nr = kp_arg_nr(ks); /* check stack status for C call */ if (!csf->has_var_arg && expected_arg_nr != arg_nr) { kp_error(ks, "wrong argument number %d, which should be %d\n", arg_nr, expected_arg_nr); goto out; } if (csf->has_var_arg && expected_arg_nr > arg_nr) { kp_error(ks, "argument number %d, which should be bigger than %d\n", arg_nr, expected_arg_nr); goto out; } /* maybe useful later, leave it here first */ cl = clvalue(kp_arg(ks, arg_nr + 1)); /* check the argument types */ for (i = 0; i < arg_nr; i++) { if (ffi_type_check(ks, csf, i) < 0) goto out; } /* platform-specific calling workflow */ ffi_call_arch(ks, csf, &rvalue); kp_verbose_printf(ks, "Finish FFI call\n"); out: return ffi_set_return(ks, rvalue, csymf_ret_id(csf)); }
int kp_strfmt(ktap_state *ks, struct trace_seq *seq) { int arg = 1; size_t sfl; ktap_value *arg_fmt = kp_arg(ks, 1); int argnum = kp_arg_nr(ks); const char *strfrmt, *strfrmt_end; strfrmt = svalue(arg_fmt); sfl = rawtsvalue(arg_fmt)->tsv.len; strfrmt_end = strfrmt + sfl; while (strfrmt < strfrmt_end) { if (*strfrmt != L_ESC) trace_seq_putc(seq, *strfrmt++); else if (*++strfrmt == L_ESC) trace_seq_putc(seq, *strfrmt++); else { /* format item */ char form[MAX_FORMAT]; if (++arg > argnum) { ktap_argerror(ks, arg, "no value"); return -1; } strfrmt = scanformat(ks, strfrmt, form); switch (*strfrmt++) { case 'c': trace_seq_printf(seq, form, nvalue(kp_arg(ks, arg))); break; case 'd': case 'i': { ktap_number n = nvalue(kp_arg(ks, arg)); INTFRM_T ni = (INTFRM_T)n; addlenmod(form, INTFRMLEN); trace_seq_printf(seq, form, ni); break; } case 'p': { char str[KSYM_SYMBOL_LEN]; SPRINT_SYMBOL(str, nvalue(kp_arg(ks, arg))); trace_seq_puts(seq, str); break; } case 'o': case 'u': case 'x': case 'X': { ktap_number n = nvalue(kp_arg(ks, arg)); unsigned INTFRM_T ni = (unsigned INTFRM_T)n; addlenmod(form, INTFRMLEN); trace_seq_printf(seq, form, ni); break; } case 's': { ktap_value *v = kp_arg(ks, arg); const char *s; size_t l; if (isnil(v)) { trace_seq_puts(seq, "nil"); return 0; } if (ttisevent(v)) { kp_event_tostring(ks, seq); return 0; } s = svalue(v); l = rawtsvalue(v)->tsv.len; if (!strchr(form, '.') && l >= 100) { /* * no precision and string is too long * to be formatted; * keep original string */ trace_seq_puts(seq, s); break; } else { trace_seq_printf(seq, form, s); break; } } default: /* also treat cases `pnLlh' */ kp_error(ks, "invalid option " KTAP_QL("%%%c") " to " KTAP_QL("format"), *(strfrmt - 1)); } } } return 0; }
static void ffi_call_x86_64(ktap_state_t *ks, csymbol_func *csf, void *rvalue) { int i; int gpr_nr; int arg_bytes; /* total bytes needed for exceeded args in stack */ int mem_bytes; /* total bytes needed for memory storage */ char *stack, *stack_p, *gpr_p, *arg_p, *mem_p, *tmp_p; int arg_nr; csymbol *rsym; ffi_type rtype; size_t rsize; bool ret_in_memory; /* New stack to call C function */ char space[NEWSTACK_SIZE]; arg_nr = kp_arg_nr(ks); rsym = csymf_ret(ks, csf); rtype = csym_type(rsym); rsize = csym_size(ks, rsym); ret_in_memory = false; if (rtype == FFI_STRUCT || rtype == FFI_UNION) { if (rsize > 16) { rvalue = kp_malloc(ks, rsize); rtype = FFI_VOID; ret_in_memory = true; } else { /* much easier to always copy 16 bytes from registers */ rvalue = kp_malloc(ks, 16); } } gpr_nr = 0; arg_bytes = mem_bytes = 0; if (ret_in_memory) gpr_nr++; /* calculate bytes needed for stack */ for (i = 0; i < arg_nr; i++) { csymbol *cs = ffi_get_arg_csym(ks, csf, i); size_t size = csym_size(ks, cs); size_t align = csym_align(ks, cs); enum arg_status st = IN_REGISTER; int n_gpr_nr = 0; if (size > 32) { st = IN_MEMORY; n_gpr_nr = 1; } else if (size > 16) st = IN_STACK; else n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE; if (gpr_nr + n_gpr_nr > MAX_GPR) { if (st == IN_MEMORY) arg_bytes += GPR_SIZE; else st = IN_STACK; } else gpr_nr += n_gpr_nr; if (st == IN_STACK) { arg_bytes = ALIGN(arg_bytes, align); arg_bytes += size; arg_bytes = ALIGN(arg_bytes, STACK_ALIGNMENT); } if (st == IN_MEMORY) { mem_bytes = ALIGN(mem_bytes, align); mem_bytes += size; mem_bytes = ALIGN(mem_bytes, STACK_ALIGNMENT); } } /* apply space to fake stack for C function call */ if (16 + REDZONE_SIZE + MAX_GPR_SIZE + arg_bytes + mem_bytes + 6 * 8 >= NEWSTACK_SIZE) { kp_error(ks, "Unable to handle that many arguments by now\n"); return; } stack = space; /* 128 bytes below %rsp is red zone */ /* stack should be 16-bytes aligned */ stack_p = ALIGN_STACK(stack + REDZONE_SIZE, 16); /* save general purpose registers here */ gpr_p = stack_p; memset(gpr_p, 0, MAX_GPR_SIZE); /* save arguments in stack here */ arg_p = gpr_p + MAX_GPR_SIZE; /* save arguments in memory here */ mem_p = arg_p + arg_bytes; /* set additional space as temporary space */ tmp_p = mem_p + mem_bytes; /* copy arguments here */ gpr_nr = 0; if (ret_in_memory) { memcpy(gpr_p, &rvalue, GPR_SIZE); gpr_p += GPR_SIZE; gpr_nr++; } for (i = 0; i < arg_nr; i++) { csymbol *cs = ffi_get_arg_csym(ks, csf, i); size_t size = csym_size(ks, cs); size_t align = csym_align(ks, cs); enum arg_status st = IN_REGISTER; int n_gpr_nr = 0; if (size > 32) { st = IN_MEMORY; n_gpr_nr = 1; } else if (size > 16) st = IN_STACK; else n_gpr_nr = ALIGN(size, GPR_SIZE) / GPR_SIZE; if (st == IN_MEMORY) mem_p = ALIGN_STACK(mem_p, align); /* Tricky way about storing it above mem_p. It won't overflow * because temp region can be temporarily used if necesseary. */ ffi_unpack(ks, mem_p, csf, i, GPR_SIZE); if (gpr_nr + n_gpr_nr > MAX_GPR) { if (st == IN_MEMORY) { memcpy(arg_p, &mem_p, GPR_SIZE); arg_p += GPR_SIZE; } else st = IN_STACK; } else { memcpy(gpr_p, mem_p, n_gpr_nr * GPR_SIZE); gpr_p += n_gpr_nr * GPR_SIZE; gpr_nr += n_gpr_nr; } if (st == IN_STACK) { arg_p = ALIGN_STACK(arg_p, align); memcpy(arg_p, mem_p, size); arg_p += size; arg_p = ALIGN_STACK(arg_p, STACK_ALIGNMENT); } if (st == IN_MEMORY) { mem_p += size; mem_p = ALIGN_STACK(mem_p, STACK_ALIGNMENT); } } kp_verbose_printf(ks, "Stack location: %p -redzone- %p -general purpose " "register used- %p -zero- %p -stack for argument- %p" " -memory for argument- %p -temp stack-\n", stack, stack_p, gpr_p, stack_p + MAX_GPR_SIZE, arg_p, mem_p); kp_verbose_printf(ks, "GPR number: %d; arg in stack: %d; " "arg in mem: %d\n", gpr_nr, arg_bytes, mem_bytes); kp_verbose_printf(ks, "Return: address %p type %d\n", rvalue, rtype); kp_verbose_printf(ks, "Number of register used: %d\n", gpr_nr); kp_verbose_printf(ks, "Start FFI call on %p\n", csf->addr); ffi_call_assem_x86_64(stack_p, tmp_p, csf->addr, rvalue, rtype); }