diff --git a/internal/weight/nm/measure.go b/internal/weight/nm/measure.go index d8baba6..80315e4 100644 --- a/internal/weight/nm/measure.go +++ b/internal/weight/nm/measure.go @@ -6,6 +6,7 @@ import ( "os/exec" "strconv" "strings" + "unicode" "unicode/utf8" ) @@ -65,27 +66,57 @@ func parseLine(s string) (*Sym, error) { var err error sym := &Sym{} - wp := strings.IndexByte(s, ' ') - if wp < 0 { + tokens := strings.Fields(s) + if len(tokens) <= 2 { return nil, fmt.Errorf("invalid sym text: %q", s) } - tokens := strings.Fields(s[wp:]) - if len(tokens) < 2 { - return nil, fmt.Errorf("invalid sym text: %q", s) + addrField := "" + sizeField := "" + typeField := "" + nameField := "" + infoField := "" + + isSymType := func(s string) bool { + return len(s) == 1 && unicode.IsLetter(rune(s[0])) } - if addr := strings.TrimSpace(s[:wp]); addr != "" { - sym.Addr, err = strconv.ParseUint(addr, 16, 64) + switch { + case isSymType(tokens[1]): + // in some cases addr is not printed + sizeField = tokens[0] + typeField = tokens[1] + if len(tokens) > 2 { + nameField = tokens[2] + } + if len(tokens) > 3 { + infoField = strings.Join(tokens[3:], " ") + } + case isSymType(tokens[2]): + addrField = tokens[0] + sizeField = tokens[1] + typeField = tokens[2] + if len(tokens) > 3 { + nameField = tokens[3] + } + if len(tokens) > 4 { + infoField = strings.Join(tokens[4:], " ") + } + default: + return nil, fmt.Errorf("unable to find type in sym: %q", s) + } + + if addrField != "" { + sym.Addr, err = strconv.ParseUint(addrField, 16, 64) if err != nil { - return nil, fmt.Errorf("invalid addr: %q", addr) + return nil, fmt.Errorf("invalid addr: %q", addrField) } } - if size := strings.TrimSpace(tokens[0]); size != "" { - sym.Size, err = strconv.ParseInt(size, 10, 64) + if sizeField != "" { + sym.Size, err = strconv.ParseInt(sizeField, 10, 64) if err != nil { - return nil, fmt.Errorf("invalid size %q: %q", s, size) + return nil, fmt.Errorf("invalid size %q: %q", s, sizeField) } // ignore external sym size @@ -94,16 +125,13 @@ func parseLine(s string) (*Sym, error) { } } - if code := strings.TrimSpace(tokens[1]); code != "" { + if code := strings.TrimSpace(typeField); code != "" { sym.Code, _ = utf8.DecodeRuneInString(code) } - if len(tokens) >= 3 { - sym.QualifiedName = tokens[2] - } - if len(tokens) >= 4 { - sym.Info = strings.Join(tokens[3:], " ") - } + sym.QualifiedName = nameField + sym.Info = infoField + if sym.QualifiedName == "" { return sym, nil } diff --git a/internal/weight/nm/measure_test.go b/internal/weight/nm/measure_test.go new file mode 100644 index 0000000..76eab86 --- /dev/null +++ b/internal/weight/nm/measure_test.go @@ -0,0 +1,29 @@ +package nm + +import ( + "strings" + "testing" +) + +var validLines = []string{ + ` 16781312 U _mmap`, + ` 16781312 U _munmap`, + ` 16781312 U _notify_is_valid_token`, + ` 16781312 U _open`, + ` 10d8fe0 8 R $f64.4024000000000000`, + ` 10d8fe8 8 R $f64.403a000000000000`, + `115d4a0 256 D time.utcLoc`, + `1091ca0 192 T type:.eq.[2]interface {}`, + `1060160 160 T type:.eq.[2]runtime.Frame`, + `1001fa0 256 T type:.eq.[6]internal/cpu.option`, +} + +func TestParseLine(t *testing.T) { + for _, line := range validLines { + _, err := parseLine(line) + t.Log(strings.Fields(line)) + if err != nil { + t.Errorf("%q parsing failed: %v", line, err) + } + } +}