[PATCH] record: Use addr2line to link to the source code

Sebastian Huber sebastian.huber at embedded-brains.de
Fri Sep 6 07:29:12 UTC 2019


Update #3665.
---
 trace/record/record-main-lttng.cc | 221 ++++++++++++++++++++++++++++++--------
 trace/wscript                     |  12 ++-
 2 files changed, 188 insertions(+), 45 deletions(-)

diff --git a/trace/record/record-main-lttng.cc b/trace/record/record-main-lttng.cc
index dda57ef..4f7b5df 100644
--- a/trace/record/record-main-lttng.cc
+++ b/trace/record/record-main-lttng.cc
@@ -28,12 +28,18 @@
 
 #include "client.h"
 
+#include <rld-process.h>
+
 #include <getopt.h>
 
 #include <cassert>
+#include <cinttypes>
 #include <cstdio>
 #include <cstring>
 #include <iostream>
+#include <map>
+#include <string>
+#include <vector>
 
 #define CTF_MAGIC 0xC1FC1FC1
 #define TASK_RUNNING 0x0000
@@ -83,6 +89,9 @@ struct EventHeaderCompact {
   uint64_t ns;
 } __attribute__((__packed__));
 
+static const size_t kEventHeaderBits =
+    sizeof(EventHeaderCompact) * BITS_PER_CHAR;
+
 struct EventRecordItem {
   EventHeaderCompact header;
   uint64_t data;
@@ -160,6 +169,8 @@ class LTTNGClient : public Client {
     }
   }
 
+  void OpenDebug(const char* elf_file);
+
   void Destroy() {
     Client::Destroy();
     CloseStreamFiles();
@@ -180,6 +191,14 @@ class LTTNGClient : public Client {
 
   size_t cpu_count_ = 0;
 
+  std::string elf_file_;
+
+  bool resolve_address_ = false;
+
+  typedef std::map<uint64_t, std::vector<char>> AddressToLineMap;
+
+  AddressToLineMap address_to_line_;
+
   static rtems_record_client_status HandlerCaller(uint64_t bt,
                                                   uint32_t cpu,
                                                   rtems_record_event event,
@@ -213,6 +232,10 @@ class LTTNGClient : public Client {
   void OpenStreamFiles(uint64_t data);
 
   void CloseStreamFiles();
+
+  AddressToLineMap::iterator AddAddressAsHexNumber(const ClientItem& item);
+
+  AddressToLineMap::iterator ResolveAddress(const ClientItem& item);
 };
 
 static uint32_t GetAPIIndexOfID(uint32_t id) {
@@ -236,6 +259,11 @@ static const uint8_t kCPUPostfix[RTEMS_RECORD_CLIENT_MAXIMUM_CPU_COUNT][2] = {
     {'2', '5'},  {'2', '6'},  {'2', '7'},  {'2', '8'},  {'2', '9'},
     {'3', '0'},  {'3', '1'}};
 
+void LTTNGClient::OpenDebug(const char* elf_file) {
+  elf_file_ = elf_file;
+  resolve_address_ = true;
+}
+
 void LTTNGClient::CopyThreadName(const ClientItem& item,
                                  size_t api_index,
                                  uint8_t* dst) const {
@@ -260,15 +288,91 @@ void LTTNGClient::CopyThreadName(const ClientItem& item,
   }
 }
 
+static bool IsCodeEvent(rtems_record_event event) {
+  switch (event) {
+    default:
+      return false;
+    case RTEMS_RECORD_CALLER:
+    case RTEMS_RECORD_LINE:
+      return true;
+  }
+}
+
+LTTNGClient::AddressToLineMap::iterator LTTNGClient::AddAddressAsHexNumber(
+    const ClientItem& item) {
+  char hex[19];
+  int n = std::snprintf(hex, sizeof(hex), "0x%" PRIx64, item.data);
+  assert(static_cast<size_t>(n) < sizeof(hex));
+  std::vector<char> code(hex, hex + n + 1);
+  return address_to_line_.emplace(item.data, std::move(code)).first;
+}
+
+LTTNGClient::AddressToLineMap::iterator LTTNGClient::ResolveAddress(
+    const ClientItem& item) {
+  AddressToLineMap::iterator it;
+
+  if (resolve_address_) {
+    rld::process::arg_container args;
+    args.push_back("addr2line");
+    args.push_back("-e");
+    args.push_back(elf_file_);
+    args.push_back("-f");
+    args.push_back("-p");
+    args.push_back("-s");
+    char hex[19];
+    int n = std::snprintf(hex, sizeof(hex), "0x%" PRIx64, item.data);
+    assert(static_cast<size_t>(n) < sizeof(hex));
+    args.push_back(hex);
+
+    rld::process::tempfile out;
+    rld::process::tempfile err;
+    rld::process::status status =
+        rld::process::execute(args[0], args, out.name(), err.name());
+
+    if (status.type == rld::process::status::normal && status.code == 0) {
+      std::string str;
+      out.open();
+      out.read(str);
+      std::vector<char> code(str.begin(), str.end());
+      code.push_back('\0');
+      it = address_to_line_.emplace(item.data, std::move(code)).first;
+    } else {
+      it = AddAddressAsHexNumber(item);
+    }
+  } else {
+    it = AddAddressAsHexNumber(item);
+  }
+
+  return it;
+}
+
 void LTTNGClient::WriteRecordItem(PerCPUContext* pcpu, const ClientItem& item) {
-  pcpu->size_in_bits += kEventRecordItemBits;
+  if (IsCodeEvent(item.event)) {
+    EventHeaderCompact header;
+    header.id = COMPACT_HEADER_ID;
+    header.event_id = item.event;
+    header.ns = item.ns;
+
+    auto it = address_to_line_.find(item.data);
+    if (it == address_to_line_.end()) {
+      it = ResolveAddress(item);
+    }
+
+    pcpu->size_in_bits += (sizeof(header) + it->second.size()) * BITS_PER_CHAR;
 
-  EventRecordItem& ri = pcpu->record_item;
-  ri.header.ns = item.ns;
-  ri.header.event_id = item.event;
-  ri.data = item.data;
+    std::fwrite(&header, sizeof(header), 1, pcpu->event_stream);
+    std::fwrite(&(*it->second.begin()), it->second.size(), 1,
+                pcpu->event_stream);
+  } else {
+    pcpu->size_in_bits += kEventRecordItemBits;
+
+    EventRecordItem& ri = pcpu->record_item;
+    ri.header.ns = item.ns;
+    ri.header.event_id = item.event;
+    ri.data = item.data;
 
-  std::fwrite(&ri, sizeof(ri), 1, pcpu->event_stream);
+    std::fwrite(&ri, sizeof(ri), 1, pcpu->event_stream);
+  }
 }
 
 void LTTNGClient::WriteSchedSwitch(PerCPUContext* pcpu,
@@ -566,18 +670,33 @@ static void GenerateMetadata() {
   std::fwrite(kMetadata, sizeof(kMetadata) - 1, 1, f);
 
   for (int i = 0; i <= RTEMS_RECORD_LAST; ++i) {
-    std::fprintf(f,
-                 "\n"
-                 "event {\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);
+    if (IsCodeEvent(static_cast<rtems_record_event>(i))) {
+      std::fprintf(f,
+                   "\n"
+                   "event {\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);
+    } else {
+      std::fprintf(f,
+                   "\n"
+                   "event {\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);
+    }
   }
 
   std::fclose(f);
@@ -596,53 +715,58 @@ static const struct option kLongOpts[] = {{"help", 0, NULL, 'h'},
                                           {NULL, 0, NULL, 0}};
 
 static void Usage(char** argv) {
-  std::cout << argv[0]
-            << " [--host=HOST] [--port=PORT] [--limit=LIMIT] [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
-            << "  INPUT-FILE                 the input file" << std::endl;
+  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;
 }
 
 int main(int argc, char** argv) {
   const char* host = "127.0.0.1";
   uint16_t port = 1234;
-  const char* file = nullptr;
+  const char* elf_file = nullptr;
+  const char* input_file = nullptr;
   int opt;
   int longindex;
 
-  while ((opt = getopt_long(argc, argv, "hH:l:p:", &kLongOpts[0],
+  while ((opt = getopt_long(argc, argv, "e:hH:l:p:", &kLongOpts[0],
                             &longindex)) != -1) {
     switch (opt) {
+      case 'e':
+        elf_file = optarg;
+        break;
       case 'h':
         Usage(argv);
         return 0;
       case 'H':
         host = optarg;
         break;
-      case 'p':
-        port = (uint16_t)strtoul(optarg, NULL, 0);
-        break;
       case 'l':
         client.set_limit(strtoull(optarg, NULL, 0));
         break;
+      case 'p':
+        port = (uint16_t)strtoul(optarg, NULL, 0);
+        break;
       default:
         return 1;
     }
   }
 
   if (optind == argc - 1) {
-    file = argv[optind];
+    input_file = argv[optind];
     ++optind;
   }
 
@@ -655,11 +779,18 @@ int main(int argc, char** argv) {
     return 1;
   }
 
+  rld::set_progname(argv[0]);
+
+  int ec = 0;
   try {
     GenerateMetadata();
 
-    if (file != nullptr) {
-      client.Open(file);
+    if (elf_file != nullptr) {
+      client.OpenDebug(elf_file);
+    }
+
+    if (input_file != nullptr) {
+      client.Open(input_file);
     } else {
       client.Connect(host, port);
     }
@@ -667,9 +798,13 @@ int main(int argc, char** argv) {
     std::signal(SIGINT, SignalHandler);
     client.Run();
     client.Destroy();
+  } catch (rld::error& e) {
+    std::cerr << "error: " << e.where << ": " << e.what << std::endl;
+    ec = 1;
   } catch (std::exception& e) {
     std::cerr << argv[0] << ": " << e.what() << std::endl;
+    ec = 2;
   }
 
-  return 0;
+  return ec;
 }
diff --git a/trace/wscript b/trace/wscript
index 36e7100..8999df9 100644
--- a/trace/wscript
+++ b/trace/wscript
@@ -43,6 +43,12 @@ def build(bld):
     #
     # Build flags.
     #
+    rtemstoolkit = '../rtemstoolkit'
+    conf['includes'] = [rtemstoolkit,
+                        rtemstoolkit + '/elftoolchain/libelf',
+                        rtemstoolkit + '/elftoolchain/libdwarf',
+                        rtemstoolkit + '/elftoolchain/common',
+                        'record']
     conf['warningflags'] = ['-Wall', '-Wextra', '-pedantic']
     conf['optflags'] = bld.env.C_OPTS
     conf['cflags'] = ['-pipe', '-g'] + conf['optflags']
@@ -68,10 +74,12 @@ def build(bld):
                           'record/record-text.c',
                           'record/record-client-base.cc',
                           'record/record-main-lttng.cc'],
-                includes = ['record'],
+                includes = conf['includes'],
                 defines = defines,
                 cflags = conf['cflags'] + conf['warningflags'],
-                linkflags = conf['linkflags'])
+                cxxflags = conf['cxxflags'] + conf['warningflags'],
+                linkflags = conf['linkflags'],
+                use = modules)
 
     #
     # Build rtems-record-text
-- 
2.16.4



More information about the devel mailing list