Browse Source

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 years ago
parent
commit
e84ac77df0

+ 23
- 12
internal/driver/fetch.go View File

317
 		// Use $HOME/pprof/binaries as default directory for local symbolization binaries
317
 		// Use $HOME/pprof/binaries as default directory for local symbolization binaries
318
 		searchPath = filepath.Join(os.Getenv("HOME"), "pprof", "binaries")
318
 		searchPath = filepath.Join(os.Getenv("HOME"), "pprof", "binaries")
319
 	}
319
 	}
320
-
321
 mapping:
320
 mapping:
322
-	for i, m := range p.Mapping {
321
+	for _, m := range p.Mapping {
323
 		var baseName string
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
 		if m.File != "" {
323
 		if m.File != "" {
335
 			baseName = filepath.Base(m.File)
324
 			baseName = filepath.Base(m.File)
336
 		}
325
 		}
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
 // fetch fetches a profile from source, within the timeout specified,
376
 // fetch fetches a profile from source, within the timeout specified,

+ 19
- 6
internal/driver/fetch_test.go View File

161
 	// Intercept http.Get calls from HTTPFetcher.
161
 	// Intercept http.Get calls from HTTPFetcher.
162
 	httpGet = stubHTTPGet
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
 		if err != nil {
174
 		if err != nil {
170
-			t.Fatalf("%s: %s", source[0], err)
175
+			t.Fatalf("%s: %s", tc.source, err)
171
 		}
176
 		}
172
 		if len(p.Sample) == 0 {
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 View File