Ver código fonte

Allow binary override for profiles with no mappings (#89)

The go runtime generates profiles in profile.proto format
without symbols or mappings. The expectation is that these
can be symbolized by passing the binary name to pprof.

The mechanism pprof uses relies to override the binary relies
on there being a mapping, and previously we moved the creation
of fake mappings to the legacy profile handlers, so profiles
parsed from profile.proto with no mappings can no longer be
symbolized.

Special case this situation to create a fake mapping and associate
all locations to it if there is a command line override but no
mappings.
Raul Silvera 8 anos atrás
pai
commit
e84ac77df0

+ 23
- 12
internal/driver/fetch.go Ver arquivo

@@ -317,20 +317,9 @@ func locateBinaries(p *profile.Profile, s *source, obj plugin.ObjTool, ui plugin
317 317
 		// Use $HOME/pprof/binaries as default directory for local symbolization binaries
318 318
 		searchPath = filepath.Join(os.Getenv("HOME"), "pprof", "binaries")
319 319
 	}
320
-
321 320
 mapping:
322
-	for i, m := range p.Mapping {
321
+	for _, m := range p.Mapping {
323 322
 		var baseName string
324
-		// Replace executable filename/buildID with the overrides from source.
325
-		// Assumes the executable is the first Mapping entry.
326
-		if i == 0 {
327
-			if s.ExecName != "" {
328
-				m.File = s.ExecName
329
-			}
330
-			if s.BuildID != "" {
331
-				m.BuildID = s.BuildID
332
-			}
333
-		}
334 323
 		if m.File != "" {
335 324
 			baseName = filepath.Base(m.File)
336 325
 		}
@@ -360,6 +349,28 @@ mapping:
360 349
 			}
361 350
 		}
362 351
 	}
352
+	// Replace executable filename/buildID with the overrides from source.
353
+	// Assumes the executable is the first Mapping entry.
354
+	if execName, buildId := s.ExecName, s.BuildID; execName != "" || buildId != "" {
355
+		if len(p.Mapping) == 0 {
356
+			// If there are no mappings, add a fake mapping to attempt symbolization.
357
+			// This is useful for some profiles generated by the golang runtime, which
358
+			// do not include any mappings. Symbolization with a fake mapping will only
359
+			// be successful against a non-PIE binary.
360
+			m := &profile.Mapping{ID: 1}
361
+			p.Mapping = []*profile.Mapping{m}
362
+			for _, l := range p.Location {
363
+				l.Mapping = m
364
+			}
365
+		}
366
+		m := p.Mapping[0]
367
+		if execName != "" {
368
+			m.File = execName
369
+		}
370
+		if buildId != "" {
371
+			m.BuildID = buildId
372
+		}
373
+	}
363 374
 }
364 375
 
365 376
 // fetch fetches a profile from source, within the timeout specified,

+ 19
- 6
internal/driver/fetch_test.go Ver arquivo

@@ -161,16 +161,29 @@ func TestFetch(t *testing.T) {
161 161
 	// Intercept http.Get calls from HTTPFetcher.
162 162
 	httpGet = stubHTTPGet
163 163
 
164
-	for _, source := range [][2]string{
165
-		{path + "go.crc32.cpu", "go.crc32.cpu"},
166
-		{"http://localhost/profile?file=cppbench.cpu", "cppbench.cpu"},
164
+	type testcase struct {
165
+		source, execName string
166
+	}
167
+
168
+	for _, tc := range []testcase{
169
+		{path + "go.crc32.cpu", ""},
170
+		{path + "go.nomappings.crash", "/bin/gotest.exe"},
171
+		{"http://localhost/profile?file=cppbench.cpu", ""},
167 172
 	} {
168
-		p, _, err := fetch(source[0], 0, 10*time.Second, &proftest.TestUI{t, 0})
173
+		p, _, _, err := grabProfile(&source{ExecName: tc.execName}, tc.source, 0, nil, testObj{}, &proftest.TestUI{t, 0})
169 174
 		if err != nil {
170
-			t.Fatalf("%s: %s", source[0], err)
175
+			t.Fatalf("%s: %s", tc.source, err)
171 176
 		}
172 177
 		if len(p.Sample) == 0 {
173
-			t.Errorf("want non-zero samples")
178
+			t.Errorf("%s: want non-zero samples", tc.source)
179
+		}
180
+		if e := tc.execName; e != "" {
181
+			switch {
182
+			case len(p.Mapping) == 0 || p.Mapping[0] == nil:
183
+				t.Errorf("%s: want mapping[0].execName == %s, got no mappings", tc.source, e)
184
+			case p.Mapping[0].File != e:
185
+				t.Errorf("%s: want mapping[0].execName == %s, got %s", tc.source, e, p.Mapping[0].File)
186
+			}
174 187
 		}
175 188
 	}
176 189
 }

BIN
internal/driver/testdata/go.nomappings.crash Ver arquivo