[GDBJIT] Provide full file names in generated DWARF (#14172)
authorIgor Kulaychuk <igor.kulaychuk@gmail.com>
Wed, 27 Sep 2017 13:32:39 +0000 (16:32 +0300)
committerJan Vorlicek <janvorli@microsoft.com>
Wed, 27 Sep 2017 13:32:39 +0000 (15:32 +0200)
* Fix compile unit info

Provide full path to source in SequencePoints for GDBJIT
Use source file and directory path for compile unit info in DWARF instead of module name.

* Add a class for building Directory Table and File Table

* Fix code inconsistencies and add comments

src/ToolBox/SOS/NETCore/SymbolReader.cs
src/vm/gdbjit.cpp
src/vm/gdbjit.h

index e0fcdb5..7a4bb52 100644 (file)
@@ -539,7 +539,7 @@ namespace SOS
 
                         DebugInfo debugInfo = new DebugInfo();
                         debugInfo.lineNumber = point.StartLine;
-                        debugInfo.fileName = GetFileName(openedReader.Reader.GetString(openedReader.Reader.GetDocument(point.Document).Name));
+                        debugInfo.fileName = openedReader.Reader.GetString(openedReader.Reader.GetDocument(point.Document).Name);
                         debugInfo.ilOffset = point.Offset;
                         points.Add(debugInfo);
                     }
index 9557b0b..9dbf9df 100644 (file)
@@ -674,7 +674,7 @@ const int DebugStringCount = sizeof(DebugStrings) / sizeof(DebugStrings[0]);
 /* Static data for .debug_abbrev */
 const unsigned char AbbrevTable[] = {
     1, DW_TAG_compile_unit, DW_CHILDREN_yes,
-        DW_AT_producer, DW_FORM_strp, DW_AT_language, DW_FORM_data2, DW_AT_name, DW_FORM_strp,
+        DW_AT_producer, DW_FORM_strp, DW_AT_language, DW_FORM_data2, DW_AT_name, DW_FORM_strp, DW_AT_comp_dir, DW_FORM_strp,
         DW_AT_stmt_list, DW_FORM_sec_offset, 0, 0,
 
     2, DW_TAG_base_type, DW_CHILDREN_no,
@@ -772,6 +772,7 @@ struct __attribute__((packed)) DebugInfoCU
     uint32_t m_prod_off;
     uint16_t m_lang;
     uint32_t m_cu_name;
+    uint32_t m_cu_dir;
     uint32_t m_line_num;
 } debugInfoCU = {
 #ifdef FEATURE_GDBJIT_LANGID_CS
@@ -2479,9 +2480,7 @@ void NotifyGdb::OnMethodPrepared(MethodDesc* methodDescPtr)
     SString modName = mod->GetFile()->GetPath();
     StackScratchBuffer scratch;
     const char* szModName = modName.GetUTF8(scratch);
-    const char *szModulePath, *szModuleFile;
-    SplitPathname(szModName, szModulePath, szModuleFile);
-
+    const char* szModuleFile = SplitFilename(szModName);
 
     int length = MultiByteToWideChar(CP_UTF8, 0, szModuleFile, -1, NULL, 0);
     if (length == 0)
@@ -2508,7 +2507,7 @@ void NotifyGdb::OnMethodPrepared(MethodDesc* methodDescPtr)
 
     if (isListedModule(wszModuleFile))
     {
-        bool bEmitted = EmitDebugInfo(elfBuilder, methodDescPtr, pCode, codeSize, szModuleFile);
+        bool bEmitted = EmitDebugInfo(elfBuilder, methodDescPtr, pCode, codeSize);
         bNotify = bNotify || bEmitted;
     }
 #ifdef FEATURE_GDBJIT_SYMTAB
@@ -2661,7 +2660,7 @@ bool NotifyGdb::EmitSymtab(Elf_Builder &elfBuilder, MethodDesc* methodDescPtr, P
 }
 #endif // FEATURE_GDBJIT_SYMTAB
 
-bool NotifyGdb::EmitDebugInfo(Elf_Builder &elfBuilder, MethodDesc* methodDescPtr, PCODE pCode, TADDR codeSize, const char *szModuleFile)
+bool NotifyGdb::EmitDebugInfo(Elf_Builder &elfBuilder, MethodDesc* methodDescPtr, PCODE pCode, TADDR codeSize)
 {
     unsigned int thunkIndexBase = elfBuilder.GetSectionCount();
 
@@ -2750,13 +2749,26 @@ bool NotifyGdb::EmitDebugInfo(Elf_Builder &elfBuilder, MethodDesc* methodDescPtr
         return false;
     }
 
+    const char *cuPath = "";
+
     /* Build .debug_line section */
-    if (!BuildLineTable(dbgLine, pCode, codeSize, symInfo, symInfoLen))
+    if (!BuildLineTable(dbgLine, pCode, codeSize, symInfo, symInfoLen, cuPath))
     {
         return false;
     }
 
-    DebugStrings[1] = szModuleFile;
+    // Split full path to compile unit into file name and directory path
+    const char *fileName = SplitFilename(cuPath);
+    int dirLen = fileName - cuPath;
+    NewArrayHolder<char> dirPath;
+    if (dirLen != 0)
+    {
+        dirPath = new char[dirLen];
+        memcpy(dirPath, DebugStrings[1], dirLen - 1);
+        dirPath[dirLen - 1] = '\0';
+    }
+    DebugStrings[1] = fileName;
+    DebugStrings[2] = dirPath ? (const char *)dirPath : "";
 
     /* Build .debug_str section */
     if (!BuildDebugStrings(dbgStr, pTypeMap, method))
@@ -2770,6 +2782,9 @@ bool NotifyGdb::EmitDebugInfo(Elf_Builder &elfBuilder, MethodDesc* methodDescPtr
         return false;
     }
 
+    DebugStrings[1] = "";
+    DebugStrings[2] = "";
+
     for (int i = 0; i < method.GetCount(); ++i)
     {
         method[i]->lines = nullptr;
@@ -2893,12 +2908,13 @@ void NotifyGdb::MethodPitched(MethodDesc* methodDescPtr)
 }
 
 /* Build the DWARF .debug_line section */
-bool NotifyGdb::BuildLineTable(MemBuf& buf, PCODE startAddr, TADDR codeSize, SymbolsInfo* lines, unsigned nlines)
+bool NotifyGdb::BuildLineTable(MemBuf& buf, PCODE startAddr, TADDR codeSize, SymbolsInfo* lines, unsigned nlines,
+                               const char * &cuPath)
 {
     MemBuf fileTable, lineProg;
 
     /* Build file table */
-    if (!BuildFileTable(fileTable, lines, nlines))
+    if (!BuildFileTable(fileTable, lines, nlines, cuPath))
         return false;
     /* Build line info program */
     if (!BuildLineProg(lineProg, startAddr, codeSize, lines, nlines))
@@ -2906,85 +2922,193 @@ bool NotifyGdb::BuildLineTable(MemBuf& buf, PCODE startAddr, TADDR codeSize, Sym
         return false;
     }
 
-    buf.MemSize = sizeof(DwarfLineNumHeader) + 1 + fileTable.MemSize + lineProg.MemSize;
+    buf.MemSize = sizeof(DwarfLineNumHeader) + fileTable.MemSize + lineProg.MemSize;
     buf.MemPtr = new char[buf.MemSize];
 
     /* Fill the line info header */
     DwarfLineNumHeader* header = reinterpret_cast<DwarfLineNumHeader*>(buf.MemPtr.GetValue());
     memcpy(buf.MemPtr, &LineNumHeader, sizeof(DwarfLineNumHeader));
-    header->m_length = buf.MemSize - sizeof(uint32_t);
-    header->m_hdr_length = sizeof(DwarfLineNumHeader) + 1 + fileTable.MemSize - 2 * sizeof(uint32_t) - sizeof(uint16_t);
-    buf.MemPtr[sizeof(DwarfLineNumHeader)] = 0; // this is for missing directory table
+    header->m_length = buf.MemSize - sizeof(header->m_length);
+
+    // Set m_hdr_field to the number of bytes following the m_hdr_field field to the beginning of the first byte of
+    // the line number program itself.
+    header->m_hdr_length = sizeof(DwarfLineNumHeader)
+                           - sizeof(header->m_length)
+                           - sizeof(header->m_version)
+                           - sizeof(header->m_hdr_length)
+                           + fileTable.MemSize;
+
     /* copy file table */
-    memcpy(buf.MemPtr + sizeof(DwarfLineNumHeader) + 1, fileTable.MemPtr, fileTable.MemSize);
+    memcpy(buf.MemPtr + sizeof(DwarfLineNumHeader), fileTable.MemPtr, fileTable.MemSize);
     /* copy line program */
-    memcpy(buf.MemPtr + sizeof(DwarfLineNumHeader) + 1 + fileTable.MemSize, lineProg.MemPtr, lineProg.MemSize);
+    memcpy(buf.MemPtr + sizeof(DwarfLineNumHeader) + fileTable.MemSize, lineProg.MemPtr, lineProg.MemSize);
 
     return true;
 }
 
-/* Buid the source files table for DWARF source line info */
-bool NotifyGdb::BuildFileTable(MemBuf& buf, SymbolsInfo* lines, unsigned nlines)
+// A class for building Directory Table and File Table (in .debug_line section) from a list of files
+class NotifyGdb::FileTableBuilder
 {
-    NewArrayHolder<const char*> files = nullptr;
-    unsigned nfiles = 0;
+    int m_capacity;
 
-    /* GetValue file names and replace them with indices in file table */
-    files = new const char*[nlines];
-    if (files == nullptr)
-        return false;
-    for (unsigned i = 0; i < nlines; ++i)
-    {
-        if (lines[i].fileName[0] == 0)
-            continue;
-        const char *filePath, *fileName;
-        SplitPathname(lines[i].fileName, filePath, fileName);
+    NewArrayHolder< NewArrayHolder<char> > m_dirs;
+    int m_dirs_count;
 
-        /* if this isn't first then we already added file, so adjust index */
-        lines[i].fileIndex = (nfiles) ? (nfiles - 1) : (nfiles);
+    struct FileEntry
+    {
+        const char* path;
+        const char* name;
+        int dir;
+    };
+    NewArrayHolder<FileEntry> m_files;
+    int m_files_count;
 
-        bool found = false;
-        for (int j = 0; j < nfiles; ++j)
+    int FindDir(const char *name) const
+    {
+        for (int i = 0; i < m_dirs_count; ++i)
         {
-            if (strcmp(fileName, files[j]) == 0)
-            {
-                found = true;
-                break;
-            }
+            if (strcmp(m_dirs[i], name) == 0)
+                return i;
         }
+        return -1;
+    }
 
-        /* add new source file */
-        if (!found)
+    int FindFile(const char *path) const
+    {
+        for (int i = 0; i < m_files_count; ++i)
         {
-            files[nfiles++] = fileName;
+            if (strcmp(m_files[i].path, path) == 0)
+                return i;
         }
+        return -1;
     }
 
-    /* build file table */
-    unsigned totalSize = 0;
+public:
 
-    for (unsigned i = 0; i < nfiles; ++i)
+    FileTableBuilder(int capacity) :
+        m_capacity(capacity),
+        m_dirs(new NewArrayHolder<char>[capacity]),
+        m_dirs_count(0),
+        m_files(new FileEntry[capacity]),
+        m_files_count(0)
     {
-        totalSize += strlen(files[i]) + 1 + 3;
     }
-    totalSize += 1;
 
-    buf.MemSize = totalSize;
-    buf.MemPtr = new char[buf.MemSize];
+    int Add(const char *path)
+    {
+        // Already exists?
+        int i = FindFile(path);
+        if (i != -1)
+            return i;
+
+        if (m_files_count >= m_capacity)
+            return -1;
+
+        // Add new file entry
+        m_files[m_files_count].path = path;
+        const char *filename = SplitFilename(path);
+        m_files[m_files_count].name = filename;
+        int dirLen = filename - path;
+        if (dirLen == 0)
+        {
+            m_files[m_files_count].dir = 0;
+            return m_files_count++;
+        }
+
+        // Construct directory path
+        NewArrayHolder<char> dirName = new char[dirLen + 1];
+        int delimiterDelta = dirLen == 1 ? 0 : 1; // Avoid empty dir entry when file is at Unix root /
+        memcpy(dirName, path, dirLen - delimiterDelta);
+        dirName[dirLen - delimiterDelta] = '\0';
+
+        // Try to find existing directory entry
+        i = FindDir(dirName);
+        if (i != -1)
+        {
+            m_files[m_files_count].dir = i + 1;
+            return m_files_count++;
+        }
+
+        // Create new directory entry
+        if (m_dirs_count >= m_capacity)
+            return -1;
+
+        m_dirs[m_dirs_count++] = dirName.Extract();
+
+        m_files[m_files_count].dir = m_dirs_count;
+        return m_files_count++;
+    }
 
-    /* copy collected file names */
-    char *ptr = buf.MemPtr;
-    for (unsigned i = 0; i < nfiles; ++i)
+    void Build(MemBuf& buf)
     {
-        strcpy(ptr, files[i]);
-        ptr += strlen(files[i]) + 1;
-        // three LEB128 entries which we don't care
-        *ptr++ = 0;
-        *ptr++ = 0;
+        unsigned totalSize = 0;
+
+        // Compute buffer size
+        for (unsigned i = 0; i < m_dirs_count; ++i)
+            totalSize += strlen(m_dirs[i]) + 1;
+        totalSize += 1;
+
+        char cnv_buf[16];
+        for (unsigned i = 0; i < m_files_count; ++i)
+        {
+            int len = Leb128Encode(static_cast<uint32_t>(m_files[i].dir), cnv_buf, sizeof(cnv_buf));
+            totalSize += strlen(m_files[i].name) + 1 + len + 2;
+        }
+        totalSize += 1;
+
+        // Fill the buffer
+        buf.MemSize = totalSize;
+        buf.MemPtr = new char[buf.MemSize];
+
+        char *ptr = buf.MemPtr;
+
+        for (unsigned i = 0; i < m_dirs_count; ++i)
+        {
+            strcpy(ptr, m_dirs[i]);
+            ptr += strlen(m_dirs[i]) + 1;
+        }
+        // final zero byte for directory table
         *ptr++ = 0;
+
+        for (unsigned i = 0; i < m_files_count; ++i)
+        {
+            strcpy(ptr, m_files[i].name);
+            ptr += strlen(m_files[i].name) + 1;
+
+            // Index in directory table
+            int len = Leb128Encode(static_cast<uint32_t>(m_files[i].dir), cnv_buf, sizeof(cnv_buf));
+            memcpy(ptr, cnv_buf, len);
+            ptr += len;
+
+            // Two LEB128 entries which we don't care
+            *ptr++ = 0;
+            *ptr++ = 0;
+        }
+        // final zero byte
+        *ptr = 0;
     }
-    // final zero byte
-    *ptr = 0;
+};
+
+/* Buid the source files table for DWARF source line info */
+bool NotifyGdb::BuildFileTable(MemBuf& buf, SymbolsInfo* lines, unsigned nlines, const char * &cuPath)
+{
+    FileTableBuilder fileTable(nlines);
+
+    cuPath = "";
+    for (unsigned i = 0; i < nlines; ++i)
+    {
+        const char* fileName = lines[i].fileName;
+
+        if (fileName[0] == '\0')
+            continue;
+
+        if (*cuPath == '\0') // Use first non-empty filename as compile unit
+            cuPath = fileName;
+
+        lines[i].fileIndex = fileTable.Add(fileName);
+    }
+
+    fileTable.Build(buf);
 
     return true;
 }
@@ -3244,6 +3368,7 @@ bool NotifyGdb::BuildDebugInfo(MemBuf& buf, PTK_TypeInfoMap pTypeMap, FunctionMe
     offset += sizeof(DebugInfoCU);
     diCU->m_prod_off = 0;
     diCU->m_cu_name = strlen(DebugStrings[0]) + 1;
+    diCU->m_cu_dir = diCU->m_cu_name + strlen(DebugStrings[1]) + 1;
     {
         auto iter = pTypeMap->Begin();
         while (iter != pTypeMap->End())
@@ -3404,22 +3529,18 @@ bool NotifyGdb::BuildSymbolTableSection(MemBuf& buf, PCODE addr, TADDR codeSize,
     return true;
 }
 
-/* Split full path name into directory & file names */
-void NotifyGdb::SplitPathname(const char* path, const char*& pathName, const char*& fileName)
+/* Split file name part from the full path */
+const char * NotifyGdb::SplitFilename(const char* path)
 {
-    char* pSlash = strrchr(path, '/');
-
-    if (pSlash != nullptr)
+    // Search for the last directory delimiter (Windows or Unix)
+    const char *pSlash = nullptr;
+    for (const char *p = path; *p != '\0'; p++)
     {
-        *pSlash = 0;
-        fileName = ++pSlash;
-        pathName = path;
-    }
-    else
-    {
-        fileName = path;
-        pathName = nullptr;
+        if (*p == '/' || *p == '\\')
+            pSlash = p;
     }
+
+    return pSlash ? pSlash + 1 : path;
 }
 
 /* ELF 32bit header */
index f7267ad..6cfe52c 100644 (file)
@@ -340,6 +340,7 @@ class Elf_Builder;
 class NotifyGdb
 {
 public:
+    class FileTableBuilder;
     static void MethodPrepared(MethodDesc* methodDescPtr);
     static void MethodPitched(MethodDesc* methodDescPtr);
     template <typename PARENT_TRAITS>
@@ -422,7 +423,7 @@ private:
 #ifdef FEATURE_GDBJIT_SYMTAB
     static bool EmitSymtab(Elf_Builder &, MethodDesc* methodDescPtr, PCODE pCode, TADDR codeSize);
 #endif // FEATURE_GDBJIT_SYMTAB
-    static bool EmitDebugInfo(Elf_Builder &, MethodDesc* methodDescPtr, PCODE pCode, TADDR codeSize, const char *szModuleFile);
+    static bool EmitDebugInfo(Elf_Builder &, MethodDesc* methodDescPtr, PCODE pCode, TADDR codeSize);
 
     static bool BuildSymbolTableSection(MemBuf& buf, PCODE addr, TADDR codeSize, int methodCount,
                                         NewArrayHolder<Elf_Symbol> &symbolNames, int symbolCount,
@@ -432,14 +433,15 @@ private:
     static bool BuildDebugAbbrev(MemBuf& buf);
     static bool BuildDebugInfo(MemBuf& buf, PTK_TypeInfoMap pTypeMap, FunctionMemberPtrArrayHolder &method);
     static bool BuildDebugPub(MemBuf& buf, const char* name, uint32_t size, uint32_t dieOffset);
-    static bool BuildLineTable(MemBuf& buf, PCODE startAddr, TADDR codeSize, SymbolsInfo* lines, unsigned nlines);
-    static bool BuildFileTable(MemBuf& buf, SymbolsInfo* lines, unsigned nlines);
+    static bool BuildLineTable(MemBuf& buf, PCODE startAddr, TADDR codeSize, SymbolsInfo* lines, unsigned nlines,
+                               const char * &cuPath);
+    static bool BuildFileTable(MemBuf& buf, SymbolsInfo* lines, unsigned nlines, const char * &cuPath);
     static bool BuildLineProg(MemBuf& buf, PCODE startAddr, TADDR codeSize, SymbolsInfo* lines, unsigned nlines);
     static void IssueSetAddress(char*& ptr, PCODE addr);
     static void IssueEndOfSequence(char*& ptr);
     static void IssueSimpleCommand(char*& ptr, uint8_t command);
     static void IssueParamCommand(char*& ptr, uint8_t command, char* param, int param_len);
-    static void SplitPathname(const char* path, const char*& pathName, const char*& fileName);
+    static const char* SplitFilename(const char* path);
     static bool CollectCalledMethods(CalledMethod* pCM, TADDR nativeCode, FunctionMemberPtrArrayHolder &method,
                                      NewArrayHolder<Elf_Symbol> &symbolNames, int &symbolCount);
 };