Pārlūkot izejas kodu

Improve regexp matching of mappings for legacy formats

Simplify regexps and define a recommended format:
  Start   End     object file name     offset(optional)   linker build id
  0x40000-0x80000 /path/to/binary      (@FF00)            abc123456

Also include some additional tests and move existing tests to legacy_profile_test.go,
closer to the code in legacy_profile.go.
Raul Silvera 8 gadus atpakaļ
vecāks
revīzija
43ebb02455
3 mainītis faili ar 149 papildinājumiem un 103 dzēšanām
  1. 42
    37
      profile/legacy_profile.go
  2. 107
    0
      profile/legacy_profile_test.go
  3. 0
    66
      profile/profile_test.go

+ 42
- 37
profile/legacy_profile.go Parādīt failu

@@ -46,9 +46,23 @@ var (
46 46
 	threadzStartRE = regexp.MustCompile(`--- threadz \d+ ---`)
47 47
 	threadStartRE  = regexp.MustCompile(`--- Thread ([[:xdigit:]]+) \(name: (.*)/(\d+)\) stack: ---`)
48 48
 
49
-	procMapsRE = regexp.MustCompile(`([[:xdigit:]]+)-([[:xdigit:]]+)\s+([-rwxp]+)\s+([[:xdigit:]]+)\s+([[:xdigit:]]+):([[:xdigit:]]+)\s+([[:digit:]]+)\s*(\S+)?`)
50
-
51
-	briefMapsRE = regexp.MustCompile(`\s*([[:xdigit:]]+)-([[:xdigit:]]+):\s*(\S+)(\s.*@)?([[:xdigit:]]+)?`)
49
+	// Regular expressions to parse process mappings. Support the format used by Linux /proc/.../maps and other tools.
50
+	// Recommended format:
51
+	// Start   End     object file name     offset(optional)   linker build id
52
+	// 0x40000-0x80000 /path/to/binary      (@FF00)            abc123456
53
+	spaceDigits = `\s+[[:digit:]]+`
54
+	hexPair     = `\s+[[:xdigit:]]+:[[:xdigit:]]+`
55
+	oSpace      = `\s*`
56
+	// Capturing expressions.
57
+	cHex           = `(?:0x)?([[:xdigit:]]+)`
58
+	cHexRange      = `\s*` + cHex + `[\s-]?` + oSpace + cHex + `:?`
59
+	cSpaceString   = `(?:\s+(\S+))?`
60
+	cSpaceHex      = `\s+([[:xdigit:]]+)`
61
+	cSpaceAtOffset = `(?:\s+\(@([[:xdigit:]]+)\))?`
62
+	cPerm          = `(?:\s+([-rwxp]+))?`
63
+
64
+	procMapsRE  = regexp.MustCompile(`^` + cHexRange + cPerm + cSpaceHex + hexPair + spaceDigits + cSpaceString + `\s*$`)
65
+	briefMapsRE = regexp.MustCompile(`^` + cHexRange + cPerm + cSpaceString + cSpaceAtOffset + cSpaceString + `\s*$`)
52 66
 )
53 67
 
54 68
 func isSpaceOrComment(line string) bool {
@@ -1008,45 +1022,36 @@ func (p *Profile) parseMemoryMapFromScanner(s *bufio.Scanner) error {
1008 1022
 }
1009 1023
 
1010 1024
 func parseMappingEntry(l string) (*Mapping, error) {
1011
-	mapping := &Mapping{}
1012
-	var err error
1013
-	if me := procMapsRE.FindStringSubmatch(l); len(me) == 9 {
1014
-		if !strings.Contains(me[3], "x") {
1015
-			// Skip non-executable entries.
1016
-			return nil, nil
1017
-		}
1018
-		if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil {
1019
-			return nil, errUnrecognized
1020
-		}
1021
-		if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil {
1022
-			return nil, errUnrecognized
1023
-		}
1024
-		if me[4] != "" {
1025
-			if mapping.Offset, err = strconv.ParseUint(me[4], 16, 64); err != nil {
1026
-				return nil, errUnrecognized
1027
-			}
1028
-		}
1029
-		mapping.File = me[8]
1030
-		return mapping, nil
1025
+	var start, end, perm, file, offset, buildID string
1026
+	if me := procMapsRE.FindStringSubmatch(l); len(me) == 6 {
1027
+		start, end, perm, offset, file = me[1], me[2], me[3], me[4], me[5]
1028
+	} else if me := briefMapsRE.FindStringSubmatch(l); len(me) == 7 {
1029
+		start, end, perm, file, offset, buildID = me[1], me[2], me[3], me[4], me[5], me[6]
1030
+	} else {
1031
+		return nil, errUnrecognized
1031 1032
 	}
1032 1033
 
1033
-	if me := briefMapsRE.FindStringSubmatch(l); len(me) == 6 {
1034
-		if mapping.Start, err = strconv.ParseUint(me[1], 16, 64); err != nil {
1035
-			return nil, errUnrecognized
1036
-		}
1037
-		if mapping.Limit, err = strconv.ParseUint(me[2], 16, 64); err != nil {
1034
+	var err error
1035
+	mapping := &Mapping{
1036
+		File:    file,
1037
+		BuildID: buildID,
1038
+	}
1039
+	if perm != "" && !strings.Contains(perm, "x") {
1040
+		// Skip non-executable entries.
1041
+		return nil, nil
1042
+	}
1043
+	if mapping.Start, err = strconv.ParseUint(start, 16, 64); err != nil {
1044
+		return nil, errUnrecognized
1045
+	}
1046
+	if mapping.Limit, err = strconv.ParseUint(end, 16, 64); err != nil {
1047
+		return nil, errUnrecognized
1048
+	}
1049
+	if offset != "" {
1050
+		if mapping.Offset, err = strconv.ParseUint(offset, 16, 64); err != nil {
1038 1051
 			return nil, errUnrecognized
1039 1052
 		}
1040
-		mapping.File = me[3]
1041
-		if me[5] != "" {
1042
-			if mapping.Offset, err = strconv.ParseUint(me[5], 16, 64); err != nil {
1043
-				return nil, errUnrecognized
1044
-			}
1045
-		}
1046
-		return mapping, nil
1047 1053
 	}
1048
-
1049
-	return nil, errUnrecognized
1054
+	return mapping, nil
1050 1055
 }
1051 1056
 
1052 1057
 type sectionType int

+ 107
- 0
profile/legacy_profile_test.go Parādīt failu

@@ -118,3 +118,110 @@ func profileOfType(sampleTypes []string) *Profile {
118 118
 	}
119 119
 	return p
120 120
 }
121
+
122
+func TestParseMappingEntry(t *testing.T) {
123
+	for _, test := range []*struct {
124
+		entry string
125
+		want  *Mapping
126
+	}{
127
+		{
128
+			entry: "00400000-02e00000 r-xp 00000000 00:00 0",
129
+			want: &Mapping{
130
+				Start: 0x400000,
131
+				Limit: 0x2e00000,
132
+			},
133
+		},
134
+		{
135
+			entry: "02e00000-02e8a000 r-xp 02a00000 00:00 15953927    /foo/bin",
136
+			want: &Mapping{
137
+				Start:  0x2e00000,
138
+				Limit:  0x2e8a000,
139
+				Offset: 0x2a00000,
140
+				File:   "/foo/bin",
141
+			},
142
+		},
143
+		{
144
+			entry: "02e00000-02e8a000 r-xp 000000 00:00 15953927    [vdso]",
145
+			want: &Mapping{
146
+				Start: 0x2e00000,
147
+				Limit: 0x2e8a000,
148
+				File:  "[vdso]",
149
+			},
150
+		},
151
+		{
152
+			entry: "  02e00000-02e8a000: /foo/bin (@2a00000)",
153
+			want: &Mapping{
154
+				Start:  0x2e00000,
155
+				Limit:  0x2e8a000,
156
+				Offset: 0x2a00000,
157
+				File:   "/foo/bin",
158
+			},
159
+		},
160
+		{
161
+			entry: "  02e00000-02e8a000: /foo/bin",
162
+			want: &Mapping{
163
+				Start: 0x2e00000,
164
+				Limit: 0x2e8a000,
165
+				File:  "/foo/bin",
166
+			},
167
+		},
168
+		{
169
+			entry: "  02e00000-02e8a000: [vdso]",
170
+			want: &Mapping{
171
+				Start: 0x2e00000,
172
+				Limit: 0x2e8a000,
173
+				File:  "[vdso]",
174
+			},
175
+		},
176
+		{entry: "0xff6810563000 0xff6810565000 r-xp abc_exe 87c4d547f895cfd6a370e08dc5c5ee7bd4199d5b",
177
+			want: &Mapping{
178
+				Start:   0xff6810563000,
179
+				Limit:   0xff6810565000,
180
+				File:    "abc_exe",
181
+				BuildID: "87c4d547f895cfd6a370e08dc5c5ee7bd4199d5b",
182
+			},
183
+		},
184
+		{entry: "7f5e5435e000-7f5e5455e000 --xp 00002000 00:00 1531        myprogram",
185
+			want: &Mapping{
186
+				Start:  0x7f5e5435e000,
187
+				Limit:  0x7f5e5455e000,
188
+				Offset: 0x2000,
189
+				File:   "myprogram",
190
+			},
191
+		},
192
+		{entry: "7f7472710000-7f7472722000 r-xp 00000000 fc:00 790190      /usr/lib/libfantastic-1.2.so",
193
+			want: &Mapping{
194
+				Start: 0x7f7472710000,
195
+				Limit: 0x7f7472722000,
196
+				File:  "/usr/lib/libfantastic-1.2.so",
197
+			},
198
+		},
199
+		{entry: "7f47a542f000-7f47a5447000: /lib/libpthread-2.15.so",
200
+			want: &Mapping{
201
+				Start: 0x7f47a542f000,
202
+				Limit: 0x7f47a5447000,
203
+				File:  "/lib/libpthread-2.15.so",
204
+			},
205
+		},
206
+		{entry: "0x40000-0x80000 /path/to/binary      (@FF00)            abc123456",
207
+			want: &Mapping{
208
+				Start:   0x40000,
209
+				Limit:   0x80000,
210
+				File:    "/path/to/binary",
211
+				Offset:  0xFF00,
212
+				BuildID: "abc123456",
213
+			},
214
+		},
215
+		{entry: "7f5e5435e000-7f5e5455e000 ---p 00002000 00:00 1531        myprogram",
216
+			want: nil,
217
+		},
218
+	} {
219
+		got, err := parseMappingEntry(test.entry)
220
+		if err != nil {
221
+			t.Error(err)
222
+		}
223
+		if !reflect.DeepEqual(test.want, got) {
224
+			t.Errorf("%s want=%v got=%v", test.entry, test.want, got)
225
+		}
226
+	}
227
+}

+ 0
- 66
profile/profile_test.go Parādīt failu

@@ -19,7 +19,6 @@ import (
19 19
 	"fmt"
20 20
 	"io/ioutil"
21 21
 	"path/filepath"
22
-	"reflect"
23 22
 	"regexp"
24 23
 	"strings"
25 24
 	"testing"
@@ -361,71 +360,6 @@ func checkAggregation(prof *Profile, a *aggTest) error {
361 360
 	return nil
362 361
 }
363 362
 
364
-func TestParseMappingEntry(t *testing.T) {
365
-	for _, test := range []*struct {
366
-		entry string
367
-		want  *Mapping
368
-	}{
369
-		{
370
-			entry: "00400000-02e00000 r-xp 00000000 00:00 0",
371
-			want: &Mapping{
372
-				Start: 0x400000,
373
-				Limit: 0x2e00000,
374
-			},
375
-		},
376
-		{
377
-			entry: "02e00000-02e8a000 r-xp 02a00000 00:00 15953927    /foo/bin",
378
-			want: &Mapping{
379
-				Start:  0x2e00000,
380
-				Limit:  0x2e8a000,
381
-				Offset: 0x2a00000,
382
-				File:   "/foo/bin",
383
-			},
384
-		},
385
-		{
386
-			entry: "02e00000-02e8a000 r-xp 000000 00:00 15953927    [vdso]",
387
-			want: &Mapping{
388
-				Start: 0x2e00000,
389
-				Limit: 0x2e8a000,
390
-				File:  "[vdso]",
391
-			},
392
-		},
393
-		{
394
-			entry: "  02e00000-02e8a000: /foo/bin (@2a00000)",
395
-			want: &Mapping{
396
-				Start:  0x2e00000,
397
-				Limit:  0x2e8a000,
398
-				Offset: 0x2a00000,
399
-				File:   "/foo/bin",
400
-			},
401
-		},
402
-		{
403
-			entry: "  02e00000-02e8a000: /foo/bin",
404
-			want: &Mapping{
405
-				Start: 0x2e00000,
406
-				Limit: 0x2e8a000,
407
-				File:  "/foo/bin",
408
-			},
409
-		},
410
-		{
411
-			entry: "  02e00000-02e8a000: [vdso]",
412
-			want: &Mapping{
413
-				Start: 0x2e00000,
414
-				Limit: 0x2e8a000,
415
-				File:  "[vdso]",
416
-			},
417
-		},
418
-	} {
419
-		got, err := parseMappingEntry(test.entry)
420
-		if err != nil {
421
-			t.Error(err)
422
-		}
423
-		if !reflect.DeepEqual(test.want, got) {
424
-			t.Errorf("%s want=%v got=%v", test.entry, test.want, got)
425
-		}
426
-	}
427
-}
428
-
429 363
 // Test merge leaves the main binary in place.
430 364
 func TestMergeMain(t *testing.T) {
431 365
 	prof := testProfile.Copy()