No Description

legacy_profile_test.go 8.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319
  1. // Copyright 2014 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 profile
  15. import (
  16. "bytes"
  17. "fmt"
  18. "reflect"
  19. "strconv"
  20. "strings"
  21. "testing"
  22. )
  23. func TestLegacyProfileType(t *testing.T) {
  24. type testcase struct {
  25. sampleTypes []string
  26. typeSet [][]string
  27. want bool
  28. setName string
  29. }
  30. heap := heapzSampleTypes
  31. cont := contentionzSampleTypes
  32. testcases := []testcase{
  33. // True cases
  34. {[]string{"allocations", "size"}, heap, true, "heapzSampleTypes"},
  35. {[]string{"objects", "space"}, heap, true, "heapzSampleTypes"},
  36. {[]string{"inuse_objects", "inuse_space"}, heap, true, "heapzSampleTypes"},
  37. {[]string{"alloc_objects", "alloc_space"}, heap, true, "heapzSampleTypes"},
  38. {[]string{"contentions", "delay"}, cont, true, "contentionzSampleTypes"},
  39. // False cases
  40. {[]string{"objects"}, heap, false, "heapzSampleTypes"},
  41. {[]string{"objects", "unknown"}, heap, false, "heapzSampleTypes"},
  42. {[]string{"contentions", "delay"}, heap, false, "heapzSampleTypes"},
  43. {[]string{"samples", "cpu"}, heap, false, "heapzSampleTypes"},
  44. {[]string{"samples", "cpu"}, cont, false, "contentionzSampleTypes"},
  45. }
  46. for _, tc := range testcases {
  47. p := profileOfType(tc.sampleTypes)
  48. if got := isProfileType(p, tc.typeSet); got != tc.want {
  49. t.Error("isProfileType({"+strings.Join(tc.sampleTypes, ",")+"},", tc.setName, "), got", got, "want", tc.want)
  50. }
  51. }
  52. }
  53. func TestCpuParse(t *testing.T) {
  54. // profileString is a legacy encoded profile, represnted by words separated by ":"
  55. // Each sample has the form value : N : stack1..stackN
  56. // EOF is represented as "0:1:0"
  57. profileString := "1:3:100:999:100:" // sample with bogus 999 and duplicate leaf
  58. profileString += "1:5:200:999:200:501:502:" // sample with bogus 999 and duplicate leaf
  59. profileString += "1:12:300:999:300:601:602:603:604:605:606:607:608:609:" // sample with bogus 999 and duplicate leaf
  60. profileString += "0:1:0000" // EOF -- must use 4 bytes for the final zero
  61. p, err := cpuProfile([]byte(profileString), 1, parseString)
  62. if err != nil {
  63. t.Fatal(err)
  64. }
  65. if err := checkTestSample(p, []uint64{100}); err != nil {
  66. t.Error(err)
  67. }
  68. if err := checkTestSample(p, []uint64{200, 500, 501}); err != nil {
  69. t.Error(err)
  70. }
  71. if err := checkTestSample(p, []uint64{300, 600, 601, 602, 603, 604, 605, 606, 607, 608}); err != nil {
  72. t.Error(err)
  73. }
  74. }
  75. func parseString(b []byte) (uint64, []byte) {
  76. slices := bytes.SplitN(b, []byte(":"), 2)
  77. var value, remainder []byte
  78. if len(slices) > 0 {
  79. value = slices[0]
  80. }
  81. if len(slices) > 1 {
  82. remainder = slices[1]
  83. }
  84. v, _ := strconv.ParseUint(string(value), 10, 64)
  85. return v, remainder
  86. }
  87. func checkTestSample(p *Profile, want []uint64) error {
  88. for _, s := range p.Sample {
  89. got := []uint64{}
  90. for _, l := range s.Location {
  91. got = append(got, l.Address)
  92. }
  93. if reflect.DeepEqual(got, want) {
  94. return nil
  95. }
  96. }
  97. return fmt.Errorf("Could not find sample : %v", want)
  98. }
  99. // profileOfType creates an empty profile with only sample types set,
  100. // for testing purposes only.
  101. func profileOfType(sampleTypes []string) *Profile {
  102. p := new(Profile)
  103. p.SampleType = make([]*ValueType, len(sampleTypes))
  104. for i, t := range sampleTypes {
  105. p.SampleType[i] = new(ValueType)
  106. p.SampleType[i].Type = t
  107. }
  108. return p
  109. }
  110. func TestParseMappingEntry(t *testing.T) {
  111. for _, test := range []*struct {
  112. entry string
  113. want *Mapping
  114. }{
  115. {
  116. entry: "00400000-02e00000 r-xp 00000000 00:00 0",
  117. want: &Mapping{
  118. Start: 0x400000,
  119. Limit: 0x2e00000,
  120. },
  121. },
  122. {
  123. entry: "02e00000-02e8a000 r-xp 02a00000 00:00 15953927 /foo/bin",
  124. want: &Mapping{
  125. Start: 0x2e00000,
  126. Limit: 0x2e8a000,
  127. Offset: 0x2a00000,
  128. File: "/foo/bin",
  129. },
  130. },
  131. {
  132. entry: "02e00000-02e8a000 r-xp 000000 00:00 15953927 [vdso]",
  133. want: &Mapping{
  134. Start: 0x2e00000,
  135. Limit: 0x2e8a000,
  136. File: "[vdso]",
  137. },
  138. },
  139. {
  140. entry: " 02e00000-02e8a000: /foo/bin (@2a00000)",
  141. want: &Mapping{
  142. Start: 0x2e00000,
  143. Limit: 0x2e8a000,
  144. Offset: 0x2a00000,
  145. File: "/foo/bin",
  146. },
  147. },
  148. {
  149. entry: " 02e00000-02e8a000: /foo/bin (deleted)",
  150. want: &Mapping{
  151. Start: 0x2e00000,
  152. Limit: 0x2e8a000,
  153. File: "/foo/bin",
  154. },
  155. },
  156. {
  157. entry: " 02e00000-02e8a000: /foo/bin",
  158. want: &Mapping{
  159. Start: 0x2e00000,
  160. Limit: 0x2e8a000,
  161. File: "/foo/bin",
  162. },
  163. },
  164. {
  165. entry: " 02e00000-02e8a000: [vdso]",
  166. want: &Mapping{
  167. Start: 0x2e00000,
  168. Limit: 0x2e8a000,
  169. File: "[vdso]",
  170. },
  171. },
  172. {entry: "0xff6810563000 0xff6810565000 r-xp abc_exe 87c4d547f895cfd6a370e08dc5c5ee7bd4199d5b",
  173. want: &Mapping{
  174. Start: 0xff6810563000,
  175. Limit: 0xff6810565000,
  176. File: "abc_exe",
  177. BuildID: "87c4d547f895cfd6a370e08dc5c5ee7bd4199d5b",
  178. },
  179. },
  180. {entry: "7f5e5435e000-7f5e5455e000 --xp 00002000 00:00 1531 myprogram",
  181. want: &Mapping{
  182. Start: 0x7f5e5435e000,
  183. Limit: 0x7f5e5455e000,
  184. Offset: 0x2000,
  185. File: "myprogram",
  186. },
  187. },
  188. {entry: "7f7472710000-7f7472722000 r-xp 00000000 fc:00 790190 /usr/lib/libfantastic-1.2.so",
  189. want: &Mapping{
  190. Start: 0x7f7472710000,
  191. Limit: 0x7f7472722000,
  192. File: "/usr/lib/libfantastic-1.2.so",
  193. },
  194. },
  195. {entry: "7f47a542f000-7f47a5447000: /lib/libpthread-2.15.so",
  196. want: &Mapping{
  197. Start: 0x7f47a542f000,
  198. Limit: 0x7f47a5447000,
  199. File: "/lib/libpthread-2.15.so",
  200. },
  201. },
  202. {entry: "0x40000-0x80000 /path/to/binary (@FF00) abc123456",
  203. want: &Mapping{
  204. Start: 0x40000,
  205. Limit: 0x80000,
  206. File: "/path/to/binary",
  207. Offset: 0xFF00,
  208. BuildID: "abc123456",
  209. },
  210. },
  211. {entry: "W1220 15:07:15.201776 8272 logger.cc:12033] --- Memory map: ---\n" +
  212. "0x40000-0x80000 /path/to/binary (@FF00) abc123456",
  213. want: &Mapping{
  214. Start: 0x40000,
  215. Limit: 0x80000,
  216. File: "/path/to/binary",
  217. Offset: 0xFF00,
  218. BuildID: "abc123456",
  219. },
  220. },
  221. {entry: "W1220 15:07:15.201776 8272 logger.cc:12033] --- Memory map: ---\n" +
  222. "W1220 15:07:15.202776 8272 logger.cc:12036] 0x40000-0x80000 /path/to/binary (@FF00) abc123456",
  223. want: &Mapping{
  224. Start: 0x40000,
  225. Limit: 0x80000,
  226. File: "/path/to/binary",
  227. Offset: 0xFF00,
  228. BuildID: "abc123456",
  229. },
  230. },
  231. {entry: "7f5e5435e000-7f5e5455e000 ---p 00002000 00:00 1531 myprogram",
  232. want: nil,
  233. },
  234. } {
  235. got, err := ParseProcMaps(strings.NewReader(test.entry))
  236. if err != nil {
  237. t.Errorf("%s: %v", test.entry, err)
  238. continue
  239. }
  240. if test.want == nil {
  241. if got, want := len(got), 0; got != want {
  242. t.Errorf("%s: got %d mappings, want %d", test.entry, got, want)
  243. }
  244. continue
  245. }
  246. if got, want := len(got), 1; got != want {
  247. t.Errorf("%s: got %d mappings, want %d", test.entry, got, want)
  248. continue
  249. }
  250. if !reflect.DeepEqual(test.want, got[0]) {
  251. t.Errorf("%s want=%v got=%v", test.entry, test.want, got[0])
  252. }
  253. }
  254. }
  255. func TestParseThreadProfileWithInvalidAddress(t *testing.T) {
  256. profile := `
  257. --- threadz 1 ---
  258. --- Thread 7eff063d9940 (name: main/25376) stack: ---
  259. PC: 0x40b688 0x4d5f51 0x40be31 0x473add693e639c6f0
  260. --- Memory map: ---
  261. 00400000-00fcb000: /home/rsilvera/cppbench/cppbench_server_main.unstripped
  262. `
  263. wantErr := "failed to parse as hex 64-bit number: 0x473add693e639c6f0"
  264. if _, gotErr := parseThread([]byte(profile)); !strings.Contains(gotErr.Error(), wantErr) {
  265. t.Errorf("parseThread(): got error %q, want error containing %q", gotErr, wantErr)
  266. }
  267. }
  268. func TestParseGoCount(t *testing.T) {
  269. for _, test := range []struct {
  270. in string
  271. typ string
  272. }{
  273. {
  274. in: `# ignored comment
  275. threadcreate profile: total 123
  276. `,
  277. typ: "threadcreate",
  278. },
  279. {
  280. in: `
  281. # ignored comment
  282. goroutine profile: total 123456
  283. `,
  284. typ: "goroutine",
  285. },
  286. {
  287. in: `
  288. sub/dir-ect_o.ry profile: total 999
  289. `,
  290. typ: "sub/dir-ect_o.ry",
  291. },
  292. } {
  293. t.Run(test.typ, func(t *testing.T) {
  294. p, err := parseGoCount([]byte(test.in))
  295. if err != nil {
  296. t.Fatalf("parseGoCount(%q) = %v", test.in, err)
  297. }
  298. if typ := p.PeriodType.Type; typ != test.typ {
  299. t.Fatalf("parseGoCount(%q).PeriodType.Type = %q want %q", test.in, typ, test.typ)
  300. }
  301. })
  302. }
  303. }