...

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

Documentation: github.com/google/pprof/profile

     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  
    15  package profile
    16  
    17  import (
    18  	"bytes"
    19  	"fmt"
    20  	"testing"
    21  
    22  	"github.com/google/pprof/internal/proftest"
    23  )
    24  
    25  func TestMapMapping(t *testing.T) {
    26  	pm := &profileMerger{
    27  		p:            &Profile{},
    28  		mappings:     make(map[mappingKey]*Mapping),
    29  		mappingsByID: make(map[uint64]mapInfo),
    30  	}
    31  	for _, tc := range []struct {
    32  		desc       string
    33  		m1         Mapping
    34  		m2         Mapping
    35  		wantMerged bool
    36  	}{
    37  		{
    38  			desc: "same file name",
    39  			m1: Mapping{
    40  				ID:   1,
    41  				File: "test-file-1",
    42  			},
    43  			m2: Mapping{
    44  				ID:   2,
    45  				File: "test-file-1",
    46  			},
    47  			wantMerged: true,
    48  		},
    49  		{
    50  			desc: "same build ID",
    51  			m1: Mapping{
    52  				ID:      3,
    53  				BuildID: "test-build-id-1",
    54  			},
    55  			m2: Mapping{
    56  				ID:      4,
    57  				BuildID: "test-build-id-1",
    58  			},
    59  			wantMerged: true,
    60  		},
    61  		{
    62  			desc: "same fake mapping",
    63  			m1: Mapping{
    64  				ID: 5,
    65  			},
    66  			m2: Mapping{
    67  				ID: 6,
    68  			},
    69  			wantMerged: true,
    70  		},
    71  		{
    72  			desc: "different start",
    73  			m1: Mapping{
    74  				ID:      7,
    75  				Start:   0x1000,
    76  				Limit:   0x2000,
    77  				BuildID: "test-build-id-2",
    78  			},
    79  			m2: Mapping{
    80  				ID:      8,
    81  				Start:   0x3000,
    82  				Limit:   0x4000,
    83  				BuildID: "test-build-id-2",
    84  			},
    85  			wantMerged: true,
    86  		},
    87  		{
    88  			desc: "different file name",
    89  			m1: Mapping{
    90  				ID:   9,
    91  				File: "test-file-2",
    92  			},
    93  			m2: Mapping{
    94  				ID:   10,
    95  				File: "test-file-3",
    96  			},
    97  		},
    98  		{
    99  			desc: "different build id",
   100  			m1: Mapping{
   101  				ID:      11,
   102  				BuildID: "test-build-id-3",
   103  			},
   104  			m2: Mapping{
   105  				ID:      12,
   106  				BuildID: "test-build-id-4",
   107  			},
   108  		},
   109  		{
   110  			desc: "different size",
   111  			m1: Mapping{
   112  				ID:      13,
   113  				Start:   0x1000,
   114  				Limit:   0x3000,
   115  				BuildID: "test-build-id-5",
   116  			},
   117  			m2: Mapping{
   118  				ID:      14,
   119  				Start:   0x1000,
   120  				Limit:   0x5000,
   121  				BuildID: "test-build-id-5",
   122  			},
   123  		},
   124  		{
   125  			desc: "different offset",
   126  			m1: Mapping{
   127  				ID:      15,
   128  				Offset:  1,
   129  				BuildID: "test-build-id-6",
   130  			},
   131  			m2: Mapping{
   132  				ID:      16,
   133  				Offset:  2,
   134  				BuildID: "test-build-id-6",
   135  			},
   136  		},
   137  	} {
   138  		t.Run(tc.desc, func(t *testing.T) {
   139  			info1 := pm.mapMapping(&tc.m1)
   140  			info2 := pm.mapMapping(&tc.m2)
   141  			gotM1, gotM2 := *info1.m, *info2.m
   142  
   143  			wantM1 := tc.m1
   144  			wantM1.ID = gotM1.ID
   145  			if gotM1 != wantM1 {
   146  				t.Errorf("first mapping got %v, want %v", gotM1, wantM1)
   147  			}
   148  
   149  			if tc.wantMerged {
   150  				if gotM1 != gotM2 {
   151  					t.Errorf("first mapping got %v, second mapping got %v, want equal", gotM1, gotM2)
   152  				}
   153  				if info1.offset != 0 {
   154  					t.Errorf("first mapping info got offset %d, want 0", info1.offset)
   155  				}
   156  				if wantOffset := int64(tc.m1.Start) - int64(tc.m2.Start); wantOffset != info2.offset {
   157  					t.Errorf("second mapping info got offset %d, want %d", info2.offset, wantOffset)
   158  				}
   159  			} else {
   160  				if gotM1.ID == gotM2.ID {
   161  					t.Errorf("first mapping got %v, second mapping got %v, want different IDs", gotM1, gotM2)
   162  				}
   163  				wantM2 := tc.m2
   164  				wantM2.ID = gotM2.ID
   165  				if gotM2 != wantM2 {
   166  					t.Errorf("second mapping got %v, want %v", gotM2, wantM2)
   167  				}
   168  			}
   169  		})
   170  	}
   171  }
   172  
   173  func TestLocationIDMap(t *testing.T) {
   174  	ids := []uint64{1, 2, 5, 9, 10, 11, 100, 1000, 1000000}
   175  	missing := []uint64{3, 4, 200}
   176  
   177  	// Populate the map,.
   178  	idmap := makeLocationIDMap(10)
   179  	for _, id := range ids {
   180  		loc := &Location{ID: id}
   181  		idmap.set(id, loc)
   182  	}
   183  
   184  	// Check ids that should be present in the map.
   185  	for _, id := range ids {
   186  		loc := idmap.get(id)
   187  		if loc == nil {
   188  			t.Errorf("No location found for %d", id)
   189  		} else if loc.ID != id {
   190  			t.Errorf("Wrong location %d found for %d", loc.ID, id)
   191  		}
   192  	}
   193  
   194  	// Check ids that should not be present in the map.
   195  	for _, id := range missing {
   196  		loc := idmap.get(id)
   197  		if loc != nil {
   198  			t.Errorf("Unexpected location %d found for %d", loc.ID, id)
   199  		}
   200  	}
   201  }
   202  
   203  func BenchmarkMerge(b *testing.B) {
   204  	data := proftest.LargeProfile(b)
   205  	for n := 1; n <= 2; n++ { // Merge either 1 or 2 instances.
   206  		b.Run(fmt.Sprintf("%d", n), func(b *testing.B) {
   207  			list := make([]*Profile, n)
   208  			for i := 0; i < n; i++ {
   209  				prof, err := Parse(bytes.NewBuffer(data))
   210  				if err != nil {
   211  					b.Fatal(err)
   212  				}
   213  				list[i] = prof
   214  			}
   215  			b.ResetTimer()
   216  			for i := 0; i < b.N; i++ {
   217  				_, err := Merge(list)
   218  				if err != nil {
   219  					b.Fatal(err)
   220  				}
   221  			}
   222  		})
   223  	}
   224  }
   225  
   226  func TestCompatibilizeSampleTypes(t *testing.T) {
   227  	for _, tc := range []struct {
   228  		desc      string
   229  		ps        []*Profile
   230  		want      []*Profile
   231  		wantError bool
   232  	}{
   233  		{
   234  			desc: "drop first sample types",
   235  			ps: []*Profile{
   236  				{
   237  					DefaultSampleType: "delete1",
   238  					SampleType: []*ValueType{
   239  						{Type: "delete1", Unit: "Unit1"},
   240  						{Type: "delete2", Unit: "Unit2"},
   241  						{Type: "keep1", Unit: "Unit3"},
   242  						{Type: "keep2", Unit: "Unit4"},
   243  						{Type: "keep3", Unit: "Unit5"},
   244  					},
   245  					Sample: []*Sample{
   246  						{Value: []int64{1, 2, 3, 4, 5}},
   247  						{Value: []int64{10, 20, 30, 40, 50}},
   248  					},
   249  				},
   250  				{
   251  					DefaultSampleType: "keep1",
   252  					SampleType: []*ValueType{
   253  						{Type: "keep1", Unit: "Unit3"},
   254  						{Type: "keep2", Unit: "Unit4"},
   255  						{Type: "keep3", Unit: "Unit5"},
   256  					},
   257  					Sample: []*Sample{
   258  						{Value: []int64{1, 2, 3}},
   259  						{Value: []int64{10, 20, 30}},
   260  					},
   261  				},
   262  			},
   263  			want: []*Profile{
   264  				{
   265  					DefaultSampleType: "keep1",
   266  					SampleType: []*ValueType{
   267  						{Type: "keep1", Unit: "Unit3"},
   268  						{Type: "keep2", Unit: "Unit4"},
   269  						{Type: "keep3", Unit: "Unit5"},
   270  					},
   271  					Sample: []*Sample{
   272  						{Value: []int64{3, 4, 5}},
   273  						{Value: []int64{30, 40, 50}},
   274  					},
   275  				},
   276  				{
   277  					DefaultSampleType: "keep1",
   278  					SampleType: []*ValueType{
   279  						{Type: "keep1", Unit: "Unit3"},
   280  						{Type: "keep2", Unit: "Unit4"},
   281  						{Type: "keep3", Unit: "Unit5"},
   282  					},
   283  					Sample: []*Sample{
   284  						{Value: []int64{1, 2, 3}},
   285  						{Value: []int64{10, 20, 30}},
   286  					},
   287  				},
   288  			},
   289  		},
   290  		{
   291  			desc: "drop last sample types",
   292  			ps: []*Profile{
   293  				{
   294  					DefaultSampleType: "delete2",
   295  					SampleType: []*ValueType{
   296  						{Type: "keep1", Unit: "Unit3"},
   297  						{Type: "keep2", Unit: "Unit4"},
   298  						{Type: "keep3", Unit: "Unit5"},
   299  						{Type: "delete1", Unit: "Unit1"},
   300  						{Type: "delete2", Unit: "Unit2"},
   301  					},
   302  					Sample: []*Sample{
   303  						{Value: []int64{1, 2, 3, 4, 5}},
   304  						{Value: []int64{10, 20, 30, 40, 50}},
   305  					},
   306  				},
   307  				{
   308  					DefaultSampleType: "keep2",
   309  					SampleType: []*ValueType{
   310  						{Type: "keep1", Unit: "Unit3"},
   311  						{Type: "keep2", Unit: "Unit4"},
   312  						{Type: "keep3", Unit: "Unit5"},
   313  					},
   314  					Sample: []*Sample{
   315  						{Value: []int64{1, 2, 3}},
   316  						{Value: []int64{10, 20, 30}},
   317  					},
   318  				},
   319  			},
   320  			want: []*Profile{
   321  				{
   322  					DefaultSampleType: "keep1",
   323  					SampleType: []*ValueType{
   324  						{Type: "keep1", Unit: "Unit3"},
   325  						{Type: "keep2", Unit: "Unit4"},
   326  						{Type: "keep3", Unit: "Unit5"},
   327  					},
   328  					Sample: []*Sample{
   329  						{Value: []int64{1, 2, 3}},
   330  						{Value: []int64{10, 20, 30}},
   331  					},
   332  				},
   333  				{
   334  					DefaultSampleType: "keep2",
   335  					SampleType: []*ValueType{
   336  						{Type: "keep1", Unit: "Unit3"},
   337  						{Type: "keep2", Unit: "Unit4"},
   338  						{Type: "keep3", Unit: "Unit5"},
   339  					},
   340  					Sample: []*Sample{
   341  						{Value: []int64{1, 2, 3}},
   342  						{Value: []int64{10, 20, 30}},
   343  					},
   344  				},
   345  			},
   346  		},
   347  		{
   348  			desc: "drop sample types and reorder",
   349  			ps: []*Profile{
   350  				{
   351  					DefaultSampleType: "keep3",
   352  					SampleType: []*ValueType{
   353  						{Type: "delete1", Unit: "Unit1"},
   354  						{Type: "keep1", Unit: "Unit3"},
   355  						{Type: "delete2", Unit: "Unit2"},
   356  						{Type: "keep2", Unit: "Unit4"},
   357  						{Type: "keep3", Unit: "Unit5"},
   358  					},
   359  					Sample: []*Sample{
   360  						{Value: []int64{1, 2, 3, 4, 5}},
   361  						{Value: []int64{10, 20, 30, 40, 50}},
   362  					},
   363  				},
   364  				{
   365  					DefaultSampleType: "keep2",
   366  					SampleType: []*ValueType{
   367  						{Type: "keep3", Unit: "Unit5"},
   368  						{Type: "keep2", Unit: "Unit4"},
   369  						{Type: "keep1", Unit: "Unit3"},
   370  					},
   371  					Sample: []*Sample{
   372  						{Value: []int64{1, 2, 3}},
   373  						{Value: []int64{10, 20, 30}},
   374  					},
   375  				},
   376  			},
   377  			want: []*Profile{
   378  				{
   379  					DefaultSampleType: "keep3",
   380  					SampleType: []*ValueType{
   381  						{Type: "keep1", Unit: "Unit3"},
   382  						{Type: "keep2", Unit: "Unit4"},
   383  						{Type: "keep3", Unit: "Unit5"},
   384  					},
   385  					Sample: []*Sample{
   386  						{Value: []int64{2, 4, 5}},
   387  						{Value: []int64{20, 40, 50}},
   388  					},
   389  				},
   390  				{
   391  					DefaultSampleType: "keep2",
   392  					SampleType: []*ValueType{
   393  						{Type: "keep1", Unit: "Unit3"},
   394  						{Type: "keep2", Unit: "Unit4"},
   395  						{Type: "keep3", Unit: "Unit5"},
   396  					},
   397  					Sample: []*Sample{
   398  						{Value: []int64{3, 2, 1}},
   399  						{Value: []int64{30, 20, 10}},
   400  					},
   401  				},
   402  			},
   403  		},
   404  		{
   405  			desc: "empty common types",
   406  			ps: []*Profile{
   407  				{
   408  					SampleType: []*ValueType{
   409  						{Type: "keep1", Unit: "Unit1"},
   410  						{Type: "keep2", Unit: "Unit2"},
   411  						{Type: "keep3", Unit: "Unit3"},
   412  					},
   413  				},
   414  				{
   415  					SampleType: []*ValueType{
   416  						{Type: "keep4", Unit: "Unit4"},
   417  						{Type: "keep5", Unit: "Unit5"},
   418  					},
   419  				},
   420  			},
   421  			wantError: true,
   422  		},
   423  	} {
   424  		t.Run(tc.desc, func(t *testing.T) {
   425  			err := CompatibilizeSampleTypes(tc.ps)
   426  			if (err != nil) != tc.wantError {
   427  				t.Fatalf("CompatibilizeSampleTypes() returned error: %v, want any error=%t", err, tc.wantError)
   428  			}
   429  			if err != nil {
   430  				return
   431  			}
   432  			for i := 0; i < len(tc.want); i++ {
   433  				gotStr := tc.ps[i].String()
   434  				wantStr := tc.want[i].String()
   435  				if gotStr != wantStr {
   436  					d, err := proftest.Diff([]byte(wantStr), []byte(gotStr))
   437  					if err != nil {
   438  						t.Fatalf("failed to get diff: %v", err)
   439  					}
   440  					t.Errorf("CompatibilizeSampleTypes(): profile[%d] got diff (-want +got)\n%s", i, string(d))
   441  				}
   442  			}
   443  		})
   444  	}
   445  }
   446  

View as plain text