...

Source file src/github.com/google/pprof/profile/legacy_profile_test.go

Documentation: github.com/google/pprof/profile

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

View as plain text