1
0
mirror of https://github.com/golang/go synced 2024-11-18 05:54:49 -07:00

cmd/link: new DWARF line table test case

Add a test case for an issue with how Go emits DWARF line tables,
specifically relating to the line table "end sequence" operator.

Updates #38192.

Change-Id: I878b262e6ca6c550c0e460c3d5a1969ac4a2c31b
Reviewed-on: https://go-review.googlesource.com/c/go/+/235917
Run-TryBot: Than McIntosh <thanm@google.com>
TryBot-Result: Gobot Gobot <gobot@golang.org>
Reviewed-by: Austin Clements <austin@google.com>
This commit is contained in:
Than McIntosh 2020-06-01 10:24:46 -04:00
parent f98b9ae07c
commit 09e791feb1
3 changed files with 130 additions and 0 deletions

View File

@ -1368,3 +1368,114 @@ func main() {
rdr.SkipChildren()
}
}
func TestIssue38192(t *testing.T) {
testenv.MustHaveGoBuild(t)
if runtime.GOOS == "plan9" {
t.Skip("skipping on plan9; no DWARF symbol table in executables")
}
// Build a test program that contains a translation unit whose
// text (from am assembly source) contains only a single instruction.
tmpdir, err := ioutil.TempDir("", "TestIssue38192")
if err != nil {
t.Fatalf("could not create directory: %v", err)
}
defer os.RemoveAll(tmpdir)
wd, err := os.Getwd()
if err != nil {
t.Fatalf("where am I? %v", err)
}
pdir := filepath.Join(wd, "testdata", "issue38192")
f := gobuildTestdata(t, tmpdir, pdir, DefaultOpt)
// Open the resulting binary and examine the DWARF it contains.
// Look for the function of interest ("main.singleInstruction")
// and verify that the line table has an entry not just for the
// single instruction but also a dummy instruction following it,
// so as to test that whoever is emitting the DWARF doesn't
// emit an end-sequence op immediately after the last instruction
// in the translation unit.
//
// NB: another way to write this test would have been to run the
// resulting executable under GDB, set a breakpoint in
// "main.singleInstruction", then verify that GDB displays the
// correct line/file information. Given the headache and flakiness
// associated with GDB-based tests these days, a direct read of
// the line table seems more desirable.
rows := []dwarf.LineEntry{}
dw, err := f.DWARF()
if err != nil {
t.Fatalf("error parsing DWARF: %v", err)
}
rdr := dw.Reader()
for {
e, err := rdr.Next()
if err != nil {
t.Fatalf("error reading DWARF: %v", err)
}
if e == nil {
break
}
if e.Tag != dwarf.TagCompileUnit {
continue
}
// NB: there can be multiple compile units named "main".
name := e.Val(dwarf.AttrName).(string)
if name != "main" {
continue
}
lnrdr, err := dw.LineReader(e)
if err != nil {
t.Fatalf("error creating DWARF line reader: %v", err)
}
if lnrdr != nil {
var lne dwarf.LineEntry
for {
err := lnrdr.Next(&lne)
if err == io.EOF {
break
}
if err != nil {
t.Fatalf("error reading next DWARF line: %v", err)
}
if !strings.HasSuffix(lne.File.Name, "ld/testdata/issue38192/oneline.s") {
continue
}
rows = append(rows, lne)
}
}
rdr.SkipChildren()
}
f.Close()
// Make sure that:
// - main.singleInstruction appears in the line table
// - more than one PC value appears the line table for
// that compilation unit.
// - at least one row has the correct line number (8)
pcs := make(map[uint64]bool)
line8seen := false
for _, r := range rows {
pcs[r.Address] = true
if r.Line == 8 {
line8seen = true
}
}
failed := false
if len(pcs) < 2 {
failed = true
t.Errorf("not enough line table rows for main.singleInstruction (got %d, wanted > 1", len(pcs))
}
if !line8seen {
failed = true
t.Errorf("line table does not contain correct line for main.singleInstruction")
}
if !failed {
return
}
for i, r := range rows {
t.Logf("row %d: A=%x F=%s L=%d\n", i, r.Address, r.File.Name, r.Line)
}
}

View File

@ -0,0 +1,11 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package main
func singleInstruction()
func main() {
singleInstruction()
}

View File

@ -0,0 +1,8 @@
// Copyright 2020 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
#include "textflag.h"
TEXT ·singleInstruction(SB),NOSPLIT,$0
RET