userspace/kp_main.c - ktap

Global variables defined

Data types defined

Functions defined

Macros defined

Source code

  1. /*
  2. * main.c - ktap compiler and loader entry
  3. *
  4. * This file is part of ktap by Jovi Zhangwei.
  5. *
  6. * Copyright (C) 2012-2013 Jovi Zhangwei <jovi.zhangwei@gmail.com>.
  7. *
  8. * ktap is free software; you can redistribute it and/or modify it
  9. * under the terms and conditions of the GNU General Public License,
  10. * version 2, as published by the Free Software Foundation.
  11. *
  12. * ktap is distributed in the hope it will be useful, but WITHOUT
  13. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
  14. * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  15. * more details.
  16. *
  17. * You should have received a copy of the GNU General Public License along with
  18. * this program; if not, write to the Free Software Foundation, Inc.,
  19. * 51 Franklin St - Fifth Floor, Boston, MA 02110-1301 USA.
  20. */

  21. #include <stdio.h>
  22. #include <stdlib.h>
  23. #include <sched.h>
  24. #include <string.h>
  25. #include <signal.h>
  26. #include <stdarg.h>
  27. #include <sys/mman.h>
  28. #include <sys/stat.h>
  29. #include <sys/ioctl.h>
  30. #include <sys/types.h>
  31. #include <unistd.h>
  32. #include <fcntl.h>
  33. #include <math.h>
  34. #include <linux/errno.h>

  35. #include "../include/ktap_types.h"
  36. #include "kp_lex.h"
  37. #include "kp_parse.h"
  38. #include "kp_symbol.h"
  39. #include "cparser.h"

  40. static void usage(const char *msg_fmt, ...)
  41. {
  42.     va_list ap;

  43.     va_start(ap, msg_fmt);
  44.     vfprintf(stderr, msg_fmt, ap);
  45.     va_end(ap);

  46.     fprintf(stderr,
  47. "Usage: ktap [options] file [script args] -- cmd [args]\n"
  48. "   or: ktap [options] -e one-liner  -- cmd [args]\n"
  49. "\n"
  50. "Options and arguments:\n"
  51. "  -o file        : send script output to file, instead of stderr\n"
  52. "  -p pid         : specific tracing pid\n"
  53. "  -C cpu         : cpu to monitor in system-wide\n"
  54. "  -T             : show timestamp for event\n"
  55. "  -V             : show version\n"
  56. "  -v             : enable verbose mode\n"
  57. "  -q             : suppress start tracing message\n"
  58. "  -d             : dry run mode(register NULL callback to perf events)\n"
  59. "  -s             : simple event tracing\n"
  60. "  -b             : list byte codes\n"
  61. "  -le [glob]     : list pre-defined events in system\n"
  62. #ifndef NO_LIBELF
  63. "  -lf DSO        : list available functions from DSO\n"
  64. "  -lm DSO        : list available sdt notes from DSO\n"
  65. #endif
  66. "  file           : program read from script file\n"
  67. "  -- cmd [args]  : workload to tracing\n");

  68.     exit(EXIT_FAILURE);
  69. }

  70. #define handle_error(str) do { perror(str); exit(-1); } while(0)

  71. ktap_option_t uparm;
  72. static int ktap_trunk_mem_size = 1024;

  73. static int kp_writer(const void* p, size_t sz, void* ud)
  74. {
  75.     if (uparm.trunk_len + sz > ktap_trunk_mem_size) {
  76.         int new_size = (uparm.trunk_len + sz) * 2;
  77.         uparm.trunk = realloc(uparm.trunk, new_size);
  78.         ktap_trunk_mem_size = new_size;
  79.     }

  80.     memcpy(uparm.trunk + uparm.trunk_len, p, sz);
  81.     uparm.trunk_len += sz;

  82.     return 0;
  83. }


  84. static int forks;
  85. static char **workload_argv;

  86. static int fork_workload(int ktap_fd)
  87. {
  88.     int pid;

  89.     pid = fork();
  90.     if (pid < 0)
  91.         handle_error("failed to fork");

  92.     if (pid > 0)
  93.         return pid;

  94.     signal(SIGTERM, SIG_DFL);

  95.     execvp("", workload_argv);

  96.     /*
  97.      * waiting ktapvm prepare all tracing event
  98.      * make it more robust in future.
  99.      */
  100.     pause();

  101.     execvp(workload_argv[0], workload_argv);

  102.     perror(workload_argv[0]);
  103.     exit(-1);

  104.     return -1;
  105. }

  106. #define KTAPVM_PATH "/sys/kernel/debug/ktap/ktapvm"

  107. static char *output_filename;

  108. static int run_ktapvm()
  109. {
  110.         int ktapvm_fd, ktap_fd;
  111.     int ret;

  112.     ktapvm_fd = open(KTAPVM_PATH, O_RDONLY);
  113.     if (ktapvm_fd < 0)
  114.         handle_error("open " KTAPVM_PATH " failed");

  115.     ktap_fd = ioctl(ktapvm_fd, 0, NULL);
  116.     if (ktap_fd < 0)
  117.         handle_error("ioctl ktapvm failed");

  118.     kp_create_reader(output_filename);

  119.     if (forks) {
  120.         uparm.trace_pid = fork_workload(ktap_fd);
  121.         uparm.workload = 1;
  122.     }

  123.     ret = ioctl(ktap_fd, KTAP_CMD_IOC_RUN, &uparm);
  124.     switch (ret) {
  125.     case -EPERM:
  126.     case -EACCES:
  127.         fprintf(stderr, "You may not have permission to run ktap\n");
  128.         break;
  129.     }

  130.     close(ktap_fd);
  131.     close(ktapvm_fd);

  132.     return ret;
  133. }

  134. int verbose;
  135. static int quiet;
  136. static int dry_run;
  137. static int dump_bytecode;
  138. static char oneline_src[1024];
  139. static int trace_pid = -1;
  140. static int trace_cpu = -1;
  141. static int print_timestamp;

  142. #define SIMPLE_ONE_LINER_FMT    \
  143.     "trace %s { print(cpu(), tid(), execname(), argstr) }"

  144. static const char *script_file;
  145. static int script_args_start;
  146. static int script_args_end;

  147. #ifndef NO_LIBELF
  148. struct binary_base
  149. {
  150.     int type;
  151.     const char *binary;
  152. };
  153. static int print_symbol(const char *name, vaddr_t addr, void *arg)
  154. {
  155.     struct binary_base *base = (struct binary_base *)arg;
  156.     const char *type = base->type == FIND_SYMBOL ?
  157.         "probe" : "sdt";

  158.     printf("%s:%s:%s\n", type, base->binary, name);
  159.     return 0;
  160. }
  161. #endif

  162. static void parse_option(int argc, char **argv)
  163. {
  164.     char pid[32] = {0};
  165.     char cpu_str[32] = {0};
  166.     char *next_arg;
  167.     int i, j;

  168.     for (i = 1; i < argc; i++) {
  169.         if (argv[i][0] != '-') {
  170.             script_file = argv[i];
  171.             if (!script_file)
  172.                 usage("");

  173.             script_args_start = i + 1;
  174.             script_args_end = argc;

  175.             for (j = i + 1; j < argc; j++) {
  176.                 if (argv[j][0] == '-' && argv[j][1] == '-')
  177.                     goto found_cmd;
  178.             }

  179.             return;
  180.         }

  181.         if (argv[i][0] == '-' && argv[i][1] == '-') {
  182.             j = i;
  183.             goto found_cmd;
  184.         }

  185.         next_arg = argv[i + 1];

  186.         switch (argv[i][1]) {
  187.         case 'o':
  188.             output_filename = malloc(strlen(next_arg) + 1);
  189.             if (!output_filename)
  190.                 return;

  191.             strncpy(output_filename, next_arg, strlen(next_arg));
  192.             i++;
  193.             break;
  194.         case 'e':
  195.             strncpy(oneline_src, next_arg, strlen(next_arg));
  196.             i++;
  197.             break;
  198.         case 'p':
  199.             strncpy(pid, next_arg, strlen(next_arg));
  200.             trace_pid = atoi(pid);
  201.             i++;
  202.             break;
  203.         case 'C':
  204.             strncpy(cpu_str, next_arg, strlen(next_arg));
  205.             trace_cpu = atoi(cpu_str);
  206.             i++;
  207.             break;
  208.         case 'T':
  209.             print_timestamp = 1;
  210.             break;
  211.         case 'v':
  212.             verbose = 1;
  213.             break;
  214.         case 'q':
  215.             quiet = 1;
  216.             break;
  217.         case 'd':
  218.             dry_run = 1;
  219.             break;
  220.         case 's':
  221.             sprintf(oneline_src, SIMPLE_ONE_LINER_FMT, next_arg);
  222.             i++;
  223.             break;
  224.         case 'b':
  225.             dump_bytecode = 1;
  226.             break;
  227.         case 'l': /* list available events */
  228.             switch (argv[i][2]) {
  229.             case 'e': /* tracepoints */
  230.                 list_available_events(next_arg);
  231.                 exit(EXIT_SUCCESS);
  232. #ifndef NO_LIBELF
  233.             case 'f': /* functions in DSO */
  234.             case 'm': /* static marks in DSO */ {
  235.                 const char *binary = next_arg;
  236.                 int type = argv[i][2] == 'f' ?
  237.                         FIND_SYMBOL : FIND_STAPSDT_NOTE;
  238.                 struct binary_base base = {
  239.                     .type = type,
  240.                     .binary = binary,
  241.                 };
  242.                 int ret;

  243.                 ret = parse_dso_symbols(binary, type,
  244.                             print_symbol,
  245.                             (void *)&base);
  246.                 if (ret <= 0) {
  247.                     fprintf(stderr,
  248.                     "error: no symbols in binary %s\n",
  249.                         binary);
  250.                     exit(EXIT_FAILURE);
  251.                 }
  252.                 exit(EXIT_SUCCESS);
  253.             }
  254. #endif
  255.             default:
  256.                 exit(EXIT_FAILURE);
  257.             }
  258.             break;
  259.         case 'V':
  260. #ifdef CONFIG_KTAP_FFI
  261.             usage("%s (with FFI)\n\n", KTAP_VERSION);
  262. #else
  263.             usage("%s\n\n", KTAP_VERSION);
  264. #endif
  265.             break;
  266.         case '?':
  267.         case 'h':
  268.             usage("");
  269.             break;
  270.         default:
  271.             usage("wrong argument\n");
  272.             break;
  273.         }
  274.     }

  275.     return;

  276. found_cmd:
  277.     script_args_end = j;
  278.     forks = 1;
  279.     workload_argv = &argv[j + 1];
  280. }

  281. static ktap_proto_t *parse(const char *chunkname, const char *src)
  282. {
  283.     LexState ls;

  284.     ls.chunkarg = chunkname ? chunkname : "?";
  285.     kp_lex_init();
  286.     kp_buf_init(&ls.sb);
  287.     kp_lex_setup(&ls, src);
  288.     return kp_parse(&ls);
  289. }

  290. static void compile(const char *input)
  291. {
  292.     ktap_proto_t *pt;
  293.     char *buff;
  294.     struct stat sb;
  295.     int fdin;

  296.     kp_str_resize();

  297.     if (oneline_src[0] != '\0') {
  298.         ffi_cparser_init();
  299.         pt = parse(input, oneline_src);
  300.         goto dump;
  301.     }

  302.     fdin = open(input, O_RDONLY);
  303.     if (fdin < 0) {
  304.         fprintf(stderr, "open file %s failed\n", input);
  305.         exit(-1);
  306.     }

  307.     if (fstat(fdin, &sb) == -1)
  308.         handle_error("fstat failed");

  309.     buff = mmap(NULL, sb.st_size, PROT_READ, MAP_PRIVATE, fdin, 0);
  310.     if (buff == MAP_FAILED)
  311.         handle_error("mmap failed");

  312.     ffi_cparser_init();
  313.     pt = parse(input, buff);

  314.     munmap(buff, sb.st_size);
  315.     close(fdin);

  316. dump:
  317.     if (dump_bytecode) {
  318. #ifdef CONFIG_KTAP_FFI
  319.         kp_dump_csymbols();
  320. #endif
  321.         kp_dump_proto(pt);
  322.         exit(0);
  323.     }

  324.     /* bcwrite */
  325.     uparm.trunk = malloc(ktap_trunk_mem_size);
  326.     if (!uparm.trunk)
  327.         handle_error("malloc failed");

  328.     kp_bcwrite(pt, kp_writer, NULL, 0);
  329.     ffi_cparser_free();
  330. }

  331. int main(int argc, char **argv)
  332. {
  333.     char **ktapvm_argv;
  334.     int new_index, i;
  335.     int ret;

  336.     if (argc == 1)
  337.         usage("");

  338.     parse_option(argc, argv);

  339.     if (oneline_src[0] != '\0')
  340.         script_file = "(command line)";

  341.     compile(script_file);

  342.     ktapvm_argv = (char **)malloc(sizeof(char *)*(script_args_end -
  343.                     script_args_start + 1));
  344.     if (!ktapvm_argv) {
  345.         fprintf(stderr, "canno allocate ktapvm_argv\n");
  346.         return -1;
  347.     }

  348.     ktapvm_argv[0] = malloc(strlen(script_file) + 1);
  349.     if (!ktapvm_argv[0]) {
  350.         fprintf(stderr, "canno allocate memory\n");
  351.         return -1;
  352.     }
  353.     strcpy(ktapvm_argv[0], script_file);
  354.     ktapvm_argv[0][strlen(script_file)] = '\0';

  355.     /* pass rest argv into ktapvm */
  356.     new_index = 1;
  357.     for (i = script_args_start; i < script_args_end; i++) {
  358.         ktapvm_argv[new_index] = malloc(strlen(argv[i]) + 1);
  359.         if (!ktapvm_argv[new_index]) {
  360.             fprintf(stderr, "canno allocate memory\n");
  361.             free(ktapvm_argv);
  362.             return -1;
  363.         }
  364.         strcpy(ktapvm_argv[new_index], argv[i]);
  365.         ktapvm_argv[new_index][strlen(argv[i])] = '\0';
  366.         new_index++;
  367.     }

  368.     uparm.argv = ktapvm_argv;
  369.     uparm.argc = new_index;
  370.     uparm.verbose = verbose;
  371.     uparm.trace_pid = trace_pid;
  372.     uparm.trace_cpu = trace_cpu;
  373.     uparm.print_timestamp = print_timestamp;
  374.     uparm.quiet = quiet;
  375.     uparm.dry_run = dry_run;

  376.     /* start running into kernel ktapvm */
  377.     ret = run_ktapvm();

  378.     cleanup_event_resources();
  379.     return ret;
  380. }