...

Source file src/github.com/google/pprof/internal/driver/tagroot_test.go

Documentation: github.com/google/pprof/internal/driver

     1  package driver
     2  
     3  import (
     4  	"fmt"
     5  	"strings"
     6  	"testing"
     7  
     8  	"github.com/google/pprof/internal/proftest"
     9  	"github.com/google/pprof/profile"
    10  )
    11  
    12  const mainBinary = "/bin/main"
    13  
    14  var cpuF = []*profile.Function{
    15  	{ID: 1, Name: "main", SystemName: "main", Filename: "main.c"},
    16  	{ID: 2, Name: "foo", SystemName: "foo", Filename: "foo.c"},
    17  	{ID: 3, Name: "foo_caller", SystemName: "foo_caller", Filename: "foo.c"},
    18  	{ID: 4, Name: "bar", SystemName: "bar", Filename: "bar.c"},
    19  }
    20  
    21  var cpuM = []*profile.Mapping{
    22  	{
    23  		ID:              1,
    24  		Start:           0x10000,
    25  		Limit:           0x40000,
    26  		File:            mainBinary,
    27  		HasFunctions:    true,
    28  		HasFilenames:    true,
    29  		HasLineNumbers:  true,
    30  		HasInlineFrames: true,
    31  	},
    32  	{
    33  		ID:              2,
    34  		Start:           0x1000,
    35  		Limit:           0x4000,
    36  		File:            "/lib/lib.so",
    37  		HasFunctions:    true,
    38  		HasFilenames:    true,
    39  		HasLineNumbers:  true,
    40  		HasInlineFrames: true,
    41  	},
    42  }
    43  
    44  var cpuL = []*profile.Location{
    45  	{
    46  		ID:      1000,
    47  		Mapping: cpuM[1],
    48  		Address: 0x1000,
    49  		Line: []profile.Line{
    50  			{Function: cpuF[0], Line: 1},
    51  		},
    52  	},
    53  	{
    54  		ID:      2000,
    55  		Mapping: cpuM[0],
    56  		Address: 0x2000,
    57  		Line: []profile.Line{
    58  			{Function: cpuF[1], Line: 2},
    59  			{Function: cpuF[2], Line: 1},
    60  		},
    61  	},
    62  	{
    63  		ID:      3000,
    64  		Mapping: cpuM[0],
    65  		Address: 0x3000,
    66  		Line: []profile.Line{
    67  			{Function: cpuF[1], Line: 2},
    68  			{Function: cpuF[2], Line: 1},
    69  		},
    70  	},
    71  	{
    72  		ID:      3001,
    73  		Mapping: cpuM[0],
    74  		Address: 0x3001,
    75  		Line: []profile.Line{
    76  			{Function: cpuF[2], Line: 2},
    77  		},
    78  	},
    79  	{
    80  		ID:      3002,
    81  		Mapping: cpuM[0],
    82  		Address: 0x3002,
    83  		Line: []profile.Line{
    84  			{Function: cpuF[2], Line: 3},
    85  		},
    86  	},
    87  	{
    88  		ID:      3003,
    89  		Mapping: cpuM[0],
    90  		Address: 0x3003,
    91  		Line: []profile.Line{
    92  			{Function: cpuF[3], Line: 1},
    93  		},
    94  	},
    95  }
    96  
    97  var testProfile1 = &profile.Profile{
    98  	TimeNanos:     10000,
    99  	PeriodType:    &profile.ValueType{Type: "cpu", Unit: "milliseconds"},
   100  	Period:        1,
   101  	DurationNanos: 10e9,
   102  	SampleType: []*profile.ValueType{
   103  		{Type: "samples", Unit: "count"},
   104  		{Type: "cpu", Unit: "milliseconds"},
   105  	},
   106  	Sample: []*profile.Sample{
   107  		{
   108  			Location: []*profile.Location{cpuL[0]},
   109  			Value:    []int64{1000, 1000},
   110  			Label: map[string][]string{
   111  				"key1": {"tag1"},
   112  				"key2": {"tag1"},
   113  			},
   114  		},
   115  		{
   116  			Location: []*profile.Location{cpuL[1], cpuL[0]},
   117  			Value:    []int64{100, 100},
   118  			Label: map[string][]string{
   119  				"key1": {"tag2"},
   120  				"key3": {"tag2"},
   121  			},
   122  		},
   123  		{
   124  			Location: []*profile.Location{cpuL[2], cpuL[0]},
   125  			Value:    []int64{10, 10},
   126  			Label: map[string][]string{
   127  				"key1": {"tag3"},
   128  				"key2": {"tag2"},
   129  			},
   130  			NumLabel: map[string][]int64{
   131  				"allocations": {1024},
   132  			},
   133  			NumUnit: map[string][]string{
   134  				"allocations": {""},
   135  			},
   136  		},
   137  		{
   138  			Location: []*profile.Location{cpuL[3], cpuL[0]},
   139  			Value:    []int64{10000, 10000},
   140  			Label: map[string][]string{
   141  				"key1": {"tag4"},
   142  				"key2": {"tag1"},
   143  			},
   144  			NumLabel: map[string][]int64{
   145  				"allocations": {1024, 2048},
   146  			},
   147  			NumUnit: map[string][]string{
   148  				"allocations": {"bytes", "b"},
   149  			},
   150  		},
   151  		{
   152  			Location: []*profile.Location{cpuL[4], cpuL[0]},
   153  			Value:    []int64{1, 1},
   154  			Label: map[string][]string{
   155  				"key1": {"tag4"},
   156  				"key2": {"tag1", "tag5"},
   157  			},
   158  			NumLabel: map[string][]int64{
   159  				"allocations": {1024, 1},
   160  			},
   161  			NumUnit: map[string][]string{
   162  				"allocations": {"byte", "kilobyte"},
   163  			},
   164  		},
   165  		{
   166  			Location: []*profile.Location{cpuL[5], cpuL[0]},
   167  			Value:    []int64{200, 200},
   168  			NumLabel: map[string][]int64{
   169  				"allocations": {1024},
   170  			},
   171  		},
   172  	},
   173  	Location: cpuL,
   174  	Function: cpuF,
   175  	Mapping:  cpuM,
   176  }
   177  
   178  func TestAddLabelNodesMatchBooleans(t *testing.T) {
   179  	type addLabelNodesTestcase struct {
   180  		name             string
   181  		tagroot, tagleaf []string
   182  		outputUnit       string
   183  		rootm, leafm     bool
   184  		// wantSampleFuncs contains expected stack functions and sample value after
   185  		// adding nodes, in the same order as in the profile. The format is as
   186  		// returned by stackCollapse function, which is "callee caller: <num>".
   187  		wantSampleFuncs []string
   188  	}
   189  	for _, tc := range []addLabelNodesTestcase{
   190  		{
   191  			name: "Without tagroot or tagleaf, add no extra nodes, and should not match",
   192  			wantSampleFuncs: []string{
   193  				"main(main.c) 1000",
   194  				"main(main.c);foo(foo.c);foo_caller(foo.c) 100",
   195  				"main(main.c);foo(foo.c);foo_caller(foo.c) 10",
   196  				"main(main.c);foo_caller(foo.c) 10000",
   197  				"main(main.c);foo_caller(foo.c) 1",
   198  				"main(main.c);bar(bar.c) 200",
   199  			},
   200  		},
   201  		{
   202  			name:    "Keys that aren't found add empty nodes, and should not match",
   203  			tagroot: []string{"key404"},
   204  			tagleaf: []string{"key404"},
   205  			wantSampleFuncs: []string{
   206  				"(key404);main(main.c);(key404) 1000",
   207  				"(key404);main(main.c);foo(foo.c);foo_caller(foo.c);(key404) 100",
   208  				"(key404);main(main.c);foo(foo.c);foo_caller(foo.c);(key404) 10",
   209  				"(key404);main(main.c);foo_caller(foo.c);(key404) 10000",
   210  				"(key404);main(main.c);foo_caller(foo.c);(key404) 1",
   211  				"(key404);main(main.c);bar(bar.c);(key404) 200",
   212  			},
   213  		},
   214  		{
   215  			name:    "tagroot adds nodes for key1 and reports a match",
   216  			tagroot: []string{"key1"},
   217  			rootm:   true,
   218  			wantSampleFuncs: []string{
   219  				"tag1(key1);main(main.c) 1000",
   220  				"tag2(key1);main(main.c);foo(foo.c);foo_caller(foo.c) 100",
   221  				"tag3(key1);main(main.c);foo(foo.c);foo_caller(foo.c) 10",
   222  				"tag4(key1);main(main.c);foo_caller(foo.c) 10000",
   223  				"tag4(key1);main(main.c);foo_caller(foo.c) 1",
   224  				"(key1);main(main.c);bar(bar.c) 200",
   225  			},
   226  		},
   227  		{
   228  			name:    "tagroot adds nodes for key2 and reports a match",
   229  			tagroot: []string{"key2"},
   230  			rootm:   true,
   231  			wantSampleFuncs: []string{
   232  				"tag1(key2);main(main.c) 1000",
   233  				"(key2);main(main.c);foo(foo.c);foo_caller(foo.c) 100",
   234  				"tag2(key2);main(main.c);foo(foo.c);foo_caller(foo.c) 10",
   235  				"tag1(key2);main(main.c);foo_caller(foo.c) 10000",
   236  				"tag1,tag5(key2);main(main.c);foo_caller(foo.c) 1",
   237  				"(key2);main(main.c);bar(bar.c) 200",
   238  			},
   239  		},
   240  		{
   241  			name:    "tagleaf adds nodes for key1 and reports a match",
   242  			tagleaf: []string{"key1"},
   243  			leafm:   true,
   244  			wantSampleFuncs: []string{
   245  				"main(main.c);tag1(key1) 1000",
   246  				"main(main.c);foo(foo.c);foo_caller(foo.c);tag2(key1) 100",
   247  				"main(main.c);foo(foo.c);foo_caller(foo.c);tag3(key1) 10",
   248  				"main(main.c);foo_caller(foo.c);tag4(key1) 10000",
   249  				"main(main.c);foo_caller(foo.c);tag4(key1) 1",
   250  				"main(main.c);bar(bar.c);(key1) 200",
   251  			},
   252  		},
   253  		{
   254  			name:    "tagleaf adds nodes for key3 and reports a match",
   255  			tagleaf: []string{"key3"},
   256  			leafm:   true,
   257  			wantSampleFuncs: []string{
   258  				"main(main.c);(key3) 1000",
   259  				"main(main.c);foo(foo.c);foo_caller(foo.c);tag2(key3) 100",
   260  				"main(main.c);foo(foo.c);foo_caller(foo.c);(key3) 10",
   261  				"main(main.c);foo_caller(foo.c);(key3) 10000",
   262  				"main(main.c);foo_caller(foo.c);(key3) 1",
   263  				"main(main.c);bar(bar.c);(key3) 200",
   264  			},
   265  		},
   266  		{
   267  			name:    "tagroot adds nodes for key1,key2 in order and reports a match",
   268  			tagroot: []string{"key1", "key2"},
   269  			rootm:   true,
   270  			wantSampleFuncs: []string{
   271  				"tag1(key1);tag1(key2);main(main.c) 1000",
   272  				"tag2(key1);(key2);main(main.c);foo(foo.c);foo_caller(foo.c) 100",
   273  				"tag3(key1);tag2(key2);main(main.c);foo(foo.c);foo_caller(foo.c) 10",
   274  				"tag4(key1);tag1(key2);main(main.c);foo_caller(foo.c) 10000",
   275  				"tag4(key1);tag1,tag5(key2);main(main.c);foo_caller(foo.c) 1",
   276  				"(key1);(key2);main(main.c);bar(bar.c) 200",
   277  			},
   278  		},
   279  		{
   280  			name:    "tagleaf adds nodes for key1,key2 in order and reports a match",
   281  			tagleaf: []string{"key1", "key2"},
   282  			leafm:   true,
   283  			wantSampleFuncs: []string{
   284  				"main(main.c);tag1(key1);tag1(key2) 1000",
   285  				"main(main.c);foo(foo.c);foo_caller(foo.c);tag2(key1);(key2) 100",
   286  				"main(main.c);foo(foo.c);foo_caller(foo.c);tag3(key1);tag2(key2) 10",
   287  				"main(main.c);foo_caller(foo.c);tag4(key1);tag1(key2) 10000",
   288  				"main(main.c);foo_caller(foo.c);tag4(key1);tag1,tag5(key2) 1",
   289  				"main(main.c);bar(bar.c);(key1);(key2) 200",
   290  			},
   291  		},
   292  		{
   293  			name:    "Numeric units are added with units with tagleaf",
   294  			tagleaf: []string{"allocations"},
   295  			leafm:   true,
   296  			wantSampleFuncs: []string{
   297  				"main(main.c);(allocations) 1000",
   298  				"main(main.c);foo(foo.c);foo_caller(foo.c);(allocations) 100",
   299  				"main(main.c);foo(foo.c);foo_caller(foo.c);1024(allocations) 10",
   300  				"main(main.c);foo_caller(foo.c);1024B,2048B(allocations) 10000",
   301  				"main(main.c);foo_caller(foo.c);1024B,1024B(allocations) 1",
   302  				"main(main.c);bar(bar.c);1024(allocations) 200",
   303  			},
   304  		},
   305  		{
   306  			name:    "Numeric units are added with units with tagroot",
   307  			tagroot: []string{"allocations"},
   308  			rootm:   true,
   309  			wantSampleFuncs: []string{
   310  				"(allocations);main(main.c) 1000",
   311  				"(allocations);main(main.c);foo(foo.c);foo_caller(foo.c) 100",
   312  				"1024(allocations);main(main.c);foo(foo.c);foo_caller(foo.c) 10",
   313  				"1024B,2048B(allocations);main(main.c);foo_caller(foo.c) 10000",
   314  				"1024B,1024B(allocations);main(main.c);foo_caller(foo.c) 1",
   315  				"1024(allocations);main(main.c);bar(bar.c) 200",
   316  			},
   317  		},
   318  		{
   319  			name:       "Numeric labels are formatted according to outputUnit",
   320  			outputUnit: "kB",
   321  			tagleaf:    []string{"allocations"},
   322  			leafm:      true,
   323  			wantSampleFuncs: []string{
   324  				"main(main.c);(allocations) 1000",
   325  				"main(main.c);foo(foo.c);foo_caller(foo.c);(allocations) 100",
   326  				"main(main.c);foo(foo.c);foo_caller(foo.c);1024(allocations) 10",
   327  				"main(main.c);foo_caller(foo.c);1kB,2kB(allocations) 10000",
   328  				"main(main.c);foo_caller(foo.c);1kB,1kB(allocations) 1",
   329  				"main(main.c);bar(bar.c);1024(allocations) 200",
   330  			},
   331  		},
   332  		{
   333  			name:    "Numeric units with no units are handled properly by tagleaf",
   334  			tagleaf: []string{"allocations"},
   335  			leafm:   true,
   336  			wantSampleFuncs: []string{
   337  				"main(main.c);(allocations) 1000",
   338  				"main(main.c);foo(foo.c);foo_caller(foo.c);(allocations) 100",
   339  				"main(main.c);foo(foo.c);foo_caller(foo.c);1024(allocations) 10",
   340  				"main(main.c);foo_caller(foo.c);1024B,2048B(allocations) 10000",
   341  				"main(main.c);foo_caller(foo.c);1024B,1024B(allocations) 1",
   342  				"main(main.c);bar(bar.c);1024(allocations) 200",
   343  			},
   344  		},
   345  	} {
   346  		tc := tc
   347  		t.Run(tc.name, func(t *testing.T) {
   348  			p := testProfile1.Copy()
   349  			rootm, leafm := addLabelNodes(p, tc.tagroot, tc.tagleaf, tc.outputUnit)
   350  			if rootm != tc.rootm {
   351  				t.Errorf("Got rootm=%v, want=%v", rootm, tc.rootm)
   352  			}
   353  			if leafm != tc.leafm {
   354  				t.Errorf("Got leafm=%v, want=%v", leafm, tc.leafm)
   355  			}
   356  			if got, want := strings.Join(stackCollapse(p), "\n")+"\n", strings.Join(tc.wantSampleFuncs, "\n")+"\n"; got != want {
   357  				diff, err := proftest.Diff([]byte(want), []byte(got))
   358  				if err != nil {
   359  					t.Fatalf("Failed to get diff: %v", err)
   360  				}
   361  				t.Errorf("Profile samples got diff(want->got):\n%s", diff)
   362  			}
   363  		})
   364  	}
   365  }
   366  
   367  // stackCollapse returns a slice of strings where each string represents one
   368  // profile sample in Brendan Gregg's "Folded Stacks" format:
   369  // "<root_fn>(filename);<fun2>(filename);<leaf_fn>(filename) <value>". This
   370  // allows the expected values for test cases to be specified in human-readable
   371  // strings.
   372  func stackCollapse(p *profile.Profile) []string {
   373  	var ret []string
   374  	for _, s := range p.Sample {
   375  		var funcs []string
   376  		for i := range s.Location {
   377  			loc := s.Location[len(s.Location)-1-i]
   378  			for _, line := range loc.Line {
   379  				funcs = append(funcs, fmt.Sprintf("%s(%s)", line.Function.Name, line.Function.Filename))
   380  			}
   381  		}
   382  		ret = append(ret, fmt.Sprintf("%s %d", strings.Join(funcs, ";"), s.Value[0]))
   383  	}
   384  	return ret
   385  }
   386  

View as plain text