Subversion Repositories HelenOS

Rev

Rev 4496 | Go to most recent revision | Blame | Compare with Previous | Last modification | View Log | Download | RSS feed

  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 <unistd.h>
  38. #include <ipc/ipc.h>
  39. #include <fibril.h>
  40. #include <errno.h>
  41. #include <udebug.h>
  42. #include <async.h>
  43. #include <task.h>
  44. #include <mem.h>
  45. #include <string.h>
  46. #include <loader/loader.h>
  47.  
  48. #include <libc.h>
  49.  
  50. // Temporary: service and method names
  51. #include "proto.h"
  52. #include <ipc/services.h>
  53. #include "../../srv/vfs/vfs.h"
  54. #include <ipc/console.h>
  55.  
  56. #include "syscalls.h"
  57. #include "ipcp.h"
  58. #include "errors.h"
  59. #include "trace.h"
  60.  
  61. #define THBUF_SIZE 64
  62. uintptr_t thread_hash_buf[THBUF_SIZE];
  63. int n_threads;
  64.  
  65. int next_thread_id;
  66.  
  67. int phoneid;
  68. int abort_trace;
  69.  
  70. uintptr_t thash;
  71. volatile int paused;
  72.  
  73. void thread_trace_start(uintptr_t thread_hash);
  74.  
  75. static proto_t *proto_console;
  76. static task_id_t task_id;
  77. static loader_t *task_ldr;
  78.  
  79. /** Combination of events/data to print. */
  80. display_mask_t display_mask;
  81.  
  82. static int program_run_fibril(void *arg);
  83.  
  84. static void program_run(void)
  85. {
  86.     fid_t fid;
  87.  
  88.     fid = fibril_create(program_run_fibril, NULL);
  89.     if (fid == 0) {
  90.         printf("Error creating fibril\n");
  91.         exit(1);
  92.     }
  93.  
  94.     fibril_add_ready(fid);
  95. }
  96.  
  97. static int program_run_fibril(void *arg)
  98. {
  99.     int rc;
  100.  
  101.     /*
  102.      * This must be done in background as it will block until
  103.      * we let the task reply to this call.
  104.      */
  105.     rc = loader_run(task_ldr);
  106.     if (rc != 0) {
  107.         printf("Error running program\n");
  108.         exit(1);
  109.     }
  110.  
  111.     free(task_ldr);
  112.     task_ldr = NULL;
  113.  
  114.     printf("program_run_fibril exiting\n");
  115.     return 0;
  116. }
  117.  
  118.  
  119. static int connect_task(task_id_t task_id)
  120. {
  121.     int rc;
  122.  
  123.     rc = ipc_connect_kbox(task_id);
  124.  
  125.     if (rc == ENOTSUP) {
  126.         printf("You do not have userspace debugging support "
  127.             "compiled in the kernel.\n");
  128.         printf("Compile kernel with 'Support for userspace debuggers' "
  129.             "(CONFIG_UDEBUG) enabled.\n");
  130.         return rc;
  131.     }
  132.  
  133.     if (rc < 0) {
  134.         printf("Error connecting\n");
  135.         printf("ipc_connect_task(%lld) -> %d ", task_id, rc);
  136.         return rc;
  137.     }
  138.  
  139.     phoneid = rc;
  140.  
  141.     rc = udebug_begin(phoneid);
  142.     if (rc < 0) {
  143.         printf("udebug_begin() -> %d\n", rc);
  144.         return rc;
  145.     }
  146.  
  147.     rc = udebug_set_evmask(phoneid, UDEBUG_EM_ALL);
  148.     if (rc < 0) {
  149.         printf("udebug_set_evmask(0x%x) -> %d\n ", UDEBUG_EM_ALL, rc);
  150.         return rc;
  151.     }
  152.  
  153.     return 0;
  154. }
  155.  
  156. static int get_thread_list(void)
  157. {
  158.     int rc;
  159.     size_t tb_copied;
  160.     size_t tb_needed;
  161.     int i;
  162.  
  163.     rc = udebug_thread_read(phoneid, thread_hash_buf,
  164.         THBUF_SIZE*sizeof(unsigned), &tb_copied, &tb_needed);
  165.     if (rc < 0) {
  166.         printf("udebug_thread_read() -> %d\n", rc);
  167.         return rc;
  168.     }
  169.  
  170.     n_threads = tb_copied / sizeof(uintptr_t);
  171.  
  172.     printf("Threads:");
  173.     for (i = 0; i < n_threads; i++) {
  174.         printf(" [%d] (hash 0x%lx)", 1+i, thread_hash_buf[i]);
  175.     }
  176.     printf("\ntotal of %u threads\n", tb_needed / sizeof(uintptr_t));
  177.  
  178.     return 0;
  179. }
  180.  
  181. void val_print(sysarg_t val, val_type_t v_type)
  182. {
  183.     switch (v_type) {
  184.     case V_VOID:
  185.         printf("<void>");
  186.         break;
  187.  
  188.     case V_INTEGER:
  189.         printf("%ld", val);
  190.         break;
  191.  
  192.     case V_HASH:
  193.     case V_PTR:
  194.         printf("0x%08lx", val);
  195.         break;
  196.  
  197.     case V_ERRNO:
  198.         if (val >= -15 && val <= 0) {
  199.             printf("%ld %s (%s)", val,
  200.                 err_desc[-val].name,
  201.                 err_desc[-val].desc);
  202.         } else {
  203.             printf("%ld", val);
  204.         }
  205.         break;
  206.     case V_INT_ERRNO:
  207.         if (val >= -15 && val < 0) {
  208.             printf("%ld %s (%s)", val,
  209.                 err_desc[-val].name,
  210.                 err_desc[-val].desc);
  211.         } else {
  212.             printf("%ld", val);
  213.         }
  214.         break;
  215.  
  216.     case V_CHAR:
  217.         if (val >= 0x20 && val < 0x7f) {
  218.             printf("'%c'", val);
  219.         } else {
  220.             switch (val) {
  221.             case '\a': printf("'\\a'"); break;
  222.             case '\b': printf("'\\b'"); break;
  223.             case '\n': printf("'\\n'"); break;
  224.             case '\r': printf("'\\r'"); break;
  225.             case '\t': printf("'\\t'"); break;
  226.             case '\\': printf("'\\\\'"); break;
  227.             default: printf("'\\x%02lX'", val); break;
  228.             }
  229.         }
  230.         break;
  231.     }
  232. }
  233.  
  234.  
  235. static void print_sc_retval(sysarg_t retval, val_type_t val_type)
  236. {
  237.     printf(" -> ");
  238.     val_print(retval, val_type);
  239.     putchar('\n');
  240. }
  241.  
  242. static void print_sc_args(sysarg_t *sc_args, int n)
  243. {
  244.     int i;
  245.  
  246.     putchar('(');
  247.     if (n > 0) printf("%ld", sc_args[0]);
  248.     for (i = 1; i < n; i++) {
  249.         printf(", %ld", sc_args[i]);
  250.     }
  251.     putchar(')');
  252. }
  253.  
  254. static void sc_ipc_call_async_fast(sysarg_t *sc_args, sysarg_t sc_rc)
  255. {
  256.     ipc_call_t call;
  257.     ipcarg_t phoneid;
  258.    
  259.     if (sc_rc == IPC_CALLRET_FATAL || sc_rc == IPC_CALLRET_TEMPORARY)
  260.         return;
  261.  
  262.     phoneid = sc_args[0];
  263.  
  264.     IPC_SET_METHOD(call, sc_args[1]);
  265.     IPC_SET_ARG1(call, sc_args[2]);
  266.     IPC_SET_ARG2(call, sc_args[3]);
  267.     IPC_SET_ARG3(call, sc_args[4]);
  268.     IPC_SET_ARG4(call, sc_args[5]);
  269.     IPC_SET_ARG5(call, 0);
  270.  
  271.     ipcp_call_out(phoneid, &call, sc_rc);
  272. }
  273.  
  274. static void sc_ipc_call_async_slow(sysarg_t *sc_args, sysarg_t sc_rc)
  275. {
  276.     ipc_call_t call;
  277.     int rc;
  278.  
  279.     if (sc_rc == IPC_CALLRET_FATAL || sc_rc == IPC_CALLRET_TEMPORARY)
  280.         return;
  281.  
  282.     memset(&call, 0, sizeof(call));
  283.     rc = udebug_mem_read(phoneid, &call.args, sc_args[1], sizeof(call.args));
  284.  
  285.     if (rc >= 0) {
  286.         ipcp_call_out(sc_args[0], &call, sc_rc);
  287.     }
  288. }
  289.  
  290. static void sc_ipc_call_sync_fast(sysarg_t *sc_args)
  291. {
  292.     ipc_call_t question, reply;
  293.     int rc;
  294.     int phoneidx;
  295.  
  296. //  printf("sc_ipc_call_sync_fast()\n");
  297.     phoneidx = sc_args[0];
  298.  
  299.     IPC_SET_METHOD(question, sc_args[1]);
  300.     IPC_SET_ARG1(question, sc_args[2]);
  301.     IPC_SET_ARG2(question, sc_args[3]);
  302.     IPC_SET_ARG3(question, sc_args[4]);
  303.     IPC_SET_ARG4(question, 0);
  304.     IPC_SET_ARG5(question, 0);
  305.  
  306. //  printf("memset\n");
  307.     memset(&reply, 0, sizeof(reply));
  308. //  printf("udebug_mem_read(phone=%d, buffer_ptr=%u, src_addr=%d, n=%d\n",
  309. //      phoneid, &reply.args, sc_args[5], sizeof(reply.args));
  310.     rc = udebug_mem_read(phoneid, &reply.args, sc_args[5], sizeof(reply.args));
  311. //  printf("dmr->%d\n", rc);
  312.     if (rc < 0) return;
  313.  
  314. //  printf("call ipc_call_sync\n");
  315.     ipcp_call_sync(phoneidx, &question, &reply);
  316. }
  317.  
  318. static void sc_ipc_call_sync_slow(sysarg_t *sc_args)
  319. {
  320.     ipc_call_t question, reply;
  321.     int rc;
  322.  
  323.     memset(&question, 0, sizeof(question));
  324.     rc = udebug_mem_read(phoneid, &question.args, sc_args[1], sizeof(question.args));
  325.     printf("dmr->%d\n", rc);
  326.     if (rc < 0) return;
  327.  
  328.     memset(&reply, 0, sizeof(reply));
  329.     rc = udebug_mem_read(phoneid, &reply.args, sc_args[2], sizeof(reply.args));
  330.     printf("dmr->%d\n", rc);
  331.     if (rc < 0) return;
  332.  
  333.     ipcp_call_sync(sc_args[0], &question, &reply);
  334. }
  335.  
  336. static void sc_ipc_wait(sysarg_t *sc_args, int sc_rc)
  337. {
  338.     ipc_call_t call;
  339.     int rc;
  340.  
  341.     if (sc_rc == 0) return;
  342.  
  343.     memset(&call, 0, sizeof(call));
  344.     rc = udebug_mem_read(phoneid, &call, sc_args[0], sizeof(call));
  345. //  printf("udebug_mem_read(phone %d, dest %d, app-mem src %d, size %d -> %d\n",
  346. //      phoneid, (int)&call, sc_args[0], sizeof(call), rc);
  347.  
  348.     if (rc >= 0) {
  349.         ipcp_call_in(&call, sc_rc);
  350.     }
  351. }
  352.  
  353. static void event_syscall_b(unsigned thread_id, uintptr_t thread_hash,
  354.     unsigned sc_id, sysarg_t sc_rc)
  355. {
  356.     sysarg_t sc_args[6];
  357.     int rc;
  358.  
  359.     /* Read syscall arguments */
  360.     rc = udebug_args_read(phoneid, thread_hash, sc_args);
  361.  
  362.     async_serialize_start();
  363.  
  364. //  printf("[%d] ", thread_id);
  365.  
  366.     if (rc < 0) {
  367.         printf("error\n");
  368.         async_serialize_end();
  369.         return;
  370.     }
  371.  
  372.     if ((display_mask & DM_SYSCALL) != 0) {
  373.         /* Print syscall name and arguments */
  374.         printf("%s", syscall_desc[sc_id].name);
  375.         print_sc_args(sc_args, syscall_desc[sc_id].n_args);
  376.     }
  377.  
  378.     async_serialize_end();
  379. }
  380.  
  381. static void event_syscall_e(unsigned thread_id, uintptr_t thread_hash,
  382.     unsigned sc_id, sysarg_t sc_rc)
  383. {
  384.     sysarg_t sc_args[6];
  385.     int rv_type;
  386.     int rc;
  387.  
  388.     /* Read syscall arguments */
  389.     rc = udebug_args_read(phoneid, thread_hash, sc_args);
  390.  
  391.     async_serialize_start();
  392.  
  393. //  printf("[%d] ", thread_id);
  394.  
  395.     if (rc < 0) {
  396.         printf("error\n");
  397.         async_serialize_end();
  398.         return;
  399.     }
  400.  
  401.     if ((display_mask & DM_SYSCALL) != 0) {
  402.         /* Print syscall return value */
  403.         rv_type = syscall_desc[sc_id].rv_type;
  404.         print_sc_retval(sc_rc, rv_type);
  405.     }
  406.  
  407.     switch (sc_id) {
  408.     case SYS_IPC_CALL_ASYNC_FAST:
  409.         sc_ipc_call_async_fast(sc_args, sc_rc);
  410.         break;
  411.     case SYS_IPC_CALL_ASYNC_SLOW:
  412.         sc_ipc_call_async_slow(sc_args, sc_rc);
  413.         break;
  414.     case SYS_IPC_CALL_SYNC_FAST:
  415.         sc_ipc_call_sync_fast(sc_args);
  416.         break;
  417.     case SYS_IPC_CALL_SYNC_SLOW:
  418.         sc_ipc_call_sync_slow(sc_args);
  419.         break;
  420.     case SYS_IPC_WAIT:
  421.         sc_ipc_wait(sc_args, sc_rc);
  422.         break;
  423.     default:
  424.         break;
  425.     }
  426.  
  427.     async_serialize_end();
  428. }
  429.  
  430. static void event_thread_b(uintptr_t hash)
  431. {
  432.     async_serialize_start();
  433.     printf("New thread, hash 0x%lx\n", hash);
  434.     async_serialize_end();
  435.  
  436.     thread_trace_start(hash);
  437. }
  438.  
  439. static int trace_loop(void *thread_hash_arg)
  440. {
  441.     int rc;
  442.     unsigned ev_type;
  443.     uintptr_t thread_hash;
  444.     unsigned thread_id;
  445.     sysarg_t val0, val1;
  446.  
  447.     thread_hash = (uintptr_t)thread_hash_arg;
  448.     thread_id = next_thread_id++;
  449.  
  450.     printf("Start tracing thread [%d] (hash 0x%lx).\n", thread_id, thread_hash);
  451.  
  452.     while (!abort_trace) {
  453.  
  454.         if (paused) {
  455.             printf("Press R to resume (and be patient).\n");
  456.             while (paused) {
  457.                 usleep(1000000);
  458.                 fibril_yield();
  459.                 printf(".");
  460.             }
  461.             printf("Resumed\n");
  462.         }
  463.  
  464.         /* Run thread until an event occurs */
  465.         rc = udebug_go(phoneid, thread_hash,
  466.             &ev_type, &val0, &val1);
  467.  
  468. //      printf("rc = %d, ev_type=%d\n", rc, ev_type);
  469.         if (ev_type == UDEBUG_EVENT_FINISHED) {
  470.             /* Done tracing this thread */
  471.             break;
  472.         }
  473.  
  474.         if (rc >= 0) {
  475.             switch (ev_type) {
  476.             case UDEBUG_EVENT_SYSCALL_B:
  477.                 event_syscall_b(thread_id, thread_hash, val0, (int)val1);
  478.                 break;
  479.             case UDEBUG_EVENT_SYSCALL_E:
  480.                 event_syscall_e(thread_id, thread_hash, val0, (int)val1);
  481.                 break;
  482.             case UDEBUG_EVENT_STOP:
  483.                 printf("Stop event\n");
  484.                 break;
  485.             case UDEBUG_EVENT_THREAD_B:
  486.                 event_thread_b(val0);
  487.                 break;
  488.             case UDEBUG_EVENT_THREAD_E:
  489.                 printf("Thread 0x%lx exited.\n", val0);
  490.                 abort_trace = 1;
  491.                 break;
  492.             default:
  493.                 printf("Unknown event type %d.\n", ev_type);
  494.                 break;
  495.             }
  496.         }
  497.  
  498.     }
  499.  
  500.     printf("Finished tracing thread [%d].\n", thread_id);
  501.     return 0;
  502. }
  503.  
  504. void thread_trace_start(uintptr_t thread_hash)
  505. {
  506.     fid_t fid;
  507.  
  508.     thash = thread_hash;
  509.  
  510.     fid = fibril_create(trace_loop, (void *)thread_hash);
  511.     if (fid == 0) {
  512.         printf("Warning: Failed creating fibril\n");
  513.     }
  514.     fibril_add_ready(fid);
  515. }
  516.  
  517. static loader_t *preload_task(const char *path, char *const argv[],
  518.     task_id_t *task_id)
  519. {
  520.     loader_t *ldr;
  521.     int rc;
  522.  
  523.     /* Spawn a program loader */   
  524.     ldr = loader_connect();
  525.     if (ldr == NULL)
  526.         return 0;
  527.  
  528.     /* Get task ID. */
  529.     rc = loader_get_task_id(ldr, task_id);
  530.     if (rc != EOK)
  531.         goto error;
  532.  
  533.     /* Send program pathname */
  534.     rc = loader_set_pathname(ldr, path);
  535.     if (rc != EOK)
  536.         goto error;
  537.  
  538.     /* Send arguments */
  539.     rc = loader_set_args(ldr, argv);
  540.     if (rc != EOK)
  541.         goto error;
  542.  
  543.     /* Load the program. */
  544.     rc = loader_load_program(ldr);
  545.     if (rc != EOK)
  546.         goto error;
  547.  
  548.     /* Success */
  549.     return ldr;
  550.  
  551.     /* Error exit */
  552. error:
  553.     loader_abort(ldr);
  554.     free(ldr);
  555.     return NULL;
  556. }
  557.  
  558. static void trace_task(task_id_t task_id)
  559. {
  560.     int i;
  561.     int rc;
  562.     int c;
  563.  
  564.     ipcp_init();
  565.  
  566.     /*
  567.      * User apps now typically have console on phone 3.
  568.      * (Phones 1 and 2 are used by the loader).
  569.      */
  570.     ipcp_connection_set(3, 0, proto_console);
  571.  
  572.     rc = get_thread_list();
  573.     if (rc < 0) {
  574.         printf("Failed to get thread list (error %d)\n", rc);
  575.         return;
  576.     }
  577.  
  578.     abort_trace = 0;
  579.  
  580.     for (i = 0; i < n_threads; i++) {
  581.         thread_trace_start(thread_hash_buf[i]);
  582.     }
  583.  
  584.     while(1) {
  585.         c = getchar();
  586.         if (c == 'q') break;
  587.         if (c == 'p') {
  588.             printf("Pause...\n");
  589.             paused = 1;
  590.             rc = udebug_stop(phoneid, thash);
  591.             printf("stop -> %d\n", rc);
  592.         }
  593.         if (c == 'r') {
  594.             paused = 0;
  595.             printf("Resume...\n");
  596.         }
  597.     }
  598.  
  599.     printf("\nTerminate debugging session...\n");
  600.     abort_trace = 1;
  601.     udebug_end(phoneid);
  602.     ipc_hangup(phoneid);
  603.  
  604.     ipcp_cleanup();
  605.  
  606.     printf("Done\n");
  607.     return;
  608. }
  609.  
  610. static void main_init(void)
  611. {
  612.     proto_t *p;
  613.     oper_t *o;
  614.  
  615.     val_type_t arg_def[OPER_MAX_ARGS] = {
  616.         V_INTEGER,
  617.         V_INTEGER,
  618.         V_INTEGER,
  619.         V_INTEGER,
  620.         V_INTEGER      
  621.     };
  622.  
  623.     val_type_t resp_def[OPER_MAX_ARGS] = {
  624.         V_INTEGER,
  625.         V_INTEGER,
  626.         V_INTEGER,
  627.         V_INTEGER,
  628.         V_INTEGER
  629.     };
  630.  
  631.     next_thread_id = 1;
  632.     paused = 0;
  633.  
  634.     proto_init();
  635.  
  636.     p = proto_new("vfs");
  637.     o = oper_new("read", 1, arg_def, V_ERRNO, 1, resp_def);
  638.     proto_add_oper(p, VFS_IN_READ, o);
  639.     o = oper_new("write", 1, arg_def, V_ERRNO, 1, resp_def);
  640.     proto_add_oper(p, VFS_IN_WRITE, o);
  641.     o = oper_new("truncate", 5, arg_def, V_ERRNO, 0, resp_def);
  642.     proto_add_oper(p, VFS_IN_TRUNCATE, o);
  643.     o = oper_new("mount", 2, arg_def, V_ERRNO, 0, resp_def);
  644.     proto_add_oper(p, VFS_IN_MOUNT, o);
  645. /*  o = oper_new("unmount", 0, arg_def);
  646.     proto_add_oper(p, VFS_IN_UNMOUNT, o);*/
  647.     o = oper_new("open", 2, arg_def, V_INT_ERRNO, 0, resp_def);
  648.     proto_add_oper(p, VFS_IN_OPEN, o);
  649.     o = oper_new("close", 1, arg_def, V_ERRNO, 0, resp_def);
  650.     proto_add_oper(p, VFS_IN_CLOSE, o);
  651.     o = oper_new("seek", 3, arg_def, V_ERRNO, 0, resp_def);
  652.     proto_add_oper(p, VFS_IN_SEEK, o);
  653.     o = oper_new("mkdir", 1, arg_def, V_ERRNO, 0, resp_def);
  654.     proto_add_oper(p, VFS_IN_MKDIR, o);
  655.     o = oper_new("unlink", 0, arg_def, V_ERRNO, 0, resp_def);
  656.     proto_add_oper(p, VFS_IN_UNLINK, o);
  657.     o = oper_new("rename", 0, arg_def, V_ERRNO, 0, resp_def);
  658.     proto_add_oper(p, VFS_IN_RENAME, o);
  659.  
  660.     proto_register(SERVICE_VFS, p);
  661.  
  662.     p = proto_new("console");
  663.     resp_def[0] = V_INTEGER; resp_def[1] = V_INTEGER;
  664.     resp_def[2] = V_INTEGER; resp_def[3] = V_CHAR;
  665.     o = oper_new("getkey", 0, arg_def, V_ERRNO, 4, resp_def);
  666.  
  667.     arg_def[0] = V_CHAR;
  668.     o = oper_new("clear", 0, arg_def, V_VOID, 0, resp_def);
  669.     proto_add_oper(p, CONSOLE_CLEAR, o);
  670.  
  671.     arg_def[0] = V_INTEGER; arg_def[1] = V_INTEGER;
  672.     o = oper_new("goto", 2, arg_def, V_VOID, 0, resp_def);
  673.     proto_add_oper(p, CONSOLE_GOTO, o);
  674.  
  675.     resp_def[0] = V_INTEGER; resp_def[1] = V_INTEGER;
  676.     o = oper_new("getsize", 0, arg_def, V_INTEGER, 2, resp_def);
  677.     proto_add_oper(p, CONSOLE_GET_SIZE, o);
  678.  
  679.     arg_def[0] = V_INTEGER;
  680.     o = oper_new("set_style", 1, arg_def, V_VOID, 0, resp_def);
  681.     proto_add_oper(p, CONSOLE_SET_STYLE, o);
  682.     arg_def[0] = V_INTEGER; arg_def[1] = V_INTEGER; arg_def[2] = V_INTEGER;
  683.     o = oper_new("set_color", 3, arg_def, V_VOID, 0, resp_def);
  684.     proto_add_oper(p, CONSOLE_SET_COLOR, o);
  685.     arg_def[0] = V_INTEGER; arg_def[1] = V_INTEGER;
  686.     o = oper_new("set_rgb_color", 2, arg_def, V_VOID, 0, resp_def);
  687.     proto_add_oper(p, CONSOLE_SET_RGB_COLOR, o);
  688.     o = oper_new("cursor_visibility", 1, arg_def, V_VOID, 0, resp_def);
  689.     proto_add_oper(p, CONSOLE_CURSOR_VISIBILITY, o);
  690.  
  691.     proto_console = p;
  692.     proto_register(SERVICE_CONSOLE, p);
  693. }
  694.  
  695. static void print_syntax()
  696. {
  697.     printf("Syntax:\n");
  698.     printf("\ttrace [+<events>] <executable> [<arg1> [...]]\n");
  699.     printf("or\ttrace [+<events>] -t <task_id>\n");
  700.     printf("Events: (default is +tp)\n");
  701.     printf("\n");
  702.     printf("\tt ... Thread creation and termination\n");
  703.     printf("\ts ... System calls\n");
  704.     printf("\ti ... Low-level IPC\n");
  705.     printf("\tp ... Protocol level\n");
  706.     printf("\n");
  707.     printf("Examples:\n");
  708.     printf("\ttrace +s /app/tetris\n");
  709.     printf("\ttrace +tsip -t 12\n");
  710. }
  711.  
  712. static display_mask_t parse_display_mask(char *text)
  713. {
  714.     display_mask_t dm;
  715.     char *c;
  716.  
  717.     c = text;
  718.  
  719.     while (*c) {
  720.         switch (*c) {
  721.         case 't': dm = dm | DM_THREAD; break;
  722.         case 's': dm = dm | DM_SYSCALL; break;
  723.         case 'i': dm = dm | DM_IPC; break;
  724.         case 'p': dm = dm | DM_SYSTEM | DM_USER; break;
  725.         default:
  726.             printf("Unexpected event type '%c'.\n", *c);
  727.             exit(1);
  728.         }
  729.  
  730.         ++c;
  731.     }
  732.  
  733.     return dm;
  734. }
  735.  
  736. static int parse_args(int argc, char *argv[])
  737. {
  738.     char *arg;
  739.     char *err_p;
  740.  
  741.     task_id = 0;
  742.  
  743.     --argc; ++argv;
  744.  
  745.     while (argc > 0) {
  746.         arg = *argv;
  747.         if (arg[0] == '+') {
  748.             display_mask = parse_display_mask(&arg[1]);
  749.         } else if (arg[0] == '-') {
  750.             if (arg[1] == 't') {
  751.                 /* Trace an already running task */
  752.                 --argc; ++argv;
  753.                 task_id = strtol(*argv, &err_p, 10);
  754.                 task_ldr = NULL;
  755.                 if (*err_p) {
  756.                     printf("Task ID syntax error\n");
  757.                     print_syntax();
  758.                     return -1;
  759.                 }
  760.             } else {
  761.                 printf("Uknown option '%s'\n", arg[0]);
  762.                 print_syntax();
  763.                 return -1;
  764.             }
  765.         } else {
  766.             break;
  767.         }
  768.  
  769.         --argc; ++argv;
  770.     }
  771.  
  772.     if (task_id != 0) {
  773.         if (argc == 0) return 0;
  774.         printf("Extra arguments\n");
  775.         print_syntax();
  776.         return -1;
  777.     }
  778.  
  779.     if (argc < 1) {
  780.         printf("Missing argument\n");
  781.         print_syntax();
  782.         return -1;
  783.     }
  784.  
  785.     /* Preload the specified program file. */
  786.     printf("Spawning '%s' with arguments:\n", *argv);
  787.     {
  788.         char **cp = argv;
  789.         while (*cp) printf("'%s'\n", *cp++);
  790.     }
  791.     task_ldr = preload_task(*argv, argv, &task_id);
  792.  
  793.     return 0;
  794. }
  795.  
  796. int main(int argc, char *argv[])
  797. {
  798.     int rc;
  799.  
  800.     printf("System Call / IPC Tracer\n");
  801.     printf("Controls: Q - Quit, P - Pause, R - Resume\n");
  802.  
  803.     display_mask = DM_THREAD | DM_SYSTEM | DM_USER;
  804.  
  805.     if (parse_args(argc, argv) < 0)
  806.         return 1;
  807.  
  808.     main_init();
  809.  
  810.     rc = connect_task(task_id);
  811.     if (rc < 0) {
  812.         printf("Failed connecting to task %lld.\n", task_id);
  813.         return 1;
  814.     }
  815.  
  816.     printf("Connected to task %lld.\n", task_id);
  817.  
  818.     if (task_ldr != NULL) {
  819.         program_run();
  820.     }
  821.  
  822.     trace_task(task_id);
  823.  
  824.     return 0;
  825. }
  826.  
  827. /** @}
  828.  */
  829.