diff --git a/include/llvm/DebugInfo/PDB/DIA/DIARawSymbol.h b/include/llvm/DebugInfo/PDB/DIA/DIARawSymbol.h index 6336b319a4f..002a46e60ff 100644 --- a/include/llvm/DebugInfo/PDB/DIA/DIARawSymbol.h +++ b/include/llvm/DebugInfo/PDB/DIA/DIARawSymbol.h @@ -97,6 +97,7 @@ public: std::string getUndecoratedName() const override; uint32_t getUnmodifiedTypeId() const override; uint32_t getUpperBoundId() const override; + Variant getValue() const override; uint32_t getVirtualBaseDispIndex() const override; uint32_t getVirtualBaseOffset() const override; uint32_t getVirtualTableShapeId() const override; @@ -193,6 +194,8 @@ public: bool isVirtualBaseClass() const override; bool isVirtualInheritance() const override; bool isVolatileType() const override; + bool wasInlined() const override; + std::string getUnused() const override; private: const DIASession &Session; diff --git a/include/llvm/DebugInfo/PDB/IPDBRawSymbol.h b/include/llvm/DebugInfo/PDB/IPDBRawSymbol.h index 5dd103ff100..dd949b25654 100644 --- a/include/llvm/DebugInfo/PDB/IPDBRawSymbol.h +++ b/include/llvm/DebugInfo/PDB/IPDBRawSymbol.h @@ -106,6 +106,7 @@ public: virtual std::string getUndecoratedName() const = 0; virtual uint32_t getUnmodifiedTypeId() const = 0; virtual uint32_t getUpperBoundId() const = 0; + virtual Variant getValue() const = 0; virtual uint32_t getVirtualBaseDispIndex() const = 0; virtual uint32_t getVirtualBaseOffset() const = 0; virtual uint32_t getVirtualTableShapeId() const = 0; @@ -202,6 +203,8 @@ public: virtual bool isVirtualBaseClass() const = 0; virtual bool isVirtualInheritance() const = 0; virtual bool isVolatileType() const = 0; + virtual bool wasInlined() const = 0; + virtual std::string getUnused() const = 0; }; } // namespace llvm diff --git a/include/llvm/DebugInfo/PDB/PDBExtras.h b/include/llvm/DebugInfo/PDB/PDBExtras.h index 5a768e74e54..1dee6656ede 100644 --- a/include/llvm/DebugInfo/PDB/PDBExtras.h +++ b/include/llvm/DebugInfo/PDB/PDBExtras.h @@ -25,6 +25,8 @@ struct stream_indent { }; raw_ostream &operator<<(raw_ostream &OS, const stream_indent &Indent); +raw_ostream &operator<<(raw_ostream &OS, const PDB_VariantType &Value); +raw_ostream &operator<<(raw_ostream &OS, const PDB_DataKind &Data); raw_ostream &operator<<(raw_ostream &OS, const PDB_RegisterId &Reg); raw_ostream &operator<<(raw_ostream &OS, const PDB_LocType &Loc); raw_ostream &operator<<(raw_ostream &OS, const PDB_ThunkOrdinal &Thunk); @@ -32,6 +34,8 @@ raw_ostream &operator<<(raw_ostream &OS, const PDB_Checksum &Checksum); raw_ostream &operator<<(raw_ostream &OS, const PDB_Lang &Lang); raw_ostream &operator<<(raw_ostream &OS, const PDB_SymType &Tag); raw_ostream &operator<<(raw_ostream &OS, const PDB_UniqueId &Id); + +raw_ostream &operator<<(raw_ostream &OS, const Variant &Value); raw_ostream &operator<<(raw_ostream &OS, const VersionInfo &Version); raw_ostream &operator<<(raw_ostream &OS, const TagStats &Stats); } diff --git a/include/llvm/DebugInfo/PDB/PDBSymbolData.h b/include/llvm/DebugInfo/PDB/PDBSymbolData.h index a67d08f9ad6..c99ef63f8e7 100644 --- a/include/llvm/DebugInfo/PDB/PDBSymbolData.h +++ b/include/llvm/DebugInfo/PDB/PDBSymbolData.h @@ -49,7 +49,7 @@ public: FORWARD_SYMBOL_METHOD(getToken) FORWARD_SYMBOL_METHOD(getTypeId) FORWARD_SYMBOL_METHOD(isUnalignedType) - // FORWARD_SYMBOL_METHOD(getValue) + FORWARD_SYMBOL_METHOD(getValue) FORWARD_SYMBOL_METHOD(getVirtualAddress) FORWARD_SYMBOL_METHOD(isVolatileType) }; diff --git a/include/llvm/DebugInfo/PDB/PDBTypes.h b/include/llvm/DebugInfo/PDB/PDBTypes.h index 0009b02505a..81d9a357107 100644 --- a/include/llvm/DebugInfo/PDB/PDBTypes.h +++ b/include/llvm/DebugInfo/PDB/PDBTypes.h @@ -433,6 +433,44 @@ struct VersionInfo { uint32_t QFE; }; +enum PDB_VariantType { + Empty, + Unknown, + Int8, + Int16, + Int32, + Int64, + Single, + Double, + UInt8, + UInt16, + UInt32, + UInt64, + Bool, +}; + +struct Variant { + Variant() + : Type(PDB_VariantType::Empty) { + } + + PDB_VariantType Type; + union { + bool Bool; + int8_t Int8; + int16_t Int16; + int32_t Int32; + int64_t Int64; + float Single; + double Double; + uint8_t UInt8; + uint16_t UInt16; + uint32_t UInt32; + uint64_t UInt64; + void* Pointer; + }; +}; + } // namespace llvm namespace std { diff --git a/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp b/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp index 530d3b5deb5..e9e5ce2fc7b 100644 --- a/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp +++ b/lib/DebugInfo/PDB/DIA/DIARawSymbol.cpp @@ -18,6 +18,60 @@ using namespace llvm; namespace { +Variant VariantFromVARIANT(const VARIANT &V) { + Variant Result; + switch (V.vt) { + case VT_I1: + Result.Int8 = V.cVal; + Result.Type = PDB_VariantType::Int8; + break; + case VT_I2: + Result.Int16 = V.iVal; + Result.Type = PDB_VariantType::Int16; + break; + case VT_I4: + Result.Int32 = V.intVal; + Result.Type = PDB_VariantType::Int32; + break; + case VT_I8: + Result.Int64 = V.llVal; + Result.Type = PDB_VariantType::Int64; + break; + case VT_UI1: + Result.UInt8 = V.bVal; + Result.Type = PDB_VariantType::UInt8; + break; + case VT_UI2: + Result.UInt16 = V.uiVal; + Result.Type = PDB_VariantType::UInt16; + break; + case VT_UI4: + Result.UInt32 = V.uintVal; + Result.Type = PDB_VariantType::UInt32; + break; + case VT_UI8: + Result.UInt64 = V.ullVal; + Result.Type = PDB_VariantType::UInt64; + break; + case VT_BOOL: + Result.Bool = (V.boolVal == VARIANT_TRUE) ? true : false; + Result.Type = PDB_VariantType::Bool; + break; + case VT_R4: + Result.Single = V.fltVal; + Result.Type = PDB_VariantType::Single; + break; + case VT_R8: + Result.Double = V.dblVal; + Result.Type = PDB_VariantType::Double; + break; + default: + Result.Type = PDB_VariantType::Unknown; + break; + } + return Result; +} + template ArgType PrivateGetDIAValue(IDiaSymbol *Symbol, HRESULT (__stdcall IDiaSymbol::*Method)(ArgType *)) { @@ -93,6 +147,18 @@ void DumpDIAValue(llvm::raw_ostream &OS, int Indent, StringRef Name, } ::SysFreeString(Value); } + +void DumpDIAValue(llvm::raw_ostream &OS, int Indent, StringRef Name, + IDiaSymbol *Symbol, + HRESULT (__stdcall IDiaSymbol::*Method)(VARIANT *)) { + VARIANT Value; + Value.vt = VT_EMPTY; + if (S_OK != (Symbol->*Method)(&Value)) + return; + Variant V = VariantFromVARIANT(Value); + OS.indent(Indent); + OS << V; +} } namespace llvm { @@ -276,6 +342,9 @@ void DIARawSymbol::dump(raw_ostream &OS, int Indent, RAW_METHOD_DUMP(OS, get_virtualBaseClass) RAW_METHOD_DUMP(OS, get_isVirtualInheritance) RAW_METHOD_DUMP(OS, get_volatileType) + RAW_METHOD_DUMP(OS, get_wasInlined) + RAW_METHOD_DUMP(OS, get_unused) + RAW_METHOD_DUMP(OS, get_value) } std::unique_ptr @@ -611,6 +680,15 @@ uint32_t DIARawSymbol::getUpperBoundId() const { return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_upperBoundId); } +Variant DIARawSymbol::getValue() const { + VARIANT Value; + Value.vt = VT_EMPTY; + if (S_OK != Symbol->get_value(&Value)) + return Variant(); + + return VariantFromVARIANT(Value); +} + uint32_t DIARawSymbol::getVirtualBaseDispIndex() const { return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_virtualBaseDispIndex); } @@ -1006,3 +1084,11 @@ bool DIARawSymbol::isVirtualInheritance() const { bool DIARawSymbol::isVolatileType() const { return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_volatileType); } + +bool DIARawSymbol::wasInlined() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_wasInlined); +} + +std::string DIARawSymbol::getUnused() const { + return PrivateGetDIAValue(Symbol, &IDiaSymbol::get_unused); +} diff --git a/lib/DebugInfo/PDB/PDBExtras.cpp b/lib/DebugInfo/PDB/PDBExtras.cpp index 28870870de5..9ead764ae76 100644 --- a/lib/DebugInfo/PDB/PDBExtras.cpp +++ b/lib/DebugInfo/PDB/PDBExtras.cpp @@ -25,6 +25,41 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const stream_indent &Indent) { return OS; } +raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_VariantType &Type) { + switch (Type) { + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Bool, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Single, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Double, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Int8, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Int16, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Int32, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, Int64, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, UInt8, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, UInt16, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, UInt32, OS) + CASE_OUTPUT_ENUM_CLASS_NAME(PDB_VariantType, UInt64, OS) + default: + OS << "Unknown"; + } + return OS; +} + +raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_DataKind &Data) { + switch (Data) { + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Unknown, "unknown", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Local, "local", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, StaticLocal, "static local", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Param, "param", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, ObjectPtr, "this ptr", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, FileStatic, "static global", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Global, "global", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Member, "member", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, StaticMember, "static member", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_DataKind, Constant, "const", OS) + } + return OS; +} + raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_RegisterId &Reg) { switch (Reg) { CASE_OUTPUT_ENUM_CLASS_NAME(PDB_RegisterId, AL, OS) @@ -82,16 +117,16 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_RegisterId &Reg) { raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_LocType &Loc) { switch (Loc) { - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, Static, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, TLS, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, RegRel, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, ThisRel, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, Enregistered, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, BitField, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, Slot, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, IlRel, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, MetaData, OS) - CASE_OUTPUT_ENUM_CLASS_NAME(PDB_LocType, Constant, OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, Static, "static", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, TLS, "tls", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, RegRel, "regrel", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, ThisRel, "thisrel", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, Enregistered, "register", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, BitField, "bitfield", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, Slot, "slot", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, IlRel, "IL rel", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, MetaData, "metadata", OS) + CASE_OUTPUT_ENUM_CLASS_STR(PDB_LocType, Constant, "constant", OS) default: OS << "Unknown"; } @@ -200,6 +235,48 @@ raw_ostream &llvm::operator<<(raw_ostream &OS, const PDB_UniqueId &Id) { return OS; } +raw_ostream &llvm::operator<<(raw_ostream &OS, const Variant &Value) { + switch (Value.Type) { + case PDB_VariantType::Bool: + OS << (Value.Bool ? "true" : "false"); + break; + case PDB_VariantType::Double: + OS << Value.Double; + break; + case PDB_VariantType::Int16: + OS << Value.Int16; + break; + case PDB_VariantType::Int32: + OS << Value.Int32; + break; + case PDB_VariantType::Int64: + OS << Value.Int64; + break; + case PDB_VariantType::Int8: + OS << Value.Int8; + break; + case PDB_VariantType::Single: + OS << Value.Single; + break; + case PDB_VariantType::UInt16: + OS << Value.Double; + break; + case PDB_VariantType::UInt32: + OS << Value.UInt32; + break; + case PDB_VariantType::UInt64: + OS << Value.UInt64; + break; + case PDB_VariantType::UInt8: + OS << Value.UInt8; + break; + default: + OS << Value.Type; + } + OS << " {" << Value.Type << "}"; + return OS; +} + raw_ostream &llvm::operator<<(raw_ostream &OS, const VersionInfo &Version) { OS << Version.Major << "." << Version.Minor << "." << Version.Build; return OS; diff --git a/lib/DebugInfo/PDB/PDBSymbolData.cpp b/lib/DebugInfo/PDB/PDBSymbolData.cpp index bffad8af751..3c4be3ad2d0 100644 --- a/lib/DebugInfo/PDB/PDBSymbolData.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolData.cpp @@ -8,8 +8,10 @@ //===----------------------------------------------------------------------===// #include +#include "llvm/DebugInfo/PDB/IPDBSession.h" #include "llvm/DebugInfo/PDB/PDBExtras.h" #include "llvm/DebugInfo/PDB/PDBSymbolData.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" #include "llvm/Support/Format.h" @@ -21,48 +23,69 @@ PDBSymbolData::PDBSymbolData(const IPDBSession &PDBSession, void PDBSymbolData::dump(raw_ostream &OS, int Indent, PDB_DumpLevel Level) const { - OS.indent(Indent); + OS << stream_indent(Indent); + PDB_LocType Loc = getLocationType(); + PDB_DataKind Kind = getDataKind(); if (Level == PDB_DumpLevel::Compact) { - PDB_LocType Loc = getLocationType(); - OS << Loc << " data ["; - int Length; switch (Loc) { - case PDB_LocType::Static: - OS << format_hex(getRelativeVirtualAddress(), 10); - Length = getLength(); + case PDB_LocType::Static: { + uint32_t RVA = getRelativeVirtualAddress(); + OS << Kind << " data["; + if (RVA != 0) + OS << format_hex(RVA, 10); + else + OS << "???"; break; + } case PDB_LocType::TLS: + OS << "threadlocal " << Kind << " data["; OS << getAddressSection() << ":" << format_hex(getAddressOffset(), 10); break; case PDB_LocType::RegRel: - OS << getRegisterId() << " + " << getOffset() << "]"; + OS << "regrel " << Kind << " data["; + OS << getRegisterId() << " + " << getOffset(); break; - case PDB_LocType::ThisRel: - OS << "this + " << getOffset() << "]"; + case PDB_LocType::ThisRel: { + uint32_t Offset = getOffset(); + OS << Kind << " data[this + " << format_hex(Offset, 4); break; + } case PDB_LocType::Enregistered: - OS << getRegisterId() << "]"; + OS << "register " << Kind << " data[" << getRegisterId(); break; case PDB_LocType::BitField: { + OS << "bitfield data[this + "; uint32_t Offset = getOffset(); uint32_t BitPos = getBitPosition(); uint32_t Length = getLength(); - uint32_t StartBits = 8 - BitPos; - uint32_t MiddleBytes = (Length - StartBits) / 8; - uint32_t EndBits = Length - StartBits - MiddleBytes * 8; - OS << format_hex(Offset, 10) << ":" << BitPos; - OS << " - " << format_hex(Offset + MiddleBytes, 10) << ":" << EndBits; + OS << format_hex(Offset, 4) << ":" << BitPos << "," << Length; break; } case PDB_LocType::Slot: OS << getSlot(); + break; + case PDB_LocType::Constant: { + OS << "constant data["; + OS << getValue(); + break; + } case PDB_LocType::IlRel: case PDB_LocType::MetaData: - case PDB_LocType::Constant: default: OS << "???"; } - OS << "] "; + } + OS << "] "; + if (Kind == PDB_DataKind::Member || Kind == PDB_DataKind::StaticMember) { + uint32_t ClassId = getClassParentId(); + if (auto Class = Session.getSymbolById(ClassId)) { + if (auto UDT = dyn_cast(Class.get())) + OS << UDT->getName(); + else + OS << "{class " << Class->getSymTag() << "}"; + OS << "::"; + } } OS << getName() << "\n"; + OS.flush(); } \ No newline at end of file diff --git a/lib/DebugInfo/PDB/PDBSymbolExe.cpp b/lib/DebugInfo/PDB/PDBSymbolExe.cpp index c058949991b..4f999d9138e 100644 --- a/lib/DebugInfo/PDB/PDBSymbolExe.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolExe.cpp @@ -29,10 +29,6 @@ void PDBSymbolExe::dump(raw_ostream &OS, int Indent, OS << "Summary for " << FileName << "\n"; - TagStats Stats; - auto ChildrenEnum = getChildStats(Stats); - OS << stream_indent(Indent + 2) << "Children: " << Stats << "\n"; - uint64_t FileSize = 0; if (!llvm::sys::fs::file_size(FileName, FileSize)) OS << " Size: " << FileSize << " bytes\n"; @@ -47,4 +43,11 @@ void PDBSymbolExe::dump(raw_ostream &OS, int Indent, if (hasPrivateSymbols()) OS << "HasPrivateSymbols "; OS << "\n"; + + TagStats Stats; + auto ChildrenEnum = getChildStats(Stats); + OS << stream_indent(Indent + 2) << "Children: " << Stats << "\n"; + while (auto Child = ChildrenEnum->getNext()) { + Child->dump(OS, Indent+2, PDB_DumpLevel::Compact); + } } diff --git a/lib/DebugInfo/PDB/PDBSymbolFunc.cpp b/lib/DebugInfo/PDB/PDBSymbolFunc.cpp index 17473c17a45..4bd96080c7f 100644 --- a/lib/DebugInfo/PDB/PDBSymbolFunc.cpp +++ b/lib/DebugInfo/PDB/PDBSymbolFunc.cpp @@ -10,10 +10,12 @@ #include #include "llvm/DebugInfo/PDB/IPDBEnumChildren.h" +#include "llvm/DebugInfo/PDB/IPDBSession.h" #include "llvm/DebugInfo/PDB/PDBSymbol.h" #include "llvm/DebugInfo/PDB/PDBSymbolFunc.h" #include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugEnd.h" #include "llvm/DebugInfo/PDB/PDBSymbolFuncDebugStart.h" +#include "llvm/DebugInfo/PDB/PDBSymbolTypeUDT.h" #include "llvm/Support/Format.h" @@ -25,16 +27,23 @@ PDBSymbolFunc::PDBSymbolFunc(const IPDBSession &PDBSession, void PDBSymbolFunc::dump(raw_ostream &OS, int Indent, PDB_DumpLevel Level) const { if (Level == PDB_DumpLevel::Compact) { + OS << stream_indent(Indent); + uint32_t FuncStart = getRelativeVirtualAddress(); uint32_t FuncEnd = FuncStart + getLength(); - OS << stream_indent(Indent); - OS << "[" << format_hex(FuncStart, 8); - if (auto DebugStart = findOneChild()) - OS << "+" << DebugStart->getRelativeVirtualAddress() - FuncStart; - OS << " - " << format_hex(FuncEnd, 8); - if (auto DebugEnd = findOneChild()) + if (FuncStart == 0 && FuncEnd == 0) { + OS << "func [???]"; + } else { + OS << "func "; + OS << "[" << format_hex(FuncStart, 8); + if (auto DebugStart = findOneChild()) + OS << "+" << DebugStart->getRelativeVirtualAddress() - FuncStart; + OS << " - " << format_hex(FuncEnd, 8); + if (auto DebugEnd = findOneChild()) OS << "-" << FuncEnd - DebugEnd->getRelativeVirtualAddress(); - OS << "] "; + OS << "] "; + } + PDB_RegisterId Reg = getLocalBasePointerRegisterId(); if (Reg == PDB_RegisterId::VFrame) OS << "(VFrame)"; @@ -42,6 +51,18 @@ void PDBSymbolFunc::dump(raw_ostream &OS, int Indent, OS << "(" << Reg << ")"; else OS << "(FPO)"; - OS << " " << getName() << "\n"; + + OS << " "; + uint32_t ClassId = getClassParentId(); + if (ClassId != 0) { + if (auto Class = Session.getSymbolById(ClassId)) { + if (auto UDT = dyn_cast(Class.get())) + OS << UDT->getName() << "::"; + else + OS << "{class " << Class->getSymTag() << "}::"; + } + } + OS << getName(); + OS << "\n"; } }