From 9509fc86a50ad107ee4d601e917cf072f135aa5e Mon Sep 17 00:00:00 2001 From: Igor Kulaychuk Date: Wed, 27 Sep 2017 16:32:39 +0300 Subject: [GDBJIT] Provide full file names in generated DWARF (#14172) * 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 | 2 +- src/vm/gdbjit.cpp | 265 +++++++++++++++++++++++--------- src/vm/gdbjit.h | 10 +- 3 files changed, 200 insertions(+), 77 deletions(-) (limited to 'src') diff --git a/src/ToolBox/SOS/NETCore/SymbolReader.cs b/src/ToolBox/SOS/NETCore/SymbolReader.cs index e0fcdb517d..7a4bb52102 100644 --- a/src/ToolBox/SOS/NETCore/SymbolReader.cs +++ b/src/ToolBox/SOS/NETCore/SymbolReader.cs @@ -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); } diff --git a/src/vm/gdbjit.cpp b/src/vm/gdbjit.cpp index 9557b0bf3e..9dbf9df1a2 100644 --- a/src/vm/gdbjit.cpp +++ b/src/vm/gdbjit.cpp @@ -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 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(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 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 > 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 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[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 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(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(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 */ diff --git a/src/vm/gdbjit.h b/src/vm/gdbjit.h index f7267ad2a1..6cfe52c36d 100644 --- a/src/vm/gdbjit.h +++ b/src/vm/gdbjit.h @@ -340,6 +340,7 @@ class Elf_Builder; class NotifyGdb { public: + class FileTableBuilder; static void MethodPrepared(MethodDesc* methodDescPtr); static void MethodPitched(MethodDesc* methodDescPtr); template @@ -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 &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 &symbolNames, int &symbolCount); }; -- cgit v1.2.3