Subversion Repositories HelenOS

Rev

Rev 3444 | Rev 3454 | 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 <syscall.h>
  39. #include <ipc/ipc.h>
  40. #include <fibril.h>
  41. #include <errno.h>
  42. #include <udebug.h>
  43. #include <async.h>
  44. #include <task.h>
  45.  
  46. // Temporary: service and method names
  47. #include "proto.h"
  48. #include <ipc/services.h>
  49. #include "../../srv/vfs/vfs.h"
  50. #include "../../srv/console/console.h"
  51.  
  52. #include "syscalls.h"
  53. #include "ipcp.h"
  54. #include "errors.h"
  55. #include "trace.h"
  56.  
  57. #define THBUF_SIZE 64
  58. unsigned thread_hash_buf[THBUF_SIZE];
  59. unsigned n_threads;
  60.  
  61. int next_thread_id;
  62.  
  63. int phoneid;
  64. int abort_trace;
  65.  
  66. unsigned thash;
  67. volatile int paused;
  68.  
  69. void thread_trace_start(unsigned thread_hash);
  70.  
  71. static proto_t *proto_console;
  72. static task_id_t task_id;
  73.  
  74. /** Combination of events/data to print. */
  75. display_mask_t display_mask;
  76.  
  77. static int task_connect(task_id_t task_id)
  78. {
  79.     int rc;
  80.  
  81.     rc = ipc_connect_kbox(task_id);
  82.  
  83.     if (rc == ENOTSUP) {
  84.         printf("You do not have userspace debugging support "
  85.             "compiled in the kernel.\n");
  86.         printf("Compile kernel with 'Support for userspace debuggers' "
  87.             "(CONFIG_UDEBUG) enabled.\n");
  88.         return rc;
  89.     }
  90.  
  91.     if (rc < 0) {
  92.         printf("Error connecting\n");
  93.         printf("ipc_connect_task(%lld) -> %d ", task_id, rc);
  94.         return rc;
  95.     }
  96.  
  97.     phoneid = rc;
  98.  
  99.     rc = udebug_begin(phoneid);
  100.     if (rc < 0) {
  101.         printf("udebug_begin() -> %d\n", rc);
  102.         return rc;
  103.     }
  104.  
  105.     rc = udebug_set_evmask(phoneid, UDEBUG_EM_ALL);
  106.     if (rc < 0) {
  107.         printf("udebug_set_evmask(0x%x) -> %d\n ", UDEBUG_EM_ALL, rc);
  108.         return rc;
  109.     }
  110.  
  111.     return 0;
  112. }
  113.  
  114. static int get_thread_list(void)
  115. {
  116.     int rc;
  117.     size_t tb_copied;
  118.     size_t tb_needed;
  119.     int i;
  120.  
  121.     rc = udebug_thread_read(phoneid, thread_hash_buf,
  122.         THBUF_SIZE*sizeof(unsigned), &tb_copied, &tb_needed);
  123.     if (rc < 0) {
  124.         printf("udebug_thread_read() -> %d\n", rc);
  125.         return rc;
  126.     }
  127.  
  128.     n_threads = tb_copied / sizeof(unsigned);
  129.  
  130.     printf("Threads:");
  131.     for (i = 0; i < n_threads; i++) {
  132.         printf(" [%d] (hash 0x%x)", 1+i, thread_hash_buf[i]);
  133.     }
  134.     printf("\ntotal of %u threads\n", tb_needed/sizeof(unsigned));
  135.  
  136.     return 0;
  137. }
  138.  
  139. static void print_sc_retval(int retval, rv_type_t rv_type)
  140. {
  141.     printf (" -> ");
  142.     if (rv_type == RV_INTEGER) {
  143.         printf("%d", retval);
  144.     } else if (rv_type == RV_HASH) {
  145.         printf("0x%08x", retval);
  146.     } else if (rv_type == RV_ERRNO) {
  147.         if (retval >= -15 && retval <= 0) {
  148.             printf("%d %s (%s)", retval,
  149.                 err_desc[retval].name,
  150.                 err_desc[retval].desc);
  151.         } else {
  152.             printf("%d", retval);
  153.         }
  154.     } else if (rv_type == RV_INT_ERRNO) {
  155.         if (retval >= -15 && retval < 0) {
  156.             printf("%d %s (%s)", retval,
  157.                 err_desc[retval].name,
  158.                 err_desc[retval].desc);
  159.         } else {
  160.             printf("%d", retval);
  161.         }
  162.     }
  163.     putchar('\n');
  164. }
  165.  
  166. static void print_sc_args(unsigned *sc_args, int n)
  167. {
  168.     int i;
  169.  
  170.     putchar('(');
  171.     if (n > 0) printf("%d", sc_args[0]);
  172.     for (i=1; i<n; i++) {
  173.         printf(", %d", sc_args[i]);
  174.     }
  175.     putchar(')');
  176. }
  177.  
  178. static void sc_ipc_call_async_fast(unsigned *sc_args, int sc_rc)
  179. {
  180.     ipc_call_t call;
  181.     int phoneid;
  182.    
  183.     if (sc_rc == IPC_CALLRET_FATAL || sc_rc == IPC_CALLRET_TEMPORARY)
  184.         return;
  185.  
  186.     phoneid = sc_args[0];
  187.  
  188.     IPC_SET_METHOD(call, sc_args[1]);
  189.     IPC_SET_ARG1(call, sc_args[2]);
  190.     IPC_SET_ARG2(call, sc_args[3]);
  191.     IPC_SET_ARG3(call, sc_args[4]);
  192.     IPC_SET_ARG4(call, sc_args[5]);
  193.     IPC_SET_ARG5(call, 0);
  194.  
  195.     ipcp_call_out(phoneid, &call, sc_rc);
  196. }
  197.  
  198. static void sc_ipc_call_async_slow(unsigned *sc_args, int sc_rc)
  199. {
  200.     ipc_call_t call;
  201.     int rc;
  202.  
  203.     if (sc_rc == IPC_CALLRET_FATAL || sc_rc == IPC_CALLRET_TEMPORARY)
  204.         return;
  205.  
  206.     memset(&call, 0, sizeof(call));
  207.     rc = udebug_mem_read(phoneid, &call.args, sc_args[1], sizeof(call.args));
  208.  
  209.     if (rc >= 0) {
  210.         ipcp_call_out(sc_args[0], &call, sc_rc);
  211.     }
  212. }
  213.  
  214. static void sc_ipc_call_sync_fast(unsigned *sc_args)
  215. {
  216.     ipc_call_t question, reply;
  217.     int rc;
  218.     int phoneidx;
  219.  
  220. //  printf("sc_ipc_call_sync_fast()\n");
  221.     phoneidx = sc_args[0];
  222.  
  223.     IPC_SET_METHOD(question, sc_args[1]);
  224.     IPC_SET_ARG1(question, sc_args[2]);
  225.     IPC_SET_ARG2(question, sc_args[3]);
  226.     IPC_SET_ARG3(question, sc_args[4]);
  227.     IPC_SET_ARG4(question, 0);
  228.     IPC_SET_ARG5(question, 0);
  229.  
  230. //  printf("memset\n");
  231.     memset(&reply, 0, sizeof(reply));
  232. //  printf("udebug_mem_read(phone=%d, buffer_ptr=%u, src_addr=%d, n=%d\n",
  233. //      phoneid, &reply.args, sc_args[5], sizeof(reply.args));
  234.     rc = udebug_mem_read(phoneid, &reply.args, sc_args[5], sizeof(reply.args));
  235. //  printf("dmr->%d\n", rc);
  236.     if (rc < 0) return;
  237.  
  238. //  printf("call ipc_call_sync\n");
  239.     ipcp_call_sync(phoneidx, &question, &reply);
  240. }
  241.  
  242. static void sc_ipc_call_sync_slow(unsigned *sc_args)
  243. {
  244.     ipc_call_t question, reply;
  245.     int rc;
  246.  
  247.     memset(&question, 0, sizeof(question));
  248.     rc = udebug_mem_read(phoneid, &question.args, sc_args[1], sizeof(question.args));
  249.     printf("dmr->%d\n", rc);
  250.     if (rc < 0) return;
  251.  
  252.     memset(&reply, 0, sizeof(reply));
  253.     rc = udebug_mem_read(phoneid, &reply.args, sc_args[2], sizeof(reply.args));
  254.     printf("dmr->%d\n", rc);
  255.     if (rc < 0) return;
  256.  
  257.     ipcp_call_sync(sc_args[0], &question, &reply);
  258. }
  259.  
  260. static void sc_ipc_wait(unsigned *sc_args, int sc_rc)
  261. {
  262.     ipc_call_t call;
  263.     int rc;
  264.  
  265.     if (sc_rc == 0) return;
  266.  
  267.     memset(&call, 0, sizeof(call));
  268.     rc = udebug_mem_read(phoneid, &call, sc_args[0], sizeof(call));
  269. //  printf("udebug_mem_read(phone %d, dest %d, app-mem src %d, size %d -> %d\n",
  270. //      phoneid, (int)&call, sc_args[0], sizeof(call), rc);
  271.  
  272.     if (rc >= 0) {
  273.         ipcp_call_in(&call, sc_rc);
  274.     }
  275. }
  276.  
  277. static void event_syscall_b(unsigned thread_id, unsigned thread_hash,  unsigned sc_id, int sc_rc)
  278. {
  279.     unsigned sc_args[6];
  280.     int rc;
  281.  
  282.     /* Read syscall arguments */
  283.     rc = udebug_args_read(phoneid, thread_hash, sc_args);
  284.  
  285.     async_serialize_start();
  286.  
  287. //  printf("[%d] ", thread_id);
  288.  
  289.     if (rc < 0) {
  290.         printf("error\n");
  291.         async_serialize_end();
  292.         return;
  293.     }
  294.  
  295.     if ((display_mask & DM_SYSCALL) != 0) {
  296.         /* Print syscall name and arguments */
  297.         printf("%s", syscall_desc[sc_id].name);
  298.         print_sc_args(sc_args, syscall_desc[sc_id].n_args);
  299.     }
  300.  
  301.     async_serialize_end();
  302. }
  303.  
  304. static void event_syscall_e(unsigned thread_id, unsigned thread_hash,  unsigned sc_id, int sc_rc)
  305. {
  306.     unsigned sc_args[6];
  307.     int rv_type;
  308.     int rc;
  309.  
  310.     /* Read syscall arguments */
  311.     rc = udebug_args_read(phoneid, thread_hash, sc_args);
  312.  
  313.     async_serialize_start();
  314.  
  315. //  printf("[%d] ", thread_id);
  316.  
  317.     if (rc < 0) {
  318.         printf("error\n");
  319.         async_serialize_end();
  320.         return;
  321.     }
  322.  
  323.     if ((display_mask & DM_SYSCALL) != 0) {
  324.         /* Print syscall return value */
  325.         rv_type = syscall_desc[sc_id].rv_type;
  326.         print_sc_retval(sc_rc, rv_type);
  327.     }
  328.  
  329.     switch (sc_id) {
  330.     case SYS_IPC_CALL_ASYNC_FAST:
  331.         sc_ipc_call_async_fast(sc_args, sc_rc);
  332.         break;
  333.     case SYS_IPC_CALL_ASYNC_SLOW:
  334.         sc_ipc_call_async_slow(sc_args, sc_rc);
  335.         break;
  336.     case SYS_IPC_CALL_SYNC_FAST:
  337.         sc_ipc_call_sync_fast(sc_args);
  338.         break;
  339.     case SYS_IPC_CALL_SYNC_SLOW:
  340.         sc_ipc_call_sync_slow(sc_args);
  341.         break;
  342.     case SYS_IPC_WAIT:
  343.         sc_ipc_wait(sc_args, sc_rc);
  344.         break;
  345.     default:
  346.         break;
  347.     }
  348.  
  349.     async_serialize_end();
  350. }
  351.  
  352. static void event_thread_b(unsigned hash)
  353. {
  354.     async_serialize_start();
  355.     printf("New thread, hash 0x%x\n", hash);
  356.     async_serialize_end();
  357.  
  358.     thread_trace_start(hash);
  359. }
  360.  
  361. static int trace_loop(void *thread_hash_arg)
  362. {
  363.     int rc;
  364.     unsigned ev_type;
  365.     unsigned thread_hash;
  366.     unsigned thread_id;
  367.     unsigned val0, val1;
  368.  
  369.     thread_hash = (unsigned)thread_hash_arg;
  370.     thread_id = next_thread_id++;
  371.  
  372.     printf("Start tracing thread [%d] (hash 0x%x)\n", thread_id, thread_hash);
  373.  
  374.     while (!abort_trace) {
  375.  
  376.         /* Run thread until an event occurs */
  377.         rc = udebug_go(phoneid, thread_hash,
  378.             &ev_type, &val0, &val1);
  379.  
  380. //      printf("rc = %d, ev_type=%d\n", rc, ev_type);
  381.         if (ev_type == UDEBUG_EVENT_FINISHED) {
  382.             /* Done tracing this thread */
  383.             break;
  384.         }
  385.  
  386.         if (rc >= 0) {
  387.             switch (ev_type) {
  388.             case UDEBUG_EVENT_SYSCALL_B:
  389.                 event_syscall_b(thread_id, thread_hash, val0, (int)val1);
  390.                 break;
  391.             case UDEBUG_EVENT_SYSCALL_E:
  392.                 event_syscall_e(thread_id, thread_hash, val0, (int)val1);
  393.                 break;
  394.             case UDEBUG_EVENT_STOP:
  395.                 printf("Stop event\n");
  396.                 printf("Waiting for resume\n");
  397.                 while (paused) {
  398.                     usleep(1000000);
  399.                     fibril_yield();
  400.                     printf(".");
  401.                 }
  402.                 printf("Resumed\n");
  403.                 break;
  404.             case UDEBUG_EVENT_THREAD_B:
  405.                 event_thread_b(val0);
  406.                 break;
  407.             case UDEBUG_EVENT_THREAD_E:
  408.                 printf("Thread 0x%x exited\n", val0);
  409.                 abort_trace = 1;
  410.                 break;
  411.             default:
  412.                 printf("Unknown event type %d\n", ev_type);
  413.                 break;
  414.             }
  415.         }
  416.  
  417.     }
  418.  
  419.     printf("Finished tracing thread [%d]\n", thread_id);
  420.     return 0;
  421. }
  422.  
  423. void thread_trace_start(unsigned thread_hash)
  424. {
  425.     fid_t fid;
  426.  
  427.     thash = thread_hash;
  428.  
  429.     fid = fibril_create(trace_loop, (void *)thread_hash);
  430.     if (fid == 0) {
  431.         printf("Warning: Failed creating fibril\n");
  432.     }
  433.     fibril_add_ready(fid);
  434. }
  435.  
  436. static void trace_active_task(task_id_t task_id)
  437. {
  438.     int i;
  439.     int rc;
  440.     int c;
  441.  
  442.     rc = task_connect(task_id);
  443.     if (rc < 0) {
  444.         printf("Failed to connect to task %lld\n", task_id);
  445.         return;
  446.     }
  447.  
  448.     printf("Connected to task %lld\n", task_id);
  449.  
  450.     ipcp_init();
  451.  
  452.     /*
  453.      * User apps now typically have console on phone 3.
  454.      * (Phones 1 and 2 are used by the loader).
  455.      */
  456.     ipcp_connection_set(3, 0, proto_console);
  457.  
  458.     rc = get_thread_list();
  459.     if (rc < 0) {
  460.         printf("Failed to get thread list (error %d)\n", rc);
  461.         return;
  462.     }
  463.  
  464.     abort_trace = 0;
  465.  
  466.     for (i = 0; i < n_threads; i++) {
  467.         thread_trace_start(thread_hash_buf[i]);
  468.     }
  469.  
  470.     while(1) {
  471.         c = getchar();
  472.         if (c == 'q') break;
  473.         if (c == 'p') {
  474.             paused = 1;
  475.             rc = udebug_stop(phoneid, thash);
  476.             printf("stop -> %d\n", rc);
  477.         }
  478.         if (c == 'r') {
  479.             paused = 0;
  480.         }
  481.     }
  482.  
  483.     printf("\nTerminate debugging session...\n");
  484.     abort_trace = 1;
  485.     udebug_end(phoneid);
  486.     ipc_hangup(phoneid);
  487.  
  488.     ipcp_cleanup();
  489.  
  490.     printf("Done\n");
  491.     return;
  492. }
  493.  
  494. static void main_init(void)
  495. {
  496.     proto_t *p;
  497.     oper_t *o;
  498.  
  499.     next_thread_id = 1;
  500.     paused = 0;
  501.  
  502.     proto_init();
  503.  
  504.     p = proto_new("vfs");
  505.     o = oper_new("read");
  506.     proto_add_oper(p, VFS_READ, o);
  507.     o = oper_new("write");
  508.     proto_add_oper(p, VFS_WRITE, o);
  509.     o = oper_new("truncate");
  510.     proto_add_oper(p, VFS_TRUNCATE, o);
  511.     o = oper_new("mount");
  512.     proto_add_oper(p, VFS_MOUNT, o);
  513.     o = oper_new("unmount");
  514.     proto_add_oper(p, VFS_UNMOUNT, o);
  515.  
  516.     proto_register(SERVICE_VFS, p);
  517.  
  518.     p = proto_new("console");
  519.     o = oper_new("getchar");
  520.     proto_add_oper(p, CONSOLE_GETCHAR, o);
  521.     o = oper_new("putchar");
  522.     proto_add_oper(p, CONSOLE_PUTCHAR, o);
  523.     o = oper_new("clear");
  524.     proto_add_oper(p, CONSOLE_CLEAR, o);
  525.     o = oper_new("goto");
  526.     proto_add_oper(p, CONSOLE_GOTO, o);
  527.     o = oper_new("getsize");
  528.     proto_add_oper(p, CONSOLE_GETSIZE, o);
  529.     o = oper_new("flush");
  530.     proto_add_oper(p, CONSOLE_FLUSH, o);
  531.     o = oper_new("set_style");
  532.     proto_add_oper(p, CONSOLE_SET_STYLE, o);
  533.     o = oper_new("cursor_visibility");
  534.     proto_add_oper(p, CONSOLE_CURSOR_VISIBILITY, o);
  535.     o = oper_new("flush");
  536.     proto_add_oper(p, CONSOLE_FLUSH, o);
  537.  
  538.     proto_console = p;
  539.     proto_register(SERVICE_CONSOLE, p);
  540. }
  541.  
  542. static void print_syntax()
  543. {
  544.     printf("Syntax:\n");
  545.     printf("\ttrace [+<events>] <executable> [<arg1> [...]]\n");
  546.     printf("or\ttrace [+<events>] -t <task_id>\n");
  547.     printf("Events: (default is +tp)\n");
  548.     printf("\n");
  549.     printf("\tt ... Thread creation and termination\n");
  550.     printf("\ts ... System calls\n");
  551.     printf("\ti ... Low-level IPC\n");
  552.     printf("\tp ... Protocol level\n");
  553.     printf("\n");
  554.     printf("Examples:\n");
  555.     printf("\ttrace +s /app/tetris\n");
  556.     printf("\ttrace +tsip -t 12\n");
  557. }
  558.  
  559. static display_mask_t parse_display_mask(char *text)
  560. {
  561.     display_mask_t dm;
  562.     char *c;
  563.  
  564.     c = text;
  565.  
  566.     while (*c) {
  567.         switch (*c) {
  568.         case 't': dm = dm | DM_THREAD; break;
  569.         case 's': dm = dm | DM_SYSCALL; break;
  570.         case 'i': dm = dm | DM_IPC; break;
  571.         case 'p': dm = dm | DM_SYSTEM | DM_USER; break;
  572.         default:
  573.             printf("Unexpected event type '%c'\n", *c);
  574.             exit(1);
  575.         }
  576.  
  577.         ++c;
  578.     }
  579.  
  580.     return dm;
  581. }
  582.  
  583. static int parse_args(int argc, char *argv[])
  584. {
  585.     char *arg;
  586.     char *err_p;
  587.  
  588.     task_id = 0;
  589.  
  590.     --argc; ++argv;
  591.  
  592.     while (argc > 0) {
  593.         arg = *argv;
  594.         if (arg[0] == '+') {
  595.             display_mask = parse_display_mask(&arg[1]);
  596.         } else if (arg[0] == '-') {
  597.             if (arg[1] == 't') {
  598.                 /* Trace an already running task */
  599.                 --argc; ++argv;
  600.                 task_id = strtol(*argv, &err_p, 10);
  601.                 if (*err_p) {
  602.                     printf("Task ID syntax error\n");
  603.                     print_syntax();
  604.                     return -1;
  605.                 }
  606.             } else {
  607.                 printf("Uknown option '%s'\n", arg[0]);
  608.                 print_syntax();
  609.                 return -1;
  610.             }
  611.         } else {
  612.             break;
  613.         }
  614.  
  615.         --argc; ++argv;
  616.     }
  617.  
  618.     if (task_id != 0) {
  619.         if (argc == 0) return;
  620.         printf("Extra arguments\n");
  621.         print_syntax();
  622.         return -1;
  623.     }
  624.  
  625.     if (argc < 1) {
  626.         printf("Missing argument\n");
  627.         print_syntax();
  628.         return -1;
  629.     }
  630.  
  631.     /* Execute the specified command and trace the new task. */
  632.     printf("Spawning '%s' with arguments:\n", *argv);
  633.     {
  634.         char **cp = argv;
  635.         while (*cp) printf("'%s'\n", *cp++);
  636.     }
  637.     task_id = task_spawn(*argv, argv);
  638.  
  639.     return 0;
  640. }
  641.  
  642. int main(int argc, char *argv[])
  643. {
  644.     printf("System Call / IPC Tracer\n");
  645.  
  646.     display_mask = DM_THREAD | DM_SYSTEM | DM_USER;
  647.  
  648.     if (parse_args(argc, argv) < 0)
  649.         return 1;
  650.  
  651.     main_init();
  652.     trace_active_task(task_id);
  653.  
  654.     return 0;
  655. }
  656.  
  657. /** @}
  658.  */
  659.