Repository: go-delve/delve
Pull request #1918 (2020-03-09 20:46:43)

service/dap: Add error handlers for unsupported and not-yet-supported requests
Reflect which requests are required and which ones are optional and controlled by flags (aka capabilities) that can be set in the initialize response to the client. For the optional ones that make sense in the context of Go/delve as well as the missing required handlers, provide stubs that currently just return the not-yet-implemented error response. This is different from vscode-go, which falls back on no-op implementation for all unsupported messages defined in the base classes for debug adaptors (see https://github.com/microsoft/vscode-debugadapter-node/blob/master/adapter/src/debugSession.ts).

Updates #1515

polinasok: I have expanded the scope of this PR (initial comment and title update). You can now think of the main dispatch as well as the unittests as a sort of roadmap showing which requests are 
- required and implemented
- required and still to be implemented (but have placeholder handlers)
- optional and can/should be implemented (and have placeholder handlers)
- optional and not to ever be supported (no handlers, just error message issued on the spot)
PTAL
derekparker: LGTM - please just rebase and we can merge!

Patch:
diff --git a/pkg/dwarf/op/op.go b/pkg/dwarf/op/op.go
index 5edcd828..7410f7c1 100644
--- a/pkg/dwarf/op/op.go
+++ b/pkg/dwarf/op/op.go
@@ -54,7 +54,11 @@ func ExecuteStackProgram(regs DwarfRegisters, instructions []byte, ptrSize int)
 		}
 		opcode := Opcode(opcodeByte)
 		if ctxt.reg && opcode != DW_OP_piece {
-			break
+			// last opcode was DW_OP_regN and next one isn't DW_OP_piece so convert
+			// the register piece into a stack value.
+			ctxt.stack = append(ctxt.stack, int64(regs.Uint64Val(ctxt.pieces[len(ctxt.pieces)-1].RegNum)))
+			ctxt.pieces = ctxt.pieces[:len(ctxt.pieces)-1]
+			ctxt.reg = false
 		}
 		fn, ok := oplut[opcode]
 		if !ok {
@@ -68,6 +72,9 @@ func ExecuteStackProgram(regs DwarfRegisters, instructions []byte, ptrSize int)
 	}
 
 	if ctxt.pieces != nil {
+		if len(ctxt.pieces) == 1 && ctxt.pieces[0].IsRegister {
+			return int64(regs.Uint64Val(ctxt.pieces[0].RegNum)), ctxt.pieces, nil
+		}
 		return 0, ctxt.pieces, nil
 	}
 
diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go
index d4b8d3ab..42d94bfc 100644
--- a/pkg/proc/bininfo.go
+++ b/pkg/proc/bininfo.go
@@ -84,8 +84,6 @@ type BinaryInfo struct {
 
 	frameEntries frame.FrameDescriptionEntries
 
-	compileUnits []*compileUnit // compileUnits is sorted by increasing DWARF offset
-
 	types       map[string]dwarfRef
 	packageVars []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
 
@@ -520,14 +518,17 @@ func (err *ErrCouldNotFindLine) Error() string {
 func (bi *BinaryInfo) LineToPC(filename string, lineno int) (pcs []uint64, err error) {
 	fileFound := false
 	var pc uint64
-	for _, cu := range bi.compileUnits {
-		if cu.lineInfo == nil || cu.lineInfo.Lookup[filename] == nil {
-			continue
-		}
-		fileFound = true
-		pc = cu.lineInfo.LineToPC(filename, lineno)
-		if pc != 0 {
-			break
+pcsearch:
+	for _, image := range bi.Images {
+		for _, cu := range image.compileUnits {
+			if cu.lineInfo == nil || cu.lineInfo.Lookup[filename] == nil {
+				continue
+			}
+			fileFound = true
+			pc = cu.lineInfo.LineToPC(filename, lineno)
+			if pc != 0 {
+				break pcsearch
+			}
 		}
 	}
 
@@ -580,9 +581,11 @@ func (bi *BinaryInfo) AllPCsForFileLines(filename string, linenos []int) map[int
 	for _, line := range linenos {
 		r[line] = make([]uint64, 0, 1)
 	}
-	for _, cu := range bi.compileUnits {
-		if cu.lineInfo != nil && cu.lineInfo.Lookup[filename] != nil {
-			cu.lineInfo.AllPCsForFileLines(filename, r)
+	for _, image := range bi.Images {
+		for _, cu := range image.compileUnits {
+			if cu.lineInfo != nil && cu.lineInfo.Lookup[filename] != nil {
+				cu.lineInfo.AllPCsForFileLines(filename, r)
+			}
 		}
 	}
 	return r
@@ -648,6 +651,8 @@ type Image struct {
 
 	typeCache map[dwarf.Offset]godwarf.Type
 
+	compileUnits []*compileUnit // compileUnits is sorted by increasing DWARF offset
+
 	dwarfTreeCache *simplelru.LRU
 
 	// runtimeTypeToDIE maps between the offset of a runtime._type in
@@ -871,7 +876,7 @@ func (bi *BinaryInfo) LocationCovers(entry *dwarf.Entry, attr dwarf.Attr) ([][2]
 	if !ok {
 		return nil, fmt.Errorf("attribute %s of unsupported type %T", attr, a)
 	}
-	cu := bi.findCompileUnitForOffset(entry.Offset)
+	cu := bi.Images[0].findCompileUnitForOffset(entry.Offset)
 	if cu == nil {
 		return nil, errors.New("could not find compile unit")
 	}
@@ -938,17 +943,19 @@ func (bi *BinaryInfo) loclistEntry(off int64, pc uint64) []byte {
 
 // findCompileUnit returns the compile unit containing address pc.
 func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit {
-	for _, cu := range bi.compileUnits {
-		for _, rng := range cu.ranges {
-			if pc >= rng[0] && pc < rng[1] {
-				return cu
+	for _, image := range bi.Images {
+		for _, cu := range image.compileUnits {
+			for _, rng := range cu.ranges {
+				if pc >= rng[0] && pc < rng[1] {
+					return cu
+				}
 			}
 		}
 	}
 	return nil
 }
 
-func (bi *BinaryInfo) findCompileUnitForOffset(off dwarf.Offset) *compileUnit {
+func (bi *Image) findCompileUnitForOffset(off dwarf.Offset) *compileUnit {
 	i := sort.Search(len(bi.compileUnits), func(i int) bool {
 		return bi.compileUnits[i].offset >= off
 	})
@@ -960,7 +967,7 @@ func (bi *BinaryInfo) findCompileUnitForOffset(off dwarf.Offset) *compileUnit {
 
 // Producer returns the value of DW_AT_producer.
 func (bi *BinaryInfo) Producer() string {
-	for _, cu := range bi.compileUnits {
+	for _, cu := range bi.Images[0].compileUnits {
 		if cu.isgo && cu.producer != "" {
 			return cu.producer
 		}
@@ -1569,7 +1576,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugLineBytes []byte, wg
 			if cu.isgo && gopkg != "" {
 				bi.PackageMap[gopkg] = append(bi.PackageMap[gopkg], escapePackagePath(strings.Replace(cu.name, "\\", "/", -1)))
 			}
-			bi.compileUnits = append(bi.compileUnits, cu)
+			image.compileUnits = append(image.compileUnits, cu)
 			if entry.Children {
 				bi.loadDebugInfoMapsCompileUnit(ctxt, image, reader, cu)
 			}
@@ -1583,7 +1590,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugLineBytes []byte, wg
 		}
 	}
 
-	sort.Sort(compileUnitsByOffset(bi.compileUnits))
+	sort.Sort(compileUnitsByOffset(image.compileUnits))
 	sort.Sort(functionsDebugInfoByEntry(bi.Functions))
 	sort.Sort(packageVarsByAddr(bi.packageVars))
 
@@ -1593,7 +1600,7 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugLineBytes []byte, wg
 	}
 
 	bi.Sources = []string{}
-	for _, cu := range bi.compileUnits {
+	for _, cu := range image.compileUnits {
 		if cu.lineInfo != nil {
 			for _, fileEntry := range cu.lineInfo.FileNames {
 				bi.Sources = append(bi.Sources, fileEntry.Path)
@@ -1997,7 +2004,7 @@ type PackageBuildInfo struct {
 // files constituting the package.
 func (bi *BinaryInfo) ListPackagesBuildInfo(includeFiles bool) []*PackageBuildInfo {
 	m := make(map[string]*PackageBuildInfo)
-	for _, cu := range bi.compileUnits {
+	for _, cu := range bi.Images[0].compileUnits {
 		if cu.image != bi.Images[0] || !cu.isgo || cu.lineInfo == nil {
 			//TODO(aarzilli): what's the correct thing to do for plugins?
 			continue
diff --git a/pkg/proc/breakpoints.go b/pkg/proc/breakpoints.go
index f3fcfb50..3e01bea5 100644
--- a/pkg/proc/breakpoints.go
+++ b/pkg/proc/breakpoints.go
@@ -436,8 +436,8 @@ func (rbpi *returnBreakpointInfo) Collect(thread Thread) []*Variable {
 		return nil
 	}
 
-	oldFrameOffset := rbpi.frameOffset + int64(g.stackhi)
-	oldSP := uint64(rbpi.spOffset + int64(g.stackhi))
+	oldFrameOffset := rbpi.frameOffset + int64(g.stack.hi)
+	oldSP := uint64(rbpi.spOffset + int64(g.stack.hi))
 	err = fakeFunctionEntryScope(scope, rbpi.fn, oldFrameOffset, oldSP)
 	if err != nil {
 		return returnInfoError("could not read function entry", err, thread)
diff --git a/pkg/proc/eval.go b/pkg/proc/eval.go
index ce85f597..d541bb87 100644
--- a/pkg/proc/eval.go
+++ b/pkg/proc/eval.go
@@ -451,36 +451,33 @@ func regsReplaceStaticBase(regs op.DwarfRegisters, image *Image) op.DwarfRegiste
 
 // PackageVariables returns the name, value, and type of all package variables in the application.
 func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
-	var vars []*Variable
-	for _, image := range scope.BinInfo.Images {
-		if image.loadErr != nil {
-			continue
+	pkgvars := make([]packageVar, len(scope.BinInfo.packageVars))
+	copy(pkgvars, scope.BinInfo.packageVars)
+	sort.Slice(pkgvars, func(i, j int) bool {
+		if pkgvars[i].cu.image.addr == pkgvars[j].cu.image.addr {
+			return pkgvars[i].offset < pkgvars[j].offset
 		}
-		reader := reader.New(image.dwarf)
-
-		var utypoff dwarf.Offset
-		utypentry, err := reader.SeekToTypeNamed("<unspecified>")
-		if err == nil {
-			utypoff = utypentry.Offset
+		return pkgvars[i].cu.image.addr < pkgvars[j].cu.image.addr
+	})
+	vars := make([]*Variable, 0, len(scope.BinInfo.packageVars))
+	for _, pkgvar := range pkgvars {
+		reader := pkgvar.cu.image.dwarfReader
+		reader.Seek(pkgvar.offset)
+		entry, err := reader.Next()
+		if err != nil {
+			return nil, err
 		}
 
-		for entry, err := reader.NextPackageVariable(); entry != nil; entry, err = reader.NextPackageVariable() {
-			if err != nil {
-				return nil, err
-			}
-
-			if typoff, ok := entry.Val(dwarf.AttrType).(dwarf.Offset); !ok || typoff == utypoff {
-				continue
-			}
-
-			// Ignore errors trying to extract values
-			val, err := extractVarInfoFromEntry(scope.BinInfo, image, regsReplaceStaticBase(scope.Regs, image), scope.Mem, godwarf.EntryToTree(entry))
-			if err != nil {
-				continue
-			}
-			val.loadValue(cfg)
-			vars = append(vars, val)
+		// Ignore errors trying to extract values
+		val, err := extractVarInfoFromEntry(scope.BinInfo, pkgvar.cu.image, regsReplaceStaticBase(scope.Regs, pkgvar.cu.image), scope.Mem, godwarf.EntryToTree(entry))
+		if val.Kind == reflect.Invalid {
+			continue
 		}
+		if err != nil {
+			continue
+		}
+		val.loadValue(cfg)
+		vars = append(vars, val)
 	}
 
 	return vars, nil
diff --git a/pkg/proc/fncall.go b/pkg/proc/fncall.go
index eac9c2fa..26d0a756 100644
--- a/pkg/proc/fncall.go
+++ b/pkg/proc/fncall.go
@@ -257,7 +257,7 @@ func evalFunctionCall(scope *EvalScope, node *ast.CallExpr) (*Variable, error) {
 		return nil, err
 	}
 	regs = regs.Copy()
-	if regs.SP()-256 <= scope.g.stacklo {
+	if regs.SP()-256 <= scope.g.stack.lo {
 		return nil, errNotEnoughStack
 	}
 	_, err = regs.Get(int(x86asm.RAX))
@@ -285,9 +285,9 @@ func evalFunctionCall(scope *EvalScope, node *ast.CallExpr) (*Variable, error) {
 
 	fncallLog("function call initiated %v frame size %d", fncall.fn, fncall.argFrameSize)
 
-	spoff := int64(scope.Regs.Uint64Val(scope.Regs.SPRegNum)) - int64(scope.g.stackhi)
-	bpoff := int64(scope.Regs.Uint64Val(scope.Regs.BPRegNum)) - int64(scope.g.stackhi)
-	fboff := scope.Regs.FrameBase - int64(scope.g.stackhi)
+	spoff := int64(scope.Regs.Uint64Val(scope.Regs.SPRegNum)) - int64(scope.g.stack.hi)
+	bpoff := int64(scope.Regs.Uint64Val(scope.Regs.BPRegNum)) - int64(scope.g.stack.hi)
+	fboff := scope.Regs.FrameBase - int64(scope.g.stack.hi)
 
 	for {
 		scope.g = scope.callCtx.doContinue()
@@ -303,10 +303,10 @@ func evalFunctionCall(scope *EvalScope, node *ast.CallExpr) (*Variable, error) {
 			}
 		}
 
-		scope.Regs.Regs[scope.Regs.SPRegNum].Uint64Val = uint64(spoff + int64(scope.g.stackhi))
-		scope.Regs.Regs[scope.Regs.BPRegNum].Uint64Val = uint64(bpoff + int64(scope.g.stackhi))
-		scope.Regs.FrameBase = fboff + int64(scope.g.stackhi)
-		scope.Regs.CFA = scope.frameOffset + int64(scope.g.stackhi)
+		scope.Regs.Regs[scope.Regs.SPRegNum].Uint64Val = uint64(spoff + int64(scope.g.stack.hi))
+		scope.Regs.Regs[scope.Regs.BPRegNum].Uint64Val = uint64(bpoff + int64(scope.g.stack.hi))
+		scope.Regs.FrameBase = fboff + int64(scope.g.stack.hi)
+		scope.Regs.CFA = scope.frameOffset + int64(scope.g.stack.hi)
 
 		finished := funcCallStep(scope, &fncall)
 		if finished {
@@ -635,7 +635,7 @@ func escapeCheck(v *Variable, name string, g *G) error {
 }
 
 func escapeCheckPointer(addr uintptr, name string, g *G) error {
-	if uint64(addr) >= g.stacklo && uint64(addr) < g.stackhi {
+	if uint64(addr) >= g.stack.lo && uint64(addr) < g.stack.hi {
 		return fmt.Errorf("stack object passed to escaping pointer: %s", name)
 	}
 	return nil
diff --git a/pkg/proc/stack.go b/pkg/proc/stack.go
index 27be3c76..907d2d56 100644
--- a/pkg/proc/stack.go
+++ b/pkg/proc/stack.go
@@ -122,13 +122,13 @@ func (g *G) stackIterator(opts StacktraceOptions) (*stackIterator, error) {
 		return newStackIterator(
 			bi, g.Thread,
 			bi.Arch.RegistersToDwarfRegisters(so.StaticBase, regs),
-			g.stackhi, stkbar, g.stkbarPos, g, opts), nil
+			g.stack.hi, stkbar, g.stkbarPos, g, opts), nil
 	}
 	so := g.variable.bi.PCToImage(g.PC)
 	return newStackIterator(
 		bi, g.variable.mem,
 		bi.Arch.addrAndStackRegsToDwarfRegisters(so.StaticBase, g.PC, g.SP, g.BP, g.LR),
-		g.stackhi, stkbar, g.stkbarPos, g, opts), nil
+		g.stack.hi, stkbar, g.stkbarPos, g, opts), nil
 }
 
 type StacktraceOptions uint16
diff --git a/pkg/proc/target_exec.go b/pkg/proc/target_exec.go
index 2d8f95af..b0016173 100644
--- a/pkg/proc/target_exec.go
+++ b/pkg/proc/target_exec.go
@@ -78,15 +78,19 @@ func (dbp *Target) Continue() error {
 
 		threads := dbp.ThreadList()
 
-		callInjectionDone, err := callInjectionProtocol(dbp, threads)
-		if err != nil {
-			return err
-		}
+		callInjectionDone, callErr := callInjectionProtocol(dbp, threads)
+		// callErr check delayed until after pickCurrentThread, which must always
+		// happen, otherwise the debugger could be left in an inconsistent
+		// state.
 
 		if err := pickCurrentThread(dbp, trapthread, threads); err != nil {
 			return err
 		}
 
+		if callErr != nil {
+			return callErr
+		}
+
 		curthread := dbp.CurrentThread()
 		curbp := curthread.Breakpoint()
 
diff --git a/pkg/proc/variables.go b/pkg/proc/variables.go
index dfc1a58b..b8ad1af5 100644
--- a/pkg/proc/variables.go
+++ b/pkg/proc/variables.go
@@ -195,8 +195,7 @@ type G struct {
 	Status    uint64
 	stkbarVar *Variable // stkbar field of g struct
 	stkbarPos int       // stkbarPos field of g struct
-	stackhi   uint64    // value of stack.hi
-	stacklo   uint64    // value of stack.lo
+	stack     stack     // value of stack
 
 	SystemStack bool // SystemStack is true if this goroutine is currently executing on a system stack.
 
@@ -213,6 +212,11 @@ type G struct {
 	labels *map[string]string // G's pprof labels, computed on demand in Labels() method
 }
 
+// stack represents a stack span in the target process.
+type stack struct {
+	hi, lo uint64
+}
+
 // GetG returns information on the G (goroutine) that is executing on this thread.
 //
 // The G structure for a thread is stored in thread local storage. Here we simply
@@ -584,7 +588,7 @@ func newVariable(name string, addr uintptr, dwarfType godwarf.Type, bi *BinaryIn
 		// b. anonymous struct types (they contain the '{' character)
 		// c. Go internal struct types used to describe maps (they contain the '<'
 		// character).
-		cu := bi.findCompileUnitForOffset(dwarfType.Common().Offset)
+		cu := bi.Images[dwarfType.Common().Index].findCompileUnitForOffset(dwarfType.Common().Offset)
 		if cu != nil && cu.isgo {
 			dwarfType = &godwarf.TypedefType{
 				CommonType: *(dwarfType.Common()),
@@ -862,8 +866,7 @@ func (v *Variable) parseG() (*G, error) {
 		variable:   v,
 		stkbarVar:  stkbarVar,
 		stkbarPos:  int(stkbarPos),
-		stackhi:    stackhi,
-		stacklo:    stacklo,
+		stack:      stack{hi: stackhi, lo: stacklo},
 	}
 	return g, nil
 }
diff --git a/service/dap/error_ids.go b/service/dap/error_ids.go
index 9f1b9f05..6fbd0b6c 100644
--- a/service/dap/error_ids.go
+++ b/service/dap/error_ids.go
@@ -4,6 +4,7 @@ package dap
 const (
 	UnsupportedCommand int = 9999
 	InternalError      int = 8888
+	NotYetImplemented  int = 7777
 
 	// The values below come from the vscode-go debug adaptor.
 	// Although the spec says they should be unique, the adaptor
diff --git a/service/dap/server.go b/service/dap/server.go
index 3ce27b6c..847de76f 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -194,82 +194,150 @@ func (s *Server) handleRequest(request dap.Message) {
 
 	switch request := request.(type) {
 	case *dap.InitializeRequest:
+		// Required
 		s.onInitializeRequest(request)
 	case *dap.LaunchRequest:
+		// Required
 		s.onLaunchRequest(request)
 	case *dap.AttachRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Required
+		// TODO: implement this request in V0
+		s.onAttachRequest(request)
 	case *dap.DisconnectRequest:
+		// Required
 		s.onDisconnectRequest(request)
 	case *dap.TerminateRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Optional (capability ‘supportsTerminateRequest‘)
+		// TODO: implement this request in V1
+		s.onTerminateRequest(request)
 	case *dap.RestartRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Optional (capability ‘supportsRestartRequest’)
+		// TODO: implement this request in V1
+		s.onRestartRequest(request)
 	case *dap.SetBreakpointsRequest:
+		// Required
 		s.onSetBreakpointsRequest(request)
 	case *dap.SetFunctionBreakpointsRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Optional (capability ‘supportsFunctionBreakpoints’)
+		// TODO: implement this request in V1
+		s.onSetFunctionBreakpointsRequest(request)
 	case *dap.SetExceptionBreakpointsRequest:
+		// Optional (capability ‘exceptionBreakpointFilters’)
 		s.onSetExceptionBreakpointsRequest(request)
 	case *dap.ConfigurationDoneRequest:
+		// Optional (capability ‘supportsConfigurationDoneRequest’)
+		// Supported by vscode-go
 		s.onConfigurationDoneRequest(request)
 	case *dap.ContinueRequest:
+		// Required
 		s.onContinueRequest(request)
 	case *dap.NextRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Required
+		// TODO: implement this request in V0
+		s.onNextRequest(request)
 	case *dap.StepInRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Required
+		// TODO: implement this request in V0
+		s.onStepInRequest(request)
 	case *dap.StepOutRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Required
+		// TODO: implement this request in V0
+		s.onStepOutRequest(request)
 	case *dap.StepBackRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Optional (capability ‘supportsStepBack’)
+		// TODO: implement this request in V1
+		s.onStepBackRequest(request)
 	case *dap.ReverseContinueRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Optional (capability ‘supportsStepBack’)
+		// TODO: implement this request in V1
+		s.onReverseContinueRequest(request)
 	case *dap.RestartFrameRequest:
+		// Optional (capability ’supportsRestartFrame’)
 		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.GotoRequest:
+		// Optional (capability ‘supportsGotoTargetsRequest’)
 		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.PauseRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Required
+		// TODO: implement this request in V0
+		s.onPauseRequest(request)
 	case *dap.StackTraceRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Required
+		// TODO: implement this request in V0
+		s.onStackTraceRequest(request)
 	case *dap.ScopesRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Required
+		// TODO: implement this request in V0
+		s.onScopesRequest(request)
 	case *dap.VariablesRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Required
+		// TODO: implement this request in V0
+		s.onVariablesRequest(request)
 	case *dap.SetVariableRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Optional (capability ‘supportsSetVariable’)
+		// Supported by vscode-go
+		// TODO: implement this request in V0
+		s.onSetVariableRequest(request)
 	case *dap.SetExpressionRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Optional (capability ‘supportsSetExpression’)
+		// TODO: implement this request in V1
+		s.onSetExpressionRequest(request)
 	case *dap.SourceRequest:
+		// Required
+		// This does not make sense in the context of Go as
+		// the source cannot be a string eval'ed at runtime.
 		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.ThreadsRequest:
+		// Required
 		s.onThreadsRequest(request)
 	case *dap.TerminateThreadsRequest:
+		// Optional (capability ‘supportsTerminateThreadsRequest’)
 		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.EvaluateRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Required - TODO
+		// TODO: implement this request in V0
+		s.onEvaluateRequest(request)
 	case *dap.StepInTargetsRequest:
+		// Optional (capability ‘supportsStepInTargetsRequest’)
 		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.GotoTargetsRequest:
+		// Optional (capability ‘supportsGotoTargetsRequest’)
 		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.CompletionsRequest:
+		// Optional (capability ‘supportsCompletionsRequest’)
 		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.ExceptionInfoRequest:
+		// Optional (capability ‘supportsExceptionInfoRequest’)
+		// TODO: does this request make sense for delve?
 		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.LoadedSourcesRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Optional (capability ‘supportsLoadedSourcesRequest’)
+		// TODO: implement this request in V1
+		s.onLoadedSourcesRequest(request)
 	case *dap.DataBreakpointInfoRequest:
+		// Optional (capability ‘supportsDataBreakpoints’)
 		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.SetDataBreakpointsRequest:
+		// Optional (capability ‘supportsDataBreakpoints’)
 		s.sendUnsupportedErrorResponse(request.Request)
 	case *dap.ReadMemoryRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Optional (capability ‘supportsReadMemoryRequest‘)
+		// TODO: implement this request in V1
+		s.onReadMemoryRequest(request)
 	case *dap.DisassembleRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Optional (capability ‘supportsDisassembleRequest’)
+		// TODO: implement this request in V1
+		s.onDisassembleRequest(request)
 	case *dap.CancelRequest:
-		s.sendUnsupportedErrorResponse(request.Request)
+		// Optional (capability ‘supportsCancelRequest’)
+		// TODO: does this request make sense for delve?
+		s.onCancelRequest(request)
 	case *dap.BreakpointLocationsRequest:
+		// Optional (capability ‘supportsBreakpointLocationsRequest’)
+		s.sendUnsupportedErrorResponse(request.Request)
+	case *dap.ModulesRequest:
+		// Optional (capability ‘supportsModulesRequest’)
+		// TODO: does this request make sense for delve?
 		s.sendUnsupportedErrorResponse(request.Request)
 	default:
 		// This is a DAP message that go-dap has a struct for, so
@@ -291,6 +359,16 @@ func (s *Server) onInitializeRequest(request *dap.InitializeRequest) {
 	response.Body.SupportsConfigurationDoneRequest = true
 	// TODO(polina): support this to match vscode-go functionality
 	response.Body.SupportsSetVariable = false
+	// TODO(polina): support these requests in addition to vscode-go feature parity
+	response.Body.SupportsTerminateRequest = false
+	response.Body.SupportsRestartRequest = false
+	response.Body.SupportsFunctionBreakpoints = false
+	response.Body.SupportsStepBack = false
+	response.Body.SupportsSetExpression = false
+	response.Body.SupportsLoadedSourcesRequest = false
+	response.Body.SupportsReadMemoryRequest = false
+	response.Body.SupportsDisassembleRequest = false
+	response.Body.SupportsCancelRequest = false
 	s.send(response)
 }
 
@@ -422,7 +500,7 @@ func (s *Server) onSetBreakpointsRequest(request *dap.SetBreakpointsRequest) {
 
 func (s *Server) onSetExceptionBreakpointsRequest(request *dap.SetExceptionBreakpointsRequest) {
 	// Unlike what DAP documentation claims, this request is always sent
-	// even though we specified no filters at initializatin. Handle as no-op.
+	// even though we specified no filters at initialization. Handle as no-op.
 	s.send(&dap.SetExceptionBreakpointsResponse{Response: *newResponse(request.Request)})
 }
 
@@ -488,6 +566,126 @@ func (s *Server) onThreadsRequest(request *dap.ThreadsRequest) {
 	s.send(response)
 }
 
+// onAttachRequest sends a not-yet-implemented error response.
+// This is a mandatory request to support.
+func (s *Server) onAttachRequest(request *dap.AttachRequest) { // TODO V0
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onNextRequest sends a not-yet-implemented error response.
+// This is a mandatory request to support.
+func (s *Server) onNextRequest(request *dap.NextRequest) { // TODO V0
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onStepInRequest sends a not-yet-implemented error response.
+// This is a mandatory request to support.
+func (s *Server) onStepInRequest(request *dap.StepInRequest) { // TODO V0
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onStepOutRequest sends a not-yet-implemented error response.
+// This is a mandatory request to support.
+func (s *Server) onStepOutRequest(request *dap.StepOutRequest) { // TODO V0
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onPauseRequest sends a not-yet-implemented error response.
+// This is a mandatory request to support.
+func (s *Server) onPauseRequest(request *dap.PauseRequest) { // TODO V0
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onStackTraceRequest sends a not-yet-implemented error response.
+// This is a mandatory request to support.
+func (s *Server) onStackTraceRequest(request *dap.StackTraceRequest) { // TODO V0
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onScopesRequest sends a not-yet-implemented error response.
+// This is a mandatory request to support.
+func (s *Server) onScopesRequest(request *dap.ScopesRequest) { // TODO V0
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onVariablesRequest sends a not-yet-implemented error response.
+// This is a mandatory request to support.
+func (s *Server) onVariablesRequest(request *dap.VariablesRequest) { // TODO V0
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onEvaluateRequest sends a not-yet-implemented error response.
+// This is a mandatory request to support.
+func (s *Server) onEvaluateRequest(request *dap.EvaluateRequest) { // TODO V0
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onTerminateRequest sends a not-yet-implemented error response.
+// Capability 'supportsTerminateRequest' is not set in 'initialize' response.
+func (s *Server) onTerminateRequest(request *dap.TerminateRequest) {
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onRestartRequest sends a not-yet-implemented error response
+// Capability 'supportsRestartRequest' is not set in 'initialize' response.
+func (s *Server) onRestartRequest(request *dap.RestartRequest) {
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onSetFunctionBreakpointsRequest sends a not-yet-implemented error response.
+// Capability 'supportsFunctionBreakpoints' is not set 'initialize' response.
+func (s *Server) onSetFunctionBreakpointsRequest(request *dap.SetFunctionBreakpointsRequest) {
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onStepBackRequest sends a not-yet-implemented error response.
+// Capability 'supportsStepBack' is not set 'initialize' response.
+func (s *Server) onStepBackRequest(request *dap.StepBackRequest) {
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onReverseContinueRequest sends a not-yet-implemented error response.
+// Capability 'supportsStepBack' is not set 'initialize' response.
+func (s *Server) onReverseContinueRequest(request *dap.ReverseContinueRequest) {
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onSetVariableRequest sends a not-yet-implemented error response.
+// Capability 'supportsSetVariable' is not set 'initialize' response.
+func (s *Server) onSetVariableRequest(request *dap.SetVariableRequest) { // TODO V0
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onSetExpression sends a not-yet-implemented error response.
+// Capability 'supportsSetExpression' is not set 'initialize' response.
+func (s *Server) onSetExpressionRequest(request *dap.SetExpressionRequest) {
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onLoadedSourcesRequest sends a not-yet-implemented error response.
+// Capability 'supportsLoadedSourcesRequest' is not set 'initialize' response.
+func (s *Server) onLoadedSourcesRequest(request *dap.LoadedSourcesRequest) {
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onReadMemoryRequest sends a not-yet-implemented error response.
+// Capability 'supportsReadMemoryRequest' is not set 'initialize' response.
+func (s *Server) onReadMemoryRequest(request *dap.ReadMemoryRequest) {
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onDisassembleRequest sends a not-yet-implemented error response.
+// Capability 'supportsDisassembleRequest' is not set 'initialize' response.
+func (s *Server) onDisassembleRequest(request *dap.DisassembleRequest) {
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
+// onCancelRequest sends a not-yet-implemented error response.
+// Capability 'supportsCancelRequest' is not set 'initialize' response.
+func (s *Server) onCancelRequest(request *dap.CancelRequest) {
+	s.sendNotYetImplementedErrorResponse(request.Request)
+}
+
 func (s *Server) sendErrorResponse(request dap.Request, id int, summary string, details string) {
 	er := &dap.ErrorResponse{}
 	er.Type = "response"
@@ -521,6 +719,11 @@ func (s *Server) sendUnsupportedErrorResponse(request dap.Request) {
 		fmt.Sprintf("cannot process '%s' request", request.Command))
 }
 
+func (s *Server) sendNotYetImplementedErrorResponse(request dap.Request) {
+	s.sendErrorResponse(request, NotYetImplemented, "Not yet implemented",
+		fmt.Sprintf("cannot process '%s' request", request.Command))
+}
+
 func newResponse(request dap.Request) *dap.Response {
 	return &dap.Response{
 		ProtocolMessage: dap.ProtocolMessage{


Test patch:
diff --git a/pkg/proc/proc_test.go b/pkg/proc/proc_test.go
index ce29f5a7..d0b7ad44 100644
--- a/pkg/proc/proc_test.go
+++ b/pkg/proc/proc_test.go
@@ -1827,6 +1827,14 @@ func TestIssue414(t *testing.T) {
 }
 
 func TestPackageVariables(t *testing.T) {
+	var skippedVariable = map[string]bool{
+		"runtime.uint16Eface": true,
+		"runtime.uint32Eface": true,
+		"runtime.uint64Eface": true,
+		"runtime.stringEface": true,
+		"runtime.sliceEface":  true,
+	}
+
 	protest.AllowRecording(t)
 	withTestProcess("testvariables", t, func(p *proc.Target, fixture protest.Fixture) {
 		err := p.Continue()
@@ -1837,6 +1845,9 @@ func TestPackageVariables(t *testing.T) {
 		assertNoError(err, t, "PackageVariables()")
 		failed := false
 		for _, v := range vars {
+			if skippedVariable[v.Name] {
+				continue
+			}
 			if v.Unreadable != nil && v.Unreadable.Error() != "no location attribute Location" {
 				failed = true
 				t.Logf("Unreadable variable %s: %v", v.Name, v.Unreadable)
diff --git a/service/dap/daptest/client.go b/service/dap/daptest/client.go
index 1a97e428..67a70966 100644
--- a/service/dap/daptest/client.go
+++ b/service/dap/daptest/client.go
@@ -59,14 +59,33 @@ func (c *Client) expectReadProtocolMessage(t *testing.T) dap.Message {
 	return m
 }
 
-func (c *Client) ExpectDisconnectResponse(t *testing.T) *dap.DisconnectResponse {
+func (c *Client) ExpectErrorResponse(t *testing.T) *dap.ErrorResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.DisconnectResponse)
+	return c.expectReadProtocolMessage(t).(*dap.ErrorResponse)
 }
 
-func (c *Client) ExpectErrorResponse(t *testing.T) *dap.ErrorResponse {
+func (c *Client) expectErrorResponse(t *testing.T, id int, message string) *dap.ErrorResponse {
 	t.Helper()
-	return c.expectReadProtocolMessage(t).(*dap.ErrorResponse)
+	er := c.expectReadProtocolMessage(t).(*dap.ErrorResponse)
+	if er.Body.Error.Id != id || er.Message != message {
+		t.Errorf("\ngot %#v\nwant Id=%d Message=%q", er, id, message)
+	}
+	return er
+}
+
+func (c *Client) ExpectNotYetImplementedErrorResponse(t *testing.T) *dap.ErrorResponse {
+	t.Helper()
+	return c.expectErrorResponse(t, 7777, "Not yet implemented")
+}
+
+func (c *Client) ExpectUnsupportedCommandErrorResponse(t *testing.T) *dap.ErrorResponse {
+	t.Helper()
+	return c.expectErrorResponse(t, 9999, "Unsupported command")
+}
+
+func (c *Client) ExpectDisconnectResponse(t *testing.T) *dap.DisconnectResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.DisconnectResponse)
 }
 
 func (c *Client) ExpectContinueResponse(t *testing.T) *dap.ContinueResponse {
@@ -128,6 +147,106 @@ func (c *Client) ExpectStackTraceResponse(t *testing.T) *dap.StackTraceResponse
 	return c.expectReadProtocolMessage(t).(*dap.StackTraceResponse)
 }
 
+func (c *Client) ExpectTerminateResponse(t *testing.T) *dap.TerminateResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.TerminateResponse)
+}
+
+func (c *Client) ExpectRestartResponse(t *testing.T) *dap.RestartResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.RestartResponse)
+}
+
+func (c *Client) ExpectSetFunctionBreakpointsResponse(t *testing.T) *dap.SetFunctionBreakpointsResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.SetFunctionBreakpointsResponse)
+}
+
+func (c *Client) ExpectStepBackResponse(t *testing.T) *dap.StepBackResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.StepBackResponse)
+}
+
+func (c *Client) ExpectReverseContinueResponse(t *testing.T) *dap.ReverseContinueResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.ReverseContinueResponse)
+}
+
+func (c *Client) ExpectRestartFrameResponse(t *testing.T) *dap.RestartFrameResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.RestartFrameResponse)
+}
+
+func (c *Client) ExpectSetExpressionResponse(t *testing.T) *dap.SetExpressionResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.SetExpressionResponse)
+}
+
+func (c *Client) ExpectTerminateThreadsResponse(t *testing.T) *dap.TerminateThreadsResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.TerminateThreadsResponse)
+}
+
+func (c *Client) ExpectStepInTargetsResponse(t *testing.T) *dap.StepInTargetsResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.StepInTargetsResponse)
+}
+
+func (c *Client) ExpectGotoTargetsResponse(t *testing.T) *dap.GotoTargetsResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.GotoTargetsResponse)
+}
+
+func (c *Client) ExpectCompletionsResponse(t *testing.T) *dap.CompletionsResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.CompletionsResponse)
+}
+
+func (c *Client) ExpectExceptionInfoResponse(t *testing.T) *dap.ExceptionInfoResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.ExceptionInfoResponse)
+}
+
+func (c *Client) ExpectLoadedSourcesResponse(t *testing.T) *dap.LoadedSourcesResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.LoadedSourcesResponse)
+}
+
+func (c *Client) ExpectDataBreakpointInfoResponse(t *testing.T) *dap.DataBreakpointInfoResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.DataBreakpointInfoResponse)
+}
+
+func (c *Client) ExpectSetDataBreakpointsResponse(t *testing.T) *dap.SetDataBreakpointsResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.SetDataBreakpointsResponse)
+}
+
+func (c *Client) ExpectReadMemoryResponse(t *testing.T) *dap.ReadMemoryResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.ReadMemoryResponse)
+}
+
+func (c *Client) ExpectDisassembleResponse(t *testing.T) *dap.DisassembleResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.DisassembleResponse)
+}
+
+func (c *Client) ExpectCancelResponse(t *testing.T) *dap.CancelResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.CancelResponse)
+}
+
+func (c *Client) ExpectBreakpointLocationsResponse(t *testing.T) *dap.BreakpointLocationsResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.BreakpointLocationsResponse)
+}
+
+func (c *Client) ExpectModulesResponse(t *testing.T) *dap.ModulesResponse {
+	t.Helper()
+	return c.expectReadProtocolMessage(t).(*dap.ModulesResponse)
+}
+
 // InitializeRequest sends an 'initialize' request.
 func (c *Client) InitializeRequest() {
 	request := &dap.InitializeRequest{Request: *c.newRequest("initialize")}
@@ -165,6 +284,13 @@ func (c *Client) LaunchRequestWithArgs(arguments map[string]interface{}) {
 	c.send(request)
 }
 
+// AttachRequest sends an 'attach' request.
+func (c *Client) AttachRequest() {
+	request := &dap.AttachRequest{Request: *c.newRequest("attach")}
+	// TODO(polina): populate meaningful arguments
+	c.send(request)
+}
+
 // DisconnectRequest sends a 'disconnect' request.
 func (c *Client) DisconnectRequest() {
 	request := &dap.DisconnectRequest{Request: *c.newRequest("disconnect")}
@@ -207,6 +333,34 @@ func (c *Client) ContinueRequest(thread int) {
 	c.send(request)
 }
 
+// NextRequest sends a 'next' request.
+func (c *Client) NextRequest() {
+	request := &dap.NextRequest{Request: *c.newRequest("next")}
+	// TODO(polina): arguments
+	c.send(request)
+}
+
+// StepInRequest sends a 'stepIn' request.
+func (c *Client) StepInRequest() {
+	request := &dap.NextRequest{Request: *c.newRequest("stepIn")}
+	// TODO(polina): arguments
+	c.send(request)
+}
+
+// StepOutRequest sends a 'stepOut' request.
+func (c *Client) StepOutRequest() {
+	request := &dap.NextRequest{Request: *c.newRequest("stepOut")}
+	// TODO(polina): arguments
+	c.send(request)
+}
+
+// PauseRequest sends a 'pause' request.
+func (c *Client) PauseRequest() {
+	request := &dap.NextRequest{Request: *c.newRequest("pause")}
+	// TODO(polina): arguments
+	c.send(request)
+}
+
 // ThreadsRequest sends a 'threads' request.
 func (c *Client) ThreadsRequest() {
 	request := &dap.ThreadsRequest{Request: *c.newRequest("threads")}
@@ -219,6 +373,138 @@ func (c *Client) StackTraceRequest() {
 	c.send(request)
 }
 
+// ScopesRequest sends a 'scopes' request.
+func (c *Client) ScopesRequest() {
+	request := &dap.ScopesRequest{Request: *c.newRequest("scopes")}
+	c.send(request)
+}
+
+// VariablesRequest sends a 'scopes' request.
+func (c *Client) VariablesRequest() {
+	request := &dap.ScopesRequest{Request: *c.newRequest("variables")}
+	c.send(request)
+}
+
+// TeriminateRequest sends a 'terminate' request.
+func (c *Client) TerminateRequest() {
+	c.send(&dap.TerminateRequest{Request: *c.newRequest("terminate")})
+}
+
+// RestartRequest sends a 'restart' request.
+func (c *Client) RestartRequest() {
+	c.send(&dap.RestartRequest{Request: *c.newRequest("restart")})
+}
+
+// SetFunctionBreakpointsRequest sends a 'setFunctionBreakpoints' request.
+func (c *Client) SetFunctionBreakpointsRequest() {
+	c.send(&dap.SetFunctionBreakpointsRequest{Request: *c.newRequest("setFunctionBreakpoints")})
+}
+
+// StepBackRequest sends a 'stepBack' request.
+func (c *Client) StepBackRequest() {
+	c.send(&dap.StepBackRequest{Request: *c.newRequest("stepBack")})
+}
+
+// ReverseContinueRequest sends a 'reverseContinue' request.
+func (c *Client) ReverseContinueRequest() {
+	c.send(&dap.ReverseContinueRequest{Request: *c.newRequest("reverseContinue")})
+}
+
+// SetVariableRequest sends a 'setVariable' request.
+func (c *Client) SetVariableRequest() {
+	c.send(&dap.ReverseContinueRequest{Request: *c.newRequest("setVariable")})
+}
+
+// RestartFrameRequest sends a 'restartFrame' request.
+func (c *Client) RestartFrameRequest() {
+	c.send(&dap.RestartFrameRequest{Request: *c.newRequest("restartFrame")})
+}
+
+// GotoRequest sends a 'goto' request.
+func (c *Client) GotoRequest() {
+	c.send(&dap.GotoRequest{Request: *c.newRequest("goto")})
+}
+
+// SetExpressionRequest sends a 'setExpression' request.
+func (c *Client) SetExpressionRequest() {
+	c.send(&dap.SetExpressionRequest{Request: *c.newRequest("setExpression")})
+}
+
+// SourceRequest sends a 'source' request.
+func (c *Client) SourceRequest() {
+	c.send(&dap.SourceRequest{Request: *c.newRequest("source")})
+}
+
+// TerminateThreadsRequest sends a 'terminateThreads' request.
+func (c *Client) TerminateThreadsRequest() {
+	c.send(&dap.TerminateThreadsRequest{Request: *c.newRequest("terminateThreads")})
+}
+
+// EvaluateRequest sends a 'evaluate' request.
+func (c *Client) EvaluateRequest() {
+	c.send(&dap.EvaluateRequest{Request: *c.newRequest("evaluate")})
+}
+
+// StepInTargetsRequest sends a 'stepInTargets' request.
+func (c *Client) StepInTargetsRequest() {
+	c.send(&dap.StepInTargetsRequest{Request: *c.newRequest("stepInTargets")})
+}
+
+// GotoTargetsRequest sends a 'gotoTargets' request.
+func (c *Client) GotoTargetsRequest() {
+	c.send(&dap.GotoTargetsRequest{Request: *c.newRequest("gotoTargets")})
+}
+
+// CompletionsRequest sends a 'completions' request.
+func (c *Client) CompletionsRequest() {
+	c.send(&dap.CompletionsRequest{Request: *c.newRequest("completions")})
+}
+
+// ExceptionInfoRequest sends a 'exceptionInfo' request.
+func (c *Client) ExceptionInfoRequest() {
+	c.send(&dap.ExceptionInfoRequest{Request: *c.newRequest("exceptionInfo")})
+}
+
+// LoadedSourcesRequest sends a 'loadedSources' request.
+func (c *Client) LoadedSourcesRequest() {
+	c.send(&dap.LoadedSourcesRequest{Request: *c.newRequest("loadedSources")})
+}
+
+// DataBreakpointInfoRequest sends a 'dataBreakpointInfo' request.
+func (c *Client) DataBreakpointInfoRequest() {
+	c.send(&dap.DataBreakpointInfoRequest{Request: *c.newRequest("dataBreakpointInfo")})
+}
+
+// SetDataBreakpointsRequest sends a 'setDataBreakpoints' request.
+func (c *Client) SetDataBreakpointsRequest() {
+	c.send(&dap.SetDataBreakpointsRequest{Request: *c.newRequest("setDataBreakpoints")})
+}
+
+// ReadMemoryRequest sends a 'readMemory' request.
+func (c *Client) ReadMemoryRequest() {
+	c.send(&dap.ReadMemoryRequest{Request: *c.newRequest("readMemory")})
+}
+
+// DisassembleRequest sends a 'disassemble' request.
+func (c *Client) DisassembleRequest() {
+	c.send(&dap.DisassembleRequest{Request: *c.newRequest("disassemble")})
+}
+
+// CancelRequest sends a 'cancel' request.
+func (c *Client) CancelRequest() {
+	c.send(&dap.CancelRequest{Request: *c.newRequest("cancel")})
+}
+
+// BreakpointLocationsRequest sends a 'breakpointLocations' request.
+func (c *Client) BreakpointLocationsRequest() {
+	c.send(&dap.BreakpointLocationsRequest{Request: *c.newRequest("breakpointLocations")})
+}
+
+// ModulesRequest sends a 'modules' request.
+func (c *Client) ModulesRequest() {
+	c.send(&dap.ModulesRequest{Request: *c.newRequest("modules")})
+}
+
 // UnknownRequest triggers dap.DecodeProtocolMessageFieldError.
 func (c *Client) UnknownRequest() {
 	request := c.newRequest("unknown")
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index f578676c..2577419e 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -168,15 +168,15 @@ func TestStopOnEntry(t *testing.T) {
 		// 8 >> stackTrace, << stackTrace
 		client.StackTraceRequest()
 		stResp := client.ExpectErrorResponse(t)
-		if stResp.Seq != 0 || stResp.RequestSeq != 8 || stResp.Message != "Unsupported command" {
-			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=8 Message=\"Unsupported command\"", stResp)
+		if stResp.Seq != 0 || stResp.RequestSeq != 8 || stResp.Message != "Not yet implemented" {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=8 Message=\"Not yet implemented\"", stResp)
 		}
 
 		// 9 >> stackTrace, << stackTrace
 		client.StackTraceRequest()
 		stResp = client.ExpectErrorResponse(t)
-		if stResp.Seq != 0 || stResp.RequestSeq != 9 || stResp.Message != "Unsupported command" {
-			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=9 Message=\"Unsupported command\"", stResp)
+		if stResp.Seq != 0 || stResp.RequestSeq != 9 || stResp.Message != "Not yet implemented" {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=9 Message=\"Not yet implemented\"", stResp)
 		}
 
 		// 10 >> continue, << continue, << terminated
@@ -366,6 +366,147 @@ func TestLaunchTestRequest(t *testing.T) {
 	})
 }
 
+func TestUnupportedCommandResponses(t *testing.T) {
+	var got *dap.ErrorResponse
+	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
+		seqCnt := 1
+		expectUnsupportedCommand := func(cmd string) {
+			t.Helper()
+			got = client.ExpectUnsupportedCommandErrorResponse(t)
+			if got.RequestSeq != seqCnt || got.Command != cmd {
+				t.Errorf("\ngot  %#v\nwant RequestSeq=%d Command=%s", got, seqCnt, cmd)
+			}
+			seqCnt++
+		}
+
+		client.RestartFrameRequest()
+		expectUnsupportedCommand("restartFrame")
+
+		client.GotoRequest()
+		expectUnsupportedCommand("goto")
+
+		client.SourceRequest()
+		expectUnsupportedCommand("source")
+
+		client.TerminateThreadsRequest()
+		expectUnsupportedCommand("terminateThreads")
+
+		client.StepInTargetsRequest()
+		expectUnsupportedCommand("stepInTargets")
+
+		client.GotoTargetsRequest()
+		expectUnsupportedCommand("gotoTargets")
+
+		client.CompletionsRequest()
+		expectUnsupportedCommand("completions")
+
+		client.ExceptionInfoRequest()
+		expectUnsupportedCommand("exceptionInfo")
+
+		client.DataBreakpointInfoRequest()
+		expectUnsupportedCommand("dataBreakpointInfo")
+
+		client.SetDataBreakpointsRequest()
+		expectUnsupportedCommand("setDataBreakpoints")
+
+		client.BreakpointLocationsRequest()
+		expectUnsupportedCommand("breakpointLocations")
+
+		client.ModulesRequest()
+		expectUnsupportedCommand("modules")
+	})
+}
+
+func TestRequiredNotYetImplementedResponses(t *testing.T) {
+	var got *dap.ErrorResponse
+	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
+		seqCnt := 1
+		expectNotYetImplemented := func(cmd string) {
+			t.Helper()
+			got = client.ExpectNotYetImplementedErrorResponse(t)
+			if got.RequestSeq != seqCnt || got.Command != cmd {
+				t.Errorf("\ngot  %#v\nwant RequestSeq=%d Command=%s", got, seqCnt, cmd)
+			}
+			seqCnt++
+		}
+
+		client.AttachRequest()
+		expectNotYetImplemented("attach")
+
+		client.NextRequest()
+		expectNotYetImplemented("next")
+
+		client.StepInRequest()
+		expectNotYetImplemented("stepIn")
+
+		client.StepOutRequest()
+		expectNotYetImplemented("stepOut")
+
+		client.PauseRequest()
+		expectNotYetImplemented("pause")
+
+		client.StackTraceRequest()
+		expectNotYetImplemented("stackTrace")
+
+		client.ScopesRequest()
+		expectNotYetImplemented("scopes")
+
+		client.VariablesRequest()
+		expectNotYetImplemented("variables")
+
+		client.EvaluateRequest()
+		expectNotYetImplemented("evaluate")
+	})
+}
+
+func TestOptionalNotYetImplementedResponses(t *testing.T) {
+	var got *dap.ErrorResponse
+	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
+		seqCnt := 1
+		expectNotYetImplemented := func(cmd string) {
+			t.Helper()
+			got = client.ExpectNotYetImplementedErrorResponse(t)
+			if got.RequestSeq != seqCnt || got.Command != cmd {
+				t.Errorf("\ngot  %#v\nwant RequestSeq=%d Command=%s", got, seqCnt, cmd)
+			}
+			seqCnt++
+		}
+
+		client.TerminateRequest()
+		expectNotYetImplemented("terminate")
+
+		client.RestartRequest()
+		expectNotYetImplemented("restart")
+
+		client.SetFunctionBreakpointsRequest()
+		expectNotYetImplemented("setFunctionBreakpoints")
+
+		client.StepBackRequest()
+		expectNotYetImplemented("stepBack")
+
+		client.ReverseContinueRequest()
+		expectNotYetImplemented("reverseContinue")
+
+		client.SetVariableRequest()
+		expectNotYetImplemented("setVariable")
+
+		client.SetExpressionRequest()
+		expectNotYetImplemented("setExpression")
+
+		client.LoadedSourcesRequest()
+		expectNotYetImplemented("loadedSources")
+
+		client.ReadMemoryRequest()
+		expectNotYetImplemented("readMemory")
+
+		client.DisassembleRequest()
+		expectNotYetImplemented("disassemble")
+
+		client.CancelRequest()
+		expectNotYetImplemented("cancel")
+	})
+}
+
 func TestBadLaunchRequests(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		seqCnt := 1