service/dap: support disassemble request (#2728)
This adds support for disassembling a region of code using the instructionPointerReference value returned in the stack trace request.
This commit is contained in:
parent
6cf7a7149d
commit
ce5238944d
@ -114,6 +114,7 @@ func (c *Client) ExpectInitializeResponseAndCapabilities(t *testing.T) *dap.Init
|
||||
SupportsClipboardContext: true,
|
||||
SupportsSteppingGranularity: true,
|
||||
SupportsLogPoints: true,
|
||||
SupportsDisassembleRequest: true,
|
||||
}
|
||||
if !reflect.DeepEqual(initResp.Body, wantCapabilities) {
|
||||
t.Errorf("capabilities in initializeResponse: got %+v, want %v", pretty(initResp.Body), pretty(wantCapabilities))
|
||||
@ -518,8 +519,17 @@ func (c *Client) ReadMemoryRequest() {
|
||||
}
|
||||
|
||||
// DisassembleRequest sends a 'disassemble' request.
|
||||
func (c *Client) DisassembleRequest() {
|
||||
c.send(&dap.DisassembleRequest{Request: *c.newRequest("disassemble")})
|
||||
func (c *Client) DisassembleRequest(memoryReference string, instructionOffset, inctructionCount int) {
|
||||
c.send(&dap.DisassembleRequest{
|
||||
Request: *c.newRequest("disassemble"),
|
||||
Arguments: dap.DisassembleArguments{
|
||||
MemoryReference: memoryReference,
|
||||
Offset: 0,
|
||||
InstructionOffset: instructionOffset,
|
||||
InstructionCount: inctructionCount,
|
||||
ResolveSymbols: false,
|
||||
},
|
||||
})
|
||||
}
|
||||
|
||||
// CancelRequest sends a 'cancel' request.
|
||||
|
||||
@ -24,6 +24,7 @@ const (
|
||||
UnableToHalt = 2010
|
||||
UnableToGetExceptionInfo = 2011
|
||||
UnableToSetVariable = 2012
|
||||
UnableToDisassemble = 2013
|
||||
// Add more codes as we support more requests
|
||||
NoDebugIsRunning = 3000
|
||||
DebuggeeIsRunning = 4000
|
||||
|
||||
@ -26,6 +26,7 @@ import (
|
||||
"regexp"
|
||||
"runtime"
|
||||
"runtime/debug"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
@ -830,6 +831,7 @@ func (s *Session) onInitializeRequest(request *dap.InitializeRequest) {
|
||||
response.Body.SupportsClipboardContext = true
|
||||
response.Body.SupportsSteppingGranularity = true
|
||||
response.Body.SupportsLogPoints = true
|
||||
response.Body.SupportsDisassembleRequest = true
|
||||
// TODO(polina): support these requests in addition to vscode-go feature parity
|
||||
response.Body.SupportsTerminateRequest = false
|
||||
response.Body.SupportsRestartRequest = false
|
||||
@ -837,7 +839,6 @@ func (s *Session) onInitializeRequest(request *dap.InitializeRequest) {
|
||||
response.Body.SupportsSetExpression = false
|
||||
response.Body.SupportsLoadedSourcesRequest = false
|
||||
response.Body.SupportsReadMemoryRequest = false
|
||||
response.Body.SupportsDisassembleRequest = false
|
||||
response.Body.SupportsCancelRequest = false
|
||||
s.send(response)
|
||||
}
|
||||
@ -2833,10 +2834,206 @@ func (s *Session) onReadMemoryRequest(request *dap.ReadMemoryRequest) {
|
||||
s.sendNotYetImplementedErrorResponse(request.Request)
|
||||
}
|
||||
|
||||
// onDisassembleRequest sends a not-yet-implemented error response.
|
||||
// Capability 'supportsDisassembleRequest' is not set 'initialize' response.
|
||||
var invalidInstruction = dap.DisassembledInstruction{
|
||||
Address: "out of bounds",
|
||||
Instruction: "invalid instruction",
|
||||
}
|
||||
|
||||
// onDisassembleRequest handles 'disassemble' requests.
|
||||
// Capability 'supportsDisassembleRequest' is set in 'initialize' response.
|
||||
func (s *Session) onDisassembleRequest(request *dap.DisassembleRequest) {
|
||||
s.sendNotYetImplementedErrorResponse(request.Request)
|
||||
// If the requested memory address is an invalid location, return all invalid instructions.
|
||||
// TODO(suzmue): consider adding fake addresses that would allow us to receive out of bounds
|
||||
// requests that include valid instructions designated by InstructionOffset or InstructionCount.
|
||||
if request.Arguments.MemoryReference == invalidInstruction.Address {
|
||||
instructions := make([]dap.DisassembledInstruction, request.Arguments.InstructionCount)
|
||||
for i := range instructions {
|
||||
instructions[i] = invalidInstruction
|
||||
}
|
||||
response := &dap.DisassembleResponse{
|
||||
Response: *newResponse(request.Request),
|
||||
Body: dap.DisassembleResponseBody{
|
||||
Instructions: instructions,
|
||||
},
|
||||
}
|
||||
s.send(response)
|
||||
return
|
||||
}
|
||||
// TODO(suzmue): microsoft/vscode#129655 is discussing the difference between
|
||||
// memory reference and instructionPointerReference, which are currently
|
||||
// being used interchangeably by vscode.
|
||||
addr, err := strconv.ParseInt(request.Arguments.MemoryReference, 0, 64)
|
||||
if err != nil {
|
||||
s.sendErrorResponse(request.Request, UnableToDisassemble, "Unable to disassemble", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
start := uint64(addr)
|
||||
maxInstructionLength := s.debugger.Target().BinInfo().Arch.MaxInstructionLength()
|
||||
byteOffset := request.Arguments.InstructionOffset * maxInstructionLength
|
||||
// Adjust the offset to include instructions before the requested address.
|
||||
if byteOffset < 0 {
|
||||
start = uint64(addr + int64(byteOffset))
|
||||
}
|
||||
// Adjust the number of instructions to include enough instructions after
|
||||
// the requested address.
|
||||
count := request.Arguments.InstructionCount
|
||||
if byteOffset > 0 {
|
||||
count += byteOffset
|
||||
}
|
||||
end := uint64(addr + int64(count*maxInstructionLength))
|
||||
|
||||
// Make sure the PCs are lined up with instructions.
|
||||
start, end = alignPCs(s.debugger.Target().BinInfo(), start, end)
|
||||
|
||||
// Disassemble the instructions
|
||||
procInstructions, err := s.debugger.Disassemble(-1, start, end)
|
||||
if err != nil {
|
||||
s.sendErrorResponse(request.Request, UnableToDisassemble, "Unable to disassemble", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Find the section of instructions that were requested.
|
||||
procInstructions, offset, err := findInstructions(procInstructions, addr, request.Arguments.InstructionOffset, request.Arguments.InstructionCount)
|
||||
if err != nil {
|
||||
s.sendErrorResponse(request.Request, UnableToDisassemble, "Unable to disassemble", err.Error())
|
||||
return
|
||||
}
|
||||
|
||||
// Turn the given range of instructions into dap instructions.
|
||||
instructions := make([]dap.DisassembledInstruction, request.Arguments.InstructionCount)
|
||||
lastFile, lastLine := "", -1
|
||||
for i := range instructions {
|
||||
if i < offset || (i-offset) >= len(procInstructions) {
|
||||
// i is not in a valid range.
|
||||
instructions[i] = invalidInstruction
|
||||
continue
|
||||
}
|
||||
instruction := api.ConvertAsmInstruction(procInstructions[i-offset], s.debugger.AsmInstructionText(&procInstructions[i-offset], proc.GoFlavour))
|
||||
instructions[i] = dap.DisassembledInstruction{
|
||||
Address: fmt.Sprintf("%#x", instruction.Loc.PC),
|
||||
InstructionBytes: fmt.Sprintf("%x", instruction.Bytes),
|
||||
Instruction: instruction.Text,
|
||||
}
|
||||
// Only set the location on the first instruction for a given line.
|
||||
if instruction.Loc.File != lastFile || instruction.Loc.Line != lastLine {
|
||||
instructions[i].Location = dap.Source{Path: instruction.Loc.File}
|
||||
instructions[i].Line = instruction.Loc.Line
|
||||
lastFile, lastLine = instruction.Loc.File, instruction.Loc.Line
|
||||
}
|
||||
}
|
||||
|
||||
response := &dap.DisassembleResponse{
|
||||
Response: *newResponse(request.Request),
|
||||
Body: dap.DisassembleResponseBody{
|
||||
Instructions: instructions,
|
||||
},
|
||||
}
|
||||
s.send(response)
|
||||
}
|
||||
|
||||
func findInstructions(procInstructions []proc.AsmInstruction, addr int64, instructionOffset, count int) ([]proc.AsmInstruction, int, error) {
|
||||
ref := sort.Search(len(procInstructions), func(i int) bool {
|
||||
return procInstructions[i].Loc.PC >= uint64(addr)
|
||||
})
|
||||
if ref == len(procInstructions) || procInstructions[ref].Loc.PC != uint64(addr) {
|
||||
return nil, -1, fmt.Errorf("could not find memory reference")
|
||||
}
|
||||
// offset is the number of instructions that should appear before the first instruction
|
||||
// returned by findInstructions.
|
||||
offset := 0
|
||||
if ref+instructionOffset < 0 {
|
||||
offset = -(ref + instructionOffset)
|
||||
}
|
||||
// Figure out the index to slice at.
|
||||
startIdx := ref + instructionOffset
|
||||
endIdx := ref + instructionOffset + count
|
||||
if endIdx <= 0 || startIdx >= len(procInstructions) {
|
||||
return []proc.AsmInstruction{}, 0, nil
|
||||
}
|
||||
// Adjust start and end to be inbounds.
|
||||
if startIdx < 0 {
|
||||
offset = -startIdx
|
||||
startIdx = 0
|
||||
}
|
||||
if endIdx > len(procInstructions) {
|
||||
endIdx = len(procInstructions)
|
||||
}
|
||||
return procInstructions[startIdx:endIdx], offset, nil
|
||||
}
|
||||
|
||||
func alignPCs(bi *proc.BinaryInfo, start, end uint64) (uint64, uint64) {
|
||||
// We want to find the function locations position that would enclose
|
||||
// the range from start to end.
|
||||
//
|
||||
// Example:
|
||||
//
|
||||
// 0x0000 instruction (func1)
|
||||
// 0x0004 instruction (func1)
|
||||
// 0x0008 instruction (func1)
|
||||
// 0x000c nop
|
||||
// 0x000e nop
|
||||
// 0x0000 nop
|
||||
// 0x0002 nop
|
||||
// 0x0004 instruction (func2)
|
||||
// 0x0008 instruction (func2)
|
||||
// 0x000c instruction (func2)
|
||||
//
|
||||
// start values:
|
||||
// < 0x0000 at func1.Entry = 0x0000
|
||||
// 0x0000-0x000b at func1.Entry = 0x0000
|
||||
// 0x000c-0x0003 at func1.End = 0x000c
|
||||
// 0x0004-0x000f at func2.Entry = 0x0004
|
||||
// > 0x000f at func2.End = 0x0010
|
||||
//
|
||||
// end values:
|
||||
// < 0x0000 at func1.Entry = 0x0000
|
||||
// 0x0000-0x000b at func1.End = 0x0000
|
||||
// 0x000c-0x0003 at func2.Entry = 0x000c
|
||||
// 0x0004-0x000f at func2.End = 0x0004
|
||||
// > 0x000f at func2.End = 0x0004
|
||||
// Handle start values:
|
||||
fn := bi.PCToFunc(start)
|
||||
if fn != nil {
|
||||
// start is in a funcition.
|
||||
start = fn.Entry
|
||||
} else if b, pc := checkOutOfAddressSpace(start, bi); b {
|
||||
start = pc
|
||||
} else {
|
||||
// Otherwise it must come after some function.
|
||||
i := sort.Search(len(bi.Functions), func(i int) bool {
|
||||
fn := bi.Functions[len(bi.Functions)-(i+1)]
|
||||
return start >= fn.End
|
||||
})
|
||||
start = bi.Functions[len(bi.Functions)-(i+1)].Entry
|
||||
}
|
||||
|
||||
// Handle end values:
|
||||
if fn := bi.PCToFunc(end); fn != nil {
|
||||
// end is in a funcition.
|
||||
end = fn.End
|
||||
} else if b, pc := checkOutOfAddressSpace(end, bi); b {
|
||||
end = pc
|
||||
} else {
|
||||
// Otherwise it must come before some function.
|
||||
i := sort.Search(len(bi.Functions), func(i int) bool {
|
||||
fn := bi.Functions[i]
|
||||
return end < fn.Entry
|
||||
})
|
||||
end = bi.Functions[i].Entry
|
||||
}
|
||||
|
||||
return start, end
|
||||
}
|
||||
|
||||
func checkOutOfAddressSpace(pc uint64, bi *proc.BinaryInfo) (bool, uint64) {
|
||||
if pc < bi.Functions[0].Entry {
|
||||
return true, bi.Functions[0].Entry
|
||||
}
|
||||
if pc >= bi.Functions[len(bi.Functions)-1].End {
|
||||
return true, bi.Functions[len(bi.Functions)-1].End
|
||||
}
|
||||
return false, pc
|
||||
}
|
||||
|
||||
// onCancelRequest sends a not-yet-implemented error response.
|
||||
|
||||
@ -11,6 +11,7 @@ import (
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strconv"
|
||||
@ -20,6 +21,7 @@ import (
|
||||
|
||||
"github.com/go-delve/delve/pkg/goversion"
|
||||
"github.com/go-delve/delve/pkg/logflags"
|
||||
"github.com/go-delve/delve/pkg/proc"
|
||||
protest "github.com/go-delve/delve/pkg/proc/test"
|
||||
"github.com/go-delve/delve/service"
|
||||
"github.com/go-delve/delve/service/api"
|
||||
@ -5436,9 +5438,6 @@ func TestOptionalNotYetImplementedResponses(t *testing.T) {
|
||||
client.ReadMemoryRequest()
|
||||
expectNotYetImplemented("readMemory")
|
||||
|
||||
client.DisassembleRequest()
|
||||
expectNotYetImplemented("disassemble")
|
||||
|
||||
client.CancelRequest()
|
||||
expectNotYetImplemented("cancel")
|
||||
|
||||
@ -6002,3 +6001,318 @@ func TestBadlyFormattedMessageToServer(t *testing.T) {
|
||||
client.ExpectDisconnectResponse(t)
|
||||
})
|
||||
}
|
||||
|
||||
func TestDisassemble(t *testing.T) {
|
||||
runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
|
||||
runDebugSessionWithBPs(t, client, "launch",
|
||||
// Launch
|
||||
func() {
|
||||
client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
|
||||
},
|
||||
// Set breakpoints
|
||||
fixture.Source, []int{17},
|
||||
[]onBreakpoint{{
|
||||
// Stop at line 17
|
||||
execute: func() {
|
||||
checkStop(t, client, 1, "main.main", 17)
|
||||
|
||||
client.StackTraceRequest(1, 0, 1)
|
||||
st := client.ExpectStackTraceResponse(t)
|
||||
if len(st.Body.StackFrames) < 1 {
|
||||
t.Fatalf("\ngot %#v\nwant len(stackframes) => 1", st)
|
||||
}
|
||||
// Request the single instruction that the program is stopped at.
|
||||
pc := st.Body.StackFrames[0].InstructionPointerReference
|
||||
client.DisassembleRequest(pc, 0, 1)
|
||||
dr := client.ExpectDisassembleResponse(t)
|
||||
if len(dr.Body.Instructions) != 1 {
|
||||
t.Errorf("\ngot %#v\nwant len(instructions) = 1", dr)
|
||||
} else if dr.Body.Instructions[0].Address != pc {
|
||||
t.Errorf("\ngot %#v\nwant instructions[0].Address = %s", dr, pc)
|
||||
}
|
||||
|
||||
// Request the instruction that the program is stopped at, and the two
|
||||
// surrounding it.
|
||||
client.DisassembleRequest(pc, -1, 3)
|
||||
dr = client.ExpectDisassembleResponse(t)
|
||||
if len(dr.Body.Instructions) != 3 {
|
||||
t.Errorf("\ngot %#v\nwant len(instructions) = 3", dr)
|
||||
} else if dr.Body.Instructions[1].Address != pc {
|
||||
t.Errorf("\ngot %#v\nwant instructions[1].Address = %s", dr, pc)
|
||||
}
|
||||
|
||||
// Request zero instrutions.
|
||||
client.DisassembleRequest(pc, 0, 0)
|
||||
dr = client.ExpectDisassembleResponse(t)
|
||||
if len(dr.Body.Instructions) != 0 {
|
||||
t.Errorf("\ngot %#v\nwant len(instructions) = 0", dr)
|
||||
}
|
||||
|
||||
// Request invalid instructions.
|
||||
client.DisassembleRequest(invalidInstruction.Address, 0, 10)
|
||||
dr = client.ExpectDisassembleResponse(t)
|
||||
if len(dr.Body.Instructions) != 10 {
|
||||
t.Errorf("\ngot %#v\nwant len(instructions) = 10", dr)
|
||||
}
|
||||
for i, got := range dr.Body.Instructions {
|
||||
if !reflect.DeepEqual(got, invalidInstruction) {
|
||||
t.Errorf("\ngot [%d]=%#v\nwant = %#v", i, got, invalidInstruction)
|
||||
}
|
||||
}
|
||||
// Bad request, not a number.
|
||||
client.DisassembleRequest("hello, world!", 0, 1)
|
||||
client.ExpectErrorResponse(t)
|
||||
|
||||
// Bad request, not an address in program.
|
||||
client.DisassembleRequest("0x5", 0, 100)
|
||||
client.ExpectErrorResponse(t)
|
||||
},
|
||||
disconnect: true,
|
||||
}},
|
||||
)
|
||||
})
|
||||
}
|
||||
|
||||
func TestAlignPCs(t *testing.T) {
|
||||
NUM_FUNCS := 10
|
||||
// Create fake functions to test align PCs.
|
||||
funcs := make([]proc.Function, NUM_FUNCS)
|
||||
for i := 0; i < len(funcs); i++ {
|
||||
funcs[i] = proc.Function{
|
||||
Entry: uint64(100 + i*10),
|
||||
End: uint64(100 + i*10 + 5),
|
||||
}
|
||||
}
|
||||
bi := &proc.BinaryInfo{
|
||||
Functions: funcs,
|
||||
}
|
||||
type args struct {
|
||||
start uint64
|
||||
end uint64
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantStart uint64
|
||||
wantEnd uint64
|
||||
}{
|
||||
{
|
||||
name: "out of bounds",
|
||||
args: args{
|
||||
start: funcs[0].Entry - 5,
|
||||
end: funcs[NUM_FUNCS-1].End + 5,
|
||||
},
|
||||
wantStart: funcs[0].Entry, // start of first function
|
||||
wantEnd: funcs[NUM_FUNCS-1].End, // end of last function
|
||||
},
|
||||
{
|
||||
name: "same function",
|
||||
args: args{
|
||||
start: funcs[1].Entry + 1,
|
||||
end: funcs[1].Entry + 2,
|
||||
},
|
||||
wantStart: funcs[1].Entry, // start of containing function
|
||||
wantEnd: funcs[1].End, // end of containing function
|
||||
},
|
||||
{
|
||||
name: "between functions",
|
||||
args: args{
|
||||
start: funcs[1].End + 1,
|
||||
end: funcs[1].End + 2,
|
||||
},
|
||||
wantStart: funcs[1].Entry, // start of function before
|
||||
wantEnd: funcs[2].Entry, // start of function after
|
||||
},
|
||||
{
|
||||
name: "start of function",
|
||||
args: args{
|
||||
start: funcs[2].Entry,
|
||||
end: funcs[5].Entry,
|
||||
},
|
||||
wantStart: funcs[2].Entry, // start of current function
|
||||
wantEnd: funcs[5].End, // end of current function
|
||||
},
|
||||
{
|
||||
name: "end of function",
|
||||
args: args{
|
||||
start: funcs[4].End,
|
||||
end: funcs[8].End,
|
||||
},
|
||||
wantStart: funcs[4].Entry, // start of current function
|
||||
wantEnd: funcs[9].Entry, // start of next function
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotStart, gotEnd := alignPCs(bi, tt.args.start, tt.args.end)
|
||||
if gotStart != tt.wantStart {
|
||||
t.Errorf("alignPCs() got start = %v, want %v", gotStart, tt.wantStart)
|
||||
}
|
||||
if gotEnd != tt.wantEnd {
|
||||
t.Errorf("alignPCs() got end = %v, want %v", gotEnd, tt.wantEnd)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func TestFindInstructions(t *testing.T) {
|
||||
numInstructions := 100
|
||||
startPC := 0x1000
|
||||
procInstructions := make([]proc.AsmInstruction, numInstructions)
|
||||
for i := 0; i < len(procInstructions); i++ {
|
||||
procInstructions[i] = proc.AsmInstruction{
|
||||
Loc: proc.Location{
|
||||
PC: uint64(startPC + 2*i),
|
||||
},
|
||||
}
|
||||
}
|
||||
type args struct {
|
||||
addr int64
|
||||
offset int
|
||||
count int
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args args
|
||||
wantInstructions []proc.AsmInstruction
|
||||
wantOffset int
|
||||
wantErr bool
|
||||
}{
|
||||
{
|
||||
name: "request all",
|
||||
args: args{
|
||||
addr: int64(startPC),
|
||||
offset: 0,
|
||||
count: 100,
|
||||
},
|
||||
wantInstructions: procInstructions,
|
||||
wantOffset: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "request all (with offset)",
|
||||
args: args{
|
||||
addr: int64(startPC + numInstructions), // the instruction addr at numInstructions/2
|
||||
offset: -numInstructions / 2,
|
||||
count: numInstructions,
|
||||
},
|
||||
wantInstructions: procInstructions,
|
||||
wantOffset: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "request half (with offset)",
|
||||
args: args{
|
||||
addr: int64(startPC),
|
||||
offset: 0,
|
||||
count: numInstructions / 2,
|
||||
},
|
||||
wantInstructions: procInstructions[:numInstructions/2],
|
||||
wantOffset: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "request half (with offset)",
|
||||
args: args{
|
||||
addr: int64(startPC),
|
||||
offset: numInstructions / 2,
|
||||
count: numInstructions / 2,
|
||||
},
|
||||
wantInstructions: procInstructions[numInstructions/2:],
|
||||
wantOffset: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "request too many",
|
||||
args: args{
|
||||
addr: int64(startPC),
|
||||
offset: 0,
|
||||
count: numInstructions * 2,
|
||||
},
|
||||
wantInstructions: procInstructions,
|
||||
wantOffset: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "request too many with offset",
|
||||
args: args{
|
||||
addr: int64(startPC),
|
||||
offset: -numInstructions,
|
||||
count: numInstructions * 2,
|
||||
},
|
||||
wantInstructions: procInstructions,
|
||||
wantOffset: numInstructions,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "request out of bounds",
|
||||
args: args{
|
||||
addr: int64(startPC),
|
||||
offset: -numInstructions,
|
||||
count: numInstructions,
|
||||
},
|
||||
wantInstructions: []proc.AsmInstruction{},
|
||||
wantOffset: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "request out of bounds",
|
||||
args: args{
|
||||
addr: int64(uint64(startPC + 2*(numInstructions-1))),
|
||||
offset: 1,
|
||||
count: numInstructions,
|
||||
},
|
||||
wantInstructions: []proc.AsmInstruction{},
|
||||
wantOffset: 0,
|
||||
wantErr: false,
|
||||
},
|
||||
{
|
||||
name: "addr out of bounds (low)",
|
||||
args: args{
|
||||
addr: 0,
|
||||
offset: 0,
|
||||
count: 100,
|
||||
},
|
||||
wantInstructions: nil,
|
||||
wantOffset: -1,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "addr out of bounds (high)",
|
||||
args: args{
|
||||
addr: int64(startPC + 2*(numInstructions+1)),
|
||||
offset: -10,
|
||||
count: 20,
|
||||
},
|
||||
wantInstructions: nil,
|
||||
wantOffset: -1,
|
||||
wantErr: true,
|
||||
},
|
||||
{
|
||||
name: "addr not aligned",
|
||||
args: args{
|
||||
addr: int64(startPC + 1),
|
||||
offset: 0,
|
||||
count: 20,
|
||||
},
|
||||
wantInstructions: nil,
|
||||
wantOffset: -1,
|
||||
wantErr: true,
|
||||
},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
gotInstructions, gotOffset, err := findInstructions(procInstructions, tt.args.addr, tt.args.offset, tt.args.count)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("findInstructions() error = %v, wantErr %v", err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(gotInstructions, tt.wantInstructions) {
|
||||
t.Errorf("findInstructions() got instructions = %v, want %v", gotInstructions, tt.wantInstructions)
|
||||
}
|
||||
if gotOffset != tt.wantOffset {
|
||||
t.Errorf("findInstructions() got offset = %v, want %v", gotOffset, tt.wantOffset)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
Reference in New Issue
Block a user