#include #include #include #include #include #include "udebug-pcap.h" static uc_resource_type_t *rbuf_type, *wbuf_type, *snapshot_type, *pcap_type; static uc_value_t *registry; static struct udebug u; static uc_vm_t *_vm; struct uc_pcap { struct pcap_context pcap; int fd; FILE *f; }; static size_t add_registry(uc_value_t *val) { size_t i = 0; while (ucv_array_get(registry, i)) i += 2; ucv_array_set(registry, i, ucv_get(val)); return i; } static void uc_udebug_notify_cb(struct udebug *ctx, struct udebug_remote_buf *rb) { uintptr_t idx = (uintptr_t)rb->priv; uc_value_t *cb, *this; uc_vm_t *vm = _vm; this = ucv_array_get(registry, idx); cb = ucv_array_get(registry, idx + 1); if (!ucv_is_callable(cb)) return; uc_vm_stack_push(vm, ucv_get(this)); uc_vm_stack_push(vm, ucv_get(cb)); if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE) return; ucv_put(uc_vm_stack_pop(vm)); } static uc_value_t * uc_udebug_init(uc_vm_t *vm, size_t nargs) { uc_value_t *arg = uc_fn_arg(0); uc_value_t *flag_auto = uc_fn_arg(1); const char *path = NULL; if (ucv_type(arg) == UC_STRING) path = ucv_string_get(arg); udebug_init(&u); u.notify_cb = uc_udebug_notify_cb; if (flag_auto && !ucv_is_truish(flag_auto)) { if (udebug_connect(&u, path)) return NULL; } else { udebug_auto_connect(&u, path); } return ucv_boolean_new(true); } static uc_value_t * uc_udebug_get_ring(uc_vm_t *vm, size_t nargs) { struct udebug_remote_buf *rb; struct udebug_packet_info *info; uc_value_t *arg = uc_fn_arg(0); uc_value_t *id, *proc, *name, *pid; int ifname_len, ifdesc_len; char *ifname_buf, *ifdesc_buf; uc_value_t *res; uintptr_t idx; #define R_IFACE_DESC "%s:%d" if (ucv_type(arg) != UC_OBJECT) return NULL; id = ucv_object_get(arg, "id", NULL); proc = ucv_object_get(arg, "proc_name", NULL); name = ucv_object_get(arg, "ring_name", NULL); pid = ucv_object_get(arg, "pid", NULL); if (ucv_type(id) != UC_INTEGER || ucv_type(proc) != UC_STRING || ucv_type(name) != UC_STRING || ucv_type(pid) != UC_INTEGER) return NULL; ifname_len = strlen(ucv_string_get(name)) + 1; ifdesc_len = sizeof(R_IFACE_DESC) + strlen(ucv_string_get(proc)) + 10; rb = calloc_a(sizeof(*rb), &info, sizeof(*info), &ifname_buf, ifname_len, &ifdesc_buf, ifdesc_len); rb->meta = info; strcpy(ifname_buf, ucv_string_get(name)); info->attr[UDEBUG_META_IFACE_NAME] = ifname_buf; snprintf(ifdesc_buf, ifdesc_len, R_IFACE_DESC, ucv_string_get(proc), (unsigned int)ucv_int64_get(pid)); info->attr[UDEBUG_META_IFACE_DESC] = ifdesc_buf; if (udebug_remote_buf_map(&u, rb, (uint32_t)ucv_int64_get(id))) { free(rb); return NULL; } res = uc_resource_new(rbuf_type, rb); idx = add_registry(res); rb->priv = (void *)idx; return res; } static void rbuf_free(void *ptr) { struct udebug_remote_buf *rb = ptr; uintptr_t idx; if (!rb) return; idx = (uintptr_t)rb->priv; ucv_array_set(registry, idx, NULL); ucv_array_set(registry, idx + 1, NULL); udebug_remote_buf_unmap(&u, rb); free(rb); } static uc_value_t * uc_udebug_rbuf_fetch(uc_vm_t *vm, size_t nargs) { struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf"); struct udebug_snapshot *s; if (!rb) return NULL; s = udebug_remote_buf_snapshot(rb); if (!s) return NULL; return uc_resource_new(snapshot_type, s); } static uc_value_t * uc_udebug_rbuf_set_poll_cb(uc_vm_t *vm, size_t nargs) { struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf"); uc_value_t *val = uc_fn_arg(0); uintptr_t idx; if (!rb) return NULL; idx = (uintptr_t)rb->priv; ucv_array_set(registry, idx + 1, ucv_get(val)); if (!u.fd.registered) udebug_add_uloop(&u); udebug_remote_buf_set_poll(&u, rb, ucv_is_callable(val)); return NULL; } static uc_value_t * uc_udebug_rbuf_set_fetch_duration(uc_vm_t *vm, size_t nargs) { struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf"); uc_value_t *val = uc_fn_arg(0); uint64_t ts; double t; if (!rb) return NULL; t = ucv_double_get(val); if (isnan(t)) return NULL; ts = udebug_timestamp(); ts -= (uint64_t)(fabs(t) * UDEBUG_TS_SEC); udebug_remote_buf_set_start_time(rb, ts); return ucv_boolean_new(true); } static uc_value_t * uc_udebug_rbuf_set_fetch_count(uc_vm_t *vm, size_t nargs) { struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf"); uc_value_t *val = uc_fn_arg(0); uint32_t count; if (!rb) return NULL; count = ucv_int64_get(val); udebug_remote_buf_set_start_offset(rb, count); return ucv_boolean_new(true); } static uc_value_t * uc_udebug_rbuf_change_flags(uc_vm_t *vm, size_t nargs) { struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf"); uc_value_t *mask = uc_fn_arg(0); uc_value_t *set = uc_fn_arg(1); if (!rb) return NULL; if (ucv_type(mask) != UC_INTEGER || ucv_type(set) != UC_INTEGER) return NULL; udebug_remote_buf_set_flags(rb, ucv_int64_get(mask), ucv_int64_get(set)); return ucv_boolean_new(true); } static uc_value_t * uc_udebug_rbuf_get_flags(uc_vm_t *vm, size_t nargs) { struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf"); if (!rb) return NULL; return ucv_int64_new(udebug_buf_flags(&rb->buf)); } static uc_value_t * uc_udebug_rbuf_close(uc_vm_t *vm, size_t nargs) { void **p = uc_fn_this("udebug.rbuf"); if (!p) return NULL; rbuf_free(*p); *p = NULL; return NULL; } static void uc_udebug_pcap_init(struct uc_pcap *p, uc_value_t *args) { uc_value_t *hw, *os, *app; struct pcap_meta meta = {}; if (ucv_type(args) == UC_OBJECT) { hw = ucv_object_get(args, "hw", NULL); os = ucv_object_get(args, "os", NULL); app = ucv_object_get(args, "app", NULL); meta.hw = ucv_string_get(hw); meta.os = ucv_string_get(os); meta.app = ucv_string_get(app); } pcap_init(&p->pcap, &meta); } static void write_retry(int fd, const void *data, size_t len) { do { ssize_t cur; cur = write(fd, data, len); if (cur < 0) { if (errno == EINTR) continue; return; } data += cur; len -= cur; } while (len > 0); } static void uc_udebug_pcap_write_block(struct uc_pcap *p) { size_t len; void *data; data = pcap_block_get(&len); write_retry(p->fd, data, len); } static uc_value_t * uc_debug_pcap_init(int fd, uc_value_t *args) { struct uc_pcap *p; if (fd < 0) return NULL; p = calloc(1, sizeof(*p)); p->fd = fd; uc_udebug_pcap_init(p, args); uc_udebug_pcap_write_block(p); return uc_resource_new(pcap_type, p); } static uc_value_t * uc_udebug_pcap_file(uc_vm_t *vm, size_t nargs) { uc_value_t *file = uc_fn_arg(0); uc_value_t *args = uc_fn_arg(1); int fd = -1; if (ucv_type(file) == UC_STRING) { fd = open(ucv_string_get(file), O_WRONLY | O_CREAT, 0644); if (ftruncate(fd, 0) < 0) { close(fd); return NULL; } } else if (!file) fd = STDOUT_FILENO; return uc_debug_pcap_init(fd, args); } static uc_value_t * uc_udebug_pcap_udp(uc_vm_t *vm, size_t nargs) { uc_value_t *host = uc_fn_arg(0); uc_value_t *port = uc_fn_arg(1); uc_value_t *args = uc_fn_arg(2); const char *port_str; int fd = -1; if (ucv_type(host) != UC_STRING) return NULL; if (ucv_type(port) == UC_STRING) port_str = ucv_string_get(port); else if (ucv_type(port) == UC_INTEGER) port_str = usock_port(ucv_int64_get(port)); else return NULL; fd = usock(USOCK_UDP, ucv_string_get(host), port_str); return uc_debug_pcap_init(fd, args); } static struct udebug_snapshot * uc_get_snapshot(uc_value_t *val) { return ucv_resource_data(val, "udebug.snapshot"); } static uc_value_t * uc_udebug_pcap_write(uc_vm_t *vm, size_t nargs) { struct uc_pcap *p = uc_fn_thisval("udebug.pcap"); uc_value_t *arg = uc_fn_arg(0); size_t n = ucv_type(arg) == UC_ARRAY ? ucv_array_length(arg) : 1; struct udebug_snapshot **s; struct udebug_iter it; if (!p) return NULL; s = alloca(n * sizeof(*s)); if (ucv_type(arg) == UC_ARRAY) for (size_t i = 0; i < n; i++) { if ((s[i] = uc_get_snapshot(ucv_array_get(arg, i))) == NULL) return NULL; } else { if ((s[0] = uc_get_snapshot(arg)) == NULL) return NULL; } udebug_iter_start(&it, s, n); while (udebug_iter_next(&it)) { struct udebug_remote_buf *rb; rb = udebug_remote_buf_get(&u, it.s->rbuf_idx); if (!pcap_interface_is_valid(&p->pcap, rb->pcap_iface)) { if (pcap_interface_rbuf_init(&p->pcap, rb)) continue; uc_udebug_pcap_write_block(p); } if (pcap_snapshot_packet_init(&u, &it)) continue; uc_udebug_pcap_write_block(p); } return NULL; } static void uc_udebug_pcap_free(void *ptr) { struct uc_pcap *p = ptr; if (!p) return; if (p->fd >= 0) close(p->fd); free(p); } static uc_value_t * uc_udebug_pcap_close(uc_vm_t *vm, size_t nargs) { void **p = uc_fn_this("udebug.pcap"); if (!p) return NULL; uc_udebug_pcap_free(*p); *p = NULL; return NULL; } static uc_value_t * uc_udebug_snapshot_get_ring(uc_vm_t *vm, size_t nargs) { struct udebug_snapshot *s = uc_fn_thisval("udebug.snapshot"); struct udebug_remote_buf *rb; uintptr_t idx; if (!s) return NULL; rb = udebug_remote_buf_get(&u, s->rbuf_idx); if (!rb) return NULL; idx = (uintptr_t)rb->priv; return ucv_array_get(registry, idx); } static uc_value_t * uc_udebug_foreach_packet(uc_vm_t *vm, size_t nargs) { uc_value_t *arg = uc_fn_arg(0); uc_value_t *fn = uc_fn_arg(1); size_t n = ucv_type(arg) == UC_ARRAY ? ucv_array_length(arg) : 1; struct udebug_snapshot **s; struct udebug_iter it; if (!ucv_is_callable(fn)) return NULL; s = alloca(n * sizeof(*s)); if (ucv_type(arg) == UC_ARRAY) for (size_t i = 0; i < n; i++) { if ((s[i] = uc_get_snapshot(ucv_array_get(arg, i))) == NULL) return NULL; } else { if ((s[0] = uc_get_snapshot(arg)) == NULL) return NULL; } udebug_iter_start(&it, s, n); while (udebug_iter_next(&it)) { uc_value_t *s_obj; if (ucv_type(arg) == UC_ARRAY) s_obj = ucv_array_get(arg, it.s_idx); else s_obj = arg; uc_vm_stack_push(vm, ucv_get(_uc_fn_this_res(vm))); uc_vm_stack_push(vm, ucv_get(fn)); uc_vm_stack_push(vm, ucv_get(s_obj)); uc_vm_stack_push(vm, ucv_string_new_length(it.data, it.len)); if (uc_vm_call(vm, true, 2) != EXCEPTION_NONE) break; ucv_put(uc_vm_stack_pop(vm)); } return NULL; } static uc_value_t * uc_udebug_create_ring(uc_vm_t *vm, size_t nargs) { uc_value_t *name, *flags_arr, *size, *entries; uc_value_t *meta_obj = uc_fn_arg(0); struct udebug_buf_flag *flags; struct udebug_buf_meta *meta; struct udebug_buf *buf; size_t flag_str_len = 0; size_t flags_len = 0; char *name_buf, *flag_name_buf; if (ucv_type(meta_obj) != UC_OBJECT) return NULL; name = ucv_object_get(meta_obj, "name", NULL); flags_arr = ucv_object_get(meta_obj, "flags", NULL); size = ucv_object_get(meta_obj, "size", NULL); entries = ucv_object_get(meta_obj, "entries", NULL); if (ucv_type(name) != UC_STRING || ucv_type(size) != UC_INTEGER || ucv_type(entries) != UC_INTEGER) return NULL; if (ucv_type(flags_arr) == UC_ARRAY) { flags_len = ucv_array_length(flags_arr); for (size_t i = 0; i < flags_len; i++) { uc_value_t *f = ucv_array_get(flags_arr, i); if (ucv_type(f) != UC_STRING) return NULL; flag_str_len += strlen(ucv_string_get(f)) + 1; } } buf = calloc_a(sizeof(*buf), &name_buf, strlen(ucv_string_get(name)) + 1, &meta, sizeof(meta), &flags, flags_len * sizeof(*flags), &flag_name_buf, flag_str_len); meta->name = strcpy(name_buf, ucv_string_get(name)); meta->format = UDEBUG_FORMAT_STRING; meta->flags = flags; for (size_t i = 0; i < flags_len; i++) { uc_value_t *f = ucv_array_get(flags_arr, i); const char *str = ucv_string_get(f); size_t len = strlen(str) + 1; flags->name = memcpy(name_buf, str, len); flags->mask = 1ULL << i; name_buf += len; meta->n_flags++; } if (udebug_buf_init(buf, ucv_int64_get(size), ucv_int64_get(entries))) { free(buf); return NULL; } udebug_buf_add(&u, buf, meta); return uc_resource_new(wbuf_type, buf); } static void wbuf_free(void *ptr) { if (!ptr) return; udebug_buf_free(ptr); free(ptr); } static uc_value_t * uc_udebug_wbuf_flags(uc_vm_t *vm, size_t nargs) { struct udebug_buf *buf = uc_fn_thisval("udebug.wbuf"); if (!buf) return NULL; return ucv_int64_new(udebug_buf_flags(buf)); } static uc_value_t * uc_udebug_wbuf_close(uc_vm_t *vm, size_t nargs) { void **p = uc_fn_this("udebug.wbuf"); if (!p) return NULL; wbuf_free(*p); *p = NULL; return NULL; } static void uc_udebug_wbuf_add_string(struct udebug_buf *buf, uc_value_t *val) { udebug_entry_init(buf); udebug_entry_append(buf, ucv_string_get(val), ucv_string_length(val)); udebug_entry_add(buf); } static uc_value_t * uc_udebug_wbuf_add(uc_vm_t *vm, size_t nargs) { struct udebug_buf *buf = uc_fn_thisval("udebug.wbuf"); uc_value_t *arg = uc_fn_arg(0); if (!buf || ucv_type(arg) != UC_STRING) return NULL; uc_udebug_wbuf_add_string(buf, arg); return ucv_boolean_new(true); } static const uc_function_list_t pcap_fns[] = { { "close", uc_udebug_pcap_close }, { "write", uc_udebug_pcap_write }, }; static const uc_function_list_t snapshot_fns[] = { { "get_ring", uc_udebug_snapshot_get_ring } }; static const uc_function_list_t wbuf_fns[] = { { "add", uc_udebug_wbuf_add }, { "flags", uc_udebug_wbuf_flags }, { "close", uc_udebug_wbuf_close }, }; static const uc_function_list_t rbuf_fns[] = { { "set_poll_cb", uc_udebug_rbuf_set_poll_cb }, { "fetch", uc_udebug_rbuf_fetch }, { "change_flags", uc_udebug_rbuf_change_flags }, { "get_flags", uc_udebug_rbuf_get_flags }, { "set_fetch_duration", uc_udebug_rbuf_set_fetch_duration }, { "set_fetch_count", uc_udebug_rbuf_set_fetch_count }, { "close", uc_udebug_rbuf_close }, }; static const uc_function_list_t global_fns[] = { { "init", uc_udebug_init }, { "create_ring", uc_udebug_create_ring }, { "get_ring", uc_udebug_get_ring }, { "pcap_file", uc_udebug_pcap_file }, { "pcap_udp", uc_udebug_pcap_udp }, { "foreach_packet", uc_udebug_foreach_packet }, }; void uc_module_init(uc_vm_t *vm, uc_value_t *scope) { _vm = vm; uc_function_list_register(scope, global_fns); wbuf_type = uc_type_declare(vm, "udebug.wbuf", wbuf_fns, wbuf_free); rbuf_type = uc_type_declare(vm, "udebug.rbuf", rbuf_fns, rbuf_free); snapshot_type = uc_type_declare(vm, "udebug.snapshot", snapshot_fns, free); pcap_type = uc_type_declare(vm, "udebug.pcap", pcap_fns, uc_udebug_pcap_free); registry = ucv_array_new(vm); uc_vm_registry_set(vm, "udebug.registry", registry); }