Parcourir la source

Stop treating -symbolize=force as the default (#210)

* Stop treating -symbolize=force as the default

The code parsing symbolize options was treating the empty option as
"force", effectively making -symbolize=force the default. This would
unnecessarily symbolize profiles that were already symbolized.

* Add more testing for symbolize option parsing

Do all parsing of symbolize options on a single function and
do more thorough testing of it.

* Have symbolz honor -force

Currently -symbolize=remote will force remote symbolization of mappings
that already had mappings. Making that depend on an explicit
-symbolize=force to make it consistent with local symbolization.

* Applied fixes from latest review iteration

* Add comment on symbolz check for resymbolization

* Reformatting comment
Raul Silvera il y a 7 ans
Parent
révision
42a41ff0aa

+ 1
- 1
internal/driver/driver_test.go Voir le fichier

@@ -472,7 +472,7 @@ func testFetchSymbols(source, post string) ([]byte, error) {
472 472
 type testSymbolzSymbolizer struct{}
473 473
 
474 474
 func (testSymbolzSymbolizer) Symbolize(variables string, sources plugin.MappingSources, p *profile.Profile) error {
475
-	return symbolz.Symbolize(sources, testFetchSymbols, p, nil)
475
+	return symbolz.Symbolize(p, false, sources, testFetchSymbols, nil)
476 476
 }
477 477
 
478 478
 func fakeDemangler(name string) string {

+ 15
- 18
internal/symbolizer/symbolizer.go Voir le fichier

@@ -42,21 +42,26 @@ type Symbolizer struct {
42 42
 // test taps for dependency injection
43 43
 var symbolzSymbolize = symbolz.Symbolize
44 44
 var localSymbolize = doLocalSymbolize
45
+var demangleFunction = Demangle
45 46
 
46 47
 // Symbolize attempts to symbolize profile p. First uses binutils on
47 48
 // local binaries; if the source is a URL it attempts to get any
48 49
 // missed entries using symbolz.
49 50
 func (s *Symbolizer) Symbolize(mode string, sources plugin.MappingSources, p *profile.Profile) error {
50
-	remote, local, force, demanglerMode := true, true, false, ""
51
+	remote, local, fast, force, demanglerMode := true, true, false, false, ""
51 52
 	for _, o := range strings.Split(strings.ToLower(mode), ":") {
52 53
 		switch o {
54
+		case "":
55
+			continue
53 56
 		case "none", "no":
54 57
 			return nil
55
-		case "local", "fastlocal":
58
+		case "local":
56 59
 			remote, local = false, true
60
+		case "fastlocal":
61
+			remote, local, fast = false, true, true
57 62
 		case "remote":
58 63
 			remote, local = true, false
59
-		case "", "force":
64
+		case "force":
60 65
 			force = true
61 66
 		default:
62 67
 			switch d := strings.TrimPrefix(o, "demangle="); d {
@@ -75,17 +80,17 @@ func (s *Symbolizer) Symbolize(mode string, sources plugin.MappingSources, p *pr
75 80
 	var err error
76 81
 	if local {
77 82
 		// Symbolize locally using binutils.
78
-		if err = localSymbolize(mode, p, s.Obj, s.UI); err != nil {
83
+		if err = localSymbolize(p, fast, force, s.Obj, s.UI); err != nil {
79 84
 			s.UI.PrintErr("local symbolization: " + err.Error())
80 85
 		}
81 86
 	}
82 87
 	if remote {
83
-		if err = symbolzSymbolize(sources, postURL, p, s.UI); err != nil {
88
+		if err = symbolzSymbolize(p, force, sources, postURL, s.UI); err != nil {
84 89
 			return err // Ran out of options.
85 90
 		}
86 91
 	}
87 92
 
88
-	Demangle(p, force, demanglerMode)
93
+	demangleFunction(p, force, demanglerMode)
89 94
 	return nil
90 95
 }
91 96
 
@@ -134,18 +139,10 @@ func statusCodeError(resp *http.Response) error {
134 139
 // doLocalSymbolize adds symbol and line number information to all locations
135 140
 // in a profile. mode enables some options to control
136 141
 // symbolization.
137
-func doLocalSymbolize(mode string, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error {
138
-	force := false
139
-	// Disable some mechanisms based on mode string.
140
-	for _, o := range strings.Split(strings.ToLower(mode), ":") {
141
-		switch {
142
-		case o == "force":
143
-			force = true
144
-		case o == "fastlocal":
145
-			if bu, ok := obj.(*binutils.Binutils); ok {
146
-				bu.SetFastSymbolization(true)
147
-			}
148
-		default:
142
+func doLocalSymbolize(prof *profile.Profile, fast, force bool, obj plugin.ObjTool, ui plugin.UI) error {
143
+	if fast {
144
+		if bu, ok := obj.(*binutils.Binutils); ok {
145
+			bu.SetFastSymbolization(true)
149 146
 		}
150 147
 	}
151 148
 

+ 50
- 10
internal/symbolizer/symbolizer_test.go Voir le fichier

@@ -17,6 +17,7 @@ package symbolizer
17 17
 import (
18 18
 	"fmt"
19 19
 	"regexp"
20
+	"sort"
20 21
 	"strings"
21 22
 	"testing"
22 23
 
@@ -101,9 +102,11 @@ func TestSymbolization(t *testing.T) {
101 102
 	defer func() {
102 103
 		symbolzSymbolize = sSym
103 104
 		localSymbolize = lSym
105
+		demangleFunction = Demangle
104 106
 	}()
105 107
 	symbolzSymbolize = symbolzMock
106 108
 	localSymbolize = localMock
109
+	demangleFunction = demangleMock
107 110
 
108 111
 	type testcase struct {
109 112
 		mode        string
@@ -117,19 +120,35 @@ func TestSymbolization(t *testing.T) {
117 120
 	for i, tc := range []testcase{
118 121
 		{
119 122
 			"local",
120
-			"local=local",
123
+			"local=[]",
121 124
 		},
122 125
 		{
123 126
 			"fastlocal",
124
-			"local=fastlocal",
127
+			"local=[fast]",
125 128
 		},
126 129
 		{
127 130
 			"remote",
128
-			"symbolz",
131
+			"symbolz=[]",
129 132
 		},
130 133
 		{
131 134
 			"",
132
-			"local=:symbolz",
135
+			"local=[]:symbolz=[]",
136
+		},
137
+		{
138
+			"demangle=none",
139
+			"demangle=[none]:force:local=[force]:symbolz=[force]",
140
+		},
141
+		{
142
+			"remote:demangle=full",
143
+			"demangle=[full]:force:symbolz=[force]",
144
+		},
145
+		{
146
+			"local:demangle=templates",
147
+			"demangle=[templates]:force:local=[force]",
148
+		},
149
+		{
150
+			"force:remote",
151
+			"force:symbolz=[force]",
133 152
 		},
134 153
 	} {
135 154
 		prof := testProfile.Copy()
@@ -137,23 +156,44 @@ func TestSymbolization(t *testing.T) {
137 156
 			t.Errorf("symbolize #%d: %v", i, err)
138 157
 			continue
139 158
 		}
159
+		sort.Strings(prof.Comments)
140 160
 		if got, want := strings.Join(prof.Comments, ":"), tc.wantComment; got != want {
141
-			t.Errorf("got %s, want %s", got, want)
161
+			t.Errorf("%q: got %s, want %s", tc.mode, got, want)
142 162
 			continue
143 163
 		}
144 164
 	}
145 165
 }
146 166
 
147
-func symbolzMock(sources plugin.MappingSources, syms func(string, string) ([]byte, error), p *profile.Profile, ui plugin.UI) error {
148
-	p.Comments = append(p.Comments, "symbolz")
167
+func symbolzMock(p *profile.Profile, force bool, sources plugin.MappingSources, syms func(string, string) ([]byte, error), ui plugin.UI) error {
168
+	var args []string
169
+	if force {
170
+		args = append(args, "force")
171
+	}
172
+	p.Comments = append(p.Comments, "symbolz=["+strings.Join(args, ",")+"]")
149 173
 	return nil
150 174
 }
151 175
 
152
-func localMock(mode string, p *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error {
153
-	p.Comments = append(p.Comments, "local="+mode)
176
+func localMock(p *profile.Profile, fast, force bool, obj plugin.ObjTool, ui plugin.UI) error {
177
+	var args []string
178
+	if fast {
179
+		args = append(args, "fast")
180
+	}
181
+	if force {
182
+		args = append(args, "force")
183
+	}
184
+	p.Comments = append(p.Comments, "local=["+strings.Join(args, ",")+"]")
154 185
 	return nil
155 186
 }
156 187
 
188
+func demangleMock(p *profile.Profile, force bool, mode string) {
189
+	if force {
190
+		p.Comments = append(p.Comments, "force")
191
+	}
192
+	if mode != "" {
193
+		p.Comments = append(p.Comments, "demangle=["+mode+"]")
194
+	}
195
+}
196
+
157 197
 func TestLocalSymbolization(t *testing.T) {
158 198
 	prof := testProfile.Copy()
159 199
 
@@ -165,7 +205,7 @@ func TestLocalSymbolization(t *testing.T) {
165 205
 	}
166 206
 
167 207
 	b := mockObjTool{}
168
-	if err := localSymbolize("", prof, b, &proftest.TestUI{T: t}); err != nil {
208
+	if err := localSymbolize(prof, false, false, b, &proftest.TestUI{T: t}); err != nil {
169 209
 		t.Fatalf("localSymbolize(): %v", err)
170 210
 	}
171 211
 

+ 6
- 5
internal/symbolz/symbolz.go Voir le fichier

@@ -36,12 +36,13 @@ var (
36 36
 
37 37
 // Symbolize symbolizes profile p by parsing data returned by a
38 38
 // symbolz handler. syms receives the symbolz query (hex addresses
39
-// separated by '+') and returns the symbolz output in a string. It
40
-// symbolizes all locations based on their addresses, regardless of
41
-// mapping.
42
-func Symbolize(sources plugin.MappingSources, syms func(string, string) ([]byte, error), p *profile.Profile, ui plugin.UI) error {
39
+// separated by '+') and returns the symbolz output in a string. If
40
+// force is false, it will only symbolize locations from mappings
41
+// not already marked as HasFunctions.
42
+func Symbolize(p *profile.Profile, force bool, sources plugin.MappingSources, syms func(string, string) ([]byte, error), ui plugin.UI) error {
43 43
 	for _, m := range p.Mapping {
44
-		if m.HasFunctions {
44
+		if !force && m.HasFunctions {
45
+			// Only check for HasFunctions as symbolz only populates function names.
45 46
 			continue
46 47
 		}
47 48
 		mappingSources := sources[m.File]

+ 56
- 27
internal/symbolz/symbolz_test.go Voir le fichier

@@ -41,12 +41,49 @@ func TestSymbolzURL(t *testing.T) {
41 41
 }
42 42
 
43 43
 func TestSymbolize(t *testing.T) {
44
+	s := plugin.MappingSources{
45
+		"buildid": []struct {
46
+			Source string
47
+			Start  uint64
48
+		}{
49
+			{Source: "http://localhost:80/profilez"},
50
+		},
51
+	}
52
+
53
+	for _, hasFunctions := range []bool{false, true} {
54
+		for _, force := range []bool{false, true} {
55
+			p := testProfile(hasFunctions)
56
+
57
+			if err := Symbolize(p, force, s, fetchSymbols, &proftest.TestUI{T: t}); err != nil {
58
+				t.Errorf("symbolz: %v", err)
59
+				continue
60
+			}
61
+			var wantSym, wantNoSym []*profile.Location
62
+			if force || !hasFunctions {
63
+				wantNoSym = p.Location[:1]
64
+				wantSym = p.Location[1:]
65
+			} else {
66
+				wantNoSym = p.Location
67
+			}
68
+
69
+			if err := checkSymbolized(wantSym, true); err != nil {
70
+				t.Errorf("symbolz hasFns=%v force=%v: %v", hasFunctions, force, err)
71
+			}
72
+			if err := checkSymbolized(wantNoSym, false); err != nil {
73
+				t.Errorf("symbolz hasFns=%v force=%v: %v", hasFunctions, force, err)
74
+			}
75
+		}
76
+	}
77
+}
78
+
79
+func testProfile(hasFunctions bool) *profile.Profile {
44 80
 	m := []*profile.Mapping{
45 81
 		{
46
-			ID:      1,
47
-			Start:   0x1000,
48
-			Limit:   0x5000,
49
-			BuildID: "buildid",
82
+			ID:           1,
83
+			Start:        0x1000,
84
+			Limit:        0x5000,
85
+			BuildID:      "buildid",
86
+			HasFunctions: hasFunctions,
50 87
 		},
51 88
 	}
52 89
 	p := &profile.Profile{
@@ -59,33 +96,25 @@ func TestSymbolize(t *testing.T) {
59 96
 		Mapping: m,
60 97
 	}
61 98
 
62
-	s := plugin.MappingSources{
63
-		"buildid": []struct {
64
-			Source string
65
-			Start  uint64
66
-		}{
67
-			{Source: "http://localhost:80/profilez"},
68
-		},
69
-	}
70
-
71
-	if err := Symbolize(s, fetchSymbols, p, &proftest.TestUI{T: t}); err != nil {
72
-		t.Errorf("symbolz: %v", err)
73
-	}
74
-
75
-	if l := p.Location[0]; len(l.Line) != 0 {
76
-		t.Errorf("unexpected symbolization for %#x: %v", l.Address, l.Line)
77
-	}
99
+	return p
100
+}
78 101
 
79
-	for _, l := range p.Location[1:] {
80
-		if len(l.Line) != 1 {
81
-			t.Errorf("failed to symbolize %#x", l.Address)
82
-			continue
102
+func checkSymbolized(locs []*profile.Location, wantSymbolized bool) error {
103
+	for _, loc := range locs {
104
+		if !wantSymbolized && len(loc.Line) != 0 {
105
+			return fmt.Errorf("unexpected symbolization for %#x: %v", loc.Address, loc.Line)
83 106
 		}
84
-		address := l.Address - l.Mapping.Start
85
-		if got, want := l.Line[0].Function.Name, fmt.Sprintf("%#x", address); got != want {
86
-			t.Errorf("symbolz %#x, got %s, want %s", address, got, want)
107
+		if wantSymbolized {
108
+			if len(loc.Line) != 1 {
109
+				return fmt.Errorf("expected symbolization for %#x: %v", loc.Address, loc.Line)
110
+			}
111
+			address := loc.Address - loc.Mapping.Start
112
+			if got, want := loc.Line[0].Function.Name, fmt.Sprintf("%#x", address); got != want {
113
+				return fmt.Errorf("symbolz %#x, got %s, want %s", address, got, want)
114
+			}
87 115
 		}
88 116
 	}
117
+	return nil
89 118
 }
90 119
 
91 120
 func fetchSymbols(source, post string) ([]byte, error) {