diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h index 233b55cc55c16..af87811f5d7d8 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFDebugFrame.h @@ -71,8 +71,8 @@ class CFIProgram { /// where a problem occurred in case an error is returned. Error parse(DWARFDataExtractor Data, uint64_t *Offset, uint64_t EndOffset); - void dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH, - unsigned IndentLevel = 1) const; + void dump(raw_ostream &OS, DIDumpOptions DumpOpts, const MCRegisterInfo *MRI, + bool IsEH, unsigned IndentLevel = 1) const; private: std::vector Instructions; @@ -121,7 +121,8 @@ class CFIProgram { static ArrayRef getOperandTypes(); /// Print \p Opcode's operand number \p OperandIdx which has value \p Operand. - void printOperand(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH, + void printOperand(raw_ostream &OS, DIDumpOptions DumpOpts, + const MCRegisterInfo *MRI, bool IsEH, const Instruction &Instr, unsigned OperandIdx, uint64_t Operand) const; }; @@ -146,8 +147,8 @@ class FrameEntry { CFIProgram &cfis() { return CFIs; } /// Dump the instructions in this CFI fragment - virtual void dump(raw_ostream &OS, const MCRegisterInfo *MRI, - bool IsEH) const = 0; + virtual void dump(raw_ostream &OS, DIDumpOptions DumpOpts, + const MCRegisterInfo *MRI, bool IsEH) const = 0; protected: const FrameKind Kind; @@ -201,7 +202,7 @@ class CIE : public FrameEntry { uint32_t getLSDAPointerEncoding() const { return LSDAPointerEncoding; } - void dump(raw_ostream &OS, const MCRegisterInfo *MRI, + void dump(raw_ostream &OS, DIDumpOptions DumpOpts, const MCRegisterInfo *MRI, bool IsEH) const override; private: @@ -242,7 +243,7 @@ class FDE : public FrameEntry { uint64_t getAddressRange() const { return AddressRange; } Optional getLSDAAddress() const { return LSDAAddress; } - void dump(raw_ostream &OS, const MCRegisterInfo *MRI, + void dump(raw_ostream &OS, DIDumpOptions DumpOpts, const MCRegisterInfo *MRI, bool IsEH) const override; static bool classof(const FrameEntry *FE) { return FE->getKind() == FK_FDE; } @@ -285,7 +286,7 @@ class DWARFDebugFrame { ~DWARFDebugFrame(); /// Dump the section data into the given stream. - void dump(raw_ostream &OS, const MCRegisterInfo *MRI, + void dump(raw_ostream &OS, DIDumpOptions DumpOpts, const MCRegisterInfo *MRI, Optional Offset) const; /// Parse the section from raw data. \p Data is assumed to contain the whole diff --git a/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h b/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h index edfa68d49a60d..82f2a237464f3 100644 --- a/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h +++ b/llvm/include/llvm/DebugInfo/DWARF/DWARFExpression.h @@ -10,10 +10,11 @@ #define LLVM_DEBUGINFO_DWARFEXPRESSION_H #include "llvm/ADT/ArrayRef.h" +#include "llvm/ADT/Optional.h" #include "llvm/ADT/iterator.h" #include "llvm/ADT/iterator_range.h" -#include "llvm/ADT/Optional.h" #include "llvm/BinaryFormat/Dwarf.h" +#include "llvm/DebugInfo/DIContext.h" #include "llvm/Support/DataExtractor.h" namespace llvm { @@ -93,8 +94,9 @@ class DWARFExpression { bool extract(DataExtractor Data, uint8_t AddressSize, uint64_t Offset, Optional Format); bool isError() { return Error; } - bool print(raw_ostream &OS, const DWARFExpression *Expr, - const MCRegisterInfo *RegInfo, DWARFUnit *U, bool isEH); + bool print(raw_ostream &OS, DIDumpOptions DumpOpts, + const DWARFExpression *Expr, const MCRegisterInfo *RegInfo, + DWARFUnit *U, bool isEH); bool verify(DWARFUnit *U); }; @@ -143,7 +145,8 @@ class DWARFExpression { iterator begin() const { return iterator(this, 0); } iterator end() const { return iterator(this, Data.getData().size()); } - void print(raw_ostream &OS, const MCRegisterInfo *RegInfo, DWARFUnit *U, + void print(raw_ostream &OS, DIDumpOptions DumpOpts, + const MCRegisterInfo *RegInfo, DWARFUnit *U, bool IsEH = false) const; /// Print the expression in a format intended to be compact and useful to a diff --git a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp index 8f9fc09d2b734..ab62ec1d04dc0 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFContext.cpp @@ -457,7 +457,7 @@ void DWARFContext::dump( shouldDump(Explicit, ".debug_frame", DIDT_ID_DebugFrame, DObj->getFrameSection().Data)) { if (Expected DF = getDebugFrame()) - (*DF)->dump(OS, getRegisterInfo(), *Off); + (*DF)->dump(OS, DumpOpts, getRegisterInfo(), *Off); else RecoverableErrorHandler(DF.takeError()); } @@ -466,7 +466,7 @@ void DWARFContext::dump( shouldDump(Explicit, ".eh_frame", DIDT_ID_DebugFrame, DObj->getEHFrameSection().Data)) { if (Expected DF = getEHFrame()) - (*DF)->dump(OS, getRegisterInfo(), *Off); + (*DF)->dump(OS, DumpOpts, getRegisterInfo(), *Off); else RecoverableErrorHandler(DF.takeError()); } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp index 5a05baca33369..b74ecac681f3a 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugFrame.cpp @@ -234,9 +234,10 @@ ArrayRef CFIProgram::getOperandTypes() { } /// Print \p Opcode's operand number \p OperandIdx which has value \p Operand. -void CFIProgram::printOperand(raw_ostream &OS, const MCRegisterInfo *MRI, - bool IsEH, const Instruction &Instr, - unsigned OperandIdx, uint64_t Operand) const { +void CFIProgram::printOperand(raw_ostream &OS, DIDumpOptions DumpOpts, + const MCRegisterInfo *MRI, bool IsEH, + const Instruction &Instr, unsigned OperandIdx, + uint64_t Operand) const { assert(OperandIdx < 2); uint8_t Opcode = Instr.Opcode; OperandType Type = getOperandTypes()[Opcode][OperandIdx]; @@ -287,12 +288,13 @@ void CFIProgram::printOperand(raw_ostream &OS, const MCRegisterInfo *MRI, case OT_Expression: assert(Instr.Expression && "missing DWARFExpression object"); OS << " "; - Instr.Expression->print(OS, MRI, nullptr, IsEH); + Instr.Expression->print(OS, DumpOpts, MRI, nullptr, IsEH); break; } } -void CFIProgram::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH, +void CFIProgram::dump(raw_ostream &OS, DIDumpOptions DumpOpts, + const MCRegisterInfo *MRI, bool IsEH, unsigned IndentLevel) const { for (const auto &Instr : Instructions) { uint8_t Opcode = Instr.Opcode; @@ -301,7 +303,7 @@ void CFIProgram::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH, OS.indent(2 * IndentLevel); OS << CallFrameString(Opcode, Arch) << ":"; for (unsigned i = 0; i < Instr.Ops.size(); ++i) - printOperand(OS, MRI, IsEH, Instr, i, Instr.Ops[i]); + printOperand(OS, DumpOpts, MRI, IsEH, Instr, i, Instr.Ops[i]); OS << '\n'; } } @@ -318,7 +320,8 @@ constexpr uint64_t getCIEId(bool IsDWARF64, bool IsEH) { return DW_CIE_ID; } -void CIE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { +void CIE::dump(raw_ostream &OS, DIDumpOptions DumpOpts, + const MCRegisterInfo *MRI, bool IsEH) const { // A CIE with a zero length is a terminator entry in the .eh_frame section. if (IsEH && Length == 0) { OS << format("%08" PRIx64, Offset) << " ZERO terminator\n"; @@ -350,11 +353,12 @@ void CIE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { OS << "\n"; } OS << "\n"; - CFIs.dump(OS, MRI, IsEH); + CFIs.dump(OS, DumpOpts, MRI, IsEH); OS << "\n"; } -void FDE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { +void FDE::dump(raw_ostream &OS, DIDumpOptions DumpOpts, + const MCRegisterInfo *MRI, bool IsEH) const { OS << format("%08" PRIx64, Offset) << format(" %0*" PRIx64, IsDWARF64 ? 16 : 8, Length) << format(" %0*" PRIx64, IsDWARF64 && !IsEH ? 16 : 8, CIEPointer) @@ -368,7 +372,7 @@ void FDE::dump(raw_ostream &OS, const MCRegisterInfo *MRI, bool IsEH) const { OS << " Format: " << FormatString(IsDWARF64) << "\n"; if (LSDAAddress) OS << format(" LSDA Address: %016" PRIx64 "\n", *LSDAAddress); - CFIs.dump(OS, MRI, IsEH); + CFIs.dump(OS, DumpOpts, MRI, IsEH); OS << "\n"; } @@ -597,15 +601,16 @@ FrameEntry *DWARFDebugFrame::getEntryAtOffset(uint64_t Offset) const { return nullptr; } -void DWARFDebugFrame::dump(raw_ostream &OS, const MCRegisterInfo *MRI, +void DWARFDebugFrame::dump(raw_ostream &OS, DIDumpOptions DumpOpts, + const MCRegisterInfo *MRI, Optional Offset) const { if (Offset) { if (auto *Entry = getEntryAtOffset(*Offset)) - Entry->dump(OS, MRI, IsEH); + Entry->dump(OS, DumpOpts, MRI, IsEH); return; } OS << "\n"; for (const auto &Entry : Entries) - Entry->dump(OS, MRI, IsEH); + Entry->dump(OS, DumpOpts, MRI, IsEH); } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp index f381263644012..44b4107781465 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDebugLoc.cpp @@ -106,15 +106,16 @@ DWARFLocationInterpreter::Interpret(const DWARFLocationEntry &E) { } } -static void dumpExpression(raw_ostream &OS, ArrayRef Data, - bool IsLittleEndian, unsigned AddressSize, - const MCRegisterInfo *MRI, DWARFUnit *U) { +static void dumpExpression(raw_ostream &OS, DIDumpOptions DumpOpts, + ArrayRef Data, bool IsLittleEndian, + unsigned AddressSize, const MCRegisterInfo *MRI, + DWARFUnit *U) { DWARFDataExtractor Extractor(Data, IsLittleEndian, AddressSize); // Note. We do not pass any format to DWARFExpression, even if the // corresponding unit is known. For now, there is only one operation, // DW_OP_call_ref, which depends on the format; it is rarely used, and // is unexpected in location tables. - DWARFExpression(Extractor, AddressSize).print(OS, MRI, U); + DWARFExpression(Extractor, AddressSize).print(OS, DumpOpts, MRI, U); } bool DWARFLocationTable::dumpLocationList(uint64_t *Offset, raw_ostream &OS, @@ -154,8 +155,8 @@ bool DWARFLocationTable::dumpLocationList(uint64_t *Offset, raw_ostream &OS, E.Kind != dwarf::DW_LLE_base_addressx && E.Kind != dwarf::DW_LLE_end_of_list) { OS << ": "; - dumpExpression(OS, E.Loc, Data.isLittleEndian(), Data.getAddressSize(), - MRI, U); + dumpExpression(OS, DumpOpts, E.Loc, Data.isLittleEndian(), + Data.getAddressSize(), MRI, U); } return true; }); diff --git a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp index f07f4e362568f..89b29b428fcee 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFDie.cpp @@ -80,7 +80,7 @@ static void dumpLocation(raw_ostream &OS, DWARFFormValue &FormValue, DataExtractor Data(StringRef((const char *)Expr.data(), Expr.size()), Ctx.isLittleEndian(), 0); DWARFExpression(Data, U->getAddressByteSize(), U->getFormParams().Format) - .print(OS, MRI, U); + .print(OS, DumpOpts, MRI, U); return; } diff --git a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp index de5e11e084f47..811716111be55 100644 --- a/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp +++ b/llvm/lib/DebugInfo/DWARF/DWARFExpression.cpp @@ -204,11 +204,15 @@ bool DWARFExpression::Operation::extract(DataExtractor Data, } static void prettyPrintBaseTypeRef(DWARFUnit *U, raw_ostream &OS, - uint64_t Operands[2], unsigned Operand) { + DIDumpOptions DumpOpts, uint64_t Operands[2], + unsigned Operand) { assert(Operand < 2 && "operand out of bounds"); auto Die = U->getDIEForOffset(U->getOffset() + Operands[Operand]); if (Die && Die.getTag() == dwarf::DW_TAG_base_type) { - OS << format(" (0x%08" PRIx64 ")", U->getOffset() + Operands[Operand]); + OS << " ("; + if (DumpOpts.Verbose) + OS << format("0x%08" PRIx64 " -> ", Operands[Operand]); + OS << format("0x%08" PRIx64 ")", U->getOffset() + Operands[Operand]); if (auto Name = Die.find(dwarf::DW_AT_name)) OS << " \"" << Name->getAsCString() << "\""; } else { @@ -217,7 +221,8 @@ static void prettyPrintBaseTypeRef(DWARFUnit *U, raw_ostream &OS, } } -static bool prettyPrintRegisterOp(DWARFUnit *U, raw_ostream &OS, uint8_t Opcode, +static bool prettyPrintRegisterOp(DWARFUnit *U, raw_ostream &OS, + DIDumpOptions DumpOpts, uint8_t Opcode, uint64_t Operands[2], const MCRegisterInfo *MRI, bool isEH) { if (!MRI) @@ -243,7 +248,7 @@ static bool prettyPrintRegisterOp(DWARFUnit *U, raw_ostream &OS, uint8_t Opcode, OS << ' ' << RegName; if (Opcode == DW_OP_regval_type) - prettyPrintBaseTypeRef(U, OS, Operands, 1); + prettyPrintBaseTypeRef(U, OS, DumpOpts, Operands, 1); return true; } } @@ -251,11 +256,10 @@ static bool prettyPrintRegisterOp(DWARFUnit *U, raw_ostream &OS, uint8_t Opcode, return false; } -bool DWARFExpression::Operation::print(raw_ostream &OS, +bool DWARFExpression::Operation::print(raw_ostream &OS, DIDumpOptions DumpOpts, const DWARFExpression *Expr, const MCRegisterInfo *RegInfo, - DWARFUnit *U, - bool isEH) { + DWARFUnit *U, bool isEH) { if (Error) { OS << ""; return false; @@ -269,7 +273,7 @@ bool DWARFExpression::Operation::print(raw_ostream &OS, (Opcode >= DW_OP_reg0 && Opcode <= DW_OP_reg31) || Opcode == DW_OP_bregx || Opcode == DW_OP_regx || Opcode == DW_OP_regval_type) - if (prettyPrintRegisterOp(U, OS, Opcode, Operands, RegInfo, isEH)) + if (prettyPrintRegisterOp(U, OS, DumpOpts, Opcode, Operands, RegInfo, isEH)) return true; for (unsigned Operand = 0; Operand < 2; ++Operand) { @@ -286,7 +290,7 @@ bool DWARFExpression::Operation::print(raw_ostream &OS, if (Opcode == DW_OP_convert && Operands[Operand] == 0) OS << " 0x0"; else - prettyPrintBaseTypeRef(U, OS, Operands, Operand); + prettyPrintBaseTypeRef(U, OS, DumpOpts, Operands, Operand); } else if (Size == Operation::WasmLocationArg) { assert(Operand == 1); switch (Operands[0]) { @@ -311,11 +315,12 @@ bool DWARFExpression::Operation::print(raw_ostream &OS, return true; } -void DWARFExpression::print(raw_ostream &OS, const MCRegisterInfo *RegInfo, - DWARFUnit *U, bool IsEH) const { +void DWARFExpression::print(raw_ostream &OS, DIDumpOptions DumpOpts, + const MCRegisterInfo *RegInfo, DWARFUnit *U, + bool IsEH) const { uint32_t EntryValExprSize = 0; for (auto &Op : *this) { - if (!Op.print(OS, this, RegInfo, U, IsEH)) { + if (!Op.print(OS, DumpOpts, this, RegInfo, U, IsEH)) { uint64_t FailOffset = Op.getEndOffset(); while (FailOffset < Data.getData().size()) OS << format(" %02x", Data.getU8(&FailOffset)); diff --git a/llvm/test/DebugInfo/X86/convert-debugloc.ll b/llvm/test/DebugInfo/X86/convert-debugloc.ll index 2aafacfd80bbb..a61127ab6932f 100644 --- a/llvm/test/DebugInfo/X86/convert-debugloc.ll +++ b/llvm/test/DebugInfo/X86/convert-debugloc.ll @@ -23,31 +23,38 @@ ; RUN: llc -mtriple=x86_64-pc-linux-gnu -dwarf-version=5 -filetype=obj -O0 < %s -debugger-tune=lldb -dwarf-op-convert=Enable -split-dwarf-file=baz.dwo | llvm-dwarfdump - \ ; RUN: | FileCheck %s --check-prefix=CONV --check-prefix=SPLITCONV --check-prefix=SPLIT "--implicit-check-not={{DW_TAG|NULL}}" +; RUN: %llc_dwarf -dwarf-version=5 -filetype=obj -O0 < %s -debugger-tune=gdb | llvm-dwarfdump -v -debug-info - \ +; RUN: | FileCheck %s --check-prefix=VERBOSE --check-prefix=CONV "--implicit-check-not={{DW_TAG|NULL}}" + + ; SPLITCONV: Compile Unit:{{.*}} DWO_id = 0xe91d8d1d7f9782c0 ; SPLIT: DW_TAG_skeleton_unit ; CONV: DW_TAG_compile_unit ; CONV:[[SIG8:.*]]: DW_TAG_base_type -; CONV-NEXT:DW_AT_name ("DW_ATE_signed_8") -; CONV-NEXT:DW_AT_encoding (DW_ATE_signed) -; CONV-NEXT:DW_AT_byte_size (0x01) +; CONV-NEXT:DW_AT_name {{.*}}"DW_ATE_signed_8") +; CONV-NEXT:DW_AT_encoding {{.*}}DW_ATE_signed) +; CONV-NEXT:DW_AT_byte_size {{.*}}0x01) ; CONV-NOT: DW_AT ; CONV:[[SIG32:.*]]: DW_TAG_base_type -; CONV-NEXT:DW_AT_name ("DW_ATE_signed_32") -; CONV-NEXT:DW_AT_encoding (DW_ATE_signed) -; CONV-NEXT:DW_AT_byte_size (0x04) +; CONV-NEXT:DW_AT_name {{.*}}"DW_ATE_signed_32") +; CONV-NEXT:DW_AT_encoding {{.*}}DW_ATE_signed) +; CONV-NEXT:DW_AT_byte_size {{.*}}0x04) ; CONV-NOT: DW_AT ; CONV: DW_TAG_subprogram ; CONV: DW_TAG_formal_parameter ; CONV: DW_TAG_variable -; CONV: DW_AT_location ( -; CONV: {{.*}}, DW_OP_convert ([[SIG8]]) "DW_ATE_signed_8", DW_OP_convert ([[SIG32]]) "DW_ATE_signed_32", DW_OP_stack_value) -; CONV: DW_AT_name ("y") +; CONV: DW_AT_location {{.*}}DW_OP_constu 0x20, DW_OP_convert ( +; VERBOSE-SAME: [[SIG8]] -> +; CONV-SAME: [[SIG8]]) "DW_ATE_signed_8", DW_OP_convert ( +; VERBOSE-SAME: [[SIG32]] -> +; CONV-SAME: [[SIG32]]) "DW_ATE_signed_32", DW_OP_stack_value) +; CONV: DW_AT_name {{.*}}"y") ; CONV: NULL ; CONV: DW_TAG_base_type -; CONV: DW_AT_name ("signed char") +; CONV: DW_AT_name {{.*}}"signed char") ; CONV: DW_TAG_base_type -; CONV: DW_AT_name ("int") +; CONV: DW_AT_name {{.*}}"int") ; CONV: NULL ; NOCONV: DW_TAG_compile_unit diff --git a/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h b/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h index 804f35ca14f91..d57f82185c635 100644 --- a/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h +++ b/llvm/tools/llvm-readobj/DwarfCFIEHPrinter.h @@ -225,7 +225,8 @@ void PrinterContext::printEHFrame(const Elf_Shdr *EHFrameShdr) const { W.getOStream() << "\n"; W.startLine() << "Program:\n"; W.indent(); - Entry.cfis().dump(W.getOStream(), nullptr, W.getIndentLevel()); + Entry.cfis().dump(W.getOStream(), DIDumpOptions(), nullptr, + W.getIndentLevel()); W.unindent(); W.unindent(); W.getOStream() << "\n"; diff --git a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp index 65e2be723090a..7706c216053cb 100644 --- a/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp +++ b/llvm/unittests/DebugInfo/DWARF/DWARFDebugFrameTest.cpp @@ -39,7 +39,7 @@ void expectDumpResult(const dwarf::CIE &TestCIE, bool IsEH, StringRef ExpectedFirstLine) { std::string Output; raw_string_ostream OS(Output); - TestCIE.dump(OS, /*MRI=*/nullptr, IsEH); + TestCIE.dump(OS, DIDumpOptions(), /*MRI=*/nullptr, IsEH); OS.flush(); StringRef FirstLine = StringRef(Output).split('\n').first; EXPECT_EQ(FirstLine, ExpectedFirstLine); @@ -49,7 +49,7 @@ void expectDumpResult(const dwarf::FDE &TestFDE, bool IsEH, StringRef ExpectedFirstLine) { std::string Output; raw_string_ostream OS(Output); - TestFDE.dump(OS, /*MRI=*/nullptr, IsEH); + TestFDE.dump(OS, DIDumpOptions(), /*MRI=*/nullptr, IsEH); OS.flush(); StringRef FirstLine = StringRef(Output).split('\n').first; EXPECT_EQ(FirstLine, ExpectedFirstLine);