Skip to content

Commit 3f61e4e

Browse files
NerixyzMichael137
andauthored
[LLDB][NativePDB] Resolve declaration for tag types (llvm#152579)
Tag types like stucts or enums didn't have a declaration attached to them. The source locations are present in the IPI stream in `LF_UDT_MOD_SRC_LINE` records: ``` 0x101F | LF_UDT_MOD_SRC_LINE [size = 18, hash = 0x1C63] udt = 0x1058, mod = 3, file = 1, line = 0 0x2789 | LF_UDT_MOD_SRC_LINE [size = 18, hash = 0x1E5A] udt = 0x1253, mod = 35, file = 93, line = 17069 ``` The file is an ID in the string table `/names`: ``` ID | String 1 | '\<unknown>' 12 | 'D:\a\_work\1\s\src\ExternalAPIs\WindowsSDKInc\c\Include\10.0.22621.0\um\wingdi.h' 93 | 'D:\a\_work\1\s\src\ExternalAPIs\WindowsSDKInc\c\Include\10.0.22621.0\um\winnt.h' ``` Here, we're not interested in `mod`. This would indicate which module contributed the UDT. I was looking at Rustc's PDB and found that it uses `<unknown>` for some types, so I added a check for that. This makes two DIA PDB shell tests to work with the native PDB plugin. --------- Co-authored-by: Michael Buch <[email protected]>
1 parent b878793 commit 3f61e4e

File tree

5 files changed

+169
-2
lines changed

5 files changed

+169
-2
lines changed

lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.cpp

Lines changed: 81 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -644,8 +644,14 @@ SymbolFileNativePDB::CreateClassStructUnion(PdbTypeSymId type_id,
644644

645645
std::string uname = GetUnqualifiedTypeName(record);
646646

647-
// FIXME: Search IPI stream for LF_UDT_MOD_SRC_LINE.
647+
llvm::Expected maybeDecl = ResolveUdtDeclaration(type_id);
648648
Declaration decl;
649+
if (maybeDecl)
650+
decl = std::move(*maybeDecl);
651+
else
652+
LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), maybeDecl.takeError(),
653+
"Failed to resolve declaration for '{1}': {0}", uname);
654+
649655
return MakeType(toOpaqueUid(type_id), ConstString(uname), size, nullptr,
650656
LLDB_INVALID_UID, Type::eEncodingIsUID, decl, ct,
651657
Type::ResolveState::Forward);
@@ -668,7 +674,14 @@ lldb::TypeSP SymbolFileNativePDB::CreateTagType(PdbTypeSymId type_id,
668674
CompilerType ct) {
669675
std::string uname = GetUnqualifiedTypeName(er);
670676

677+
llvm::Expected maybeDecl = ResolveUdtDeclaration(type_id);
671678
Declaration decl;
679+
if (maybeDecl)
680+
decl = std::move(*maybeDecl);
681+
else
682+
LLDB_LOG_ERROR(GetLog(LLDBLog::Symbols), maybeDecl.takeError(),
683+
"Failed to resolve declaration for '{1}': {0}", uname);
684+
672685
TypeSP underlying_type = GetOrCreateType(er.UnderlyingType);
673686

674687
return MakeType(
@@ -2556,3 +2569,70 @@ SymbolFileNativePDB::GetContextForType(TypeIndex ti) {
25562569
}
25572570
return ctx;
25582571
}
2572+
2573+
void SymbolFileNativePDB::CacheUdtDeclarations() {
2574+
for (CVType cvt : m_index->ipi().typeArray()) {
2575+
switch (cvt.kind()) {
2576+
case LF_UDT_SRC_LINE: {
2577+
UdtSourceLineRecord udt_src;
2578+
llvm::cantFail(TypeDeserializer::deserializeAs(cvt, udt_src));
2579+
m_udt_declarations.try_emplace(
2580+
udt_src.UDT, UdtDeclaration{/*FileNameIndex=*/udt_src.SourceFile,
2581+
/*IsIpiIndex=*/true,
2582+
/*Line=*/udt_src.LineNumber});
2583+
} break;
2584+
case LF_UDT_MOD_SRC_LINE: {
2585+
UdtModSourceLineRecord udt_mod_src;
2586+
llvm::cantFail(TypeDeserializer::deserializeAs(cvt, udt_mod_src));
2587+
// Some types might be contributed by multiple modules. We assume that
2588+
// they all point to the same file and line because we can only provide
2589+
// one location.
2590+
m_udt_declarations.try_emplace(
2591+
udt_mod_src.UDT,
2592+
UdtDeclaration{/*FileNameIndex=*/udt_mod_src.SourceFile,
2593+
/*IsIpiIndex=*/false,
2594+
/*Line=*/udt_mod_src.LineNumber});
2595+
} break;
2596+
default:
2597+
break;
2598+
}
2599+
}
2600+
}
2601+
2602+
llvm::Expected<Declaration>
2603+
SymbolFileNativePDB::ResolveUdtDeclaration(PdbTypeSymId type_id) {
2604+
std::call_once(m_cached_udt_declarations, [this] { CacheUdtDeclarations(); });
2605+
2606+
auto it = m_udt_declarations.find(type_id.index);
2607+
if (it == m_udt_declarations.end())
2608+
return llvm::createStringError("No UDT declaration found");
2609+
2610+
llvm::StringRef file_name;
2611+
if (it->second.IsIpiIndex) {
2612+
CVType cvt = m_index->ipi().getType(it->second.FileNameIndex);
2613+
if (cvt.kind() != LF_STRING_ID)
2614+
return llvm::createStringError("File name was not a LF_STRING_ID");
2615+
2616+
StringIdRecord sid;
2617+
llvm::cantFail(TypeDeserializer::deserializeAs(cvt, sid));
2618+
file_name = sid.String;
2619+
} else {
2620+
// The file name index is an index into the string table
2621+
auto string_table = m_index->pdb().getStringTable();
2622+
if (!string_table)
2623+
return string_table.takeError();
2624+
2625+
llvm::Expected<llvm::StringRef> string =
2626+
string_table->getStringTable().getString(
2627+
it->second.FileNameIndex.getIndex());
2628+
if (!string)
2629+
return string.takeError();
2630+
file_name = *string;
2631+
}
2632+
2633+
// rustc sets the filename to "<unknown>" for some files
2634+
if (file_name == "\\<unknown>")
2635+
return Declaration();
2636+
2637+
return Declaration(FileSpec(file_name), it->second.Line);
2638+
}

lldb/source/Plugins/SymbolFile/NativePDB/SymbolFileNativePDB.h

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -262,6 +262,9 @@ class SymbolFileNativePDB : public SymbolFileCommon {
262262

263263
void CacheFunctionNames();
264264

265+
void CacheUdtDeclarations();
266+
llvm::Expected<Declaration> ResolveUdtDeclaration(PdbTypeSymId type_id);
267+
265268
llvm::BumpPtrAllocator m_allocator;
266269

267270
lldb::addr_t m_obj_load_address = 0;
@@ -283,6 +286,18 @@ class SymbolFileNativePDB : public SymbolFileCommon {
283286
llvm::DenseMap<llvm::codeview::TypeIndex, llvm::codeview::TypeIndex>
284287
m_parent_types;
285288

289+
struct UdtDeclaration {
290+
/// This could either be an index into the `/names` section (string table,
291+
/// LF_UDT_MOD_SRC_LINE) or, this could be an index into the IPI stream to a
292+
/// LF_STRING_ID record (LF_UDT_SRC_LINE).
293+
llvm::codeview::TypeIndex FileNameIndex;
294+
bool IsIpiIndex;
295+
296+
uint32_t Line;
297+
};
298+
llvm::DenseMap<llvm::codeview::TypeIndex, UdtDeclaration> m_udt_declarations;
299+
std::once_flag m_cached_udt_declarations;
300+
286301
lldb_private::UniqueCStringMap<uint32_t> m_type_base_names;
287302

288303
/// mangled name/full function name -> Global ID(s)
Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
; Test that the declaration for UDTs won't be "<unknown>" or "\<unknown>".
2+
; Rustc sets the location of some builtin types to this string.
3+
4+
; REQUIRES: system-windows
5+
; RUN: %build --compiler=clang-cl --nodefaultlib -o %t.exe -- %s
6+
; RUN: lldb-test symbols %t.exe | FileCheck %s
7+
8+
; there shouldn't be a declaration (would be between size and compiler_type)
9+
; CHECK: Type{{.*}} , name = "Foo", size = 1, compiler_type = {{.*}} struct Foo {
10+
11+
; This is edited output from clang simulates rustc behavior (see !17)
12+
; Source:
13+
; struct Foo {};
14+
;
15+
; int main() { Foo f; }
16+
17+
18+
; ModuleID = 'main.cpp'
19+
source_filename = "main.cpp"
20+
target datalayout = "e-m:w-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
21+
target triple = "x86_64-pc-windows-msvc19.44.35207"
22+
23+
%struct.Foo = type { i8 }
24+
25+
; Function Attrs: mustprogress noinline norecurse nounwind optnone uwtable
26+
define dso_local noundef i32 @main() #0 !dbg !9 {
27+
%1 = alloca %struct.Foo, align 1
28+
#dbg_declare(ptr %1, !14, !DIExpression(), !16)
29+
ret i32 0, !dbg !16
30+
}
31+
32+
attributes #0 = { mustprogress noinline norecurse nounwind optnone uwtable "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
33+
34+
!llvm.dbg.cu = !{!0}
35+
!llvm.module.flags = !{!2, !3, !4, !5, !6, !7}
36+
!llvm.ident = !{!8}
37+
38+
!0 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !1, producer: "clang version 20.1.6", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, splitDebugInlining: false, nameTableKind: None)
39+
!1 = !DIFile(filename: "main.cpp", directory: "F:\\Dev\\rust-dbg-test", checksumkind: CSK_MD5, checksum: "b8942260dadf9ec35328889f05afb954")
40+
!2 = !{i32 2, !"CodeView", i32 1}
41+
!3 = !{i32 2, !"Debug Info Version", i32 3}
42+
!4 = !{i32 1, !"wchar_size", i32 2}
43+
!5 = !{i32 8, !"PIC Level", i32 2}
44+
!6 = !{i32 7, !"uwtable", i32 2}
45+
!7 = !{i32 1, !"MaxTLSAlign", i32 65536}
46+
!8 = !{!"clang version 20.1.6"}
47+
!9 = distinct !DISubprogram(name: "main", scope: !1, file: !1, line: 3, type: !10, scopeLine: 3, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !0, retainedNodes: !13)
48+
!10 = !DISubroutineType(types: !11)
49+
!11 = !{!12}
50+
!12 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed)
51+
!13 = !{}
52+
!14 = !DILocalVariable(name: "f", scope: !9, file: !1, line: 3, type: !15)
53+
!15 = distinct !DICompositeType(tag: DW_TAG_structure_type, name: "Foo", file: !17, line: 1, size: 8, flags: DIFlagTypePassByValue, elements: !13, identifier: ".?AUFoo@@")
54+
!16 = !DILocation(line: 3, scope: !9)
55+
; This is how rustc emits some types
56+
!17 = !DIFile(filename: "<unknown>", directory: "")

lldb/test/Shell/SymbolFile/PDB/class-layout.test

Lines changed: 11 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,19 @@ RUN: lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix
1212
RUN: lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=BASE %s
1313
RUN: lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=FRIEND %s
1414
RUN: lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=CLASS %s
15+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck %s
16+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=ENUM %s
17+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=UNION %s
18+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=STRUCT %s
19+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=COMPLEX %s
20+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=LIST %s
21+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=UNNAMED-STRUCT %s
22+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=BASE %s
23+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=FRIEND %s
24+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/ClassLayoutTest.cpp.exe | FileCheck --check-prefix=CLASS %s
1525

1626
CHECK: Module [[MOD:.*]]
17-
CHECK: SymbolFile pdb ([[MOD]])
27+
CHECK: SymbolFile {{(native-)?}}pdb ([[MOD]])
1828
CHECK: {{^[0-9A-F]+}}: CompileUnit{{[{]0x[0-9a-f]+[}]}}, language = "c++", file = '{{.*}}\ClassLayoutTest.cpp'
1929

2030
ENUM: name = "Enum", size = 4, decl = ClassLayoutTest.cpp:5

lldb/test/Shell/SymbolFile/PDB/enums-layout.test

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,12 @@ RUN: lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-
77
RUN: lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=UCHAR-ENUM %s
88
RUN: lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=CLASS-ENUM %s
99
RUN: lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=STRUCT-ENUM %s
10+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=ENUM %s
11+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=CONST-ENUM %s
12+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=EMPTY-ENUM %s
13+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=UCHAR-ENUM %s
14+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=CLASS-ENUM %s
15+
RUN: env LLDB_USE_NATIVE_PDB_READER=1 lldb-test symbols %t.dir/SimpleTypesTest.cpp.enums.exe | FileCheck --check-prefix=STRUCT-ENUM %s
1016

1117
; FIXME: PDB does not have information about scoped enumeration (Enum class) so the
1218
; compiler type used is the same as the one for unscoped enumeration.

0 commit comments

Comments
 (0)