golang.org/toolchain@v0.0.1-go1.9rc2.windows-amd64/src/cmd/vendor/github.com/google/pprof/profile/legacy_profile_test.go (about)

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