[PATCH 05/12] record: Add support for user defined event names

Sebastian Huber sebastian.huber at embedded-brains.de
Mon Mar 16 10:50:25 UTC 2020


---
 trace/record/client.h              |  29 ++++++++
 trace/record/record-client-base.cc |  40 ++++++++++
 trace/record/record-main-lttng.cc  | 145 ++++++++++++++++++++++++-------------
 3 files changed, 164 insertions(+), 50 deletions(-)

diff --git a/trace/record/client.h b/trace/record/client.h
index fc240ca..2fe8d51 100644
--- a/trace/record/client.h
+++ b/trace/record/client.h
@@ -37,8 +37,10 @@
 #include <csignal>
 #include <cstring>
 #include <iostream>
+#include <map>
 #include <stdexcept>
 #include <string>
+#include <utility>
 
 class ErrnoException : public std::runtime_error {
  public:
@@ -69,6 +71,33 @@ class FileDescriptor {
   ssize_t (*reader_)(int fd, void* buf, size_t n) = nullptr;
 };
 
+class ConfigFile {
+ public:
+  static const std::string kNoError;
+
+  ConfigFile() = default;
+
+  ConfigFile(const ConfigFile&) = delete;
+
+  ConfigFile& operator=(const ConfigFile&) = delete;
+
+  typedef std::string (*Parser)(void* arg, const char* name, const char* value);
+
+  void AddParser(const char* section, Parser parser, void* arg);
+
+  void Parse(const char* file);
+
+ private:
+  std::map<std::string, std::pair<Parser, void*> > parser_;
+
+  std::string error_;
+
+  static int INIHandler(void* user,
+                        const char* section,
+                        const char* name,
+                        const char* value);
+};
+
 class Client {
  public:
   Client() = default;
diff --git a/trace/record/record-client-base.cc b/trace/record/record-client-base.cc
index 197b654..8c467d4 100644
--- a/trace/record/record-client-base.cc
+++ b/trace/record/record-client-base.cc
@@ -48,6 +48,8 @@
 #include <cassert>
 #include <cstring>
 
+#include <ini.h>
+
 static ssize_t ReadFile(int fd, void* buf, size_t n) {
   return ::read(fd, buf, n);
 }
@@ -104,6 +106,44 @@ void FileDescriptor::Destroy() {
   }
 }
 
+const std::string ConfigFile::kNoError;
+
+void ConfigFile::AddParser(const char* section, Parser parser, void* arg) {
+  parser_[section] = std::make_pair(parser, arg);
+}
+
+void ConfigFile::Parse(const char* file) {
+  int status = ini_parse(file, INIHandler, this);
+  if (status < 0) {
+    throw ErrnoException(std::string("cannot parse configuration file '") +
+                         file + "'");
+  } else if (status > 0) {
+    throw std::runtime_error(
+        std::string("invalid line ") + std::to_string(status) +
+        " in configuration file '" + file + "': " + error_);
+  }
+}
+
+int ConfigFile::INIHandler(void* user,
+                           const char* section,
+                           const char* name,
+                           const char* value) {
+  ConfigFile* self = static_cast<ConfigFile*>(user);
+  auto it = self->parser_.find(section);
+  if (it != self->parser_.end()) {
+    std::string error = (*it->second.first)(it->second.second, name, value);
+    if (error == kNoError) {
+      return 1;
+    }
+
+    self->error_ = error;
+  } else {
+    self->error_ = std::string("unknown section: ") + section;
+  }
+
+  return 0;
+}
+
 void Client::Run() {
   uint64_t todo = UINT64_MAX;
 
diff --git a/trace/record/record-main-lttng.cc b/trace/record/record-main-lttng.cc
index 267c1f8..891281e 100644
--- a/trace/record/record-main-lttng.cc
+++ b/trace/record/record-main-lttng.cc
@@ -35,6 +35,7 @@
 #include <getopt.h>
 
 #include <cassert>
+#include <cerrno>
 #include <cinttypes>
 #include <cstdio>
 #include <cstring>
@@ -152,26 +153,11 @@ struct PerCPUContext {
 
 class LTTNGClient : public Client {
  public:
-  LTTNGClient() {
-    Initialize(LTTNGClient::HandlerCaller);
-
-    std::memset(&pkt_ctx_, 0, sizeof(pkt_ctx_));
-    std::memcpy(pkt_ctx_.header.uuid, kUUID, sizeof(pkt_ctx_.header.uuid));
-    pkt_ctx_.header.ctf_magic = CTF_MAGIC;
-
-    for (size_t i = 0; i < RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT; ++i) {
-      PerCPUContext& pcpu = per_cpu_[i];
-      pcpu.sched_switch.header.id = COMPACT_HEADER_ID;
-      pcpu.sched_switch.header.event_id = 1024;
-      pcpu.irq_handler_entry.header.id = COMPACT_HEADER_ID;
-      pcpu.irq_handler_entry.header.event_id = 1025;
-      pcpu.irq_handler_entry.name[0] = '\0';
-      pcpu.irq_handler_exit.header.id = COMPACT_HEADER_ID;
-      pcpu.irq_handler_exit.header.event_id = 1026;
-      pcpu.irq_handler_exit.ret = 1;
-      pcpu.record_item.header.id = COMPACT_HEADER_ID;
-    }
-  }
+  LTTNGClient();
+
+  void ParseConfigFile(const char* config_file);
+
+  void GenerateMetadata();
 
   void OpenExecutable(const char* elf_file);
 
@@ -207,6 +193,8 @@ class LTTNGClient : public Client {
 
   AddressToLineMap address_to_line_;
 
+  std::vector<std::string> event_to_name_;
+
   static rtems_record_client_status HandlerCaller(uint64_t bt,
                                                   uint32_t cpu,
                                                   rtems_record_event event,
@@ -237,6 +225,10 @@ class LTTNGClient : public Client {
 
   void PrintItem(const ClientItem& item);
 
+  static std::string EventNameParser(void* arg,
+                                     const char* name,
+                                     const char* value);
+
   void OpenStreamFiles(uint64_t data);
 
   void CloseStreamFiles();
@@ -246,6 +238,32 @@ class LTTNGClient : public Client {
   AddressToLineMap::iterator ResolveAddress(const ClientItem& item);
 };
 
+LTTNGClient::LTTNGClient() : event_to_name_(RTEMS_RECORD_LAST + 1) {
+  Initialize(LTTNGClient::HandlerCaller);
+
+  std::memset(&pkt_ctx_, 0, sizeof(pkt_ctx_));
+  std::memcpy(pkt_ctx_.header.uuid, kUUID, sizeof(pkt_ctx_.header.uuid));
+  pkt_ctx_.header.ctf_magic = CTF_MAGIC;
+
+  for (size_t i = 0; i < RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT; ++i) {
+    PerCPUContext& pcpu = per_cpu_[i];
+    pcpu.sched_switch.header.id = COMPACT_HEADER_ID;
+    pcpu.sched_switch.header.event_id = 1024;
+    pcpu.irq_handler_entry.header.id = COMPACT_HEADER_ID;
+    pcpu.irq_handler_entry.header.event_id = 1025;
+    pcpu.irq_handler_entry.name[0] = '\0';
+    pcpu.irq_handler_exit.header.id = COMPACT_HEADER_ID;
+    pcpu.irq_handler_exit.header.event_id = 1026;
+    pcpu.irq_handler_exit.ret = 1;
+    pcpu.record_item.header.id = COMPACT_HEADER_ID;
+  }
+
+  for (int i = 0; i <= RTEMS_RECORD_LAST; ++i) {
+    event_to_name_[i] =
+        rtems_record_event_text(static_cast<rtems_record_event>(i));
+  }
+}
+
 static uint32_t GetAPIIndexOfID(uint32_t id) {
   return ((id >> 24) & 0x7) - 1;
 }
@@ -512,6 +530,29 @@ rtems_record_client_status LTTNGClient::Handler(uint64_t bt,
   return RTEMS_RECORD_CLIENT_SUCCESS;
 }
 
+std::string LTTNGClient::EventNameParser(void* arg,
+                                         const char* name,
+                                         const char* value) {
+  LTTNGClient* self = static_cast<LTTNGClient*>(arg);
+  errno = 0;
+  char* end;
+  long event = std::strtol(name, &end, 0);
+  if (errno == 0 && *end == '\0' && event >= 0 && event <= RTEMS_RECORD_LAST) {
+    self->event_to_name_[event] = value;
+    return ConfigFile::kNoError;
+  }
+
+  return std::string("invalid event: ") + name;
+}
+
+void LTTNGClient::ParseConfigFile(const char* config_file) {
+  if (config_file != nullptr) {
+    ConfigFile file;
+    file.AddParser("EventNames", EventNameParser, this);
+    file.Parse(config_file);
+  }
+}
+
 void LTTNGClient::OpenStreamFiles(uint64_t data) {
   // Assertions are ensured by C record client
   assert(cpu_count_ == 0 && data < RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT);
@@ -671,7 +712,7 @@ static const char kMetadata[] =
     "\t};\n"
     "};\n";
 
-static void GenerateMetadata() {
+void LTTNGClient::GenerateMetadata() {
   FILE* f = std::fopen("metadata", "w");
   if (f == NULL) {
     throw ErrnoException("cannot create file 'metadata'");
@@ -684,28 +725,26 @@ static void GenerateMetadata() {
       std::fprintf(f,
                    "\n"
                    "event {\n"
-                   "\tname = %s;\n"
+                   "\tname = \"%s\";\n"
                    "\tid = %i;\n"
                    "\tstream_id = 0;\n"
                    "\tfields := struct {\n"
                    "\t\tstring _code;\n"
                    "\t};\n"
                    "};\n",
-                   rtems_record_event_text(static_cast<rtems_record_event>(i)),
-                   i);
+                   event_to_name_[i].c_str(), i);
     } else {
       std::fprintf(f,
                    "\n"
                    "event {\n"
-                   "\tname = %s;\n"
+                   "\tname = \"%s\";\n"
                    "\tid = %i;\n"
                    "\tstream_id = 0;\n"
                    "\tfields := struct {\n"
                    "\t\txint64_t _data;\n"
                    "\t};\n"
                    "};\n",
-                   rtems_record_event_text(static_cast<rtems_record_event>(i)),
-                   i);
+                   event_to_name_[i].c_str(), i);
     }
   }
 
@@ -719,29 +758,30 @@ static void SignalHandler(int s) {
   std::signal(s, SIG_DFL);
 }
 
-static const struct option kLongOpts[] = {{"help", 0, NULL, 'h'},
-                                          {"host", 1, NULL, 'H'},
-                                          {"port", 1, NULL, 'p'},
-                                          {NULL, 0, NULL, 0}};
+static const struct option kLongOpts[] = {
+    {"elf", 1, NULL, 'e'},   {"help", 0, NULL, 'h'}, {"host", 1, NULL, 'H'},
+    {"limit", 1, NULL, 'l'}, {"port", 1, NULL, 'p'}, {"config", 1, NULL, 'c'},
+    {NULL, 0, NULL, 0}};
 
 static void Usage(char** argv) {
-  std::cout
-      << argv[0]
-      << " [--host=HOST] [--port=PORT] [--limit=LIMIT] [--elf=ELF] [INPUT-FILE]"
-      << std::endl
-      << std::endl
-      << "Mandatory arguments to long options are mandatory for short "
-         "options too."
-      << std::endl
-      << "  -h, --help                 print this help text" << std::endl
-      << "  -H, --host=HOST            the host IPv4 address of the "
-         "record server"
-      << std::endl
-      << "  -p, --port=PORT            the TCP port of the record server"
-      << std::endl
-      << "  -l, --limit=LIMIT          limit in bytes to process" << std::endl
-      << "  -e, --elf=ELF              the ELF executable file" << std::endl
-      << "  INPUT-FILE                 the input file" << std::endl;
+  std::cout << argv[0] << " [OPTION]... [INPUT-FILE]" << std::endl
+            << std::endl
+            << "Mandatory arguments to long options are mandatory for short "
+               "options too."
+            << std::endl
+            << "  -h, --help                 print this help text" << std::endl
+            << "  -H, --host=HOST            the host IPv4 address of the "
+               "record server"
+            << std::endl
+            << "  -p, --port=PORT            the TCP port of the record server"
+            << std::endl
+            << "  -l, --limit=LIMIT          limit in bytes to process"
+            << std::endl
+            << "  -e, --elf=ELF              the ELF executable file"
+            << std::endl
+            << "  -c, --config=CONFIG        an INI-style configuration file"
+            << std::endl
+            << "  INPUT-FILE                 the input file" << std::endl;
 }
 
 int main(int argc, char** argv) {
@@ -749,10 +789,11 @@ int main(int argc, char** argv) {
   uint16_t port = 1234;
   const char* elf_file = nullptr;
   const char* input_file = nullptr;
+  const char* config_file = nullptr;
   int opt;
   int longindex;
 
-  while ((opt = getopt_long(argc, argv, "e:hH:l:p:", &kLongOpts[0],
+  while ((opt = getopt_long(argc, argv, "e:hH:l:p:c:", &kLongOpts[0],
                             &longindex)) != -1) {
     switch (opt) {
       case 'e':
@@ -770,6 +811,9 @@ int main(int argc, char** argv) {
       case 'p':
         port = (uint16_t)strtoul(optarg, NULL, 0);
         break;
+      case 'c':
+        config_file = optarg;
+        break;
       default:
         return 1;
     }
@@ -790,7 +834,8 @@ int main(int argc, char** argv) {
   }
 
   try {
-    GenerateMetadata();
+    client.ParseConfigFile(config_file);
+    client.GenerateMetadata();
 
     if (elf_file != nullptr) {
       client.OpenExecutable(elf_file);
-- 
2.16.4



More information about the devel mailing list