source: mainline/uspace/app/trace/trace.c@ 01900b6

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since 01900b6 was 01900b6, checked in by Martin Decky <martin@…>, 6 years ago

Use an optional output argument instead of errno to propagate the error

The use of errno is troublesome in all other than top-level library
functions since the value in errno might get overwritten by subsequent
inner calls on the error path (e.g. cleanup, deallocation, etc.). The
optional output argument makes it possible to explicitly ignore the
error code if it is not needed, but still to pass it reliably back to
the original caller.

This change affecs async_connect_me_to(),
async_connect_me_to_blocking(), async_connect_kbox(), service_connect(),
service_connect_blocking() and loader_connect().

  • Property mode set to 100644
File size: 17.1 KB
Line 
1/*
2 * Copyright (c) 2008 Jiri Svoboda
3 * All rights reserved.
4 *
5 * Redistribution and use in source and binary forms, with or without
6 * modification, are permitted provided that the following conditions
7 * are met:
8 *
9 * - Redistributions of source code must retain the above copyright
10 * notice, this list of conditions and the following disclaimer.
11 * - Redistributions in binary form must reproduce the above copyright
12 * notice, this list of conditions and the following disclaimer in the
13 * documentation and/or other materials provided with the distribution.
14 * - The name of the author may not be used to endorse or promote products
15 * derived from this software without specific prior written permission.
16 *
17 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18 * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20 * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21 * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22 * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23 * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25 * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26 * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27 */
28
29/** @addtogroup trace
30 * @{
31 */
32/** @file
33 */
34
35#include <stdio.h>
36#include <stdlib.h>
37#include <stdbool.h>
38#include <stdint.h>
39#include <stddef.h>
40#include <str_error.h>
41#include <inttypes.h>
42#include <fibril.h>
43#include <errno.h>
44#include <udebug.h>
45#include <async.h>
46#include <task.h>
47#include <mem.h>
48#include <str.h>
49#include <io/console.h>
50#include <io/keycode.h>
51#include <fibril_synch.h>
52#include <vfs/vfs.h>
53
54#include <libc.h>
55
56/* Temporary: service and method names */
57#include "proto.h"
58#include <ipc/services.h>
59#include <ipc/vfs.h>
60#include <ipc/console.h>
61
62#include "syscalls.h"
63#include "ipcp.h"
64#include "trace.h"
65
66#define THBUF_SIZE 64
67uintptr_t thread_hash_buf[THBUF_SIZE];
68int n_threads;
69
70int next_thread_id;
71
72ipc_call_t thread_ipc_req[THBUF_SIZE];
73
74async_sess_t *sess;
75bool abort_trace;
76
77uintptr_t thash;
78static bool paused;
79static fibril_condvar_t state_cv;
80static fibril_mutex_t state_lock;
81
82static bool cev_valid;
83static kbd_event_t cev;
84
85void thread_trace_start(uintptr_t thread_hash);
86
87static char *cmd_path;
88static char **cmd_args;
89
90static task_id_t task_id;
91static task_wait_t task_w;
92static bool task_wait_for;
93
94/** Combination of events/data to print. */
95display_mask_t display_mask;
96
97static errno_t cev_fibril(void *arg);
98
99static void cev_fibril_start(void)
100{
101 fid_t fid;
102
103 fid = fibril_create(cev_fibril, NULL);
104 if (fid == 0) {
105 printf("Error creating fibril\n");
106 exit(1);
107 }
108
109 fibril_add_ready(fid);
110}
111
112static errno_t program_run(void)
113{
114 errno_t rc;
115
116 rc = task_spawnv_debug(&task_id, &task_w, cmd_path,
117 (const char *const *)cmd_args, &sess);
118
119 if (rc == ENOTSUP) {
120 printf("You do not have userspace debugging support "
121 "compiled in the kernel.\n");
122 printf("Compile kernel with 'Support for userspace debuggers' "
123 "(CONFIG_UDEBUG) enabled.\n");
124 }
125
126 if (rc != EOK) {
127 printf("Error running program (%s)\n", str_error_name(rc));
128 return rc;
129 }
130
131 return EOK;
132}
133
134static errno_t connect_task(task_id_t task_id)
135{
136 errno_t rc;
137 bool debug_started = false;
138 bool wait_set_up = false;
139
140 if (sess == NULL) {
141 sess = async_connect_kbox(task_id, &rc);
142 if (sess == NULL) {
143 printf("Error connecting to task %" PRIu64 ".\n",
144 task_id);
145 goto error;
146 }
147
148 rc = udebug_begin(sess);
149 if (rc != EOK) {
150 printf("Error starting debug session.\n");
151 goto error;
152 }
153
154 debug_started = true;
155
156 rc = task_setup_wait(task_id, &task_w);
157 if (rc != EOK) {
158 printf("Error setting up wait for task termination.\n");
159 goto error;
160 }
161
162 wait_set_up = true;
163 }
164
165 rc = udebug_set_evmask(sess, UDEBUG_EM_ALL);
166 if (rc != EOK) {
167 printf("udebug_set_evmask(0x%x) -> %s\n ", UDEBUG_EM_ALL, str_error_name(rc));
168 return rc;
169 }
170
171 return EOK;
172error:
173 if (wait_set_up)
174 task_cancel_wait(&task_w);
175 if (debug_started)
176 udebug_end(sess);
177 if (sess != NULL)
178 async_hangup(sess);
179 return rc;
180}
181
182static errno_t get_thread_list(void)
183{
184 errno_t rc;
185 size_t tb_copied;
186 size_t tb_needed;
187 int i;
188
189 rc = udebug_thread_read(sess, thread_hash_buf,
190 THBUF_SIZE * sizeof(unsigned), &tb_copied, &tb_needed);
191 if (rc != EOK) {
192 printf("udebug_thread_read() -> %s\n", str_error_name(rc));
193 return rc;
194 }
195
196 n_threads = tb_copied / sizeof(uintptr_t);
197
198 printf("Threads:");
199 for (i = 0; i < n_threads; i++) {
200 printf(" [%d] (hash %p)", 1 + i, (void *) thread_hash_buf[i]);
201 }
202 printf("\ntotal of %zu threads\n", tb_needed / sizeof(uintptr_t));
203
204 return EOK;
205}
206
207void val_print(sysarg_t val, val_type_t v_type)
208{
209 long sval;
210
211 sval = (long) val;
212
213 switch (v_type) {
214 case V_VOID:
215 printf("<void>");
216 break;
217
218 case V_INTEGER:
219 printf("%ld", sval);
220 break;
221
222 case V_HASH:
223 case V_PTR:
224 printf("%p", (void *) val);
225 break;
226
227 case V_ERRNO:
228 if (sval >= -15 && sval <= 0) {
229 printf("%ld %s (%s)", sval,
230 str_error_name((errno_t) sval),
231 str_error((errno_t) sval));
232 } else {
233 printf("%ld", sval);
234 }
235 break;
236 case V_INT_ERRNO:
237 if (sval >= -15 && sval < 0) {
238 printf("%ld %s (%s)", sval,
239 str_error_name((errno_t) sval),
240 str_error((errno_t) sval));
241 } else {
242 printf("%ld", sval);
243 }
244 break;
245
246 case V_CHAR:
247 if (sval >= 0x20 && sval < 0x7f) {
248 printf("'%c'", (char) sval);
249 } else {
250 switch (sval) {
251 case '\a':
252 printf("'\\a'");
253 break;
254 case '\b':
255 printf("'\\b'");
256 break;
257 case '\n':
258 printf("'\\n'");
259 break;
260 case '\r':
261 printf("'\\r'");
262 break;
263 case '\t':
264 printf("'\\t'");
265 break;
266 case '\\':
267 printf("'\\\\'");
268 break;
269 default:
270 printf("'\\x%02" PRIxn "'", val);
271 break;
272 }
273 }
274 break;
275 }
276}
277
278static void print_sc_retval(sysarg_t retval, val_type_t val_type)
279{
280 printf(" -> ");
281 val_print(retval, val_type);
282 putchar('\n');
283}
284
285static void print_sc_args(sysarg_t *sc_args, int n)
286{
287 int i;
288
289 putchar('(');
290 if (n > 0)
291 printf("%" PRIun, sc_args[0]);
292 for (i = 1; i < n; i++) {
293 printf(", %" PRIun, sc_args[i]);
294 }
295 putchar(')');
296}
297
298static void sc_ipc_call_async_fast(sysarg_t *sc_args, errno_t sc_rc)
299{
300 ipc_call_t call;
301 cap_phone_handle_t phandle;
302
303 if (sc_rc != EOK)
304 return;
305
306 phandle = (cap_phone_handle_t) sc_args[0];
307
308 ipc_set_imethod(&call, sc_args[1]);
309 ipc_set_arg1(&call, sc_args[2]);
310 ipc_set_arg2(&call, sc_args[3]);
311 ipc_set_arg3(&call, sc_args[4]);
312 ipc_set_arg4(&call, sc_args[5]);
313 ipc_set_arg5(&call, 0);
314
315 ipcp_call_out(phandle, &call, 0);
316}
317
318static void sc_ipc_call_async_slow(sysarg_t *sc_args, errno_t sc_rc)
319{
320 ipc_call_t call;
321 errno_t rc;
322
323 if (sc_rc != EOK)
324 return;
325
326 memset(&call, 0, sizeof(call));
327 rc = udebug_mem_read(sess, &call.args, sc_args[1], sizeof(call.args));
328
329 if (rc == EOK) {
330 ipcp_call_out((cap_phone_handle_t) sc_args[0], &call, 0);
331 }
332}
333
334static void sc_ipc_wait(sysarg_t *sc_args, cap_call_handle_t sc_rc)
335{
336 ipc_call_t call;
337 errno_t rc;
338
339 if (sc_rc == 0)
340 return;
341
342 memset(&call, 0, sizeof(call));
343 rc = udebug_mem_read(sess, &call, sc_args[0], sizeof(call));
344
345 if (rc == EOK)
346 ipcp_call_in(&call, sc_rc);
347}
348
349static void event_syscall_b(unsigned thread_id, uintptr_t thread_hash,
350 unsigned sc_id, sysarg_t sc_rc)
351{
352 sysarg_t sc_args[6];
353 errno_t rc;
354
355 /* Read syscall arguments */
356 rc = udebug_args_read(sess, thread_hash, sc_args);
357
358 if (rc != EOK) {
359 printf("error\n");
360 return;
361 }
362
363 if ((display_mask & DM_SYSCALL) != 0) {
364 /* Print syscall name and arguments */
365 if (syscall_desc_defined(sc_id)) {
366 printf("%s", syscall_desc[sc_id].name);
367 print_sc_args(sc_args, syscall_desc[sc_id].n_args);
368 } else {
369 printf("unknown_syscall<%d>", sc_id);
370 print_sc_args(sc_args, 6);
371 }
372 }
373}
374
375static void event_syscall_e(unsigned thread_id, uintptr_t thread_hash,
376 unsigned sc_id, sysarg_t sc_rc)
377{
378 sysarg_t sc_args[6];
379 int rv_type;
380 errno_t rc;
381
382 /* Read syscall arguments */
383 rc = udebug_args_read(sess, thread_hash, sc_args);
384
385 if (rc != EOK) {
386 printf("error\n");
387 return;
388 }
389
390 if ((display_mask & DM_SYSCALL) != 0) {
391 /* Print syscall return value */
392 if (syscall_desc_defined(sc_id))
393 rv_type = syscall_desc[sc_id].rv_type;
394 else
395 rv_type = V_PTR;
396 print_sc_retval(sc_rc, rv_type);
397 }
398
399 switch (sc_id) {
400 case SYS_IPC_CALL_ASYNC_FAST:
401 sc_ipc_call_async_fast(sc_args, (errno_t) sc_rc);
402 break;
403 case SYS_IPC_CALL_ASYNC_SLOW:
404 sc_ipc_call_async_slow(sc_args, (errno_t) sc_rc);
405 break;
406 case SYS_IPC_WAIT:
407 sc_ipc_wait(sc_args, (cap_call_handle_t) sc_rc);
408 break;
409 default:
410 break;
411 }
412}
413
414static void event_thread_b(uintptr_t hash)
415{
416 printf("New thread, hash %p\n", (void *) hash);
417 thread_trace_start(hash);
418}
419
420static errno_t trace_loop(void *thread_hash_arg)
421{
422 errno_t rc;
423 unsigned ev_type;
424 uintptr_t thread_hash;
425 unsigned thread_id;
426 sysarg_t val0, val1;
427
428 thread_hash = (uintptr_t)thread_hash_arg;
429 thread_id = next_thread_id++;
430 if (thread_id >= THBUF_SIZE) {
431 printf("Too many threads.\n");
432 return ELIMIT;
433 }
434
435 printf("Start tracing thread [%u] (hash %p).\n",
436 thread_id, (void *) thread_hash);
437
438 while (!abort_trace) {
439
440 fibril_mutex_lock(&state_lock);
441 if (paused) {
442 printf("Thread [%u] paused. Press R to resume.\n",
443 thread_id);
444
445 while (paused)
446 fibril_condvar_wait(&state_cv, &state_lock);
447
448 printf("Thread [%u] resumed.\n", thread_id);
449 }
450 fibril_mutex_unlock(&state_lock);
451
452 /* Run thread until an event occurs */
453 rc = udebug_go(sess, thread_hash,
454 &ev_type, &val0, &val1);
455
456 if (ev_type == UDEBUG_EVENT_FINISHED) {
457 /* Done tracing this thread */
458 break;
459 }
460
461 if (rc == EOK) {
462 switch (ev_type) {
463 case UDEBUG_EVENT_SYSCALL_B:
464 event_syscall_b(thread_id, thread_hash, val0, (int)val1);
465 break;
466 case UDEBUG_EVENT_SYSCALL_E:
467 event_syscall_e(thread_id, thread_hash, val0, (int)val1);
468 break;
469 case UDEBUG_EVENT_STOP:
470 printf("Stop event\n");
471 fibril_mutex_lock(&state_lock);
472 paused = true;
473 fibril_mutex_unlock(&state_lock);
474 break;
475 case UDEBUG_EVENT_THREAD_B:
476 event_thread_b(val0);
477 break;
478 case UDEBUG_EVENT_THREAD_E:
479 printf("Thread %" PRIun " exited.\n", val0);
480 fibril_mutex_lock(&state_lock);
481 abort_trace = true;
482 fibril_condvar_broadcast(&state_cv);
483 fibril_mutex_unlock(&state_lock);
484 break;
485 default:
486 printf("Unknown event type %d.\n", ev_type);
487 break;
488 }
489 }
490
491 }
492
493 printf("Finished tracing thread [%d].\n", thread_id);
494 return EOK;
495}
496
497void thread_trace_start(uintptr_t thread_hash)
498{
499 fid_t fid;
500
501 thash = thread_hash;
502
503 fid = fibril_create(trace_loop, (void *)thread_hash);
504 if (fid == 0) {
505 printf("Warning: Failed creating fibril\n");
506 }
507 fibril_add_ready(fid);
508}
509
510static errno_t cev_fibril(void *arg)
511{
512 cons_event_t event;
513
514 (void) arg;
515
516 console_ctrl_t *console = console_init(stdin, stdout);
517
518 while (true) {
519 fibril_mutex_lock(&state_lock);
520 while (cev_valid)
521 fibril_condvar_wait(&state_cv, &state_lock);
522 fibril_mutex_unlock(&state_lock);
523
524 if (!console_get_event(console, &event))
525 return EINVAL;
526
527 if (event.type == CEV_KEY) {
528 fibril_mutex_lock(&state_lock);
529 cev = event.ev.key;
530 cev_valid = true;
531 fibril_condvar_broadcast(&state_cv);
532 fibril_mutex_unlock(&state_lock);
533 }
534 }
535}
536
537static void trace_task(task_id_t task_id)
538{
539 kbd_event_t ev;
540 bool done;
541 int i;
542 errno_t rc;
543
544 ipcp_init();
545
546 rc = get_thread_list();
547 if (rc != EOK) {
548 printf("Failed to get thread list (%s)\n", str_error(rc));
549 return;
550 }
551
552 abort_trace = false;
553
554 for (i = 0; i < n_threads; i++) {
555 thread_trace_start(thread_hash_buf[i]);
556 }
557
558 done = false;
559
560 while (!done) {
561 fibril_mutex_lock(&state_lock);
562 while (!cev_valid && !abort_trace)
563 fibril_condvar_wait(&state_cv, &state_lock);
564 fibril_mutex_unlock(&state_lock);
565
566 ev = cev;
567
568 fibril_mutex_lock(&state_lock);
569 cev_valid = false;
570 fibril_condvar_broadcast(&state_cv);
571 fibril_mutex_unlock(&state_lock);
572
573 if (abort_trace)
574 break;
575
576 if (ev.type != KEY_PRESS)
577 continue;
578
579 switch (ev.key) {
580 case KC_Q:
581 done = true;
582 break;
583 case KC_P:
584 printf("Pause...\n");
585 rc = udebug_stop(sess, thash);
586 if (rc != EOK)
587 printf("Error: stop -> %s\n", str_error_name(rc));
588 break;
589 case KC_R:
590 fibril_mutex_lock(&state_lock);
591 paused = false;
592 fibril_condvar_broadcast(&state_cv);
593 fibril_mutex_unlock(&state_lock);
594 printf("Resume...\n");
595 break;
596 default:
597 break;
598 }
599 }
600
601 printf("\nTerminate debugging session...\n");
602 abort_trace = true;
603 udebug_end(sess);
604 async_hangup(sess);
605
606 ipcp_cleanup();
607
608 printf("Done\n");
609 return;
610}
611
612static void main_init(void)
613{
614 proto_t *p;
615 oper_t *o;
616
617 val_type_t arg_def[OPER_MAX_ARGS] = {
618 V_INTEGER,
619 V_INTEGER,
620 V_INTEGER,
621 V_INTEGER,
622 V_INTEGER
623 };
624
625 val_type_t resp_def[OPER_MAX_ARGS] = {
626 V_INTEGER,
627 V_INTEGER,
628 V_INTEGER,
629 V_INTEGER,
630 V_INTEGER
631 };
632
633 next_thread_id = 1;
634 paused = false;
635 cev_valid = false;
636
637 fibril_mutex_initialize(&state_lock);
638 fibril_condvar_initialize(&state_cv);
639
640 proto_init();
641
642 p = proto_new("vfs");
643 o = oper_new("read", 3, arg_def, V_ERRNO, 1, resp_def);
644 proto_add_oper(p, VFS_IN_READ, o);
645 o = oper_new("write", 3, arg_def, V_ERRNO, 1, resp_def);
646 proto_add_oper(p, VFS_IN_WRITE, o);
647 o = oper_new("vfs_resize", 5, arg_def, V_ERRNO, 0, resp_def);
648 proto_add_oper(p, VFS_IN_RESIZE, o);
649 o = oper_new("vfs_stat", 1, arg_def, V_ERRNO, 0, resp_def);
650 proto_add_oper(p, VFS_IN_STAT, o);
651 o = oper_new("vfs_put", 1, arg_def, V_ERRNO, 0, resp_def);
652 proto_add_oper(p, VFS_IN_PUT, o);
653 o = oper_new("vfs_mount", 2, arg_def, V_ERRNO, 0, resp_def);
654 proto_add_oper(p, VFS_IN_MOUNT, o);
655#if 0
656 o = oper_new("unmount", 0, arg_def);
657 proto_add_oper(p, VFS_IN_UNMOUNT, o);
658#endif
659 o = oper_new("vfs_sync", 1, arg_def, V_ERRNO, 0, resp_def);
660 proto_add_oper(p, VFS_IN_SYNC, o);
661 o = oper_new("rename", 0, arg_def, V_ERRNO, 0, resp_def);
662 proto_add_oper(p, VFS_IN_RENAME, o);
663 o = oper_new("vfs_statfs", 0, arg_def, V_ERRNO, 0, resp_def);
664 proto_add_oper(p, VFS_IN_STATFS, o);
665 o = oper_new("vfs_walk", 2, arg_def, V_INT_ERRNO, 0, resp_def);
666 proto_add_oper(p, VFS_IN_WALK, o);
667 o = oper_new("vfs_open", 2, arg_def, V_ERRNO, 0, resp_def);
668 proto_add_oper(p, VFS_IN_OPEN, o);
669 o = oper_new("vfs_unlink", 3, arg_def, V_ERRNO, 0, resp_def);
670 proto_add_oper(p, VFS_IN_UNLINK, o);
671
672 proto_register(SERVICE_VFS, p);
673}
674
675static void print_syntax(void)
676{
677 printf("Syntax:\n");
678 printf("\ttrace [+<events>] <executable> [<arg1> [...]]\n");
679 printf("or\ttrace [+<events>] -t <task_id>\n");
680 printf("Events: (default is +tp)\n");
681 printf("\n");
682 printf("\tt ... Thread creation and termination\n");
683 printf("\ts ... System calls\n");
684 printf("\ti ... Low-level IPC\n");
685 printf("\tp ... Protocol level\n");
686 printf("\n");
687 printf("Examples:\n");
688 printf("\ttrace +s /app/tetris\n");
689 printf("\ttrace +tsip -t 12\n");
690}
691
692static display_mask_t parse_display_mask(const char *text)
693{
694 display_mask_t dm = 0;
695 const char *c = text;
696
697 while (*c) {
698 switch (*c) {
699 case 't':
700 dm = dm | DM_THREAD;
701 break;
702 case 's':
703 dm = dm | DM_SYSCALL;
704 break;
705 case 'i':
706 dm = dm | DM_IPC;
707 break;
708 case 'p':
709 dm = dm | DM_SYSTEM | DM_USER;
710 break;
711 default:
712 printf("Unexpected event type '%c'.\n", *c);
713 exit(1);
714 }
715
716 ++c;
717 }
718
719 return dm;
720}
721
722static int parse_args(int argc, char *argv[])
723{
724 char *err_p;
725
726 task_id = 0;
727
728 --argc;
729 ++argv;
730
731 while (argc > 0) {
732 char *arg = *argv;
733 if (arg[0] == '+') {
734 display_mask = parse_display_mask(&arg[1]);
735 } else if (arg[0] == '-') {
736 if (arg[1] == 't') {
737 /* Trace an already running task */
738 --argc;
739 ++argv;
740 task_id = strtol(*argv, &err_p, 10);
741 task_wait_for = false;
742 if (*err_p) {
743 printf("Task ID syntax error\n");
744 print_syntax();
745 return -1;
746 }
747 } else {
748 printf("Uknown option '%c'\n", arg[0]);
749 print_syntax();
750 return -1;
751 }
752 } else {
753 break;
754 }
755
756 --argc;
757 ++argv;
758 }
759
760 if (task_id != 0) {
761 if (argc == 0)
762 return 0;
763 printf("Extra arguments\n");
764 print_syntax();
765 return -1;
766 }
767
768 if (argc < 1) {
769 printf("Missing argument\n");
770 print_syntax();
771 return -1;
772 }
773
774 /* Preload the specified program file. */
775 printf("Spawning '%s' with arguments:\n", *argv);
776
777 char **cp = argv;
778 while (*cp)
779 printf("'%s'\n", *cp++);
780
781 cmd_path = *argv;
782 cmd_args = argv;
783 task_wait_for = true;
784
785 return 0;
786}
787
788int main(int argc, char *argv[])
789{
790 errno_t rc;
791 task_exit_t texit;
792 int retval;
793
794 printf("System Call / IPC Tracer\n");
795 printf("Controls: Q - Quit, P - Pause, R - Resume\n");
796
797 display_mask = DM_THREAD | DM_SYSTEM | DM_USER;
798
799 if (parse_args(argc, argv) < 0)
800 return 1;
801
802 main_init();
803
804 if (cmd_path != NULL)
805 program_run();
806
807 rc = connect_task(task_id);
808 if (rc != EOK) {
809 printf("Failed connecting to task %" PRIu64 ".\n", task_id);
810 return 1;
811 }
812
813 printf("Connected to task %" PRIu64 ".\n", task_id);
814
815 cev_fibril_start();
816 trace_task(task_id);
817
818 if (task_wait_for) {
819 printf("Waiting for task to exit.\n");
820
821 rc = task_wait(&task_w, &texit, &retval);
822 if (rc != EOK) {
823 printf("Failed waiting for task.\n");
824 return -1;
825 }
826
827 if (texit == TASK_EXIT_NORMAL) {
828 printf("Task exited normally, return value %d.\n",
829 retval);
830 } else {
831 printf("Task exited unexpectedly.\n");
832 }
833 }
834
835 return 0;
836}
837
838/** @}
839 */
Note: See TracBrowser for help on using the repository browser.