From 5fe3b49a0540aacf685273a43b0fb31b44cf5dd6 Mon Sep 17 00:00:00 2001 From: "Bryan C. Mills" Date: Thu, 3 Oct 2019 18:07:53 +0000 Subject: [PATCH] Revert "cmd/compile: walk the progs to generate debug_lines" This reverts CL 196661. Reason for revert: broke TestGdb* tests on mips64le, ppc64le, and s390x builders. Change-Id: I3b5c97c840819a0d407b943f7cf7e2d97f06042d Reviewed-on: https://go-review.googlesource.com/c/go/+/198697 Run-TryBot: Bryan C. Mills Reviewed-by: Brad Fitzpatrick TryBot-Result: Gobot Gobot --- src/cmd/internal/obj/dwarf.go | 119 +++++++++++++++++++++++----------- src/cmd/internal/obj/pcln.go | 29 +++++++++ 2 files changed, 110 insertions(+), 38 deletions(-) diff --git a/src/cmd/internal/obj/dwarf.go b/src/cmd/internal/obj/dwarf.go index 5efafe11b86..d8f3de3b692 100644 --- a/src/cmd/internal/obj/dwarf.go +++ b/src/cmd/internal/obj/dwarf.go @@ -8,7 +8,6 @@ package obj import ( "cmd/internal/dwarf" - "cmd/internal/src" "fmt" ) @@ -32,51 +31,95 @@ const ( func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) { dctxt := dwCtxt{ctxt} + // The Pcfile table is used to generate the debug_lines section, and the file + // indices for that data could differ from the files we write out for the + // debug_lines section. Here we generate a LUT between those two indices. + fileNums := make(map[int32]int64) + for i, filename := range s.Func.Pcln.File { + if symbolIndex := ctxt.PosTable.FileIndex(filename); symbolIndex >= 0 { + fileNums[int32(i)] = int64(symbolIndex) + 1 + } else { + panic(fmt.Sprintf("First time we've seen filename: %q", filename)) + } + } + // Set up the debug_lines state machine. // NB: This state machine is reset to this state when we've finished // generating the line table. See below. // TODO: Once delve can support multiple DW_LNS_end_statements, we don't have // to do this. - stmt := true - line := int64(1) + is_stmt := uint8(1) pc := s.Func.Text.Pc - name := "" - prologue, wrotePrologue := false, false + line := 1 + file := 1 - // Walk the progs, generating the DWARF table. - for p := s.Func.Text; p != nil; p = p.Link { - prologue = prologue || (p.Pos.Xlogue() == src.PosPrologueEnd) - // If we're not at a real instruction, keep looping! - if p.Pos.Line() == 0 || (p.Link != nil && p.Link.Pc == pc) { - continue - } - newStmt := p.Pos.IsStmt() != src.PosNotStmt - newName, newLine := linkgetlineFromPos(ctxt, p.Pos) + // The linker will insert the DW_LNE_set_address once determined; therefore, + // it's omitted here. - // Output debug info. - wrote := false - if name != newName { - newFile := ctxt.PosTable.FileIndex(newName) + 1 // 1 indexing for the table. + // Generate the actual line information. + // We use the pcline and pcfile to generate this section, and it's suboptimal. + // Likely better would be to generate this dirrectly from the progs and not + // parse those tables. + // TODO: Generate from the progs if it's faster. + pcfile := NewPCIter(uint32(ctxt.Arch.Arch.MinLC)) + pcline := NewPCIter(uint32(ctxt.Arch.Arch.MinLC)) + pcstmt := NewPCIter(uint32(ctxt.Arch.Arch.MinLC)) + pcfile.Init(s.Func.Pcln.Pcfile.P) + pcline.Init(s.Func.Pcln.Pcline.P) + var pctostmtData Pcdata + funcpctab(ctxt, &pctostmtData, s, "pctostmt", pctostmt, nil) + pcstmt.Init(pctostmtData.P) + var thispc uint32 + + for !pcfile.Done && !pcline.Done { + // Only changed if it advanced + if int32(file) != pcfile.Value { dctxt.AddUint8(lines, dwarf.DW_LNS_set_file) - dwarf.Uleb128put(dctxt, lines, int64(newFile)) - name = newName - wrote = true - } - if prologue && !wrotePrologue { - dctxt.AddUint8(lines, uint8(dwarf.DW_LNS_set_prologue_end)) - wrotePrologue = true - wrote = true - } - if stmt != newStmt { - dctxt.AddUint8(lines, uint8(dwarf.DW_LNS_negate_stmt)) - stmt = newStmt - wrote = true + dwarf.Uleb128put(dctxt, lines, fileNums[pcfile.Value]) + file = int(pcfile.Value) } - if line != int64(newLine) || wrote { - pcdelta := (p.Pc - pc) / int64(ctxt.Arch.MinLC) - putpclcdelta(ctxt, dctxt, lines, uint64(pcdelta), int64(newLine)-line) - line, pc = int64(newLine), p.Pc + // Only changed if it advanced + if is_stmt != uint8(pcstmt.Value) { + new_stmt := uint8(pcstmt.Value) + switch new_stmt &^ 1 { + case PrologueEnd: + dctxt.AddUint8(lines, uint8(dwarf.DW_LNS_set_prologue_end)) + case EpilogueBegin: + // TODO if there is a use for this, add it. + // Don't forget to increase OPCODE_BASE by 1 and add entry for standard_opcode_lengths[11] + panic("unsupported EpilogueBegin") + } + new_stmt &= 1 + if is_stmt != new_stmt { + is_stmt = new_stmt + dctxt.AddUint8(lines, uint8(dwarf.DW_LNS_negate_stmt)) + } + } + + // putpcldelta makes a row in the DWARF matrix, always, even if line is unchanged. + putpclcdelta(ctxt, dctxt, lines, uint64(s.Func.Text.Pc+int64(thispc)-pc), int64(pcline.Value)-int64(line)) + + pc = s.Func.Text.Pc + int64(thispc) + line = int(pcline.Value) + + // Take the minimum step forward for the three iterators + thispc = pcfile.NextPC + if pcline.NextPC < thispc { + thispc = pcline.NextPC + } + if !pcstmt.Done && pcstmt.NextPC < thispc { + thispc = pcstmt.NextPC + } + + if pcfile.NextPC == thispc { + pcfile.Next() + } + if !pcstmt.Done && pcstmt.NextPC == thispc { + pcstmt.Next() + } + if pcline.NextPC == thispc { + pcline.Next() } } @@ -86,16 +129,16 @@ func (ctxt *Link) generateDebugLinesSymbol(s, lines *LSym) { // file = 1 // line = 1 // column = 0 - // stmt = set in header, we assume true + // is_stmt = set in header, we assume true // basic_block = false // Careful readers of the DWARF specification will note that we don't reset // the address of the state machine -- but this will happen at the beginning - // of the NEXT block of opcodes. + // of the NEXT block of opcodes. (See the SetAddress call above.) dctxt.AddUint8(lines, dwarf.DW_LNS_set_file) dwarf.Uleb128put(dctxt, lines, 1) dctxt.AddUint8(lines, dwarf.DW_LNS_advance_line) dwarf.Sleb128put(dctxt, lines, int64(1-line)) - if !stmt { + if is_stmt != 1 { dctxt.AddUint8(lines, dwarf.DW_LNS_negate_stmt) } dctxt.AddUint8(lines, dwarf.DW_LNS_copy) diff --git a/src/cmd/internal/obj/pcln.go b/src/cmd/internal/obj/pcln.go index c47897a263f..ca1eda8d1ec 100644 --- a/src/cmd/internal/obj/pcln.go +++ b/src/cmd/internal/obj/pcln.go @@ -5,6 +5,7 @@ package obj import ( + "cmd/internal/src" "encoding/binary" "log" ) @@ -248,6 +249,34 @@ func pctospadj(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg in return oldval + p.Spadj } +// pctostmt returns either, +// if phase==0, then whether the current instruction is a step-target (Dwarf is_stmt) +// bit-or'd with whether the current statement is a prologue end or epilogue begin +// else (phase == 1), zero. +// +func pctostmt(ctxt *Link, sym *LSym, oldval int32, p *Prog, phase int32, arg interface{}) int32 { + if phase == 1 { + return 0 // Ignored; also different from initial value of -1, if that ever matters. + } + s := p.Pos.IsStmt() + l := p.Pos.Xlogue() + + var is_stmt int32 + + // PrologueEnd, at least, is passed to the next instruction + switch l { + case src.PosPrologueEnd: + is_stmt = PrologueEnd + case src.PosEpilogueBegin: + is_stmt = EpilogueBegin + } + + if s != src.PosNotStmt { + is_stmt |= 1 // either PosDefaultStmt from asm, or PosIsStmt from go + } + return is_stmt +} + // pctopcdata computes the pcdata value in effect at p. // A PCDATA instruction sets the value in effect at future // non-PCDATA instructions.