lib-ucode: retry partial writes
[project/udebug.git] / lib-ucode.c
1 #include <math.h>
2 #include <libubox/utils.h>
3 #include <libubox/usock.h>
4 #include <libubox/udebug.h>
5 #include <ucode/module.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 write_retry(int fd, const void *data, size_t len)
278 {
279 do {
280 ssize_t cur;
281
282 cur = write(fd, data, len);
283 if (cur < 0) {
284 if (errno == EINTR)
285 continue;
286
287 return;
288 }
289
290 data += cur;
291 len -= cur;
292 } while (len > 0);
293 }
294
295 static void
296 uc_udebug_pcap_write_block(struct uc_pcap *p)
297 {
298 size_t len;
299 void *data;
300
301 data = pcap_block_get(&len);
302 write_retry(p->fd, data, len);
303 }
304
305 static uc_value_t *
306 uc_debug_pcap_init(int fd, uc_value_t *args)
307 {
308 struct uc_pcap *p;
309
310 if (fd < 0)
311 return NULL;
312
313 p = calloc(1, sizeof(*p));
314 p->fd = fd;
315 uc_udebug_pcap_init(p, args);
316 uc_udebug_pcap_write_block(p);
317
318 return uc_resource_new(pcap_type, p);
319 }
320
321 static uc_value_t *
322 uc_udebug_pcap_file(uc_vm_t *vm, size_t nargs)
323 {
324 uc_value_t *file = uc_fn_arg(0);
325 uc_value_t *args = uc_fn_arg(1);
326 int fd = -1;
327
328 if (ucv_type(file) == UC_STRING)
329 fd = open(ucv_string_get(file), O_WRONLY | O_CREAT, 0644);
330 else if (!file)
331 fd = STDOUT_FILENO;
332
333 return uc_debug_pcap_init(fd, args);
334 }
335
336 static uc_value_t *
337 uc_udebug_pcap_udp(uc_vm_t *vm, size_t nargs)
338 {
339 uc_value_t *host = uc_fn_arg(0);
340 uc_value_t *port = uc_fn_arg(1);
341 uc_value_t *args = uc_fn_arg(2);
342 const char *port_str;
343 int fd = -1;
344
345 if (ucv_type(host) != UC_STRING)
346 return NULL;
347
348 if (ucv_type(port) == UC_STRING)
349 port_str = ucv_string_get(port);
350 else if (ucv_type(port) == UC_INTEGER)
351 port_str = usock_port(ucv_int64_get(port));
352 else
353 return NULL;
354
355 fd = usock(USOCK_UDP, ucv_string_get(host), port_str);
356
357 return uc_debug_pcap_init(fd, args);
358 }
359
360 static struct udebug_snapshot *
361 uc_get_snapshot(uc_value_t *val)
362 {
363 return ucv_resource_data(val, "udebug.snapshot");
364 }
365
366 static uc_value_t *
367 uc_udebug_pcap_write(uc_vm_t *vm, size_t nargs)
368 {
369 struct uc_pcap *p = uc_fn_thisval("udebug.pcap");
370 uc_value_t *arg = uc_fn_arg(0);
371 size_t n = ucv_type(arg) == UC_ARRAY ? ucv_array_length(arg) : 1;
372 struct udebug_snapshot **s;
373 struct udebug_iter it;
374
375 if (!p)
376 return NULL;
377
378 s = alloca(n * sizeof(*s));
379 if (ucv_type(arg) == UC_ARRAY)
380 for (size_t i = 0; i < n; i++) {
381 if ((s[i] = uc_get_snapshot(ucv_array_get(arg, i))) == NULL)
382 return NULL;
383 } else {
384 if ((s[0] = uc_get_snapshot(arg)) == NULL)
385 return NULL;
386 }
387
388 udebug_iter_start(&it, s, n);
389 while (udebug_iter_next(&it)) {
390 struct udebug_remote_buf *rb;
391
392 rb = udebug_remote_buf_get(&u, it.s->rbuf_idx);
393 if (!pcap_interface_is_valid(&p->pcap, rb->pcap_iface)) {
394 if (pcap_interface_rbuf_init(&p->pcap, rb))
395 continue;
396
397 uc_udebug_pcap_write_block(p);
398 }
399
400 if (pcap_snapshot_packet_init(&u, &it))
401 continue;
402
403 uc_udebug_pcap_write_block(p);
404 }
405
406 return NULL;
407 }
408
409 static void
410 uc_udebug_pcap_free(void *ptr)
411 {
412 struct uc_pcap *p = ptr;
413
414 if (!p)
415 return;
416
417 if (p->fd >= 0)
418 close(p->fd);
419 free(p);
420 }
421
422 static uc_value_t *
423 uc_udebug_pcap_close(uc_vm_t *vm, size_t nargs)
424 {
425 void **p = uc_fn_this("udebug.pcap");
426
427 if (!p)
428 return NULL;
429
430 uc_udebug_pcap_free(*p);
431 *p = NULL;
432
433 return NULL;
434 }
435
436 static uc_value_t *
437 uc_udebug_snapshot_get_ring(uc_vm_t *vm, size_t nargs)
438 {
439 struct udebug_snapshot *s = uc_fn_thisval("udebug.snapshot");
440 struct udebug_remote_buf *rb;
441 uintptr_t idx;
442
443 if (!s)
444 return NULL;
445
446 rb = udebug_remote_buf_get(&u, s->rbuf_idx);
447 if (!rb)
448 return NULL;
449
450 idx = (uintptr_t)rb->priv;
451 return ucv_array_get(registry, idx);
452 }
453
454 static uc_value_t *
455 uc_udebug_foreach_packet(uc_vm_t *vm, size_t nargs)
456 {
457 uc_value_t *arg = uc_fn_arg(0);
458 uc_value_t *fn = uc_fn_arg(1);
459 size_t n = ucv_type(arg) == UC_ARRAY ? ucv_array_length(arg) : 1;
460 struct udebug_snapshot **s;
461 struct udebug_iter it;
462
463 if (!ucv_is_callable(fn))
464 return NULL;
465
466 s = alloca(n * sizeof(*s));
467 if (ucv_type(arg) == UC_ARRAY)
468 for (size_t i = 0; i < n; i++) {
469 if ((s[i] = uc_get_snapshot(ucv_array_get(arg, i))) == NULL)
470 return NULL;
471 } else {
472 if ((s[0] = uc_get_snapshot(arg)) == NULL)
473 return NULL;
474 }
475
476 udebug_iter_start(&it, s, n);
477 while (udebug_iter_next(&it)) {
478 uc_value_t *s_obj;
479
480 if (ucv_type(arg) == UC_ARRAY)
481 s_obj = ucv_array_get(arg, it.s_idx);
482 else
483 s_obj = arg;
484
485 uc_vm_stack_push(vm, ucv_get(_uc_fn_this_res(vm)));
486 uc_vm_stack_push(vm, ucv_get(fn));
487 uc_vm_stack_push(vm, ucv_get(s_obj));
488 uc_vm_stack_push(vm, ucv_string_new_length(it.data, it.len));
489
490 if (uc_vm_call(vm, true, 2) != EXCEPTION_NONE)
491 break;
492
493 ucv_put(uc_vm_stack_pop(vm));
494 }
495
496 return NULL;
497 }
498
499 static uc_value_t *
500 uc_udebug_create_ring(uc_vm_t *vm, size_t nargs)
501 {
502 uc_value_t *name, *flags_arr, *size, *entries;
503 uc_value_t *meta_obj = uc_fn_arg(0);
504 struct udebug_buf_flag *flags;
505 struct udebug_buf_meta *meta;
506 struct udebug_buf *buf;
507 size_t flag_str_len = 0;
508 size_t flags_len = 0;
509 char *name_buf, *flag_name_buf;
510
511 if (ucv_type(meta_obj) != UC_OBJECT)
512 return NULL;
513
514 name = ucv_object_get(meta_obj, "name", NULL);
515 flags_arr = ucv_object_get(meta_obj, "flags", NULL);
516 size = ucv_object_get(meta_obj, "size", NULL);
517 entries = ucv_object_get(meta_obj, "entries", NULL);
518
519 if (ucv_type(name) != UC_STRING ||
520 ucv_type(size) != UC_INTEGER || ucv_type(entries) != UC_INTEGER)
521 return NULL;
522
523 if (ucv_type(flags_arr) == UC_ARRAY) {
524 flags_len = ucv_array_length(flags_arr);
525 for (size_t i = 0; i < flags_len; i++) {
526 uc_value_t *f = ucv_array_get(flags_arr, i);
527 if (ucv_type(f) != UC_STRING)
528 return NULL;
529 flag_str_len += strlen(ucv_string_get(f)) + 1;
530 }
531 }
532
533 buf = calloc_a(sizeof(*buf),
534 &name_buf, strlen(ucv_string_get(name)) + 1,
535 &meta, sizeof(meta),
536 &flags, flags_len * sizeof(*flags),
537 &flag_name_buf, flag_str_len);
538 meta->name = strcpy(name_buf, ucv_string_get(name));
539 meta->format = UDEBUG_FORMAT_STRING;
540 meta->flags = flags;
541
542 for (size_t i = 0; i < flags_len; i++) {
543 uc_value_t *f = ucv_array_get(flags_arr, i);
544 const char *str = ucv_string_get(f);
545 size_t len = strlen(str) + 1;
546
547 flags->name = memcpy(name_buf, str, len);
548 flags->mask = 1ULL << i;
549 name_buf += len;
550 meta->n_flags++;
551 }
552
553 if (udebug_buf_init(buf, ucv_int64_get(size), ucv_int64_get(entries))) {
554 free(buf);
555 return NULL;
556 }
557
558 udebug_buf_add(&u, buf, meta);
559
560 return uc_resource_new(wbuf_type, buf);
561 }
562
563 static void wbuf_free(void *ptr)
564 {
565 if (!ptr)
566 return;
567
568 udebug_buf_free(ptr);
569 free(ptr);
570 }
571
572 static uc_value_t *
573 uc_udebug_wbuf_flags(uc_vm_t *vm, size_t nargs)
574 {
575 struct udebug_buf *buf = uc_fn_thisval("udebug.wbuf");
576
577 if (!buf)
578 return NULL;
579
580 return ucv_int64_new(udebug_buf_flags(buf));
581 }
582
583 static uc_value_t *
584 uc_udebug_wbuf_close(uc_vm_t *vm, size_t nargs)
585 {
586 void **p = uc_fn_this("udebug.wbuf");
587
588 if (!p)
589 return NULL;
590
591 wbuf_free(*p);
592 *p = NULL;
593
594 return NULL;
595 }
596
597 static void
598 uc_udebug_wbuf_add_string(struct udebug_buf *buf, uc_value_t *val)
599 {
600 udebug_entry_init(buf);
601 udebug_entry_append(buf, ucv_string_get(val), ucv_string_length(val));
602 udebug_entry_add(buf);
603 }
604
605 static uc_value_t *
606 uc_udebug_wbuf_add(uc_vm_t *vm, size_t nargs)
607 {
608 struct udebug_buf *buf = uc_fn_thisval("udebug.wbuf");
609 uc_value_t *arg = uc_fn_arg(0);
610
611 if (!buf || ucv_type(arg) != UC_STRING)
612 return NULL;
613
614 uc_udebug_wbuf_add_string(buf, arg);
615
616 return ucv_boolean_new(true);
617 }
618
619 static const uc_function_list_t pcap_fns[] = {
620 { "close", uc_udebug_pcap_close },
621 { "write", uc_udebug_pcap_write },
622 };
623
624 static const uc_function_list_t snapshot_fns[] = {
625 { "get_ring", uc_udebug_snapshot_get_ring }
626 };
627
628 static const uc_function_list_t wbuf_fns[] = {
629 { "add", uc_udebug_wbuf_add },
630 { "flags", uc_udebug_wbuf_flags },
631 { "close", uc_udebug_wbuf_close },
632 };
633
634 static const uc_function_list_t rbuf_fns[] = {
635 { "set_poll_cb", uc_udebug_rbuf_set_poll_cb },
636 { "fetch", uc_udebug_rbuf_fetch },
637 { "change_flags", uc_udebug_rbuf_change_flags },
638 { "get_flags", uc_udebug_rbuf_get_flags },
639 { "set_fetch_duration", uc_udebug_rbuf_set_fetch_duration },
640 { "set_fetch_count", uc_udebug_rbuf_set_fetch_count },
641 { "close", uc_udebug_rbuf_close },
642 };
643
644 static const uc_function_list_t global_fns[] = {
645 { "init", uc_udebug_init },
646 { "create_ring", uc_udebug_create_ring },
647 { "get_ring", uc_udebug_get_ring },
648 { "pcap_file", uc_udebug_pcap_file },
649 { "pcap_udp", uc_udebug_pcap_udp },
650 { "foreach_packet", uc_udebug_foreach_packet },
651 };
652
653 void uc_module_init(uc_vm_t *vm, uc_value_t *scope)
654 {
655 _vm = vm;
656 uc_function_list_register(scope, global_fns);
657
658 wbuf_type = uc_type_declare(vm, "udebug.wbuf", wbuf_fns, wbuf_free);
659 rbuf_type = uc_type_declare(vm, "udebug.rbuf", rbuf_fns, rbuf_free);
660 snapshot_type = uc_type_declare(vm, "udebug.snapshot", snapshot_fns, free);
661 pcap_type = uc_type_declare(vm, "udebug.pcap", pcap_fns, uc_udebug_pcap_free);
662
663 registry = ucv_array_new(vm);
664 uc_vm_registry_set(vm, "udebug.registry", registry);
665 }