瀏覽代碼

Create a fake mapping for profile.proto profiles (#135)

* Create a fake mapping for profile.proto profiles

If a profile has mappings but no profiles, pprof may be unable to
symbolize it offline, as it uses the mappings to keep track of which
locations need symbolization.

This fixes #120.

Added the test, verified it fails on Mac with Go 1.7 before the fix, and
passes with the fix. The test is done by augmenting the existing test
for handling https+insecure:// schema in URLs. This is a bit vague but I
figured that this test needed an updated anyway since as we moved it
recently we stopped exercising the symbolization as part of the test
which was its original intention in fixing #94. Can split the tests if
things do look too ugly.

* Fix the test to include the failed regex matching error in the message.
Alexey Alexandrov 8 年之前
父節點
當前提交
1047541c19
共有 3 個檔案被更改,包括 51 行新增16 行删除
  1. 11
    11
      internal/driver/fetch.go
  2. 27
    2
      internal/driver/fetch_test.go
  3. 13
    3
      internal/proftest/proftest.go

+ 11
- 11
internal/driver/fetch.go 查看文件

373
 			}
373
 			}
374
 		}
374
 		}
375
 	}
375
 	}
376
+	if len(p.Mapping) == 0 {
377
+		// If there are no mappings, add a fake mapping to attempt symbolization.
378
+		// This is useful for some profiles generated by the golang runtime, which
379
+		// do not include any mappings. Symbolization with a fake mapping will only
380
+		// be successful against a non-PIE binary.
381
+		m := &profile.Mapping{ID: 1}
382
+		p.Mapping = []*profile.Mapping{m}
383
+		for _, l := range p.Location {
384
+			l.Mapping = m
385
+		}
386
+	}
376
 	// Replace executable filename/buildID with the overrides from source.
387
 	// Replace executable filename/buildID with the overrides from source.
377
 	// Assumes the executable is the first Mapping entry.
388
 	// Assumes the executable is the first Mapping entry.
378
 	if execName, buildID := s.ExecName, s.BuildID; execName != "" || buildID != "" {
389
 	if execName, buildID := s.ExecName, s.BuildID; execName != "" || buildID != "" {
379
-		if len(p.Mapping) == 0 {
380
-			// If there are no mappings, add a fake mapping to attempt symbolization.
381
-			// This is useful for some profiles generated by the golang runtime, which
382
-			// do not include any mappings. Symbolization with a fake mapping will only
383
-			// be successful against a non-PIE binary.
384
-			m := &profile.Mapping{ID: 1}
385
-			p.Mapping = []*profile.Mapping{m}
386
-			for _, l := range p.Location {
387
-				l.Mapping = m
388
-			}
389
-		}
390
 		m := p.Mapping[0]
390
 		m := p.Mapping[0]
391
 		if execName != "" {
391
 		if execName != "" {
392
 			m.File = execName
392
 			m.File = execName

+ 27
- 2
internal/driver/fetch_test.go 查看文件

35
 	"testing"
35
 	"testing"
36
 	"time"
36
 	"time"
37
 
37
 
38
+	"github.com/google/pprof/internal/binutils"
38
 	"github.com/google/pprof/internal/plugin"
39
 	"github.com/google/pprof/internal/plugin"
39
 	"github.com/google/pprof/internal/proftest"
40
 	"github.com/google/pprof/internal/proftest"
41
+	"github.com/google/pprof/internal/symbolizer"
40
 	"github.com/google/pprof/profile"
42
 	"github.com/google/pprof/profile"
41
 )
43
 )
42
 
44
 
275
 	defer os.Remove(outputTempFile.Name())
277
 	defer os.Remove(outputTempFile.Name())
276
 	defer outputTempFile.Close()
278
 	defer outputTempFile.Close()
277
 
279
 
278
-	address := "https+insecure://" + l.Addr().String() + "/debug/pprof/profile?seconds=5"
279
-	p, _, _, err := grabProfile(&source{}, address, 0, nil, testObj{}, &proftest.TestUI{T: t})
280
+	address := "https+insecure://" + l.Addr().String() + "/debug/pprof/profile"
281
+	s := &source{
282
+		Sources:   []string{address},
283
+		Seconds:   10,
284
+		Timeout:   10,
285
+		Symbolize: "remote",
286
+	}
287
+	o := &plugin.Options{
288
+		Obj: &binutils.Binutils{},
289
+		UI:  &proftest.TestUI{T: t, IgnoreRx: "Saved profile in"},
290
+	}
291
+	o.Sym = &symbolizer.Symbolizer{Obj: o.Obj, UI: o.UI}
292
+	p, err := fetchProfiles(s, o)
280
 	if err != nil {
293
 	if err != nil {
281
 		t.Fatal(err)
294
 		t.Fatal(err)
282
 	}
295
 	}
283
 	if len(p.SampleType) == 0 {
296
 	if len(p.SampleType) == 0 {
284
 		t.Fatalf("grabProfile(%s) got empty profile: len(p.SampleType)==0", address)
297
 		t.Fatalf("grabProfile(%s) got empty profile: len(p.SampleType)==0", address)
285
 	}
298
 	}
299
+	if err := checkProfileHasFunction(p, "TestHttpsInsecure"); err != nil {
300
+		t.Fatalf("grabProfile(%s) %v", address, err)
301
+	}
302
+}
303
+
304
+func checkProfileHasFunction(p *profile.Profile, fname string) error {
305
+	for _, f := range p.Function {
306
+		if strings.Contains(f.Name, fname) {
307
+			return nil
308
+		}
309
+	}
310
+	return fmt.Errorf("got %s, want function %q", p.String(), fname)
286
 }
311
 }
287
 
312
 
288
 func selfSignedCert(t *testing.T) tls.Certificate {
313
 func selfSignedCert(t *testing.T) tls.Certificate {

+ 13
- 3
internal/proftest/proftest.go 查看文件

22
 	"io/ioutil"
22
 	"io/ioutil"
23
 	"os"
23
 	"os"
24
 	"os/exec"
24
 	"os/exec"
25
+	"regexp"
25
 	"testing"
26
 	"testing"
26
 )
27
 )
27
 
28
 
71
 }
72
 }
72
 
73
 
73
 // TestUI implements the plugin.UI interface, triggering test failures
74
 // TestUI implements the plugin.UI interface, triggering test failures
74
-// if more than Ignore errors are printed.
75
+// if more than Ignore errors not matching IgnoreRx are printed.
75
 type TestUI struct {
76
 type TestUI struct {
76
-	T      *testing.T
77
-	Ignore int
77
+	T        *testing.T
78
+	Ignore   int
79
+	IgnoreRx string
78
 }
80
 }
79
 
81
 
80
 // ReadLine returns no input, as no input is expected during testing.
82
 // ReadLine returns no input, as no input is expected during testing.
89
 // PrintErr messages may trigger an error failure. A fixed number of
91
 // PrintErr messages may trigger an error failure. A fixed number of
90
 // error messages are permitted when appropriate.
92
 // error messages are permitted when appropriate.
91
 func (ui *TestUI) PrintErr(args ...interface{}) {
93
 func (ui *TestUI) PrintErr(args ...interface{}) {
94
+	if ui.IgnoreRx != "" {
95
+		if matched, err := regexp.MatchString(ui.IgnoreRx, fmt.Sprint(args)); matched || err != nil {
96
+			if err != nil {
97
+				ui.T.Errorf("failed to match against regex %q: %v", ui.IgnoreRx, err)
98
+			}
99
+			return
100
+		}
101
+	}
92
 	if ui.Ignore > 0 {
102
 	if ui.Ignore > 0 {
93
 		ui.Ignore--
103
 		ui.Ignore--
94
 		return
104
 		return