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.
 
 
 

200 lines
5.6 KiB

  1. // Copyright 2017 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 report
  15. import (
  16. "bytes"
  17. "io/ioutil"
  18. "os"
  19. "path/filepath"
  20. "regexp"
  21. "runtime"
  22. "strings"
  23. "testing"
  24. "github.com/google/pprof/internal/binutils"
  25. "github.com/google/pprof/profile"
  26. )
  27. func TestWebList(t *testing.T) {
  28. if runtime.GOOS != "linux" || runtime.GOARCH != "amd64" {
  29. t.Skip("weblist only tested on x86-64 linux")
  30. }
  31. cpu := readProfile(filepath.Join("testdata", "sample.cpu"), t)
  32. rpt := New(cpu, &Options{
  33. OutputFormat: WebList,
  34. Symbol: regexp.MustCompile("busyLoop"),
  35. SampleValue: func(v []int64) int64 { return v[1] },
  36. SampleUnit: cpu.SampleType[1].Unit,
  37. })
  38. var buf bytes.Buffer
  39. if err := Generate(&buf, rpt, &binutils.Binutils{}); err != nil {
  40. t.Fatalf("could not generate weblist: %v", err)
  41. }
  42. output := buf.String()
  43. for _, expect := range []string{"func busyLoop", "callq", "math.Abs"} {
  44. if !strings.Contains(output, expect) {
  45. t.Errorf("weblist output does not contain '%s':\n%s", expect, output)
  46. }
  47. }
  48. }
  49. func TestOpenSourceFile(t *testing.T) {
  50. tempdir, err := ioutil.TempDir("", "")
  51. if err != nil {
  52. t.Fatalf("failed to create temp dir: %v", err)
  53. }
  54. const lsep = string(filepath.ListSeparator)
  55. for _, tc := range []struct {
  56. desc string
  57. searchPath string
  58. trimPath string
  59. fs []string
  60. path string
  61. wantPath string // If empty, error is wanted.
  62. }{
  63. {
  64. desc: "exact absolute path is found",
  65. fs: []string{"foo/bar.cc"},
  66. path: "$dir/foo/bar.cc",
  67. wantPath: "$dir/foo/bar.cc",
  68. },
  69. {
  70. desc: "exact relative path is found",
  71. searchPath: "$dir",
  72. fs: []string{"foo/bar.cc"},
  73. path: "foo/bar.cc",
  74. wantPath: "$dir/foo/bar.cc",
  75. },
  76. {
  77. desc: "multiple search path",
  78. searchPath: "some/path" + lsep + "$dir",
  79. fs: []string{"foo/bar.cc"},
  80. path: "foo/bar.cc",
  81. wantPath: "$dir/foo/bar.cc",
  82. },
  83. {
  84. desc: "relative path is found in parent dir",
  85. searchPath: "$dir/foo/bar",
  86. fs: []string{"bar.cc", "foo/bar/baz.cc"},
  87. path: "bar.cc",
  88. wantPath: "$dir/bar.cc",
  89. },
  90. {
  91. desc: "trims configured prefix",
  92. searchPath: "$dir",
  93. trimPath: "some-path" + lsep + "/some/remote/path",
  94. fs: []string{"my-project/foo/bar.cc"},
  95. path: "/some/remote/path/my-project/foo/bar.cc",
  96. wantPath: "$dir/my-project/foo/bar.cc",
  97. },
  98. {
  99. desc: "trims heuristically",
  100. searchPath: "$dir/my-project",
  101. fs: []string{"my-project/foo/bar.cc"},
  102. path: "/some/remote/path/my-project/foo/bar.cc",
  103. wantPath: "$dir/my-project/foo/bar.cc",
  104. },
  105. {
  106. desc: "error when not found",
  107. path: "foo.cc",
  108. },
  109. } {
  110. t.Run(tc.desc, func(t *testing.T) {
  111. defer func() {
  112. if err := os.RemoveAll(tempdir); err != nil {
  113. t.Fatalf("failed to remove dir %q: %v", tempdir, err)
  114. }
  115. }()
  116. for _, f := range tc.fs {
  117. path := filepath.Join(tempdir, filepath.FromSlash(f))
  118. dir := filepath.Dir(path)
  119. if err := os.MkdirAll(dir, 0755); err != nil {
  120. t.Fatalf("failed to create dir %q: %v", dir, err)
  121. }
  122. if err := ioutil.WriteFile(path, nil, 0644); err != nil {
  123. t.Fatalf("failed to create file %q: %v", path, err)
  124. }
  125. }
  126. tc.searchPath = filepath.FromSlash(strings.Replace(tc.searchPath, "$dir", tempdir, -1))
  127. tc.path = filepath.FromSlash(strings.Replace(tc.path, "$dir", tempdir, 1))
  128. tc.wantPath = filepath.FromSlash(strings.Replace(tc.wantPath, "$dir", tempdir, 1))
  129. if file, err := openSourceFile(tc.path, tc.searchPath, tc.trimPath); err != nil && tc.wantPath != "" {
  130. t.Errorf("openSourceFile(%q, %q, %q) = err %v, want path %q", tc.path, tc.searchPath, tc.trimPath, err, tc.wantPath)
  131. } else if err == nil {
  132. defer file.Close()
  133. gotPath := file.Name()
  134. if tc.wantPath == "" {
  135. t.Errorf("openSourceFile(%q, %q, %q) = %q, want error", tc.path, tc.searchPath, tc.trimPath, gotPath)
  136. } else if gotPath != tc.wantPath {
  137. t.Errorf("openSourceFile(%q, %q, %q) = %q, want path %q", tc.path, tc.searchPath, tc.trimPath, gotPath, tc.wantPath)
  138. }
  139. }
  140. })
  141. }
  142. }
  143. func TestIndentation(t *testing.T) {
  144. for _, c := range []struct {
  145. str string
  146. wantIndent int
  147. }{
  148. {"", 0},
  149. {"foobar", 0},
  150. {" foo", 2},
  151. {"\tfoo", 8},
  152. {"\t foo", 9},
  153. {" \tfoo", 8},
  154. {" \tfoo", 8},
  155. {" \tfoo", 16},
  156. } {
  157. if n := indentation(c.str); n != c.wantIndent {
  158. t.Errorf("indentation(%v): got %d, want %d", c.str, n, c.wantIndent)
  159. }
  160. }
  161. }
  162. func readProfile(fname string, t *testing.T) *profile.Profile {
  163. file, err := os.Open(fname)
  164. if err != nil {
  165. t.Fatalf("%s: could not open profile: %v", fname, err)
  166. }
  167. defer file.Close()
  168. p, err := profile.Parse(file)
  169. if err != nil {
  170. t.Fatalf("%s: could not parse profile: %v", fname, err)
  171. }
  172. // Fix file names so they do not include absolute path names.
  173. fix := func(s string) string {
  174. const testdir = "/internal/report/"
  175. pos := strings.Index(s, testdir)
  176. if pos == -1 {
  177. return s
  178. }
  179. return s[pos+len(testdir):]
  180. }
  181. for _, m := range p.Mapping {
  182. m.File = fix(m.File)
  183. }
  184. for _, f := range p.Function {
  185. f.Filename = fix(f.Filename)
  186. }
  187. return p
  188. }