Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions _fixtures/readmem_json.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package main

import (
"crypto/sha256"
"encoding/hex"
"encoding/json"
"fmt"
"runtime"
"strings"
"unsafe"
)

// Long json for MemoryReference
func main() {
runtime.Breakpoint()

payload := strings.Repeat("AB", 2500)

b, _ := json.Marshal(struct {
Data string `json:"data"`
}{Data: payload})

bytesString := []byte("this\nis\nit")
nonprint := []byte{242, 243, 244, 245}
maps := map[string]string{"some": "non"}

jsonString := string(b)

hashed := sha256.Sum256(b)
jsonHash := hex.EncodeToString(hashed[:]) // used to validate fullness of a string

ptr := unsafe.StringData(jsonString)
jsonAddr := fmt.Sprintf("%p", ptr) // used to validate string address

runtime.Breakpoint()

fmt.Println(jsonString)
fmt.Println(jsonHash)
fmt.Println(jsonAddr)
fmt.Println(bytesString)
fmt.Println(nonprint)
fmt.Println(maps)
}
11 changes: 9 additions & 2 deletions service/dap/daptest/client.go
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,7 @@ func (c *Client) InitializeRequest() {
SupportsVariableType: true,
SupportsVariablePaging: true,
SupportsRunInTerminalRequest: true,
SupportsMemoryReferences: true,
Locale: "en-us",
}
c.send(request)
Expand Down Expand Up @@ -550,8 +551,14 @@ func (c *Client) SetDataBreakpointsRequest() {
}

// ReadMemoryRequest sends a 'readMemory' request.
func (c *Client) ReadMemoryRequest() {
c.send(&dap.ReadMemoryRequest{Request: *c.newRequest("readMemory")})
func (c *Client) ReadMemoryRequest(ref string, offset, count int) {
c.send(&dap.ReadMemoryRequest{
Request: *c.newRequest("readMemory"),
Arguments: dap.ReadMemoryArguments{
MemoryReference: ref,
Offset: offset,
Count: count,
}})
}

// DisassembleRequest sends a 'disassemble' request.
Expand Down
1 change: 1 addition & 0 deletions service/dap/error_ids.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const (
UnableToDisassemble = 2013
UnableToListRegisters = 2014
UnableToRunDlvCommand = 2015
UnableToReadMemory = 2016

// Add more codes as we support more requests

Expand Down
162 changes: 157 additions & 5 deletions service/dap/server.go
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ package dap
import (
"bufio"
"bytes"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
Expand Down Expand Up @@ -104,6 +105,71 @@ type Server struct {
sessionMu sync.Mutex
}

// memRef describe address and its size to stream data from
type memRef struct {
addr uint64
size int64
}

type referencesCollection struct {
mu sync.Mutex
refs map[string]memRef
}

func (r *referencesCollection) get(reference string) (memRef, bool) {
r.mu.Lock()
defer r.mu.Unlock()

ref, ok := r.refs[reference]

return ref, ok
}

func isAddressable(v *proc.Variable) bool {
if v == nil || v.Unreadable != nil {
return false
}

switch v.Kind {
case reflect.Slice, reflect.String:
return true
}

return false
}

func (r *referencesCollection) put(v *proc.Variable) string {
if !isAddressable(v) {
return ""
}

r.mu.Lock()
defer r.mu.Unlock()

if r.refs == nil {
r.refs = make(map[string]memRef)
}

addr := v.Addr
if v.Base != 0 {
addr = v.Base
}

ref := fmt.Sprintf("0x%x", addr)
r.refs[ref] = memRef{addr: addr, size: v.Len}

return ref
}

func (r *referencesCollection) reset() {
r.mu.Lock()
defer r.mu.Unlock()

if len(r.refs) > 0 {
r.refs = make(map[string]memRef)
}
}

// Session is an abstraction for serving and shutting down
// a DAP debug session with a pre-connected client.
// TODO(polina): move this to a different file/package
Expand All @@ -119,6 +185,8 @@ type Session struct {
// Reset at every stop.
// See also comment for convertVariable.
variableHandles *handlesMap[*fullyQualifiedVariable]
// referencesCollection track references map for DAP client
referencesCollection referencesCollection
// args tracks special settings for handling debug session requests.
args launchAttachArgs
// exceptionErr tracks the runtime error that last occurred.
Expand Down Expand Up @@ -774,7 +842,7 @@ func (s *Session) handleRequest(request dap.Message) {
case *dap.LoadedSourcesRequest: // Optional (capability 'supportsLoadedSourcesRequest')
/*TODO*/ s.onLoadedSourcesRequest(request) // Not yet implemented
case *dap.ReadMemoryRequest: // Optional (capability 'supportsReadMemoryRequest')
/*TODO*/ s.onReadMemoryRequest(request) // Not yet implemented
s.onReadMemoryRequest(request)
case *dap.CancelRequest: // Optional (capability 'supportsCancelRequest')
/*TODO*/ s.onCancelRequest(request) // Not yet implemented (does this make sense?)
case *dap.ModulesRequest: // Optional (capability 'supportsModulesRequest')
Expand Down Expand Up @@ -871,7 +939,7 @@ func (s *Session) onInitializeRequest(request *dap.InitializeRequest) {
response.Body.SupportsRestartRequest = true
response.Body.SupportsSetExpression = false
response.Body.SupportsLoadedSourcesRequest = false
response.Body.SupportsReadMemoryRequest = false
response.Body.SupportsReadMemoryRequest = true
response.Body.SupportsCancelRequest = false
response.Body.ExceptionBreakpointFilters = []dap.ExceptionBreakpointsFilter{
{Filter: proc.UnrecoveredPanic, Label: "Unrecovered Panics", Default: true},
Expand Down Expand Up @@ -2656,6 +2724,7 @@ func (s *Session) childrenToDAPVariables(v *fullyQualifiedVariable) []dap.Variab
VariablesReference: cvarref,
IndexedVariables: getIndexedVariableCount(c),
NamedVariables: getNamedVariableCount(c),
MemoryReference: s.referencesCollection.put(c),
}
}
}
Expand Down Expand Up @@ -3355,10 +3424,92 @@ func (s *Session) onLoadedSourcesRequest(request *dap.LoadedSourcesRequest) {
s.sendNotYetImplementedErrorResponse(request.Request)
}

// onReadMemoryRequest sends a not-yet-implemented error response.
// Capability 'supportsReadMemoryRequest' is not set 'initialize' response.
// onReadMemoryRequest handles DAP read memory requests
func (s *Session) onReadMemoryRequest(request *dap.ReadMemoryRequest) {
s.sendNotYetImplementedErrorResponse(request.Request)
args := request.Arguments

if args.Count < 0 {
s.sendErrorResponse(request.Request, UnableToReadMemory, "Unable to read memory", "negative count")
return
}

ref, ok := s.referencesCollection.get(args.MemoryReference)
if !ok {
s.sendErrorResponse(request.Request, UnableToReadMemory, "Unable to read memory", "unknown memoryReference")
return
}

if args.Count == 0 {
s.send(makeReadMemoryResponse(request.Request, ref.addr, nil, 0))
return
}

endReq := int64(args.Offset + args.Count)

startRead := min(max(int64(args.Offset), 0), ref.size)
endRead := min(max(endReq, 0), ref.size)

memAddr := ref.addr + uint64(startRead)

readCount := endRead - startRead
if readCount <= 0 {
unreadable := max(args.Count, 0)

s.send(makeReadMemoryResponse(request.Request, memAddr, nil, unreadable))
return
}

unreadable := max(int64(args.Count)-readCount, 0)

data, n, err := s.readTargetMemory(memAddr, readCount)
if err != nil {
s.sendErrorResponse(request.Request, UnableToReadMemory, "Unable to read memory", err.Error())
return
}

if int64(n) < readCount {
unreadable += readCount - int64(n)
}

s.send(makeReadMemoryResponse(request.Request, memAddr, data, int(unreadable)))
}

func (s *Session) readTargetMemory(addr uint64, count int64) (data []byte, n int, err error) {
if count <= 0 {
return nil, 0, nil
}

buf := make([]byte, count)

tgrp, unlock := s.debugger.LockTargetGroup()
defer unlock()

n, err = tgrp.Selected.Memory().ReadMemory(buf, addr)
if err != nil {
return nil, 0, err
}

if n > 0 {
data = buf[:n]
}

return data, n, nil
}

func makeReadMemoryResponse(req dap.Request, addr uint64, data []byte, unreadable int) *dap.ReadMemoryResponse {
var response string
if len(data) > 0 {
response = base64.StdEncoding.EncodeToString(data)
}

return &dap.ReadMemoryResponse{
Response: *newResponse(req),
Body: dap.ReadMemoryResponseBody{
Address: fmt.Sprintf("%#x", addr),
Data: response,
UnreadableBytes: unreadable,
},
}
}

var invalidInstruction = dap.DisassembledInstruction{
Expand Down Expand Up @@ -3814,6 +3965,7 @@ Use 'Continue' to resume the original step command.`
func (s *Session) resetHandlesForStoppedEvent() {
s.stackFrameHandles.reset()
s.variableHandles.reset()
s.referencesCollection.reset()
s.exceptionErr = nil
}

Expand Down
Loading