Initial import
[project/udebug.git] / lib-ucode.c
1 #include <math.h>
2 #include <libubox/utils.h>
3 #include <libubox/usock.h>
4 #include <ucode/module.h>
5 #include "udebug.h"
6 #include "udebug-pcap.h"
7
8 static uc_resource_type_t *rbuf_type, *wbuf_type, *snapshot_type, *pcap_type;
9 static uc_value_t *registry;
10 static struct udebug u;
11 static uc_vm_t *_vm;
12
13 struct uc_pcap {
14 struct pcap_context pcap;
15 int fd;
16 FILE *f;
17 };
18
19 static size_t add_registry(uc_value_t *val)
20 {
21 size_t i = 0;
22
23 while (ucv_array_get(registry, i))
24 i += 2;
25
26 ucv_array_set(registry, i, ucv_get(val));
27
28 return i;
29 }
30
31 static void
32 uc_udebug_notify_cb(struct udebug *ctx, struct udebug_remote_buf *rb)
33 {
34 uintptr_t idx = (uintptr_t)rb->priv;
35 uc_value_t *cb, *this;
36 uc_vm_t *vm = _vm;
37
38 this = ucv_array_get(registry, idx);
39 cb = ucv_array_get(registry, idx + 1);
40
41 if (!ucv_is_callable(cb))
42 return;
43
44 uc_vm_stack_push(vm, ucv_get(this));
45 uc_vm_stack_push(vm, ucv_get(cb));
46 if (uc_vm_call(vm, true, 0) != EXCEPTION_NONE)
47 return;
48
49 ucv_put(uc_vm_stack_pop(vm));
50 }
51
52 static uc_value_t *
53 uc_udebug_init(uc_vm_t *vm, size_t nargs)
54 {
55 uc_value_t *arg = uc_fn_arg(0);
56 uc_value_t *flag_auto = uc_fn_arg(1);
57 const char *path = NULL;
58
59 if (ucv_type(arg) == UC_STRING)
60 path = ucv_string_get(arg);
61
62 udebug_init(&u);
63 u.notify_cb = uc_udebug_notify_cb;
64 if (flag_auto && !ucv_is_truish(flag_auto)) {
65 if (udebug_connect(&u, path))
66 return NULL;
67 } else {
68 udebug_auto_connect(&u, path);
69 }
70
71 return ucv_boolean_new(true);
72 }
73
74 static uc_value_t *
75 uc_udebug_get_ring(uc_vm_t *vm, size_t nargs)
76 {
77 struct udebug_remote_buf *rb;
78 struct udebug_packet_info *info;
79 uc_value_t *arg = uc_fn_arg(0);
80 uc_value_t *id, *proc, *name, *pid;
81 int ifname_len, ifdesc_len;
82 char *ifname_buf, *ifdesc_buf;
83 uc_value_t *res;
84 uintptr_t idx;
85
86 #define R_IFACE_DESC "%s:%d"
87
88 if (ucv_type(arg) != UC_OBJECT)
89 return NULL;
90
91 id = ucv_object_get(arg, "id", NULL);
92 proc = ucv_object_get(arg, "proc_name", NULL);
93 name = ucv_object_get(arg, "ring_name", NULL);
94 pid = ucv_object_get(arg, "pid", NULL);
95
96 if (ucv_type(id) != UC_INTEGER ||
97 ucv_type(proc) != UC_STRING ||
98 ucv_type(name) != UC_STRING ||
99 ucv_type(pid) != UC_INTEGER)
100 return NULL;
101
102 ifname_len = strlen(ucv_string_get(name)) + 1;
103 ifdesc_len = sizeof(R_IFACE_DESC) + strlen(ucv_string_get(proc)) + 10;
104 rb = calloc_a(sizeof(*rb),
105 &info, sizeof(*info),
106 &ifname_buf, ifname_len,
107 &ifdesc_buf, ifdesc_len);
108 rb->meta = info;
109
110 strcpy(ifname_buf, ucv_string_get(name));
111 info->attr[UDEBUG_META_IFACE_NAME] = ifname_buf;
112 snprintf(ifdesc_buf, ifdesc_len, R_IFACE_DESC,
113 ucv_string_get(proc), (unsigned int)ucv_int64_get(pid));
114 info->attr[UDEBUG_META_IFACE_DESC] = ifdesc_buf;
115
116 if (udebug_remote_buf_map(&u, rb, (uint32_t)ucv_int64_get(id))) {
117 free(rb);
118 return NULL;
119 }
120
121 res = uc_resource_new(rbuf_type, rb);
122 idx = add_registry(res);
123 rb->priv = (void *)idx;
124
125 return res;
126 }
127
128 static void rbuf_free(void *ptr)
129 {
130 struct udebug_remote_buf *rb = ptr;
131 uintptr_t idx;
132
133 if (!rb)
134 return;
135
136 idx = (uintptr_t)rb->priv;
137 ucv_array_set(registry, idx, NULL);
138 ucv_array_set(registry, idx + 1, NULL);
139 udebug_remote_buf_unmap(&u, rb);
140 free(rb);
141 }
142
143 static uc_value_t *
144 uc_udebug_rbuf_fetch(uc_vm_t *vm, size_t nargs)
145 {
146 struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf");
147 struct udebug_snapshot *s;
148
149 if (!rb)
150 return NULL;
151
152 s = udebug_remote_buf_snapshot(rb);
153 if (!s)
154 return NULL;
155
156 return uc_resource_new(snapshot_type, s);
157 }
158
159 static uc_value_t *
160 uc_udebug_rbuf_set_poll_cb(uc_vm_t *vm, size_t nargs)
161 {
162 struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf");
163 uc_value_t *val = uc_fn_arg(0);
164 uintptr_t idx;
165
166 if (!rb)
167 return NULL;
168
169 idx = (uintptr_t)rb->priv;
170 ucv_array_set(registry, idx + 1, ucv_get(val));
171 if (!u.fd.registered)
172 udebug_add_uloop(&u);
173 udebug_remote_buf_set_poll(&u, rb, ucv_is_callable(val));
174
175 return NULL;
176 }
177
178 static uc_value_t *
179 uc_udebug_rbuf_set_fetch_duration(uc_vm_t *vm, size_t nargs)
180 {
181 struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf");
182 uc_value_t *val = uc_fn_arg(0);
183 uint64_t ts;
184 double t;
185
186 if (!rb)
187 return NULL;
188
189 t = ucv_double_get(val);
190 if (isnan(t))
191 return NULL;
192
193 ts = udebug_timestamp();
194 ts -= (uint64_t)(fabs(t) * UDEBUG_TS_SEC);
195 udebug_remote_buf_set_start_time(rb, ts);
196
197 return ucv_boolean_new(true);
198 }
199
200 static uc_value_t *
201 uc_udebug_rbuf_set_fetch_count(uc_vm_t *vm, size_t nargs)
202 {
203 struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf");
204 uc_value_t *val = uc_fn_arg(0);
205 uint32_t count;
206
207 if (!rb)
208 return NULL;
209
210 count = ucv_int64_get(val);
211 udebug_remote_buf_set_start_offset(rb, count);
212
213 return ucv_boolean_new(true);
214 }
215
216 static uc_value_t *
217 uc_udebug_rbuf_change_flags(uc_vm_t *vm, size_t nargs)
218 {
219 struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf");
220 uc_value_t *mask = uc_fn_arg(0);
221 uc_value_t *set = uc_fn_arg(1);
222
223 if (!rb)
224 return NULL;
225
226 if (ucv_type(mask) != UC_INTEGER || ucv_type(set) != UC_INTEGER)
227 return NULL;
228
229 udebug_remote_buf_set_flags(rb, ucv_int64_get(mask), ucv_int64_get(set));
230 return ucv_boolean_new(true);
231 }
232
233 static uc_value_t *
234 uc_udebug_rbuf_get_flags(uc_vm_t *vm, size_t nargs)
235 {
236 struct udebug_remote_buf *rb = uc_fn_thisval("udebug.rbuf");
237 if (!rb)
238 return NULL;
239
240 return ucv_int64_new(udebug_buf_flags(&rb->buf));
241 }
242
243 static uc_value_t *
244 uc_udebug_rbuf_close(uc_vm_t *vm, size_t nargs)
245 {
246 void **p = uc_fn_this("udebug.rbuf");
247
248 if (!p)
249 return NULL;
250
251 rbuf_free(*p);
252 *p = NULL;
253
254 return NULL;
255 }
256
257 static void
258 uc_udebug_pcap_init(struct uc_pcap *p, uc_value_t *args)
259 {
260 uc_value_t *hw, *os, *app;
261 struct pcap_meta meta = {};
262
263 if (ucv_type(args) == UC_OBJECT) {
264 hw = ucv_object_get(args, "hw", NULL);
265 os = ucv_object_get(args, "os", NULL);
266 app = ucv_object_get(args, "app", NULL);
267
268 meta.hw = ucv_string_get(hw);
269 meta.os = ucv_string_get(os);
270 meta.app = ucv_string_get(app);
271 }
272
273 pcap_init(&p->pcap, &meta);
274 }
275
276 static void
277 uc_udebug_pcap_write_block(struct uc_pcap *p)
278 {
279 size_t len;
280 void *data;
281 int ret;
282
283 data = pcap_block_get(&len);
284 do {
285 ret = write(p->fd, data, len);
286 } while (ret < 0 && errno == EINTR);
287 }
288
289 static uc_value_t *
290 uc_debug_pcap_init(int fd, uc_value_t *args)
291 {
292 struct uc_pcap *p;
293
294 if (fd < 0)
295 return NULL;
296
297 p = calloc(1, sizeof(*p));
298 p->fd = fd;
299 uc_udebug_pcap_init(p, args);
300 uc_udebug_pcap_write_block(p);
301
302 return uc_resource_new(pcap_type, p);
303 }
304
305 static uc_value_t *
306 uc_udebug_pcap_file(uc_vm_t *vm, size_t nargs)
307 {
308 uc_value_t *file = uc_fn_arg(0);
309 uc_value_t *args = uc_fn_arg(1);
310 int fd = -1;
311
312 if (ucv_type(file) == UC_STRING)
313 fd = open(ucv_string_get(file), O_WRONLY | O_CREAT, 0644);
314 else if (!file)
315 fd = STDOUT_FILENO;
316
317 return uc_debug_pcap_init(fd, args);
318 }
319
320 static uc_value_t *
321 uc_udebug_pcap_udp(uc_vm_t *vm, size_t nargs)
322 {
323 uc_value_t *host = uc_fn_arg(0);
324 uc_value_t *port = uc_fn_arg(1);
325 uc_value_t *args = uc_fn_arg(2);
326 const char *port_str;
327 int fd = -1;
328
329 if (ucv_type(host) != UC_STRING)
330 return NULL;
331
332 if (ucv_type(port) == UC_STRING)
333 port_str = ucv_string_get(port);
334 else if (ucv_type(port) == UC_INTEGER)
335 port_str = usock_port(ucv_int64_get(port));
336 else
337 return NULL;
338
339 fd = usock(USOCK_UDP, ucv_string_get(host), port_str);
340
341 return uc_debug_pcap_init(fd, args);
342 }
343
344 static struct udebug_snapshot *
345 uc_get_snapshot(uc_value_t *val)
346 {
347 return ucv_resource_data(val, "udebug.snapshot");
348 }
349
350 static uc_value_t *
351 uc_udebug_pcap_write(uc_vm_t *vm, size_t nargs)
352 {
353 struct uc_pcap *p = uc_fn_thisval("udebug.pcap");
354 uc_value_t *arg = uc_fn_arg(0);
355 size_t n = ucv_type(arg) == UC_ARRAY ? ucv_array_length(arg) : 1;
356 struct udebug_snapshot **s;
357 struct udebug_iter it;
358
359 if (!p)
360 return NULL;
361
362 s = alloca(n * sizeof(*s));
363 if (ucv_type(arg) == UC_ARRAY)
364 for (size_t i = 0; i < n; i++) {
365 if ((s[i] = uc_get_snapshot(ucv_array_get(arg, i))) == NULL)
366 return NULL;
367 } else {
368 if ((s[0] = uc_get_snapshot(arg)) == NULL)
369 return NULL;
370 }
371
372 udebug_iter_start(&it, s, n);
373 while (udebug_iter_next(&it)) {
374 struct udebug_remote_buf *rb;
375
376 rb = udebug_remote_buf_get(&u, it.s->rbuf_idx);
377 if (!pcap_interface_is_valid(&p->pcap, rb->pcap_iface)) {
378 if (pcap_interface_rbuf_init(&p->pcap, rb))
379 continue;
380
381 uc_udebug_pcap_write_block(p);
382 }
383
384 if (pcap_snapshot_packet_init(&u, &it))
385 continue;
386
387 uc_udebug_pcap_write_block(p);
388 }
389
390 return NULL;
391 }
392
393 static void
394 uc_udebug_pcap_free(void *ptr)
395 {
396 struct uc_pcap *p = ptr;
397
398 if (!p)
399 return;
400
401 if (p->fd >= 0)
402 close(p->fd);
403 free(p);
404 }
405
406 static uc_value_t *
407 uc_udebug_pcap_close(uc_vm_t *vm, size_t nargs)
408 {
409 void **p = uc_fn_this("udebug.pcap");
410
411 if (!p)
412 return NULL;
413
414 uc_udebug_pcap_free(*p);
415 *p = NULL;
416
417 return NULL;
418 }
419
420 static uc_value_t *
421 uc_udebug_snapshot_get_ring(uc_vm_t *vm, size_t nargs)
422 {
423 struct udebug_snapshot *s = uc_fn_thisval("udebug.snapshot");
424 struct udebug_remote_buf *rb;
425 uintptr_t idx;
426
427 if (!s)
428 return NULL;
429
430 rb = udebug_remote_buf_get(&u, s->rbuf_idx);
431 if (!rb)
432 return NULL;
433
434 idx = (uintptr_t)rb->priv;
435 return ucv_array_get(registry, idx);
436 }
437
438 static uc_value_t *
439 uc_udebug_foreach_packet(uc_vm_t *vm, size_t nargs)
440 {
441 uc_value_t *arg = uc_fn_arg(0);
442 uc_value_t *fn = uc_fn_arg(1);
443 size_t n = ucv_type(arg) == UC_ARRAY ? ucv_array_length(arg) : 1;
444 struct udebug_snapshot **s;
445 struct udebug_iter it;
446
447 if (!ucv_is_callable(fn))
448 return NULL;
449
450 s = alloca(n * sizeof(*s));
451 if (ucv_type(arg) == UC_ARRAY)
452 for (size_t i = 0; i < n; i++) {
453 if ((s[i] = uc_get_snapshot(ucv_array_get(arg, i))) == NULL)
454 return NULL;
455 } else {
456 if ((s[0] = uc_get_snapshot(arg)) == NULL)
457 return NULL;
458 }
459
460 udebug_iter_start(&it, s, n);
461 while (udebug_iter_next(&it)) {
462 uc_value_t *s_obj;
463
464 if (ucv_type(arg) == UC_ARRAY)
465 s_obj = ucv_array_get(arg, it.s_idx);
466 else
467 s_obj = arg;
468
469 uc_vm_stack_push(vm, ucv_get(_uc_fn_this_res(vm)));
470 uc_vm_stack_push(vm, ucv_get(fn));
471 uc_vm_stack_push(vm, ucv_get(s_obj));
472 uc_vm_stack_push(vm, ucv_string_new_length(it.data, it.len));
473
474 if (uc_vm_call(vm, true, 2) != EXCEPTION_NONE)
475 break;
476
477 ucv_put(uc_vm_stack_pop(vm));
478 }
479
480 return NULL;
481 }
482
483 static uc_value_t *
484 uc_udebug_create_ring(uc_vm_t *vm, size_t nargs)
485 {
486 uc_value_t *name, *flags_arr, *size, *entries;
487 uc_value_t *meta_obj = uc_fn_arg(0);
488 struct udebug_buf_flag *flags;
489 struct udebug_buf_meta *meta;
490 struct udebug_buf *buf;
491 size_t flag_str_len = 0;
492 size_t flags_len = 0;
493 char *name_buf, *flag_name_buf;
494
495 if (ucv_type(meta_obj) != UC_OBJECT)
496 return NULL;
497
498 name = ucv_object_get(meta_obj, "name", NULL);
499 flags_arr = ucv_object_get(meta_obj, "flags", NULL);
500 size = ucv_object_get(meta_obj, "size", NULL);
501 entries = ucv_object_get(meta_obj, "entries", NULL);
502
503 if (ucv_type(name) != UC_STRING ||
504 ucv_type(size) != UC_INTEGER || ucv_type(entries) != UC_INTEGER)
505 return NULL;
506
507 if (ucv_type(flags_arr) == UC_ARRAY) {
508 flags_len = ucv_array_length(flags_arr);
509 for (size_t i = 0; i < flags_len; i++) {
510 uc_value_t *f = ucv_array_get(flags_arr, i);
511 if (ucv_type(f) != UC_STRING)
512 return NULL;
513 flag_str_len += strlen(ucv_string_get(f)) + 1;
514 }
515 }
516
517 buf = calloc_a(sizeof(*buf),
518 &name_buf, strlen(ucv_string_get(name)) + 1,
519 &meta, sizeof(meta),
520 &flags, flags_len * sizeof(*flags),
521 &flag_name_buf, flag_str_len);
522 meta->name = strcpy(name_buf, ucv_string_get(name));
523 meta->format = UDEBUG_FORMAT_STRING;
524 meta->flags = flags;
525
526 for (size_t i = 0; i < flags_len; i++) {
527 uc_value_t *f = ucv_array_get(flags_arr, i);
528 const char *str = ucv_string_get(f);
529 size_t len = strlen(str) + 1;
530
531 flags->name = memcpy(name_buf, str, len);
532 flags->mask = 1ULL << i;
533 name_buf += len;
534 meta->n_flags++;
535 }
536
537 if (udebug_buf_init(buf, ucv_int64_get(size), ucv_int64_get(entries))) {
538 free(buf);
539 return NULL;
540 }
541
542 udebug_buf_add(&u, buf, meta);
543
544 return uc_resource_new(wbuf_type, buf);
545 }
546
547 static void wbuf_free(void *ptr)
548 {
549 if (!ptr)
550 return;
551
552 udebug_buf_free(ptr);
553 free(ptr);
554 }
555
556 static uc_value_t *
557 uc_udebug_wbuf_flags(uc_vm_t *vm, size_t nargs)
558 {
559 struct udebug_buf *buf = uc_fn_thisval("udebug.wbuf");
560
561 if (!buf)
562 return NULL;
563
564 return ucv_int64_new(udebug_buf_flags(buf));
565 }
566
567 static uc_value_t *
568 uc_udebug_wbuf_close(uc_vm_t *vm, size_t nargs)
569 {
570 void **p = uc_fn_this("udebug.wbuf");
571
572 if (!p)
573 return NULL;
574
575 wbuf_free(*p);
576 *p = NULL;
577
578 return NULL;
579 }
580
581 static void
582 uc_udebug_wbuf_add_string(struct udebug_buf *buf, uc_value_t *val)
583 {
584 udebug_entry_init(buf);
585 udebug_entry_append(buf, ucv_string_get(val), ucv_string_length(val));
586 udebug_entry_add(buf);
587 }
588
589 static uc_value_t *
590 uc_udebug_wbuf_add(uc_vm_t *vm, size_t nargs)
591 {
592 struct udebug_buf *buf = uc_fn_thisval("udebug.wbuf");
593 uc_value_t *arg = uc_fn_arg(0);
594
595 if (!buf || ucv_type(arg) != UC_STRING)
596 return NULL;
597
598 uc_udebug_wbuf_add_string(buf, arg);
599
600 return ucv_boolean_new(true);
601 }
602
603 static const uc_function_list_t pcap_fns[] = {
604 { "close", uc_udebug_pcap_close },
605 { "write", uc_udebug_pcap_write },
606 };
607
608 static const uc_function_list_t snapshot_fns[] = {
609 { "get_ring", uc_udebug_snapshot_get_ring }
610 };
611
612 static const uc_function_list_t wbuf_fns[] = {
613 { "add", uc_udebug_wbuf_add },
614 { "flags", uc_udebug_wbuf_flags },
615 { "close", uc_udebug_wbuf_close },
616 };
617
618 static const uc_function_list_t rbuf_fns[] = {
619 { "set_poll_cb", uc_udebug_rbuf_set_poll_cb },
620 { "fetch", uc_udebug_rbuf_fetch },
621 { "change_flags", uc_udebug_rbuf_change_flags },
622 { "get_flags", uc_udebug_rbuf_get_flags },
623 { "set_fetch_duration", uc_udebug_rbuf_set_fetch_duration },
624 { "set_fetch_count", uc_udebug_rbuf_set_fetch_count },
625 { "close", uc_udebug_rbuf_close },
626 };
627
628 static const uc_function_list_t global_fns[] = {
629 { "init", uc_udebug_init },
630 { "create_ring", uc_udebug_create_ring },
631 { "get_ring", uc_udebug_get_ring },
632 { "pcap_file", uc_udebug_pcap_file },
633 { "pcap_udp", uc_udebug_pcap_udp },
634 { "foreach_packet", uc_udebug_foreach_packet },
635 };
636
637 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
638 {
639 _vm = vm;
640 uc_function_list_register(scope, global_fns);
641
642 wbuf_type = uc_type_declare(vm, "udebug.wbuf", wbuf_fns, wbuf_free);
643 rbuf_type = uc_type_declare(vm, "udebug.rbuf", rbuf_fns, rbuf_free);
644 snapshot_type = uc_type_declare(vm, "udebug.snapshot", snapshot_fns, free);
645 pcap_type = uc_type_declare(vm, "udebug.pcap", pcap_fns, uc_udebug_pcap_free);
646
647 registry = ucv_array_new(vm);
648 uc_vm_registry_set(vm, "udebug.registry", registry);
649 }