// Copyright 2014 Google Inc. All Rights Reserved. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package binutils import ( "fmt" "testing" "github.com/google/pprof/internal/plugin" ) var testAddrMap = map[int]string{ 1000: "_Z3fooid.clone2", 2000: "_ZNSaIiEC1Ev.clone18", 3000: "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", } func functionName(level int) (name string) { if name = testAddrMap[level]; name != "" { return name } return fmt.Sprintf("fun%d", level) } func TestAddr2Liner(t *testing.T) { const offset = 0x500 a := addr2Liner{&mockAddr2liner{}, offset, nil} for i := 1; i < 8; i++ { addr := i*0x1000 + offset s, err := a.addrInfo(uint64(addr)) if err != nil { t.Fatalf("addrInfo(%#x): %v", addr, err) } if len(s) != i { t.Fatalf("addrInfo(%#x): got len==%d, want %d", addr, len(s), i) } for l, f := range s { level := (len(s) - l) * 1000 want := plugin.Frame{Func: functionName(level), File: fmt.Sprintf("file%d", level), Line: level} if f != want { t.Errorf("AddrInfo(%#x)[%d]: = %+v, want %+v", addr, l, f, want) } } } s, err := a.addrInfo(0xFFFF) if err != nil { t.Fatalf("addrInfo(0xFFFF): %v", err) } if len(s) != 0 { t.Fatalf("AddrInfo(0xFFFF): got len==%d, want 0", len(s)) } a.rw.close() } type mockAddr2liner struct { output []string } func (a *mockAddr2liner) write(s string) error { var lines []string switch s { case "1000": lines = []string{"_Z3fooid.clone2", "file1000:1000"} case "2000": lines = []string{"_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"} case "3000": lines = []string{"_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"} case "4000": lines = []string{"fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"} case "5000": lines = []string{"fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"} case "6000": lines = []string{"fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"} case "7000": lines = []string{"fun7000", "file7000:7000", "fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"} case "8000": lines = []string{"fun8000", "file8000:8000", "fun7000", "file7000:7000", "fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"} case "9000": lines = []string{"fun9000", "file9000:9000", "fun8000", "file8000:8000", "fun7000", "file7000:7000", "fun6000", "file6000:6000", "fun5000", "file5000:5000", "fun4000", "file4000:4000", "_ZNSt6vectorIS_IS_IiSaIiEESaIS1_EESaIS3_EEixEm", "file3000:3000", "_ZNSaIiEC1Ev.clone18", "file2000:2000", "_Z3fooid.clone2", "file1000:1000"} default: lines = []string{"??", "??:0"} } a.output = append(a.output, "0x"+s) a.output = append(a.output, lines...) return nil } func (a *mockAddr2liner) readLine() (string, error) { if len(a.output) == 0 { return "", fmt.Errorf("end of file") } next := a.output[0] a.output = a.output[1:] return next, nil } func (a *mockAddr2liner) close() { } func TestAddr2LinerLookup(t *testing.T) { oddSizedMap := addr2LinerNM{ m: []symbolInfo{ {0x1000, "0x1000"}, {0x2000, "0x2000"}, {0x3000, "0x3000"}, }, } evenSizedMap := addr2LinerNM{ m: []symbolInfo{ {0x1000, "0x1000"}, {0x2000, "0x2000"}, {0x3000, "0x3000"}, {0x4000, "0x4000"}, }, } for _, a := range []*addr2LinerNM{ &oddSizedMap, &evenSizedMap, } { for address, want := range map[uint64]string{ 0x1000: "0x1000", 0x1001: "0x1000", 0x1FFF: "0x1000", 0x2000: "0x2000", 0x2001: "0x2000", } { if got, _ := a.addrInfo(address); !checkAddress(got, address, want) { t.Errorf("%x: got %v, want %s", address, got, want) } } } } func checkAddress(got []plugin.Frame, address uint64, want string) bool { if len(got) != 1 { return false } return got[0].Func == want }