source: mainline/uspace/app/trace/trace.c@ cf9a1e2

lfn serial ticket/834-toolchain-update topic/msim-upgrade topic/simplify-dev-export
Last change on this file since cf9a1e2 was cf9a1e2, checked in by Jiri Svoboda <jiri@…>, 6 years ago

Fix use of unitialized variable

Found by gcc -Og

  • 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);
142 if (sess == NULL) {
143 printf("Error connecting to task %" PRIu64 ".\n",
144 task_id);
145 rc = EIO;
146 goto error;
147 }
148
149 rc = udebug_begin(sess);
150 if (rc != EOK) {
151 printf("Error starting debug session.\n");
152 goto error;
153 }
154
155 debug_started = true;
156
157 rc = task_setup_wait(task_id, &task_w);
158 if (rc != EOK) {
159 printf("Error setting up wait for task termination.\n");
160 goto error;
161 }
162
163 wait_set_up = true;
164 }
165
166 rc = udebug_set_evmask(sess, UDEBUG_EM_ALL);
167 if (rc != EOK) {
168 printf("udebug_set_evmask(0x%x) -> %s\n ", UDEBUG_EM_ALL, str_error_name(rc));
169 return rc;
170 }
171
172 return EOK;
173error:
174 if (wait_set_up)
175 task_cancel_wait(&task_w);
176 if (debug_started)
177 udebug_end(sess);
178 if (sess != NULL)
179 async_hangup(sess);
180 return rc;
181}
182
183static errno_t get_thread_list(void)
184{
185 errno_t rc;
186 size_t tb_copied;
187 size_t tb_needed;
188 int i;
189
190 rc = udebug_thread_read(sess, thread_hash_buf,
191 THBUF_SIZE * sizeof(unsigned), &tb_copied, &tb_needed);
192 if (rc != EOK) {
193 printf("udebug_thread_read() -> %s\n", str_error_name(rc));
194 return rc;
195 }
196
197 n_threads = tb_copied / sizeof(uintptr_t);
198
199 printf("Threads:");
200 for (i = 0; i < n_threads; i++) {
201 printf(" [%d] (hash %p)", 1 + i, (void *) thread_hash_buf[i]);
202 }
203 printf("\ntotal of %zu threads\n", tb_needed / sizeof(uintptr_t));
204
205 return EOK;
206}
207
208void val_print(sysarg_t val, val_type_t v_type)
209{
210 long sval;
211
212 sval = (long) val;
213
214 switch (v_type) {
215 case V_VOID:
216 printf("<void>");
217 break;
218
219 case V_INTEGER:
220 printf("%ld", sval);
221 break;
222
223 case V_HASH:
224 case V_PTR:
225 printf("%p", (void *) val);
226 break;
227
228 case V_ERRNO:
229 if (sval >= -15 && sval <= 0) {
230 printf("%ld %s (%s)", sval,
231 str_error_name((errno_t) sval),
232 str_error((errno_t) sval));
233 } else {
234 printf("%ld", sval);
235 }
236 break;
237 case V_INT_ERRNO:
238 if (sval >= -15 && sval < 0) {
239 printf("%ld %s (%s)", sval,
240 str_error_name((errno_t) sval),
241 str_error((errno_t) sval));
242 } else {
243 printf("%ld", sval);
244 }
245 break;
246
247 case V_CHAR:
248 if (sval >= 0x20 && sval < 0x7f) {
249 printf("'%c'", (char) sval);
250 } else {
251 switch (sval) {
252 case '\a':
253 printf("'\\a'");
254 break;
255 case '\b':
256 printf("'\\b'");
257 break;
258 case '\n':
259 printf("'\\n'");
260 break;
261 case '\r':
262 printf("'\\r'");
263 break;
264 case '\t':
265 printf("'\\t'");
266 break;
267 case '\\':
268 printf("'\\\\'");
269 break;
270 default:
271 printf("'\\x%02" PRIxn "'", val);
272 break;
273 }
274 }
275 break;
276 }
277}
278
279static void print_sc_retval(sysarg_t retval, val_type_t val_type)
280{
281 printf(" -> ");
282 val_print(retval, val_type);
283 putchar('\n');
284}
285
286static void print_sc_args(sysarg_t *sc_args, int n)
287{
288 int i;
289
290 putchar('(');
291 if (n > 0)
292 printf("%" PRIun, sc_args[0]);
293 for (i = 1; i < n; i++) {
294 printf(", %" PRIun, sc_args[i]);
295 }
296 putchar(')');
297}
298
299static void sc_ipc_call_async_fast(sysarg_t *sc_args, errno_t sc_rc)
300{
301 ipc_call_t call;
302 cap_phone_handle_t phandle;
303
304 if (sc_rc != EOK)
305 return;
306
307 phandle = (cap_phone_handle_t) sc_args[0];
308
309 ipc_set_imethod(&call, sc_args[1]);
310 ipc_set_arg1(&call, sc_args[2]);
311 ipc_set_arg2(&call, sc_args[3]);
312 ipc_set_arg3(&call, sc_args[4]);
313 ipc_set_arg4(&call, sc_args[5]);
314 ipc_set_arg5(&call, 0);
315
316 ipcp_call_out(phandle, &call, 0);
317}
318
319static void sc_ipc_call_async_slow(sysarg_t *sc_args, errno_t sc_rc)
320{
321 ipc_call_t call;
322 errno_t rc;
323
324 if (sc_rc != EOK)
325 return;
326
327 memset(&call, 0, sizeof(call));
328 rc = udebug_mem_read(sess, &call.args, sc_args[1], sizeof(call.args));
329
330 if (rc == EOK) {
331 ipcp_call_out((cap_phone_handle_t) sc_args[0], &call, 0);
332 }
333}
334
335static void sc_ipc_wait(sysarg_t *sc_args, cap_call_handle_t sc_rc)
336{
337 ipc_call_t call;
338 errno_t rc;
339
340 if (sc_rc == 0)
341 return;
342
343 memset(&call, 0, sizeof(call));
344 rc = udebug_mem_read(sess, &call, sc_args[0], sizeof(call));
345
346 if (rc == EOK)
347 ipcp_call_in(&call, sc_rc);
348}
349
350static void event_syscall_b(unsigned thread_id, uintptr_t thread_hash,
351 unsigned sc_id, sysarg_t sc_rc)
352{
353 sysarg_t sc_args[6];
354 errno_t rc;
355
356 /* Read syscall arguments */
357 rc = udebug_args_read(sess, thread_hash, sc_args);
358
359 if (rc != EOK) {
360 printf("error\n");
361 return;
362 }
363
364 if ((display_mask & DM_SYSCALL) != 0) {
365 /* Print syscall name and arguments */
366 if (syscall_desc_defined(sc_id)) {
367 printf("%s", syscall_desc[sc_id].name);
368 print_sc_args(sc_args, syscall_desc[sc_id].n_args);
369 } else {
370 printf("unknown_syscall<%d>", sc_id);
371 print_sc_args(sc_args, 6);
372 }
373 }
374}
375
376static void event_syscall_e(unsigned thread_id, uintptr_t thread_hash,
377 unsigned sc_id, sysarg_t sc_rc)
378{
379 sysarg_t sc_args[6];
380 int rv_type;
381 errno_t rc;
382
383 /* Read syscall arguments */
384 rc = udebug_args_read(sess, thread_hash, sc_args);
385
386 if (rc != EOK) {
387 printf("error\n");
388 return;
389 }
390
391 if ((display_mask & DM_SYSCALL) != 0) {
392 /* Print syscall return value */
393 if (syscall_desc_defined(sc_id))
394 rv_type = syscall_desc[sc_id].rv_type;
395 else
396 rv_type = V_PTR;
397 print_sc_retval(sc_rc, rv_type);
398 }
399
400 switch (sc_id) {
401 case SYS_IPC_CALL_ASYNC_FAST:
402 sc_ipc_call_async_fast(sc_args, (errno_t) sc_rc);
403 break;
404 case SYS_IPC_CALL_ASYNC_SLOW:
405 sc_ipc_call_async_slow(sc_args, (errno_t) sc_rc);
406 break;
407 case SYS_IPC_WAIT:
408 sc_ipc_wait(sc_args, (cap_call_handle_t) sc_rc);
409 break;
410 default:
411 break;
412 }
413}
414
415static void event_thread_b(uintptr_t hash)
416{
417 printf("New thread, hash %p\n", (void *) hash);
418 thread_trace_start(hash);
419}
420
421static errno_t trace_loop(void *thread_hash_arg)
422{
423 errno_t rc;
424 unsigned ev_type;
425 uintptr_t thread_hash;
426 unsigned thread_id;
427 sysarg_t val0, val1;
428
429 thread_hash = (uintptr_t)thread_hash_arg;
430 thread_id = next_thread_id++;
431 if (thread_id >= THBUF_SIZE) {
432 printf("Too many threads.\n");
433 return ELIMIT;
434 }
435
436 printf("Start tracing thread [%u] (hash %p).\n",
437 thread_id, (void *) thread_hash);
438
439 while (!abort_trace) {
440
441 fibril_mutex_lock(&state_lock);
442 if (paused) {
443 printf("Thread [%u] paused. Press R to resume.\n",
444 thread_id);
445
446 while (paused)
447 fibril_condvar_wait(&state_cv, &state_lock);
448
449 printf("Thread [%u] resumed.\n", thread_id);
450 }
451 fibril_mutex_unlock(&state_lock);
452
453 /* Run thread until an event occurs */
454 rc = udebug_go(sess, thread_hash,
455 &ev_type, &val0, &val1);
456
457 if (ev_type == UDEBUG_EVENT_FINISHED) {
458 /* Done tracing this thread */
459 break;
460 }
461
462 if (rc == EOK) {
463 switch (ev_type) {
464 case UDEBUG_EVENT_SYSCALL_B:
465 event_syscall_b(thread_id, thread_hash, val0, (int)val1);
466 break;
467 case UDEBUG_EVENT_SYSCALL_E:
468 event_syscall_e(thread_id, thread_hash, val0, (int)val1);
469 break;
470 case UDEBUG_EVENT_STOP:
471 printf("Stop event\n");
472 fibril_mutex_lock(&state_lock);
473 paused = true;
474 fibril_mutex_unlock(&state_lock);
475 break;
476 case UDEBUG_EVENT_THREAD_B:
477 event_thread_b(val0);
478 break;
479 case UDEBUG_EVENT_THREAD_E:
480 printf("Thread %" PRIun " exited.\n", val0);
481 fibril_mutex_lock(&state_lock);
482 abort_trace = true;
483 fibril_condvar_broadcast(&state_cv);
484 fibril_mutex_unlock(&state_lock);
485 break;
486 default:
487 printf("Unknown event type %d.\n", ev_type);
488 break;
489 }
490 }
491
492 }
493
494 printf("Finished tracing thread [%d].\n", thread_id);
495 return EOK;
496}
497
498void thread_trace_start(uintptr_t thread_hash)
499{
500 fid_t fid;
501
502 thash = thread_hash;
503
504 fid = fibril_create(trace_loop, (void *)thread_hash);
505 if (fid == 0) {
506 printf("Warning: Failed creating fibril\n");
507 }
508 fibril_add_ready(fid);
509}
510
511static errno_t cev_fibril(void *arg)
512{
513 cons_event_t event;
514
515 (void) arg;
516
517 console_ctrl_t *console = console_init(stdin, stdout);
518
519 while (true) {
520 fibril_mutex_lock(&state_lock);
521 while (cev_valid)
522 fibril_condvar_wait(&state_cv, &state_lock);
523 fibril_mutex_unlock(&state_lock);
524
525 if (!console_get_event(console, &event))
526 return EINVAL;
527
528 if (event.type == CEV_KEY) {
529 fibril_mutex_lock(&state_lock);
530 cev = event.ev.key;
531 cev_valid = true;
532 fibril_condvar_broadcast(&state_cv);
533 fibril_mutex_unlock(&state_lock);
534 }
535 }
536}
537
538static void trace_task(task_id_t task_id)
539{
540 kbd_event_t ev;
541 bool done;
542 int i;
543 errno_t rc;
544
545 ipcp_init();
546
547 rc = get_thread_list();
548 if (rc != EOK) {
549 printf("Failed to get thread list (%s)\n", str_error(rc));
550 return;
551 }
552
553 abort_trace = false;
554
555 for (i = 0; i < n_threads; i++) {
556 thread_trace_start(thread_hash_buf[i]);
557 }
558
559 done = false;
560
561 while (!done) {
562 fibril_mutex_lock(&state_lock);
563 while (!cev_valid && !abort_trace)
564 fibril_condvar_wait(&state_cv, &state_lock);
565 fibril_mutex_unlock(&state_lock);
566
567 ev = cev;
568
569 fibril_mutex_lock(&state_lock);
570 cev_valid = false;
571 fibril_condvar_broadcast(&state_cv);
572 fibril_mutex_unlock(&state_lock);
573
574 if (abort_trace)
575 break;
576
577 if (ev.type != KEY_PRESS)
578 continue;
579
580 switch (ev.key) {
581 case KC_Q:
582 done = true;
583 break;
584 case KC_P:
585 printf("Pause...\n");
586 rc = udebug_stop(sess, thash);
587 if (rc != EOK)
588 printf("Error: stop -> %s\n", str_error_name(rc));
589 break;
590 case KC_R:
591 fibril_mutex_lock(&state_lock);
592 paused = false;
593 fibril_condvar_broadcast(&state_cv);
594 fibril_mutex_unlock(&state_lock);
595 printf("Resume...\n");
596 break;
597 default:
598 break;
599 }
600 }
601
602 printf("\nTerminate debugging session...\n");
603 abort_trace = true;
604 udebug_end(sess);
605 async_hangup(sess);
606
607 ipcp_cleanup();
608
609 printf("Done\n");
610 return;
611}
612
613static void main_init(void)
614{
615 proto_t *p;
616 oper_t *o;
617
618 val_type_t arg_def[OPER_MAX_ARGS] = {
619 V_INTEGER,
620 V_INTEGER,
621 V_INTEGER,
622 V_INTEGER,
623 V_INTEGER
624 };
625
626 val_type_t resp_def[OPER_MAX_ARGS] = {
627 V_INTEGER,
628 V_INTEGER,
629 V_INTEGER,
630 V_INTEGER,
631 V_INTEGER
632 };
633
634 next_thread_id = 1;
635 paused = false;
636 cev_valid = false;
637
638 fibril_mutex_initialize(&state_lock);
639 fibril_condvar_initialize(&state_cv);
640
641 proto_init();
642
643 p = proto_new("vfs");
644 o = oper_new("read", 3, arg_def, V_ERRNO, 1, resp_def);
645 proto_add_oper(p, VFS_IN_READ, o);
646 o = oper_new("write", 3, arg_def, V_ERRNO, 1, resp_def);
647 proto_add_oper(p, VFS_IN_WRITE, o);
648 o = oper_new("vfs_resize", 5, arg_def, V_ERRNO, 0, resp_def);
649 proto_add_oper(p, VFS_IN_RESIZE, o);
650 o = oper_new("vfs_stat", 1, arg_def, V_ERRNO, 0, resp_def);
651 proto_add_oper(p, VFS_IN_STAT, o);
652 o = oper_new("vfs_put", 1, arg_def, V_ERRNO, 0, resp_def);
653 proto_add_oper(p, VFS_IN_PUT, o);
654 o = oper_new("vfs_mount", 2, arg_def, V_ERRNO, 0, resp_def);
655 proto_add_oper(p, VFS_IN_MOUNT, o);
656#if 0
657 o = oper_new("unmount", 0, arg_def);
658 proto_add_oper(p, VFS_IN_UNMOUNT, o);
659#endif
660 o = oper_new("vfs_sync", 1, arg_def, V_ERRNO, 0, resp_def);
661 proto_add_oper(p, VFS_IN_SYNC, o);
662 o = oper_new("rename", 0, arg_def, V_ERRNO, 0, resp_def);
663 proto_add_oper(p, VFS_IN_RENAME, o);
664 o = oper_new("vfs_statfs", 0, arg_def, V_ERRNO, 0, resp_def);
665 proto_add_oper(p, VFS_IN_STATFS, o);
666 o = oper_new("vfs_walk", 2, arg_def, V_INT_ERRNO, 0, resp_def);
667 proto_add_oper(p, VFS_IN_WALK, o);
668 o = oper_new("vfs_open", 2, arg_def, V_ERRNO, 0, resp_def);
669 proto_add_oper(p, VFS_IN_OPEN, o);
670 o = oper_new("vfs_unlink", 3, arg_def, V_ERRNO, 0, resp_def);
671 proto_add_oper(p, VFS_IN_UNLINK, o);
672
673 proto_register(SERVICE_VFS, p);
674}
675
676static void print_syntax(void)
677{
678 printf("Syntax:\n");
679 printf("\ttrace [+<events>] <executable> [<arg1> [...]]\n");
680 printf("or\ttrace [+<events>] -t <task_id>\n");
681 printf("Events: (default is +tp)\n");
682 printf("\n");
683 printf("\tt ... Thread creation and termination\n");
684 printf("\ts ... System calls\n");
685 printf("\ti ... Low-level IPC\n");
686 printf("\tp ... Protocol level\n");
687 printf("\n");
688 printf("Examples:\n");
689 printf("\ttrace +s /app/tetris\n");
690 printf("\ttrace +tsip -t 12\n");
691}
692
693static display_mask_t parse_display_mask(const char *text)
694{
695 display_mask_t dm = 0;
696 const char *c = text;
697
698 while (*c) {
699 switch (*c) {
700 case 't':
701 dm = dm | DM_THREAD;
702 break;
703 case 's':
704 dm = dm | DM_SYSCALL;
705 break;
706 case 'i':
707 dm = dm | DM_IPC;
708 break;
709 case 'p':
710 dm = dm | DM_SYSTEM | DM_USER;
711 break;
712 default:
713 printf("Unexpected event type '%c'.\n", *c);
714 exit(1);
715 }
716
717 ++c;
718 }
719
720 return dm;
721}
722
723static int parse_args(int argc, char *argv[])
724{
725 char *err_p;
726
727 task_id = 0;
728
729 --argc;
730 ++argv;
731
732 while (argc > 0) {
733 char *arg = *argv;
734 if (arg[0] == '+') {
735 display_mask = parse_display_mask(&arg[1]);
736 } else if (arg[0] == '-') {
737 if (arg[1] == 't') {
738 /* Trace an already running task */
739 --argc;
740 ++argv;
741 task_id = strtol(*argv, &err_p, 10);
742 task_wait_for = false;
743 if (*err_p) {
744 printf("Task ID syntax error\n");
745 print_syntax();
746 return -1;
747 }
748 } else {
749 printf("Uknown option '%c'\n", arg[0]);
750 print_syntax();
751 return -1;
752 }
753 } else {
754 break;
755 }
756
757 --argc;
758 ++argv;
759 }
760
761 if (task_id != 0) {
762 if (argc == 0)
763 return 0;
764 printf("Extra arguments\n");
765 print_syntax();
766 return -1;
767 }
768
769 if (argc < 1) {
770 printf("Missing argument\n");
771 print_syntax();
772 return -1;
773 }
774
775 /* Preload the specified program file. */
776 printf("Spawning '%s' with arguments:\n", *argv);
777
778 char **cp = argv;
779 while (*cp)
780 printf("'%s'\n", *cp++);
781
782 cmd_path = *argv;
783 cmd_args = argv;
784 task_wait_for = true;
785
786 return 0;
787}
788
789int main(int argc, char *argv[])
790{
791 errno_t rc;
792 task_exit_t texit;
793 int retval;
794
795 printf("System Call / IPC Tracer\n");
796 printf("Controls: Q - Quit, P - Pause, R - Resume\n");
797
798 display_mask = DM_THREAD | DM_SYSTEM | DM_USER;
799
800 if (parse_args(argc, argv) < 0)
801 return 1;
802
803 main_init();
804
805 if (cmd_path != NULL)
806 program_run();
807
808 rc = connect_task(task_id);
809 if (rc != EOK) {
810 printf("Failed connecting to task %" PRIu64 ".\n", task_id);
811 return 1;
812 }
813
814 printf("Connected to task %" PRIu64 ".\n", task_id);
815
816 cev_fibril_start();
817 trace_task(task_id);
818
819 if (task_wait_for) {
820 printf("Waiting for task to exit.\n");
821
822 rc = task_wait(&task_w, &texit, &retval);
823 if (rc != EOK) {
824 printf("Failed waiting for task.\n");
825 return -1;
826 }
827
828 if (texit == TASK_EXIT_NORMAL) {
829 printf("Task exited normally, return value %d.\n",
830 retval);
831 } else {
832 printf("Task exited unexpectedly.\n");
833 }
834 }
835
836 return 0;
837}
838
839/** @}
840 */
Note: See TracBrowser for help on using the repository browser.