WIP: Include the build-id in the heaptrack output and use it to find libs
authorMilian Wolff <milian.wolff@kdab.com>
Tue, 4 Apr 2017 17:41:23 +0000 (19:41 +0200)
committerYour Name <you@example.com>
Fri, 18 Aug 2017 09:51:43 +0000 (09:51 +0000)
CMakeLists.txt
src/interpret/heaptrack_interpret.cpp
src/track/libheaptrack.cpp

index 0cdde70..3decea1 100644 (file)
@@ -14,7 +14,7 @@ if(NOT CMAKE_BUILD_TYPE)
 endif()
 
 set(HEAPTRACK_VERSION_MAJOR 1)
-set(HEAPTRACK_VERSION_MINOR 0)
+set(HEAPTRACK_VERSION_MINOR 1)
 set(HEAPTRACK_VERSION_PATCH 0)
 set(HEAPTRACK_LIB_VERSION 1.0.0)
 set(HEAPTRACK_LIB_SOVERSION 1)
index eab1100..41b0628 100644 (file)
@@ -304,23 +304,35 @@ struct AccumulatedTraceData
         return ipId;
     }
 
+    bool fileIsReadable(const string& path) const
+    {
+        return access(path.c_str(), R_OK) == 0;
+    }
+
     std::string findDebugFile(const std::string& input) const
     {
         // TODO: also try to find a debug file by build-id
         // TODO: also lookup in (user-configurable) debug path
         std::string file = input + ".debug";
-        if (access(file.c_str(), R_OK) == 0) {
-            return file;
-        } else {
-            return input;
+        return fileIsReadable(file) ? file : input;
+    }
+
+    std::string findBuildIdFile(const string& buildId) const
+    {
+        if (buildId.empty()) {
+            return {};
         }
+        // TODO: also lookup in (user-configurable) debug path
+        const auto path = "/usr/lib/debug/.build-id/" + buildId.substr(0, 2) + '/' + buildId.substr(2) + ".debug";
+        cerr << path << endl;
+        return fileIsReadable(path) ? path : string();
     }
 
     /**
      * Prevent the same file from being initialized multiple times.
      * This drastically cuts the memory consumption down
      */
-    backtrace_state* findBacktraceState(const std::string& originalFileName, uintptr_t addressStart)
+    backtrace_state* findBacktraceState(const std::string& originalFileName, const string& buildId, uintptr_t addressStart)
     {
         if (boost::algorithm::starts_with(originalFileName, "linux-vdso.so")) {
             // prevent warning, since this will always fail
@@ -332,7 +344,9 @@ struct AccumulatedTraceData
             return it->second;
         }
 
-        const auto fileName = findDebugFile(originalFileName);
+        // TODO: also lookup in (user-configurable) sysroot path
+        const auto buildIdFile = findBuildIdFile(buildId);
+        const auto fileName = buildIdFile.empty() ? findDebugFile(originalFileName) : buildIdFile;
 
         struct CallbackData
         {
@@ -407,17 +421,22 @@ int main(int /*argc*/, char** /*argv*/)
             if (fileName == "-") {
                 data.clearModules();
             } else {
-                if (fileName == "x") {
-                    fileName = exe;
-                }
-                std::string internedString;
-                const auto moduleIndex = data.intern(fileName, &internedString);
+                string buildId;
+                reader >> buildId;
+
                 uintptr_t addressStart = 0;
                 if (!(reader >> addressStart)) {
                     cerr << "failed to parse line: " << reader.line() << endl;
                     return 1;
                 }
-                auto state = data.findBacktraceState(internedString, addressStart);
+
+                if (fileName == "x") {
+                    fileName = exe;
+                }
+                std::string internedString;
+                const auto moduleIndex = data.intern(fileName, &internedString);
+
+                auto state = data.findBacktraceState(internedString, buildId, addressStart);
                 uintptr_t vAddr = 0;
                 uintptr_t memSize = 0;
                 while ((reader >> vAddr) && (reader >> memSize)) {
index 0fc47b6..7eba14b 100644 (file)
@@ -24,6 +24,7 @@
 
 #include "libheaptrack.h"
 
+#include <cstring>
 #include <cstdio>
 #include <cstdlib>
 #include <fcntl.h>
@@ -402,7 +403,53 @@ private:
 
         debugLog<VerboseOutput>("dlopen_notify_callback: %s %zx", fileName, info->dlpi_addr);
 
-        if (fprintf(heaptrack->s_data->out, "m %s %zx", fileName, info->dlpi_addr) < 0) {
+        const auto MAX_BUILD_ID_SIZE = 20u;
+        unsigned raw_build_id_size = 0;
+        unsigned char raw_build_id[MAX_BUILD_ID_SIZE] = {};
+
+        for (int i = 0; i < info->dlpi_phnum; i++) {
+            const auto& phdr = info->dlpi_phdr[i];
+            if (raw_build_id_size == 0 && phdr.p_type == PT_NOTE) {
+                auto segmentAddr = phdr.p_vaddr + info->dlpi_addr;
+                const auto segmentEnd = segmentAddr + phdr.p_memsz;
+                const ElfW(Nhdr)* nhdr = nullptr;
+                while (segmentAddr < segmentEnd) {
+                    nhdr = reinterpret_cast<ElfW(Nhdr)*>(segmentAddr);
+                    if (nhdr->n_type == NT_GNU_BUILD_ID) {
+                        break;
+                    }
+                    segmentAddr += sizeof(ElfW(Nhdr)) + nhdr->n_namesz + nhdr->n_descsz;
+                }
+                if (nhdr->n_type == NT_GNU_BUILD_ID) {
+                    const auto buildIdAddr = segmentAddr + sizeof(ElfW(Nhdr)) + nhdr->n_namesz;
+                    if (buildIdAddr + nhdr->n_descsz <= segmentEnd && nhdr->n_descsz <= MAX_BUILD_ID_SIZE) {
+                        const auto* buildId = reinterpret_cast<const unsigned char*>(buildIdAddr);
+                        raw_build_id_size = nhdr->n_descsz;
+                        std::memcpy(raw_build_id, buildId, raw_build_id_size);
+                        break;
+                    }
+                }
+            }
+        }
+
+        if (fprintf(heaptrack->s_data->out, "m %s ", fileName) < 0) {
+            heaptrack->writeError();
+            return 1;
+        }
+        if (raw_build_id_size == 0) {
+            if (fprintf(heaptrack->s_data->out, "- ") < 0) {
+                heaptrack->writeError();
+                return 1;
+            }
+        } else {
+            for (unsigned i = 0; i < raw_build_id_size; ++i) {
+                if (fprintf(heaptrack->s_data->out, "%02x", raw_build_id[i]) < 0) {
+                    heaptrack->writeError();
+                    return 1;
+                }
+            }
+        }
+        if (fprintf(heaptrack->s_data->out, " %zx", info->dlpi_addr) < 0) {
             heaptrack->writeError();
             return 1;
         }