mirror of
https://github.com/dutchcoders/transfer.sh.git
synced 2020-11-18 19:53:40 -08:00
301 lines
6.7 KiB
Go
301 lines
6.7 KiB
Go
// 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 symbolizer
|
|
|
|
import (
|
|
"fmt"
|
|
"regexp"
|
|
"sort"
|
|
"strings"
|
|
"testing"
|
|
|
|
"github.com/google/pprof/internal/plugin"
|
|
"github.com/google/pprof/internal/proftest"
|
|
"github.com/google/pprof/profile"
|
|
)
|
|
|
|
var testM = []*profile.Mapping{
|
|
{
|
|
ID: 1,
|
|
Start: 0x1000,
|
|
Limit: 0x5000,
|
|
File: "mapping",
|
|
},
|
|
}
|
|
|
|
var testL = []*profile.Location{
|
|
{
|
|
ID: 1,
|
|
Mapping: testM[0],
|
|
Address: 1000,
|
|
},
|
|
{
|
|
ID: 2,
|
|
Mapping: testM[0],
|
|
Address: 2000,
|
|
},
|
|
{
|
|
ID: 3,
|
|
Mapping: testM[0],
|
|
Address: 3000,
|
|
},
|
|
{
|
|
ID: 4,
|
|
Mapping: testM[0],
|
|
Address: 4000,
|
|
},
|
|
{
|
|
ID: 5,
|
|
Mapping: testM[0],
|
|
Address: 5000,
|
|
},
|
|
}
|
|
|
|
var testProfile = profile.Profile{
|
|
DurationNanos: 10e9,
|
|
SampleType: []*profile.ValueType{
|
|
{Type: "cpu", Unit: "cycles"},
|
|
},
|
|
Sample: []*profile.Sample{
|
|
{
|
|
Location: []*profile.Location{testL[0]},
|
|
Value: []int64{1},
|
|
},
|
|
{
|
|
Location: []*profile.Location{testL[1], testL[0]},
|
|
Value: []int64{10},
|
|
},
|
|
{
|
|
Location: []*profile.Location{testL[2], testL[0]},
|
|
Value: []int64{100},
|
|
},
|
|
{
|
|
Location: []*profile.Location{testL[3], testL[0]},
|
|
Value: []int64{1},
|
|
},
|
|
{
|
|
Location: []*profile.Location{testL[4], testL[3], testL[0]},
|
|
Value: []int64{10000},
|
|
},
|
|
},
|
|
Location: testL,
|
|
Mapping: testM,
|
|
PeriodType: &profile.ValueType{Type: "cpu", Unit: "milliseconds"},
|
|
Period: 10,
|
|
}
|
|
|
|
func TestSymbolization(t *testing.T) {
|
|
sSym := symbolzSymbolize
|
|
lSym := localSymbolize
|
|
defer func() {
|
|
symbolzSymbolize = sSym
|
|
localSymbolize = lSym
|
|
demangleFunction = Demangle
|
|
}()
|
|
symbolzSymbolize = symbolzMock
|
|
localSymbolize = localMock
|
|
demangleFunction = demangleMock
|
|
|
|
type testcase struct {
|
|
mode string
|
|
wantComment string
|
|
}
|
|
|
|
s := Symbolizer{
|
|
Obj: mockObjTool{},
|
|
UI: &proftest.TestUI{T: t},
|
|
}
|
|
for i, tc := range []testcase{
|
|
{
|
|
"local",
|
|
"local=[]",
|
|
},
|
|
{
|
|
"fastlocal",
|
|
"local=[fast]",
|
|
},
|
|
{
|
|
"remote",
|
|
"symbolz=[]",
|
|
},
|
|
{
|
|
"",
|
|
"local=[]:symbolz=[]",
|
|
},
|
|
{
|
|
"demangle=none",
|
|
"demangle=[none]:force:local=[force]:symbolz=[force]",
|
|
},
|
|
{
|
|
"remote:demangle=full",
|
|
"demangle=[full]:force:symbolz=[force]",
|
|
},
|
|
{
|
|
"local:demangle=templates",
|
|
"demangle=[templates]:force:local=[force]",
|
|
},
|
|
{
|
|
"force:remote",
|
|
"force:symbolz=[force]",
|
|
},
|
|
} {
|
|
prof := testProfile.Copy()
|
|
if err := s.Symbolize(tc.mode, nil, prof); err != nil {
|
|
t.Errorf("symbolize #%d: %v", i, err)
|
|
continue
|
|
}
|
|
sort.Strings(prof.Comments)
|
|
if got, want := strings.Join(prof.Comments, ":"), tc.wantComment; got != want {
|
|
t.Errorf("%q: got %s, want %s", tc.mode, got, want)
|
|
continue
|
|
}
|
|
}
|
|
}
|
|
|
|
func symbolzMock(p *profile.Profile, force bool, sources plugin.MappingSources, syms func(string, string) ([]byte, error), ui plugin.UI) error {
|
|
var args []string
|
|
if force {
|
|
args = append(args, "force")
|
|
}
|
|
p.Comments = append(p.Comments, "symbolz=["+strings.Join(args, ",")+"]")
|
|
return nil
|
|
}
|
|
|
|
func localMock(p *profile.Profile, fast, force bool, obj plugin.ObjTool, ui plugin.UI) error {
|
|
var args []string
|
|
if fast {
|
|
args = append(args, "fast")
|
|
}
|
|
if force {
|
|
args = append(args, "force")
|
|
}
|
|
p.Comments = append(p.Comments, "local=["+strings.Join(args, ",")+"]")
|
|
return nil
|
|
}
|
|
|
|
func demangleMock(p *profile.Profile, force bool, mode string) {
|
|
if force {
|
|
p.Comments = append(p.Comments, "force")
|
|
}
|
|
if mode != "" {
|
|
p.Comments = append(p.Comments, "demangle=["+mode+"]")
|
|
}
|
|
}
|
|
|
|
func TestLocalSymbolization(t *testing.T) {
|
|
prof := testProfile.Copy()
|
|
|
|
if prof.HasFunctions() {
|
|
t.Error("unexpected function names")
|
|
}
|
|
if prof.HasFileLines() {
|
|
t.Error("unexpected filenames or line numbers")
|
|
}
|
|
|
|
b := mockObjTool{}
|
|
if err := localSymbolize(prof, false, false, b, &proftest.TestUI{T: t}); err != nil {
|
|
t.Fatalf("localSymbolize(): %v", err)
|
|
}
|
|
|
|
for _, loc := range prof.Location {
|
|
if err := checkSymbolizedLocation(loc.Address, loc.Line); err != nil {
|
|
t.Errorf("location %d: %v", loc.Address, err)
|
|
}
|
|
}
|
|
if !prof.HasFunctions() {
|
|
t.Error("missing function names")
|
|
}
|
|
if !prof.HasFileLines() {
|
|
t.Error("missing filenames or line numbers")
|
|
}
|
|
}
|
|
|
|
func checkSymbolizedLocation(a uint64, got []profile.Line) error {
|
|
want, ok := mockAddresses[a]
|
|
if !ok {
|
|
return fmt.Errorf("unexpected address")
|
|
}
|
|
if len(want) != len(got) {
|
|
return fmt.Errorf("want len %d, got %d", len(want), len(got))
|
|
}
|
|
|
|
for i, w := range want {
|
|
g := got[i]
|
|
if g.Function.Name != w.Func {
|
|
return fmt.Errorf("want function: %q, got %q", w.Func, g.Function.Name)
|
|
}
|
|
if g.Function.Filename != w.File {
|
|
return fmt.Errorf("want filename: %q, got %q", w.File, g.Function.Filename)
|
|
}
|
|
if g.Line != int64(w.Line) {
|
|
return fmt.Errorf("want lineno: %d, got %d", w.Line, g.Line)
|
|
}
|
|
}
|
|
return nil
|
|
}
|
|
|
|
var mockAddresses = map[uint64][]plugin.Frame{
|
|
1000: {frame("fun11", "file11.src", 10)},
|
|
2000: {frame("fun21", "file21.src", 20), frame("fun22", "file22.src", 20)},
|
|
3000: {frame("fun31", "file31.src", 30), frame("fun32", "file32.src", 30), frame("fun33", "file33.src", 30)},
|
|
4000: {frame("fun41", "file41.src", 40), frame("fun42", "file42.src", 40), frame("fun43", "file43.src", 40), frame("fun44", "file44.src", 40)},
|
|
5000: {frame("fun51", "file51.src", 50), frame("fun52", "file52.src", 50), frame("fun53", "file53.src", 50), frame("fun54", "file54.src", 50), frame("fun55", "file55.src", 50)},
|
|
}
|
|
|
|
func frame(fname, file string, line int) plugin.Frame {
|
|
return plugin.Frame{
|
|
Func: fname,
|
|
File: file,
|
|
Line: line}
|
|
}
|
|
|
|
type mockObjTool struct{}
|
|
|
|
func (mockObjTool) Open(file string, start, limit, offset uint64) (plugin.ObjFile, error) {
|
|
return mockObjFile{frames: mockAddresses}, nil
|
|
}
|
|
|
|
func (mockObjTool) Disasm(file string, start, end uint64) ([]plugin.Inst, error) {
|
|
return nil, fmt.Errorf("disassembly not supported")
|
|
}
|
|
|
|
type mockObjFile struct {
|
|
frames map[uint64][]plugin.Frame
|
|
}
|
|
|
|
func (mockObjFile) Name() string {
|
|
return ""
|
|
}
|
|
|
|
func (mockObjFile) Base() uint64 {
|
|
return 0
|
|
}
|
|
|
|
func (mockObjFile) BuildID() string {
|
|
return ""
|
|
}
|
|
|
|
func (mf mockObjFile) SourceLine(addr uint64) ([]plugin.Frame, error) {
|
|
return mf.frames[addr], nil
|
|
}
|
|
|
|
func (mockObjFile) Symbols(r *regexp.Regexp, addr uint64) ([]*plugin.Sym, error) {
|
|
return []*plugin.Sym{}, nil
|
|
}
|
|
|
|
func (mockObjFile) Close() error {
|
|
return nil
|
|
}
|