bug-hurd
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[PATCH 2/2] RFC: add kernel trace utilities


From: Luca Dariz
Subject: [PATCH 2/2] RFC: add kernel trace utilities
Date: Sat, 9 Mar 2024 15:03:12 +0100

These are some utilities I used to decode and control the kernel
trace, compiled as part of gnumach just so it's easier to build
them. I'm not sure if they should be in their own package, for now
they complement the kernel tracing patch so it can be tested more
easily.

With ktrace we can collect the trace of a specific executable, or to
manually start/stop tracing.

With parsetrace we can decode the trace file collected (with ktrace or
from gdb) handling different configurations and mixed 32/64 bit
kernel/user environments. This helps a lot when bootstrapping a new
platform. The usage is very basic for now, but advanced filtering can
be implemented (even with bindings to other languages).
---
 tests/Makefrag.am    |   9 +
 tests/ktrace.c       | 199 +++++++++++++++
 tests/parsetrace.cpp | 594 +++++++++++++++++++++++++++++++++++++++++++
 3 files changed, 802 insertions(+)
 create mode 100644 tests/ktrace.c
 create mode 100644 tests/parsetrace.cpp

diff --git a/tests/Makefrag.am b/tests/Makefrag.am
index faabdf44..df1e1e90 100644
--- a/tests/Makefrag.am
+++ b/tests/Makefrag.am
@@ -32,3 +32,12 @@ clean-local: $(USER_TESTS_CLEAN)
        rm -fr tests/include-mach
 
 endif # !PLATFORM_xen
+
+parsetrace: tests/parsetrace.cpp $(MACH_TESTINSTALL)
+       $(CXX) -Wall -ggdb3 -I$(MACH_TESTINCLUDE) -I$(MIG_OUTDIR) -o $@ $<
+
+ktrace: tests/ktrace.c $(MIG_OUTDIR)/gnumach.user.c $(MIG_OUTDIR)/mach.user.c
+       $(CC) -Wall -ggdb3 -I$(MACH_TESTINCLUDE) -I$(MIG_OUTDIR) -o $@ $^
+
+
+include tests/hurd-integration.mk
diff --git a/tests/ktrace.c b/tests/ktrace.c
new file mode 100644
index 00000000..b8b7c607
--- /dev/null
+++ b/tests/ktrace.c
@@ -0,0 +1,199 @@
+/*
+ *  Copyright (C) 2024 Free Software Foundation
+ *
+ * This program is free software ; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation ; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY ; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the program ; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <error.h>
+#include <getopt.h>
+#include <unistd.h>
+#include <sys/wait.h>
+
+#include <hurd.h>
+#include <mach/trace.h>
+#include <gnumach.user.h>
+#include <mach.user.h>
+
+
+/* start kernel tracing */
+void do_start(mach_port_t hostp, size_t bufsize, int kinds)
+{
+  int err = ktrace_start(hostp, bufsize, kinds);
+  if (err)
+    error(2, err, "ktrace_start");
+}
+
+/* stop kernel tracing */
+void do_stop(mach_port_t hostp)
+{
+  int err = ktrace_stop(hostp);
+  if (err)
+    error(2, err, "ktrace_stop");
+}
+
+/* collect tracing data and free the kernel resources */
+void do_collect(mach_port_t hostp, const char *path)
+{
+  vm_offset_t buf, last_ptr;
+  mach_msg_type_number_t bufsize;
+  int flags, err;
+  err = ktrace_collect(hostp, &buf, &bufsize, &last_ptr, &flags);
+  if (err)
+    error(1, err, "ktrace_collect");
+  printf("out %s size %u last %lu flags %x overrun %d\n", path, bufsize, 
last_ptr, flags, flags & 0xF);
+  if (bufsize < last_ptr)
+    printf("ERROR overrun?\n");
+  FILE *fp = fopen(path, "w");
+  if (fp)
+    {
+      trace_header_t th;
+      memset(&th, 0, sizeof(th));
+      memcpy(th.magic, TH_MAGIC, 4);
+      th.flags = flags;
+      th.last = last_ptr;
+      err = fwrite(&th, 1, sizeof(th), fp);
+      if (err <= 0)
+        error(0, errno, "fwrite header");
+
+      err = fwrite((void*)buf, 1, bufsize, fp);
+      if (err < bufsize)
+        error(0, errno, "fwrite");
+      fclose(fp);
+    }
+  else
+    error(0, errno, "fopen, trace file not written");
+
+  err = vm_deallocate(mach_task_self(), buf, bufsize);
+  if (err)
+    error(0, err, "vm_deallocate");
+
+  err = ktrace_free(hostp);
+  if (err)
+    error(1, err, "ktrace_free");
+}
+
+static const char *usage_text =
+  "usage: ktrace [ -n BUFSIZE_M ] [ -m MASK ] [ -o TRACEFILE ] 
start|stop|collect|run [ prog arg1 arg2 ... ]\n\n"
+  "  -n BUFSIZE (on start) is in MiB, default 10 MiB\n"
+  "  -m MASK (on start) is an integer bitwise-mask of the traced events, 
default all:\n"
+  "      1 - raw syscall entry/exit\n"
+  "      2 - user or kernel trap\n"
+  "      4 - context switch\n"
+  "      8 - interrupt handlers\n"
+  "      16 - raw IPC send/recv, traced on copyin/copyout. If mach_msg() fails 
copyout is skipped.\n"
+  "  -o TRACEFILE (on collect) will contain the dump of trace ring buffer, the 
default file is trace.bin\n\n"
+  "start|stop|collect allow to manage the tracing freely\n"
+  "run atomatically starts and stop tracing of the command [ prog arg1 arg2 
... ].\n"
+  ;
+
+int main(int argc, char *argv[])
+{
+  int err;
+  mach_port_t hostp;
+  size_t bufsize = 10 * 1024 * 1024;
+  char *outfile = "trace.bin";
+  int kinds = TRACE_TYPE_ALL;
+
+  while (1)
+    {
+      int c = getopt(argc, argv, "hn:o:m:");
+      if (c == -1)
+        break;
+
+      switch (c)
+        {
+        case 'h':
+          fprintf(stderr, usage_text);
+          exit(1);
+          break;
+        case '?':
+          break;
+        case 'n':
+          {
+            int bs = atoi(optarg);
+            if (bs)
+              bufsize = bs * 1024 * 1024;
+          }
+          break;
+        case 'm':
+          {
+            int mask = atoi(optarg);
+            if (mask)
+              kinds = mask;
+          }
+          break;
+        case 'o':
+          outfile = strdup(optarg);
+          break;
+
+        default:
+          printf("?? getopt returned character code 0%o ??\n", c);
+        }
+    }
+
+  if (optind >= argc)
+    {
+      fprintf(stderr, "Expected argument after options\n");
+      exit(EXIT_FAILURE);
+    }
+
+  err = get_privileged_ports(&hostp, NULL);
+  if (err)
+    error(1, err, "host priv");
+
+  if (strcmp(argv[optind], "start") == 0)
+    {
+      do_start(hostp, bufsize, kinds);
+    }
+  else if (strcmp(argv[optind], "stop") == 0)
+    {
+      do_stop(hostp);
+    }
+  else if (strcmp(argv[optind], "collect") == 0)
+    {
+      do_collect(hostp, outfile);
+    }
+  else if (strcmp(argv[optind], "run") == 0)
+    {
+      int pid, wstatus;
+      printf("ktrace pid %d\n", getpid());
+      if ((pid = fork()) == 0)
+        {
+          if (task_set_name(mach_task_self(), "ktrace-starting"))
+            error(3, err, "task_set_name");
+          do_start(hostp, bufsize, kinds);
+          err = execvp(argv[optind + 1], argv + optind + 1);
+          error(3, err, "exec");
+        }
+      else if (pid < 0)
+        error(1, pid, "can't fork, trace will not be collected");
+      else
+        {
+          printf("waiting for pid %d\n", pid);
+          waitpid(pid, &wstatus, 0);
+          do_stop(hostp);
+          do_collect(hostp, outfile);
+        }
+    }
+  else
+    {
+      fprintf(stderr, "Invalid action: %s\n", argv[optind]);
+      exit(EXIT_FAILURE);
+    }
+  return 0;
+}
diff --git a/tests/parsetrace.cpp b/tests/parsetrace.cpp
new file mode 100644
index 00000000..869f3957
--- /dev/null
+++ b/tests/parsetrace.cpp
@@ -0,0 +1,594 @@
+/*
+ *  Copyright (C) 2024 Free Software Foundation
+ *
+ * This program is free software ; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation ; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY ; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with the program ; if not, write to the Free Software
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ */
+
+#include <cassert>
+#include <cstring>
+#include <filesystem>
+#include <fstream>
+#include <iostream>
+#include <sstream>
+#include <stdint.h>
+#include <string>
+#include <tuple>
+#include <unordered_map>
+#include <vector>
+
+#include <unistd.h>
+
+#include <mach/trace.h>
+#include <mach/message.h>
+#include <mach/machine/trap.h>
+
+// TODO should be exported
+#define TASK_NAME_SIZE 32
+#define mach_msg_align(x, alignment)                                    \
+  ( ( ((vm_offset_t)(x)) + ((alignment)-1) ) & ~((alignment)-1) )
+
+struct trace_config
+{
+  bool k64, u64;
+  // endianness, in the future
+  uint32_t last, ov_first;
+  // see i386_saved_state
+  int trap_nregs;
+  int trap_reg_trapno;
+  int trap_reg_cr2;
+  int trap_reg_ip;
+};
+
+/* FIXME */
+#define _MACH_I386_SYSCALL_SW_H_
+#define _MACH_X86_64_SYSCALL_SW_H_
+#define kernel_trap(name, number, args) {-(number), {# name, args}},
+static std::unordered_map<int, std::tuple<std::string, int>> syscalls = {
+#include <mach/syscall_sw.h>
+};
+#undef kernel_trap
+
+static std::unordered_map<int, std::string> traporigin = {
+  {0, "user"},
+  {1, "kernel"},
+  {2, "astintr"},
+};
+
+#define trap_name(code) {code, # code},
+static std::unordered_map<int, std::string> trapname = {
+  trap_name(T_DIVIDE_ERROR)
+  trap_name(T_NO_FPU)
+  trap_name(T_INVALID_OPCODE)
+  trap_name(T_DOUBLE_FAULT)
+  trap_name(T_PAGE_FAULT)
+};
+#undef trap_name
+
+using RpcIdMap = std::unordered_map<uint32_t, std::tuple<std::string, int>>;
+
+RpcIdMap msgidmap;
+
+const int task_name_len = 12;  // displayed length, for readability
+
+struct trace_config
+read_file_header(std::ifstream& tf)
+{
+  struct trace_config c;
+  trace_header_t thead;
+  tf.read((char*)&thead, sizeof(trace_header_t));
+  if (memcmp(TH_MAGIC, thead.magic, sizeof(thead.magic)))
+    {
+      std::cerr << "error wrong header " << thead.magic << std::endl;
+      return {};
+    }
+  
+  c.k64 = thead.flags & TH_KERNEL64;
+  c.u64 = thead.flags & TH_USER64;
+  c.last = thead.last;
+  c.ov_first = thead.ov_first;
+  std::cout << "Reading trace " << (c.k64 ? "K64" : "K32") << " " << (c.u64 ? 
"U64" : "U32")
+            << std::hex << " " << c.last << " " << c.ov_first << std::endl;
+
+  if (c.k64 && c.u64)
+    {
+      c.trap_nregs = 23;
+      c.trap_reg_trapno = 16;
+      c.trap_reg_cr2 = 11;
+      c.trap_reg_ip = 18;
+    }
+  else if (c.k64 && (! c.u64))
+    {
+      c.trap_nregs = 31;
+      c.trap_reg_trapno = 20;
+      c.trap_reg_cr2 = 15;
+      c.trap_reg_ip = 22;
+    }
+  else if ((! c.k64) && (! c.u64))
+    {
+      c.trap_nregs = 23;
+      c.trap_reg_trapno = 12;
+      c.trap_reg_cr2 = 7;
+      c.trap_reg_ip = 14;
+    }
+  else
+    {
+      std::cerr << "error unsupported configuration" << std::endl;
+      return {};
+    }
+    
+  return c;
+}
+
+template <typename T>
+T read_val(std::ifstream& tf, trace_config& c)
+{
+  T val;
+  // TODO handle endiannes if needed
+  tf.read((char*)&val, sizeof(val));
+  return val;
+}
+
+// this works for both pointers and registers
+uint64_t read_ptr(std::ifstream& tf, trace_config& c)
+{
+  if (c.k64)
+    return read_val<uint64_t>(tf, c);
+  else
+    return read_val<uint32_t>(tf, c);
+}
+
+uint64_t read_uptr(std::ifstream& tf, trace_config& c)
+{
+  if (c.u64)
+    return read_val<uint64_t>(tf, c);
+  else
+    return read_val<uint32_t>(tf, c);
+}
+
+std::tuple<std::string, uint64_t, size_t, size_t>
+read_msg_header(std::ifstream& tf, trace_config& c)
+{
+  char task_name[TASK_NAME_SIZE];
+  tf.read((char*)task_name, TASK_NAME_SIZE);
+  uint64_t thid = read_ptr(tf, c);
+  uint32_t typesize;
+  tf.read((char*)&typesize, sizeof(typesize));
+  return std::make_tuple(task_name, thid, (typesize & 0xF), (typesize >> 8) & 
0xFFFFFF);
+}
+
+std::tuple<int, int, std::vector<long>>
+read_msg_data_syscall(std::ifstream& tf, trace_config& c, size_t dsize)
+{
+  uint32_t numdir;
+  tf.read((char*)&numdir, sizeof(numdir));
+  int nargs = (dsize - sizeof(numdir)) / (c.k64 ? 8 : 4);
+  std::vector<long> args;
+  for (int i=0; i<nargs; i++)
+    args.push_back(read_ptr(tf, c));
+  return std::make_tuple(numdir & 0x7F, (numdir >> 7) & 0x1, args);
+}
+
+std::tuple<std::string, long, std::string, long>
+read_msg_data_csw(std::ifstream& tf, trace_config& c, size_t dsize)
+{
+  char from_name[TASK_NAME_SIZE], to_name[TASK_NAME_SIZE];
+  tf.read((char*)from_name, TASK_NAME_SIZE);
+  long fromth = read_ptr(tf, c);
+  read_ptr(tf, c); // skip continuation for now
+  tf.read((char*)to_name, TASK_NAME_SIZE);
+  long toth = read_ptr(tf, c);
+  return std::make_tuple(from_name, fromth, to_name, toth);
+}
+
+std::tuple<int, std::vector<long>>
+read_msg_data_trap(std::ifstream& tf, trace_config& c, size_t dsize)
+{
+  int orig;
+  tf.read((char*)&orig, sizeof(orig));
+  std::vector<long> regs;
+  for (int i=0; i<c.trap_nregs; i++)
+    regs.push_back(read_ptr(tf, c));
+  return std::make_tuple(orig & 0xF, regs);
+}
+
+std::tuple<int>
+read_msg_data_interrupt(std::ifstream& tf, trace_config& c, size_t dsize)
+{
+  int irqn;
+  tf.read((char*)&irqn, sizeof(irqn));
+  return std::make_tuple(irqn);
+}
+
+std::string to_hex_string(long n)
+{
+  std::ostringstream o;
+  o << "0x" << std::hex << n;
+  return o.str();
+}
+
+uint64_t
+unpack_int(size_t size, const uint8_t *buf)
+{
+  // TODO handle endiannes if needed
+  if (size == 8)
+    return buf[0];
+  else if (size == 16)
+    return ((int)buf[1] << 8) | (int)buf[0];
+  else if (size == 32)
+    return ((int)buf[3] << 24) | ((int)buf[2] << 16) | ((int)buf[1] << 8) | 
(int)buf[0];
+  else if (size == 64)
+    return (unpack_int(32, buf + 4) << 32) | unpack_int(32, buf);
+  else
+    return -1;
+}
+
+void
+err_buf(const char *desc, const uint8_t* buf, size_t len)
+{
+  std::cerr << desc << " " << std::hex;
+  for (size_t i=0; i<len; i++)
+    std::cerr << std::setw(2) << std::setfill('0') << (int)buf[i];
+  std::cerr << std::endl;
+}
+
+// newsize name size number inline
+std::tuple<size_t, int, size_t, size_t, bool>
+decode_msg_descriptor(trace_config& c, const uint8_t* buf, size_t maxsize)
+{
+  bool is_inline;
+  int name, fsize, number;
+  size_t dsize;
+  if (c.u64)
+    {
+      dsize = 8;
+      if (maxsize < dsize)  // min desc size
+        return std::make_tuple(maxsize, -1, 0, 0, true);
+      //err_buf("descriptor", buf, dsize);
+      name = buf[0];
+      fsize = unpack_int(16, buf + 1);;
+      is_inline = ((buf[3] >> 5) & 1);
+      number = unpack_int(32, buf + 4);
+      //std::cerr << std::dec << name << " " << fsize  << " " << number  << " 
" << is_inline << std::endl;
+    }
+  else
+    {
+      dsize = 4;
+      if (maxsize < dsize)  // short desc size
+        return std::make_tuple(maxsize, -1, 0, 0, true);
+
+      if (buf[3] & 0x2)
+        {      
+          dsize = 12;
+          if (maxsize < dsize)  // long desc size
+            return std::make_tuple(maxsize, -1, 0, 0, true);
+          name = unpack_int(16, buf + 4);
+          fsize = unpack_int(16, buf + 6);
+          is_inline = ((buf[3] >> 4) & 1);
+          number = unpack_int(32, buf + 8);
+        }
+      else
+        {
+          name = unpack_int(8, buf);
+          fsize = unpack_int(8, buf + 1);
+          is_inline = ((buf[3] >> 4) & 1);
+          number = unpack_int(8, buf + 2) | ((unpack_int(8, buf + 3) & 0xF) << 
8);
+        }
+    }
+
+  return std::make_tuple(dsize, name, fsize, number, is_inline);
+}
+
+std::string
+decode_int(int num, size_t size, const uint8_t *buf)
+{
+  std::string s;
+  if (num == 1)
+    s += to_hex_string(unpack_int(size, buf));
+  else if (num < 15)  // for readabilty
+    {
+      s += "[";
+      for (int i=0; i<num; i++)
+        s += to_hex_string(unpack_int(size, buf + i*(size/8))) + " ";
+      s += "]";
+    }
+  else
+    s += "[array of " + std::to_string(num) +  "]";
+  return s;
+}
+
+std::string
+decode_str(int num, size_t size, const uint8_t *buf)
+{
+  std::string s;
+  //std::cerr << std::dec << num << " " << size << std::endl;
+  for (int i=0; i<num; i++)
+    {
+      char c = buf[i];
+      if (c)
+        s += c;
+      else
+        break;
+    }
+  return s;
+}
+
+std::vector<std::string>
+decode_msg_payload(std::ifstream& tf, trace_config& c, size_t psize)
+{
+  if (psize == 0)
+    return {};
+
+  uint8_t thebuf[psize];
+  tf.read((char*)thebuf, psize);
+  if ((psize & 0x3) != 0)
+    return {"unaligned"};
+
+  uint8_t *buf = (uint8_t*)thebuf;
+  std::vector<std::string> fields;
+  size_t pos = 0;
+  //err_buf("payload", thebuf, psize);
+  do {
+    auto [dsize, ftype, fsize, fnum, finline] = decode_msg_descriptor(c, buf + 
pos, psize - pos);
+    pos += dsize;
+    if (! finline)
+      {
+        fields.push_back("<OOL>");
+        pos += c.u64 ? 8 : 4;
+      }
+    else
+      {
+        switch(ftype)
+          {
+          case MACH_MSG_TYPE_BOOLEAN:
+          case MACH_MSG_TYPE_INTEGER_8:
+          case MACH_MSG_TYPE_INTEGER_16:
+          case MACH_MSG_TYPE_INTEGER_32:
+          case MACH_MSG_TYPE_INTEGER_64:
+          case MACH_MSG_TYPE_PORT_NAME:
+          case MACH_MSG_TYPE_PORT_RECEIVE:
+          case MACH_MSG_TYPE_PORT_SEND:
+          case MACH_MSG_TYPE_PORT_SEND_ONCE:
+          case MACH_MSG_TYPE_COPY_SEND:
+          case MACH_MSG_TYPE_MAKE_SEND:
+          case MACH_MSG_TYPE_MAKE_SEND_ONCE:
+            fields.push_back(decode_int(fnum, fsize, buf + pos));
+            break;
+          case MACH_MSG_TYPE_CHAR:
+          case MACH_MSG_TYPE_STRING:
+            fields.push_back(decode_str(fnum, fsize, buf + pos));
+            break;
+          case -1:
+          default:
+            if (ftype < 0 || ftype > MACH_MSG_TYPE_LAST)
+              {
+                std::cerr << "invalid field type " << ftype << std::endl;
+                err_buf("invalid descriptor", buf + pos, dsize);
+              }
+            else
+              std::cerr << "unhandled field type " << ftype << std::endl;
+            fields.push_back("<X>");
+            break;
+          }
+        //err_buf("field", buf + pos, (fsize * fnum) / 8);
+        pos += (fsize * fnum) / 8;
+        pos = mach_msg_align(pos, c.u64 ? 8 : 4);
+      }
+  } while (pos < psize);
+
+  return fields;
+}
+
+RpcIdMap
+load_msgids(const char *path)
+{
+  const std::filesystem::path dir=path;
+  RpcIdMap msgidmap;
+  for (auto const& entry : std::filesystem::directory_iterator{dir})
+    {
+      std::ifstream f{entry.path()};
+      std::string fdefs, rpcname;
+      int baseid, rpcrelid, reqid, respid;
+      while (f.good())
+        {
+          f >> fdefs >> baseid >> rpcname >> rpcrelid >> reqid >> respid;
+          if (reqid)
+            msgidmap[reqid] = std::make_tuple(rpcname, 0);
+          if (respid)
+            msgidmap[respid] = std::make_tuple(rpcname, 1);
+        }
+    }
+  return msgidmap;
+}
+
+std::tuple<int, std::string, int, std::vector<std::string>>
+read_msg_data_rpc(std::ifstream& tf, trace_config& c, size_t dsize, RpcIdMap 
msgidmap)
+{
+  int flags;
+  tf.read((char*)&flags, sizeof(flags));
+  size_t msize = (flags & 0xFFFF);
+  size_t hsize = (4 * 4) + 8 * ((int)c.u64 + 1);  // expected header size
+  read_val<uint32_t>(tf, c);  // bits
+  uint32_t h_size = read_val<uint32_t>(tf, c);
+  read_uptr(tf, c);  // remote port
+  read_uptr(tf, c);  // local port
+  read_val<uint32_t>(tf, c);  // seqno
+  uint32_t h_id = read_val<uint32_t>(tf, c);
+  //std::cout << std::dec << hsize << " " << msize << " " << dsize << " " << 
h_size << std::endl;
+  assert(msize >= hsize);
+  size_t newdsize = msize - hsize;
+  //std::cout << std::dec << (msize - hsize) << " " << newdsize << std::endl;
+  assert((h_size == 0) || (h_size == msize));
+  assert((msize + sizeof(flags)) == dsize);
+
+  auto [rpcname, rpcdir] = msgidmap[h_id];
+  if (rpcname.length() == 0)
+    rpcname = "unknown" + std::to_string(h_id);
+
+  return std::make_tuple((flags >> 16) & 1, rpcname, rpcdir,
+                         decode_msg_payload(tf, c, newdsize));
+}
+
+size_t msg_header_size(trace_config& c)
+{
+  return c.k64 ? (TASK_NAME_SIZE + 8 + 4) : (TASK_NAME_SIZE + 4 + 4);
+}
+
+void print_args(std::vector<std::string>& args, size_t n)
+{
+  std::cout << "(";
+  if (n != args.size())
+    std::cout << "MISM";
+  for (auto it = args.begin(); it != args.end(); it++)
+    {
+      std::cout << *it;
+      if (it != (args.end() - 1))
+        std::cout << ", ";
+    }
+  std::cout << ")";
+}
+
+void print_args(std::vector<long>& args, size_t n)
+{
+  std::vector<std::string> sargs;
+  for (auto& ia : args)
+    sargs.push_back(to_hex_string(ia));
+  print_args(sargs, n);
+}
+
+void print_trace_file(const char *file_path)
+{  
+  std::ifstream tf{file_path, std::ios::binary};
+  struct trace_config c = read_file_header(tf);
+  while (tf.tellg() < c.last)
+    {
+      auto [task_name, thid, ttype, tsize] = read_msg_header(tf, c);
+      //std::cerr << "--- " << std::hex << thid << " " << std::dec << ttype << 
" " << tsize << std::endl;
+      if (!tf.good())
+        break;
+      std::ostringstream pre;
+      while (task_name.length() < task_name_len)
+        task_name = " " + task_name;
+      pre << task_name.substr(0, task_name_len) << "/" << std::hex << (thid & 
0xFFFFFFFF) << "\t";
+      std::string curth = pre.str();
+      size_t dsize = tsize - msg_header_size(c);
+
+      switch (ttype)
+        {
+        case DEBUG_TRACE_SYSCALL:
+          {
+            auto [num, dir, args] = read_msg_data_syscall(tf, c, dsize);
+            auto [scname, scnargs] = syscalls[num];
+            //std::cerr << "sys" << num << " " << dir << " " << scname << 
std::endl;
+            if (scname != "mach_print")  // mostly noise in tests
+              {
+                if (scname.length() == 0)
+                  scname = "unknown" + std::to_string(num);
+                std::cout << curth << "SYS " << scname;
+                if (dir == 0)
+                  print_args(args, scnargs);
+                if (dir == 1)
+                  std::cout << " -> " << std::hex << args[0];
+                std::cout << std::endl;
+              }
+            break;
+          }
+
+        case DEBUG_TRACE_CSW:
+          {
+            auto [from_name, fromth, to_name, toth] = read_msg_data_csw(tf, c, 
dsize);
+            std::cout << curth << "CSW " << from_name << "/" << (fromth & 
0xFFFFFFFF)
+                      << " -> " << to_name << "/" << (toth & 0xFFFFFFFF) << 
std::endl;
+            break;
+          }
+
+        case DEBUG_TRACE_TRAP:
+          {
+            auto [origin, regs] = read_msg_data_trap(tf, c, dsize);
+            int trapno = regs[c.trap_reg_trapno];
+            std::cout << curth << "TRP " << traporigin[origin]
+                      << " " << trapname[trapno];
+            if (trapno  == T_PAGE_FAULT)
+              std::cout << " address 0x" << std::hex << regs[c.trap_reg_cr2]
+                        << " IP 0x" << regs[c.trap_reg_ip];
+            // TODO other interesting traps
+            std::cout << std::endl;
+            break;
+          }
+
+        case DEBUG_TRACE_IRQ:
+          {
+            auto [irqn] = read_msg_data_interrupt(tf, c, dsize);
+            std::cout << curth << "IRQ " << irqn << std::endl;
+            break;
+          }
+
+        case DEBUG_TRACE_RPC:
+          {
+            auto [scdir, name, dir, args] = read_msg_data_rpc(tf, c, dsize, 
msgidmap);
+            std::cout << curth << "RPC " << (scdir ? "recv" : "send") << " " 
<< name;
+            if (dir)
+              {
+                std::cout << "() -> ";
+                print_args(args, args.size());
+              }
+            else
+              print_args(args, args.size());
+            std::cout << std::endl;
+            break;
+          }
+
+        default:
+          {
+            std::cout << "Unknown trace type " << ttype << std::endl;
+            char buf[10000];
+            tf.read(buf, dsize);
+            break;
+          }
+        }
+    }
+
+}
+
+int main(int argc, char *argv[])
+{
+  const char *msgids_dir = "/usr/share/msgids";
+  int option;
+  while ((option = getopt(argc, argv, "d:h")) != EOF)
+    switch(option)
+      {
+      case 'd':
+        msgids_dir = optarg;
+        break;
+      case 'h':
+        std::cout <<
+          "usage: parsetrace [-d MSGIDS_DIR] [-h] TRACE_FILE\n" <<
+          "Decode a gnumach trace.\n\n" <<
+          "  -d MSGIDS_DIR    Alternative folder for msgids.\n" <<
+          "  -h               This help.\n";
+        return 1;
+      default:
+        return 1;
+      }
+
+  if (optind >= argc)
+    {
+      std::cout << "Expected trace file" << std::endl;
+      return 1;
+    }
+
+  msgidmap = load_msgids(msgids_dir);
+  print_trace_file(argv[optind]);
+  return 0;
+}
-- 
2.39.2




reply via email to

[Prev in Thread] Current Thread [Next in Thread]