You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 

159 lines
4.0 KiB

  1. // Copyright 2018 Google Inc. All Rights Reserved.
  2. //
  3. // Licensed under the Apache License, Version 2.0 (the "License");
  4. // you may not use this file except in compliance with the License.
  5. // You may obtain a copy of the License at
  6. //
  7. // http://www.apache.org/licenses/LICENSE-2.0
  8. //
  9. // Unless required by applicable law or agreed to in writing, software
  10. // distributed under the License is distributed on an "AS IS" BASIS,
  11. // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  12. // See the License for the specific language governing permissions and
  13. // limitations under the License.
  14. package dwarf_test
  15. import (
  16. "fmt"
  17. "io/ioutil"
  18. "os"
  19. "os/exec"
  20. "path/filepath"
  21. "runtime"
  22. "strings"
  23. "testing"
  24. "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/dwarf"
  25. "cloud.google.com/go/cmd/go-cloud-debug-agent/internal/debug/elf"
  26. )
  27. var (
  28. pcspTempDir string
  29. pcsptestBinary string
  30. )
  31. func doPCToSPTest(self bool) bool {
  32. // For now, only works on amd64 platforms.
  33. if runtime.GOARCH != "amd64" {
  34. return false
  35. }
  36. // Self test reads test binary; only works on Linux or Mac.
  37. if self {
  38. if runtime.GOOS != "linux" && runtime.GOOS != "darwin" {
  39. return false
  40. }
  41. }
  42. // Command below expects "sh", so Unix.
  43. if runtime.GOOS == "windows" || runtime.GOOS == "plan9" {
  44. return false
  45. }
  46. if pcsptestBinary != "" {
  47. return true
  48. }
  49. var err error
  50. pcspTempDir, err = ioutil.TempDir("", "pcsptest")
  51. if err != nil {
  52. panic(err)
  53. }
  54. if strings.Contains(pcspTempDir, " ") {
  55. panic("unexpected space in tempdir")
  56. }
  57. // This command builds pcsptest from testdata/pcsptest.go.
  58. pcsptestBinary = filepath.Join(pcspTempDir, "pcsptest")
  59. command := fmt.Sprintf("go tool compile -o %s.6 testdata/pcsptest.go && go tool link -H %s -o %s %s.6",
  60. pcsptestBinary, runtime.GOOS, pcsptestBinary, pcsptestBinary)
  61. cmd := exec.Command("sh", "-c", command)
  62. cmd.Stdout = os.Stdout
  63. cmd.Stderr = os.Stderr
  64. if err := cmd.Run(); err != nil {
  65. panic(err)
  66. }
  67. return true
  68. }
  69. func endPCToSPTest() {
  70. if pcspTempDir != "" {
  71. os.RemoveAll(pcspTempDir)
  72. pcspTempDir = ""
  73. pcsptestBinary = ""
  74. }
  75. }
  76. func TestPCToSPOffset(t *testing.T) {
  77. t.Skip("gets a stack layout it doesn't expect")
  78. if !doPCToSPTest(false) {
  79. return
  80. }
  81. defer endPCToSPTest()
  82. data, err := getData(pcsptestBinary)
  83. if err != nil {
  84. t.Fatal(err)
  85. }
  86. entry, err := data.LookupFunction("main.test")
  87. if err != nil {
  88. t.Fatal("lookup startPC:", err)
  89. }
  90. startPC, ok := entry.Val(dwarf.AttrLowpc).(uint64)
  91. if !ok {
  92. t.Fatal(`DWARF data for function "main.test" has no low PC`)
  93. }
  94. endPC, ok := entry.Val(dwarf.AttrHighpc).(uint64)
  95. if !ok {
  96. t.Fatal(`DWARF data for function "main.test" has no high PC`)
  97. }
  98. const addrSize = 8 // TODO: Assumes amd64.
  99. const argSize = 8 // Defined by int64 arguments in test binary.
  100. // On 64-bit machines, the first offset must be one address size,
  101. // for the return PC.
  102. offset, err := data.PCToSPOffset(startPC)
  103. if err != nil {
  104. t.Fatal("startPC:", err)
  105. }
  106. if offset != addrSize {
  107. t.Fatalf("expected %d at start of function; got %d", addrSize, offset)
  108. }
  109. // On 64-bit machines, expect some 8s and some 32s. (See the
  110. // comments in testdata/pcsptest.go.
  111. // TODO: The test could be stronger, but not much unless we
  112. // disassemble the binary.
  113. count := make(map[int64]int)
  114. for pc := startPC; pc < endPC; pc++ {
  115. offset, err := data.PCToSPOffset(pc)
  116. if err != nil {
  117. t.Fatal("scanning function:", err)
  118. }
  119. count[offset]++
  120. }
  121. if len(count) != 2 {
  122. t.Errorf("expected 2 offset values, got %d; counts are: %v", len(count), count)
  123. }
  124. if count[addrSize] == 0 {
  125. t.Errorf("expected some values at offset %d; got %v", addrSize, count)
  126. }
  127. if count[addrSize+3*argSize] == 0 {
  128. t.Errorf("expected some values at offset %d; got %v", addrSize+3*argSize, count)
  129. }
  130. }
  131. func getData(file string) (*dwarf.Data, error) {
  132. switch runtime.GOOS {
  133. case "linux":
  134. f, err := elf.Open(file)
  135. if err != nil {
  136. return nil, err
  137. }
  138. dwarf, err := f.DWARF()
  139. if err != nil {
  140. return nil, err
  141. }
  142. f.Close()
  143. return dwarf, nil
  144. }
  145. panic("unimplemented DWARF for GOOS=" + runtime.GOOS)
  146. }