Repository: go-delve/delve
Pull request #2090 (2020-06-21 07:29:13)

dwarf/line: support DWARF version 5
DWARFv5 has a new format for the header, directory table and line table
of the debug_line section.



Patch:
diff --git a/README.md b/README.md
index a8824739..a594e7b1 100644
--- a/README.md
+++ b/README.md
@@ -12,6 +12,7 @@ The GitHub issue tracker is for **bugs** only. Please use the [developer mailing
   - [Linux](Documentation/installation/linux/install.md)
   - [macOS](Documentation/installation/osx/install.md)
   - [Windows](Documentation/installation/windows/install.md)
+  - [FreeBSD](Documentation/installation/freebsd/install.md)
 - [Getting Started](Documentation/cli/getting_started.md)
 - [Documentation](Documentation)
   - [Command line options](Documentation/usage/dlv.md)
diff --git a/go.mod b/go.mod
index bfd00bf0..3c079067 100644
--- a/go.mod
+++ b/go.mod
@@ -3,7 +3,7 @@ module github.com/go-delve/delve
 go 1.11
 
 require (
-	github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5
+	github.com/cosiner/argv v0.1.0
 	github.com/cpuguy83/go-md2man v1.0.10 // indirect
 	github.com/creack/pty v1.1.9
 	github.com/google/go-dap v0.2.0
diff --git a/go.sum b/go.sum
index 48f65028..10724b18 100644
--- a/go.sum
+++ b/go.sum
@@ -1,5 +1,7 @@
 github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5 h1:rIXlvz2IWiupMFlC45cZCXZFvKX/ExBcSLrDy2G0Lp8=
 github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5/go.mod h1:p/NrK5tF6ICIly4qwEDsf6VDirFiWWz0FenfYBwJaKQ=
+github.com/cosiner/argv v0.1.0 h1:BVDiEL32lwHukgJKP87btEPenzrrHUjajs/8yzaqcXg=
+github.com/cosiner/argv v0.1.0/go.mod h1:EusR6TucWKX+zFgtdUsKT2Cvg45K5rtpCcWz4hK06d8=
 github.com/cpuguy83/go-md2man v1.0.10 h1:BSKMNlYxDvnunlTymqtgONjNnaRV1sTpcovwwjF22jk=
 github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE=
 github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w=
diff --git a/pkg/dwarf/godwarf/type.go b/pkg/dwarf/godwarf/type.go
index c8d01ab7..c9605ec2 100644
--- a/pkg/dwarf/godwarf/type.go
+++ b/pkg/dwarf/godwarf/type.go
@@ -513,6 +513,22 @@ func (t *ChanType) stringIntl(recCheck recCheck) string {
 	return "chan " + t.ElemType.String()
 }
 
+// An UnsupportedType is a placeholder returned in situations where we
+// encounter a type that isn't supported.
+type UnsupportedType struct {
+	CommonType
+	Tag dwarf.Tag
+}
+
+func (t *UnsupportedType) stringIntl(recCheck) string {
+	if t.Name != "" {
+		return t.Name
+	}
+	return fmt.Sprintf("(unsupported type %s)", t.Tag.String())
+}
+
+func (t *UnsupportedType) String() string { return t.stringIntl(nil) }
+
 // Type reads the type at off in the DWARF ``info'' section.
 func ReadType(d *dwarf.Data, index int, off dwarf.Offset, typeCache map[dwarf.Offset]Type) (Type, error) {
 	typ, err := readType(d, "info", d.Reader(), off, typeCache, nil)
@@ -1008,6 +1024,16 @@ func readType(d *dwarf.Data, name string, r *dwarf.Reader, off dwarf.Offset, typ
 		typ = t
 		typeCache[off] = t
 		t.Name, _ = e.Val(dwarf.AttrName).(string)
+
+	default:
+		// This is some other type DIE that we're currently not
+		// equipped to handle. Return an abstract "unsupported type"
+		// object in such cases.
+		t := new(UnsupportedType)
+		typ = t
+		typeCache[off] = t
+		t.Tag = e.Tag
+		t.Name, _ = e.Val(dwarf.AttrName).(string)
 	}
 
 	if err != nil {
diff --git a/pkg/dwarf/line/line_parser.go b/pkg/dwarf/line/line_parser.go
index 71dac59f..8bf58b44 100644
--- a/pkg/dwarf/line/line_parser.go
+++ b/pkg/dwarf/line/line_parser.go
@@ -84,8 +84,13 @@ func Parse(compdir string, buf *bytes.Buffer, logfn func(string, ...interface{})
 	dbl.normalizeBackslash = normalizeBackslash
 
 	parseDebugLinePrologue(dbl, buf)
-	parseIncludeDirs(dbl, buf)
-	parseFileEntries(dbl, buf)
+	if dbl.Prologue.Version >= 5 {
+		parseIncludeDirs5(dbl, buf)
+		parseFileEntries5(dbl, buf)
+	} else {
+		parseIncludeDirs2(dbl, buf)
+		parseFileEntries2(dbl, buf)
+	}
 
 	// Instructions size calculation breakdown:
 	//   - dbl.Prologue.UnitLength is the length of the entire unit, not including the 4 bytes to represent that length.
@@ -101,6 +106,12 @@ func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
 
 	p.UnitLength = binary.LittleEndian.Uint32(buf.Next(4))
 	p.Version = binary.LittleEndian.Uint16(buf.Next(2))
+	if p.Version >= 5 {
+		dbl.ptrSize = int(buf.Next(1)[0])  // address_size
+		dbl.ptrSize += int(buf.Next(1)[0]) // segment_selector_size
+	}
+
+	// Version 4 or earlier
 	p.Length = binary.LittleEndian.Uint32(buf.Next(4))
 	p.MinInstrLength = uint8(buf.Next(1)[0])
 	if p.Version == 4 {
@@ -119,7 +130,8 @@ func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
 	dbl.Prologue = p
 }
 
-func parseIncludeDirs(info *DebugLineInfo, buf *bytes.Buffer) {
+// parseIncludeDirs2 parses the directory table for DWARF version 2 through 4.
+func parseIncludeDirs2(info *DebugLineInfo, buf *bytes.Buffer) {
 	for {
 		str, _ := util.ParseString(buf)
 		if str == "" {
@@ -130,7 +142,33 @@ func parseIncludeDirs(info *DebugLineInfo, buf *bytes.Buffer) {
 	}
 }
 
-func parseFileEntries(info *DebugLineInfo, buf *bytes.Buffer) {
+// parseIncludeDirs5 parses the directory table for DWARF version 5.
+func parseIncludeDirs5(info *DebugLineInfo, buf *bytes.Buffer) {
+	dirEntryFormReader := readEntryFormat(buf, info.Logf)
+	dirCount, _ := util.DecodeULEB128(buf)
+	info.IncludeDirs = make([]string, 0, dirCount)
+	for i := uint64(0); i < dirCount; i++ {
+		dirEntryFormReader.reset()
+		for dirEntryFormReader.next(buf) {
+			switch dirEntryFormReader.contentType {
+			case _DW_LNCT_path:
+				if dirEntryFormReader.formCode != _DW_FORM_string {
+					info.IncludeDirs = append(info.IncludeDirs, dirEntryFormReader.str)
+				} else {
+					//TODO(aarzilli): support debug_string, debug_line_str
+					info.Logf("unsupported string form %#x", dirEntryFormReader.formCode)
+				}
+			case _DW_LNCT_directory_index:
+			case _DW_LNCT_timestamp:
+			case _DW_LNCT_size:
+			case _DW_LNCT_MD5:
+			}
+		}
+	}
+}
+
+// parseFileEntries2 parses the file table for DWARF 2 through 4
+func parseFileEntries2(info *DebugLineInfo, buf *bytes.Buffer) {
 	for {
 		entry := readFileEntry(info, buf, true)
 		if entry.Path == "" {
@@ -165,3 +203,43 @@ func readFileEntry(info *DebugLineInfo, buf *bytes.Buffer, exitOnEmptyPath bool)
 
 	return entry
 }
+
+// parseFileEntries5 parses the file table for DWARF 5
+func parseFileEntries5(info *DebugLineInfo, buf *bytes.Buffer) {
+	fileEntryFormReader := readEntryFormat(buf, info.Logf)
+	fileCount, _ := util.DecodeULEB128(buf)
+	info.FileNames = make([]*FileEntry, 0, fileCount)
+	for i := 0; i < int(fileCount); i++ {
+		fileEntryFormReader.reset()
+		for fileEntryFormReader.next(buf) {
+			entry := new(FileEntry)
+			var path string
+			var diridx int = -1
+
+			switch fileEntryFormReader.contentType {
+			case _DW_LNCT_path:
+				if fileEntryFormReader.formCode != _DW_FORM_string {
+					path = fileEntryFormReader.str
+				} else {
+					//TODO(aarzilli): support debug_string, debug_line_str
+					info.Logf("unsupported string form %#x", fileEntryFormReader.formCode)
+				}
+			case _DW_LNCT_directory_index:
+				diridx = int(fileEntryFormReader.u64)
+			case _DW_LNCT_timestamp:
+				entry.LastModTime = fileEntryFormReader.u64
+			case _DW_LNCT_size:
+				entry.Length = fileEntryFormReader.u64
+			case _DW_LNCT_MD5:
+				// not implemented
+			}
+
+			if diridx >= 0 && !filepath.IsAbs(path) && diridx < len(info.IncludeDirs) {
+				path = filepath.Join(info.IncludeDirs[diridx], path)
+			}
+			entry.Path = path
+			info.FileNames = append(info.FileNames, entry)
+			info.Lookup[entry.Path] = entry
+		}
+	}
+}
diff --git a/pkg/dwarf/line/parse_util.go b/pkg/dwarf/line/parse_util.go
new file mode 100644
index 00000000..10dd9633
--- /dev/null
+++ b/pkg/dwarf/line/parse_util.go
@@ -0,0 +1,144 @@
+package line
+
+import (
+	"bytes"
+	"encoding/binary"
+
+	"github.com/go-delve/delve/pkg/dwarf/util"
+)
+
+const (
+	_DW_FORM_block      = 0x09
+	_DW_FORM_block1     = 0x0a
+	_DW_FORM_block2     = 0x03
+	_DW_FORM_block4     = 0x04
+	_DW_FORM_data1      = 0x0b
+	_DW_FORM_data2      = 0x05
+	_DW_FORM_data4      = 0x06
+	_DW_FORM_data8      = 0x07
+	_DW_FORM_data16     = 0x1e
+	_DW_FORM_flag       = 0x0c
+	_DW_FORM_line_strp  = 0x1f
+	_DW_FORM_sdata      = 0x0d
+	_DW_FORM_sec_offset = 0x17
+	_DW_FORM_string     = 0x08
+	_DW_FORM_strp       = 0x0e
+	_DW_FORM_strx       = 0x1a
+	_DW_FORM_strx1      = 0x25
+	_DW_FORM_strx2      = 0x26
+	_DW_FORM_strx3      = 0x27
+	_DW_FORM_strx4      = 0x28
+	_DW_FORM_udata      = 0x0f
+)
+
+const (
+	_DW_LNCT_path = 0x1 + iota
+	_DW_LNCT_directory_index
+	_DW_LNCT_timestamp
+	_DW_LNCT_size
+	_DW_LNCT_MD5
+)
+
+type formReader struct {
+	logf         func(string, ...interface{})
+	contentTypes []uint64
+	formCodes    []uint64
+
+	contentType uint64
+	formCode    uint64
+
+	block []byte
+	u64   uint64
+	i64   int64
+	str   string
+
+	nexti int
+}
+
+func readEntryFormat(buf *bytes.Buffer, logf func(string, ...interface{})) *formReader {
+	count := buf.Next(1)[0]
+	r := &formReader{
+		logf:         logf,
+		contentTypes: make([]uint64, count),
+		formCodes:    make([]uint64, count),
+	}
+	for i := range r.contentTypes {
+		r.contentTypes[i], _ = util.DecodeULEB128(buf)
+		r.formCodes[i], _ = util.DecodeULEB128(buf)
+	}
+	return r
+}
+
+func (rdr *formReader) reset() {
+	rdr.nexti = 0
+}
+
+func (rdr *formReader) next(buf *bytes.Buffer) bool {
+	if rdr.nexti >= len(rdr.contentTypes) {
+		return false
+	}
+
+	rdr.contentType = rdr.contentTypes[rdr.nexti]
+	rdr.formCode = rdr.formCodes[rdr.nexti]
+
+	switch rdr.formCode {
+	case _DW_FORM_block:
+		n, _ := util.DecodeULEB128(buf)
+		rdr.readBlock(buf, n)
+
+	case _DW_FORM_block1:
+		rdr.readBlock(buf, uint64(buf.Next(1)[0]))
+
+	case _DW_FORM_block2:
+		rdr.readBlock(buf, uint64(binary.LittleEndian.Uint16(buf.Next(2))))
+
+	case _DW_FORM_block4:
+		rdr.readBlock(buf, uint64(binary.LittleEndian.Uint32(buf.Next(4))))
+
+	case _DW_FORM_data1, _DW_FORM_flag, _DW_FORM_strx1:
+		rdr.u64 = uint64(buf.Next(1)[0])
+
+	case _DW_FORM_data2, _DW_FORM_strx2:
+		rdr.u64 = uint64(binary.LittleEndian.Uint16(buf.Next(2)))
+
+	case _DW_FORM_data4, _DW_FORM_line_strp, _DW_FORM_sec_offset, _DW_FORM_strp, _DW_FORM_strx4:
+		rdr.u64 = uint64(binary.LittleEndian.Uint32(buf.Next(4)))
+
+	case _DW_FORM_data8:
+		rdr.u64 = binary.LittleEndian.Uint64(buf.Next(8))
+
+	case _DW_FORM_data16:
+		rdr.readBlock(buf, 16)
+
+	case _DW_FORM_sdata:
+		rdr.i64, _ = util.DecodeSLEB128(buf)
+
+	case _DW_FORM_udata, _DW_FORM_strx:
+		rdr.u64, _ = util.DecodeULEB128(buf)
+
+	case _DW_FORM_string:
+		rdr.str, _ = util.ParseString(buf)
+
+	case _DW_FORM_strx3:
+		rdr.u64 = uint64(binary.LittleEndian.Uint32(append(buf.Next(3), 0x0)))
+
+	default:
+		if rdr.logf != nil {
+			rdr.logf("unknown form code %#x", rdr.formCode)
+		}
+		rdr.formCodes[rdr.nexti] = ^uint64(0) // only print error once
+	case ^uint64(0):
+		// do nothing
+	}
+
+	rdr.nexti++
+	return true
+}
+
+func (rdr *formReader) readBlock(buf *bytes.Buffer, n uint64) {
+	if cap(rdr.block) < int(n) {
+		rdr.block = make([]byte, 0, n)
+	}
+	rdr.block = rdr.block[:n]
+	buf.Read(rdr.block)
+}
diff --git a/pkg/dwarf/line/state_machine.go b/pkg/dwarf/line/state_machine.go
index c307a106..b5c8abd2 100644
--- a/pkg/dwarf/line/state_machine.go
+++ b/pkg/dwarf/line/state_machine.go
@@ -501,10 +501,14 @@ func advanceline(sm *StateMachine, buf *bytes.Buffer) {
 
 func setfile(sm *StateMachine, buf *bytes.Buffer) {
 	i, _ := util.DecodeULEB128(buf)
-	if i-1 < uint64(len(sm.dbl.FileNames)) {
-		sm.file = sm.dbl.FileNames[i-1].Path
+	if sm.dbl.Prologue.Version < 5 {
+		// in DWARF v5 files are indexed starting from 0, in v4 and prior the index starts at 1
+		i--
+	}
+	if i < uint64(len(sm.dbl.FileNames)) {
+		sm.file = sm.dbl.FileNames[i].Path
 	} else {
-		j := (i - 1) - uint64(len(sm.dbl.FileNames))
+		j := i - uint64(len(sm.dbl.FileNames))
 		if j < uint64(len(sm.definedFiles)) {
 			sm.file = sm.definedFiles[j].Path
 		} else {
diff --git a/pkg/proc/bininfo.go b/pkg/proc/bininfo.go
index 2eebab01..e88107dd 100644
--- a/pkg/proc/bininfo.go
+++ b/pkg/proc/bininfo.go
@@ -1618,6 +1618,8 @@ func (bi *BinaryInfo) loadDebugInfoMaps(image *Image, debugLineBytes []byte, wg
 func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContext, image *Image, reader *reader.Reader, cu *compileUnit) {
 	hasAttrGoPkgName := goversion.ProducerAfterOrEqual(cu.producer, 1, 13)
 
+	depth := 0
+
 	for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
 		if err != nil {
 			image.setLoadError("error reading debug_info: %v", err)
@@ -1625,7 +1627,11 @@ func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContex
 		}
 		switch entry.Tag {
 		case 0:
-			return
+			if depth == 0 {
+				return
+			} else {
+				depth--
+			}
 		case dwarf.TagImportedUnit:
 			bi.loadDebugInfoMapsImportedUnit(entry, ctxt, image, cu)
 			reader.SkipChildren()
@@ -1695,6 +1701,11 @@ func (bi *BinaryInfo) loadDebugInfoMapsCompileUnit(ctxt *loadDebugInfoMapsContex
 					bi.addConcreteSubprogram(entry, ctxt, reader, cu)
 				}
 			}
+
+		default:
+			if entry.Children {
+				depth++
+			}
 		}
 	}
 }
diff --git a/pkg/proc/eval.go b/pkg/proc/eval.go
index f43ee5b2..ddcb7001 100644
--- a/pkg/proc/eval.go
+++ b/pkg/proc/eval.go
@@ -261,13 +261,16 @@ func (scope *EvalScope) Locals() ([]*Variable, error) {
 			locationExpr := v.LocationExpr
 			declLine := v.DeclLine
 			v = v.maybeDereference()
-			if v.Addr == 0 {
+			if v.Addr == 0 && v.Unreadable == nil {
 				v.Unreadable = fmt.Errorf("no address for escaped variable")
 			}
 			v.Name = name[1:]
 			v.Flags |= VariableEscaped
-			locationExpr.isEscaped = true
-			v.LocationExpr = locationExpr
+			// See https://github.com/go-delve/delve/issues/2049 for details
+			if locationExpr != nil {
+				locationExpr.isEscaped = true
+				v.LocationExpr = locationExpr
+			}
 			v.DeclLine = declLine
 			vars[i] = v
 		}
diff --git a/pkg/terminal/command.go b/pkg/terminal/command.go
index 9ec2bf00..1feecf90 100644
--- a/pkg/terminal/command.go
+++ b/pkg/terminal/command.go
@@ -1043,10 +1043,11 @@ func parseNewArgv(args string) (resetArgs bool, newArgv []string, err error) {
 	if args == "" {
 		return false, nil, nil
 	}
-	v, err := argv.Argv([]rune(args), argv.ParseEnv(os.Environ()),
-		func(s []rune, _ map[string]string) ([]rune, error) {
-			return nil, fmt.Errorf("Backtick not supported in '%s'", string(s))
-		})
+	v, err := argv.Argv(args,
+		func(s string) (string, error) {
+			return "", fmt.Errorf("Backtick not supported in '%s'", s)
+		},
+		nil)
 	if err != nil {
 		return false, nil, err
 	}
diff --git a/pkg/terminal/terminal.go b/pkg/terminal/terminal.go
index 54fe799e..27f67f37 100644
--- a/pkg/terminal/terminal.go
+++ b/pkg/terminal/terminal.go
@@ -155,7 +155,7 @@ func (t *Term) sigintGuard(ch <-chan os.Signal, multiClient bool) {
 					t.Close()
 				}
 			default:
-				fmt.Println("only s or q allowed")
+				fmt.Println("only p or q allowed")
 			}
 
 		} else {
diff --git a/service/dap/error_ids.go b/service/dap/error_ids.go
index 6fbd0b6c..52a08b73 100644
--- a/service/dap/error_ids.go
+++ b/service/dap/error_ids.go
@@ -1,18 +1,18 @@
 package dap
 
 // Unique identifiers for messages returned for errors from requests.
+// These values are not mandated by DAP (other than the uniqueness
+// requirement), so each implementation is free to choose their own.
 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
-	// reuses 3000 for launch, attach and program exit failures.
-	// TODO(polina): confirm if the extension expects specific ids
-	// for specific cases, and we must match the existing adaptor
-	// or if these codes can evolve.
-	FailedToContinue       = 3000
-	UnableToDisplayThreads = 2003
+	// Where applicable and for consistency only,
+	// values below are inspired the original vscode-go debug adaptor.
+	FailedToLaunch            = 3000
+	FailedtoAttach            = 3001
+	UnableToDisplayThreads    = 2003
+	UnableToProduceStackTrace = 2004
 	// Add more codes as we support more requests
 )
diff --git a/service/dap/handles.go b/service/dap/handles.go
new file mode 100644
index 00000000..05bfd3dd
--- /dev/null
+++ b/service/dap/handles.go
@@ -0,0 +1,34 @@
+package dap
+
+const startHandle = 1000
+
+// handlesMap maps arbitrary values to unique sequential ids.
+// This provides convenient abstraction of references, offering
+// opacity and allowing simplification of complex identifiers.
+// Based on
+// https://github.com/microsoft/vscode-debugadapter-node/blob/master/adapter/src/handles.ts
+type handlesMap struct {
+	nextHandle  int
+	handleToVal map[int]interface{}
+}
+
+func newHandlesMap() *handlesMap {
+	return &handlesMap{startHandle, make(map[int]interface{})}
+}
+
+func (hs *handlesMap) reset() {
+	hs.nextHandle = startHandle
+	hs.handleToVal = make(map[int]interface{})
+}
+
+func (hs *handlesMap) create(value interface{}) int {
+	next := hs.nextHandle
+	hs.nextHandle++
+	hs.handleToVal[next] = value
+	return next
+}
+
+func (hs *handlesMap) get(handle int) (interface{}, bool) {
+	v, ok := hs.handleToVal[handle]
+	return v, ok
+}
diff --git a/service/dap/server.go b/service/dap/server.go
index c2fd0f20..cf2481d0 100644
--- a/service/dap/server.go
+++ b/service/dap/server.go
@@ -52,10 +52,27 @@ type Server struct {
 	debugger *debugger.Debugger
 	// log is used for structured logging.
 	log *logrus.Entry
-	// stopOnEntry is set to automatically stop the debugee after start.
-	stopOnEntry bool
 	// binaryToRemove is the compiled binary to be removed on disconnect.
 	binaryToRemove string
+	// stackFrameHandles keep track of unique ids created across all goroutines.
+	stackFrameHandles *handlesMap
+	// args tracks special settings for handling debug session requests.
+	args launchAttachArgs
+}
+
+// launchAttachArgs captures arguments from launch/attach request that
+// impact handling of subsequent requests.
+type launchAttachArgs struct {
+	// stopOnEntry is set to automatically stop the debugee after start.
+	stopOnEntry bool
+	// stackTraceDepth is the maximum length of the returned list of stack frames.
+	stackTraceDepth int
+}
+
+// defaultArgs borrows the defaults for the arguments from the original vscode-go adapter.
+var defaultArgs = launchAttachArgs{
+	stopOnEntry:     false,
+	stackTraceDepth: 50,
 }
 
 // NewServer creates a new DAP Server. It takes an opened Listener
@@ -67,10 +84,12 @@ func NewServer(config *service.Config) *Server {
 	logflags.WriteDAPListeningMessage(config.Listener.Addr().String())
 	logger.Debug("DAP server pid = ", os.Getpid())
 	return &Server{
-		config:   config,
-		listener: config.Listener,
-		stopChan: make(chan struct{}),
-		log:      logger,
+		config:            config,
+		listener:          config.Listener,
+		stopChan:          make(chan struct{}),
+		log:               logger,
+		stackFrameHandles: newHandlesMap(),
+		args:              defaultArgs,
 	}
 }
 
@@ -263,7 +282,6 @@ func (s *Server) handleRequest(request dap.Message) {
 		s.onPauseRequest(request)
 	case *dap.StackTraceRequest:
 		// Required
-		// TODO: implement this request in V0
 		s.onStackTraceRequest(request)
 	case *dap.ScopesRequest:
 		// Required
@@ -381,7 +399,7 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 	program, ok := request.Arguments["program"].(string)
 	if !ok || program == "" {
 		s.sendErrorResponse(request.Request,
-			FailedToContinue, "Failed to launch",
+			FailedToLaunch, "Failed to launch",
 			"The program attribute is missing in debug configuration.")
 		return
 	}
@@ -408,7 +426,7 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 			buildFlags, ok = buildFlagsArg.(string)
 			if !ok {
 				s.sendErrorResponse(request.Request,
-					FailedToContinue, "Failed to launch",
+					FailedToLaunch, "Failed to launch",
 					fmt.Sprintf("'buildFlags' attribute '%v' in debug configuration is not a string.", buildFlagsArg))
 				return
 			}
@@ -422,7 +440,7 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 		}
 		if err != nil {
 			s.sendErrorResponse(request.Request,
-				FailedToContinue, "Failed to launch",
+				FailedToLaunch, "Failed to launch",
 				fmt.Sprintf("Build error: %s", err.Error()))
 			return
 		}
@@ -433,13 +451,18 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 	// TODO(polina): support "remote" mode
 	if mode != "exec" && mode != "debug" && mode != "test" {
 		s.sendErrorResponse(request.Request,
-			FailedToContinue, "Failed to launch",
+			FailedToLaunch, "Failed to launch",
 			fmt.Sprintf("Unsupported 'mode' value %q in debug configuration.", mode))
 		return
 	}
 
 	stop, ok := request.Arguments["stopOnEntry"]
-	s.stopOnEntry = ok && stop == true
+	s.args.stopOnEntry = ok && stop == true
+
+	depth, ok := request.Arguments["stackTraceDepth"].(float64)
+	if ok && depth > 0 {
+		s.args.stackTraceDepth = int(depth)
+	}
 
 	var targetArgs []string
 	args, ok := request.Arguments["args"]
@@ -447,7 +470,7 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 		argsParsed, ok := args.([]interface{})
 		if !ok {
 			s.sendErrorResponse(request.Request,
-				FailedToContinue, "Failed to launch",
+				FailedToLaunch, "Failed to launch",
 				fmt.Sprintf("'args' attribute '%v' in debug configuration is not an array.", args))
 			return
 		}
@@ -455,7 +478,7 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 			argParsed, ok := arg.(string)
 			if !ok {
 				s.sendErrorResponse(request.Request,
-					FailedToContinue, "Failed to launch",
+					FailedToLaunch, "Failed to launch",
 					fmt.Sprintf("value '%v' in 'args' attribute in debug configuration is not a string.", arg))
 				return
 			}
@@ -469,7 +492,7 @@ func (s *Server) onLaunchRequest(request *dap.LaunchRequest) {
 	var err error
 	if s.debugger, err = debugger.New(&s.config.Debugger, s.config.ProcessArgs); err != nil {
 		s.sendErrorResponse(request.Request,
-			FailedToContinue, "Failed to launch", err.Error())
+			FailedToLaunch, "Failed to launch", err.Error())
 		return
 	}
 
@@ -532,7 +555,7 @@ func (s *Server) onSetExceptionBreakpointsRequest(request *dap.SetExceptionBreak
 }
 
 func (s *Server) onConfigurationDoneRequest(request *dap.ConfigurationDoneRequest) {
-	if s.stopOnEntry {
+	if s.args.stopOnEntry {
 		e := &dap.StoppedEvent{
 			Event: *newEvent("stopped"),
 			Body:  dap.StoppedEventBody{Reason: "entry", ThreadId: 1, AllThreadsStopped: true},
@@ -540,7 +563,7 @@ func (s *Server) onConfigurationDoneRequest(request *dap.ConfigurationDoneReques
 		s.send(e)
 	}
 	s.send(&dap.ConfigurationDoneResponse{Response: *newResponse(request.Request)})
-	if !s.stopOnEntry {
+	if !s.args.stopOnEntry {
 		s.doContinue()
 	}
 }
@@ -623,10 +646,44 @@ func (s *Server) onPauseRequest(request *dap.PauseRequest) { // TODO V0
 	s.sendNotYetImplementedErrorResponse(request.Request)
 }
 
-// onStackTraceRequest sends a not-yet-implemented error response.
+// stackFrame represents the index of a frame within
+// the context of a stack of a specific goroutine.
+type stackFrame struct {
+	goroutineID int
+	frameIndex  int
+}
+
+// onStackTraceRequest handles ‘stackTrace’ requests.
 // This is a mandatory request to support.
-func (s *Server) onStackTraceRequest(request *dap.StackTraceRequest) { // TODO V0
-	s.sendNotYetImplementedErrorResponse(request.Request)
+func (s *Server) onStackTraceRequest(request *dap.StackTraceRequest) {
+	goroutineID := request.Arguments.ThreadId
+	locs, err := s.debugger.Stacktrace(goroutineID, s.args.stackTraceDepth, 0, nil /*skip locals & args*/)
+	if err != nil {
+		s.sendErrorResponse(request.Request, UnableToProduceStackTrace, "Unable to produce stack trace", err.Error())
+		return
+	}
+
+	stackFrames := make([]dap.StackFrame, len(locs))
+	for i, loc := range locs {
+		uniqueStackFrameID := s.stackFrameHandles.create(stackFrame{goroutineID, i})
+		stackFrames[i] = dap.StackFrame{Id: uniqueStackFrameID, Line: loc.Line}
+		stackFrames[i].Name = loc.Function.Name()
+		if loc.File != "<autogenerated>" {
+			stackFrames[i].Source = dap.Source{Name: filepath.Base(loc.File), Path: loc.File}
+		}
+		stackFrames[i].Column = 0
+	}
+	if request.Arguments.StartFrame > 0 {
+		stackFrames = stackFrames[min(request.Arguments.StartFrame, len(stackFrames)):]
+	}
+	if request.Arguments.Levels > 0 {
+		stackFrames = stackFrames[:min(request.Arguments.Levels, len(stackFrames))]
+	}
+	response := &dap.StackTraceResponse{
+		Response: *newResponse(request.Request),
+		Body:     dap.StackTraceResponseBody{StackFrames: stackFrames, TotalFrames: len(locs)},
+	}
+	s.send(response)
 }
 
 // onScopesRequest sends a not-yet-implemented error response.
@@ -788,6 +845,7 @@ func (s *Server) doContinue() {
 		}
 		return
 	}
+	s.stackFrameHandles.reset()
 	if state.Exited {
 		e := &dap.TerminatedEvent{Event: *newEvent("terminated")}
 		s.send(e)
diff --git a/service/dap/utils.go b/service/dap/utils.go
new file mode 100644
index 00000000..5b4ff3b4
--- /dev/null
+++ b/service/dap/utils.go
@@ -0,0 +1,10 @@
+package dap
+
+// min returns the lowest-valued integer
+// between the two passed into it.
+func min(i, j int) int {
+	if i < j {
+		return i
+	}
+	return j
+}
diff --git a/vendor/github.com/cosiner/argv/README.md b/vendor/github.com/cosiner/argv/README.md
index 7f2f8d21..6e2b495c 100644
--- a/vendor/github.com/cosiner/argv/README.md
+++ b/vendor/github.com/cosiner/argv/README.md
@@ -13,7 +13,7 @@ Documentation can be found at [Godoc](https://godoc.org/github.com/cosiner/argv)
 # Example
 ```Go
 func TestArgv(t *testing.T) {
-	args, err := argv.Argv([]rune(" ls   `echo /`   |  wc  -l "), os.Environ(), argv.Run)
+	args, err := argv.Argv([]rune(" ls   `echo /`   |  wc  -l "), argv.ParseEnv(os.Environ()), argv.Run)
 	if err != nil {
 	    t.Fatal(err)
 	}
@@ -21,7 +21,7 @@ func TestArgv(t *testing.T) {
 	    []string{"ls", "/"},
 	    []string{"wc", "-l"},
 	}
-	if !reflect.DeepDqual(args, expects) {
+	if !reflect.DeepEqual(args, expects) {
 	    t.Fatal(args)
 	}
 }
diff --git a/vendor/github.com/cosiner/argv/argv.go b/vendor/github.com/cosiner/argv/argv.go
index be6bca81..eebf801d 100644
--- a/vendor/github.com/cosiner/argv/argv.go
+++ b/vendor/github.com/cosiner/argv/argv.go
@@ -1,34 +1,10 @@
 // Package argv parse command line string into arguments array using the bash syntax.
 package argv
 
-import "strings"
-
-// ParseEnv parsing environment variables as key/value pair.
-//
-// Item will be ignored if one of the key and value is empty.
-func ParseEnv(env []string) map[string]string {
-	var m map[string]string
-	for _, e := range env {
-		secs := strings.SplitN(e, "=", 2)
-		if len(secs) == 2 {
-			key := strings.TrimSpace(secs[0])
-			val := strings.TrimSpace(secs[1])
-			if key == "" || val == "" {
-				continue
-			}
-			if m == nil {
-				m = make(map[string]string)
-			}
-			m[key] = val
-		}
-	}
-	return m
-}
-
 // Argv split cmdline string as array of argument array by the '|' character.
 //
 // The parsing rules is same as bash. The environment variable will be replaced
 // and string surround by '`' will be passed to reverse quote parser.
-func Argv(cmdline []rune, env map[string]string, reverseQuoteParser ReverseQuoteParser) ([][]string, error) {
-	return NewParser(NewScanner(cmdline, env), reverseQuoteParser).Parse()
+func Argv(cmdline string, backquoteExpander, stringExpander Expander) ([][]string, error) {
+	return NewParser(NewScanner(cmdline), backquoteExpander, stringExpander).Parse()
 }
diff --git a/vendor/github.com/cosiner/argv/cmd.go b/vendor/github.com/cosiner/argv/cmd.go
index 8e17b963..f723c60a 100644
--- a/vendor/github.com/cosiner/argv/cmd.go
+++ b/vendor/github.com/cosiner/argv/cmd.go
@@ -1,33 +1,13 @@
 package argv
 
 import (
-	"bytes"
 	"errors"
 	"io"
+	"os"
 	"os/exec"
-	"strings"
 )
 
-// Run execute cmdline string and return the output
-func Run(cmdline []rune, env map[string]string) ([]rune, error) {
-	args, err := Argv(cmdline, env, Run)
-	if err != nil {
-		return nil, err
-	}
-	cmds, err := Cmds(args)
-	if err != nil {
-		return nil, err
-	}
-
-	output := bytes.NewBuffer(make([]byte, 0, 1024))
-	err = Pipe(nil, output, cmds...)
-	str := output.String()
-	str = strings.TrimSpace(str)
-	return []rune(str), err
-}
-
-// Cmds generate exec.Cmd for each command.
-func Cmds(args [][]string) ([]*exec.Cmd, error) {
+func Cmds(args ...[]string) ([]*exec.Cmd, error) {
 	var cmds []*exec.Cmd
 	for _, argv := range args {
 		if len(argv) == 0 {
@@ -39,30 +19,38 @@ func Cmds(args [][]string) ([]*exec.Cmd, error) {
 	return cmds, nil
 }
 
-// Pipe pipe previous command's stdout to next command's stdin, if in or
-// out is nil, it will be ignored.
-func Pipe(in io.Reader, out io.Writer, cmds ...*exec.Cmd) error {
+func Pipe(stdin io.Reader, stdout, stderr io.Writer, cmds ...*exec.Cmd) error {
+	if stdin == nil {
+		stdin = os.Stdin
+	}
+	if stdout == nil {
+		stdout = os.Stdout
+	}
+	if stderr == nil {
+		stderr = os.Stderr
+	}
 	l := len(cmds)
 	if l == 0 {
 		return nil
 	}
-
 	var err error
-	for i := 1; i < l; i++ {
-		cmds[i].Stdin, err = cmds[i-1].StdoutPipe()
-		if err != nil {
-			break
+	for i := 0; i < l; i++ {
+		if i == 0 {
+			cmds[i].Stdin = stdin
+		} else {
+			cmds[i].Stdin, err = cmds[i-1].StdoutPipe()
+			if stderr != nil {
+				break
+			}
+		}
+		cmds[i].Stderr = stderr
+		if i == l-1 {
+			cmds[i].Stdout = stdout
 		}
 	}
 	if err != nil {
 		return err
 	}
-	if in != nil {
-		cmds[0].Stdin = in
-	}
-	if out != nil {
-		cmds[l-1].Stdout = out
-	}
 	for i := range cmds {
 		err = cmds[i].Start()
 		if err != nil {
diff --git a/vendor/github.com/cosiner/argv/go.mod b/vendor/github.com/cosiner/argv/go.mod
new file mode 100644
index 00000000..6ed4ddac
--- /dev/null
+++ b/vendor/github.com/cosiner/argv/go.mod
@@ -0,0 +1,3 @@
+module github.com/cosiner/argv
+
+go 1.13
diff --git a/vendor/github.com/cosiner/argv/parser.go b/vendor/github.com/cosiner/argv/parser.go
index 2903ee7c..3473b8ac 100644
--- a/vendor/github.com/cosiner/argv/parser.go
+++ b/vendor/github.com/cosiner/argv/parser.go
@@ -1,17 +1,20 @@
 package argv
 
-import "errors"
+import (
+	"errors"
+	"fmt"
+	"os"
+)
 
 type (
-	// ReverseQuoteParser parse strings quoted by '`' and return it's result. Commonly,
-	// it should run it os command.
-	ReverseQuoteParser func([]rune, map[string]string) ([]rune, error)
+	Expander func(string) (string, error)
 
 	// Parser take tokens from Scanner, and do syntax checking, and generate the splitted arguments array.
 	Parser struct {
-		s      *Scanner
-		tokbuf []Token
-		r      ReverseQuoteParser
+		s                 *Scanner
+		tokbuf            []Token
+		backQuoteExpander Expander
+		stringExpander    Expander
 
 		sections    [][]string
 		currSection []string
@@ -22,16 +25,23 @@ type (
 )
 
 // NewParser create a cmdline string parser.
-func NewParser(s *Scanner, r ReverseQuoteParser) *Parser {
-	if r == nil {
-		r = func(r []rune, env map[string]string) ([]rune, error) {
-			return r, nil
+func NewParser(s *Scanner, backQuoteExpander, stringExpander Expander) *Parser {
+	if backQuoteExpander == nil {
+		backQuoteExpander = func(r string) (string, error) {
+			return r, fmt.Errorf("back quote doesn't allowed")
+		}
+	}
+	if stringExpander == nil {
+		stringExpander = func(runes string) (string, error) {
+			s := os.ExpandEnv(string(runes))
+			return s, nil
 		}
 	}
 
 	return &Parser{
-		s: s,
-		r: r,
+		s:                 s,
+		backQuoteExpander: backQuoteExpander,
+		stringExpander:    stringExpander,
 	}
 }
 
@@ -61,7 +71,7 @@ func (p *Parser) unreadToken(tok Token) {
 //   Section = [Space] SpacedSection { SpacedSection }
 //   SpacedSection = MultipleUnit [Space]
 //   MultipleUnit = Unit {Unit}
-//   Unit = String | ReverseQuote
+//   Unit = String | BackQuote | SingleQuote | DoubleQuote
 func (p *Parser) Parse() ([][]string, error) {
 	err := p.cmdline()
 	if err != nil {
@@ -96,23 +106,26 @@ func (p *Parser) section() error {
 		return err
 	}
 
-	var isFirst = true
+	var hasUnit bool
 	for {
-		unit, err := p.spacedSection()
-		if isFirst {
-			isFirst = false
-		} else {
-			if err == ErrInvalidSyntax {
-				break
-			}
-		}
+		unit, ok, err := p.spacedSection()
 		if err != nil {
 			return err
 		}
+		if !ok {
+			break
+		}
+		hasUnit = true
 
-		p.appendUnit(leftSpace, unit)
+		err = p.appendUnit(leftSpace, unit)
+		if err != nil {
+			return err
+		}
 		leftSpace = unit.rightSpace
 	}
+	if !hasUnit {
+		return ErrInvalidSyntax
+	}
 	return nil
 }
 
@@ -121,47 +134,45 @@ type unit struct {
 	toks       []Token
 }
 
-func (p *Parser) spacedSection() (u unit, err error) {
-	u.toks, err = p.multipleUnit()
+func (p *Parser) spacedSection() (u unit, ok bool, err error) {
+	u.toks, ok, err = p.multipleUnit()
 	if err != nil {
 		return
 	}
+	if !ok {
+		return u, false, nil
+	}
 	u.rightSpace, err = p.optional(TokSpace)
 	return
 }
 
-func (p *Parser) multipleUnit() ([]Token, error) {
+func (p *Parser) multipleUnit() ([]Token, bool, error) {
 	var (
-		toks    []Token
-		isFirst = true
+		toks []Token
 	)
 	for {
-		tok, err := p.unit()
-		if isFirst {
-			isFirst = false
-		} else {
-			if err == ErrInvalidSyntax {
-				break
-			}
-		}
+		tok, ok, err := p.unit()
 		if err != nil {
-			return nil, err
+			return nil, false, err
+		}
+		if !ok {
+			break
 		}
 		toks = append(toks, tok)
 	}
-	return toks, nil
+	return toks, len(toks) > 0, nil
 }
 
-func (p *Parser) unit() (Token, error) {
+func (p *Parser) unit() (Token, bool, error) {
 	tok, err := p.nextToken()
 	if err != nil {
-		return tok, err
+		return tok, false, err
 	}
-	if p.accept(tok.Type, TokString, TokReversequote) {
-		return tok, nil
+	if p.accept(tok.Type, TokString, TokStringSingleQuote, TokStringDoubleQuote, TokBackQuote) {
+		return tok, true, nil
 	}
 	p.unreadToken(tok)
-	return tok, ErrInvalidSyntax
+	return tok, false, nil
 }
 
 func (p *Parser) optional(typ TokenType) (bool, error) {
@@ -190,14 +201,27 @@ func (p *Parser) appendUnit(leftSpace bool, u unit) error {
 		p.currStr = p.currStr[:0]
 	}
 	for _, tok := range u.toks {
-		if tok.Type == TokReversequote {
-			val, err := p.r(tok.Value, p.s.envs())
+		switch tok.Type {
+		case TokBackQuote:
+			expanded, err := p.backQuoteExpander(string(tok.Value))
+			if err != nil {
+				return fmt.Errorf("expand string back quoted failed: %s, %w", string(tok.Value), err)
+			}
+			p.currStr = append(p.currStr, []rune(expanded)...)
+		case TokString:
+			expanded, err := p.stringExpander(string(tok.Value))
 			if err != nil {
-				return err
+				return fmt.Errorf("expand string failed: %s, %w", string(tok.Value), err)
 			}
-			p.currStr = append(p.currStr, val...)
-		} else {
+			p.currStr = append(p.currStr, []rune(expanded)...)
+		case TokStringSingleQuote:
 			p.currStr = append(p.currStr, tok.Value...)
+		case TokStringDoubleQuote:
+			expanded, err := p.stringExpander(string(tok.Value))
+			if err != nil {
+				return fmt.Errorf("expand string double quoted failed: %s, %w", string(tok.Value), err)
+			}
+			p.currStr = append(p.currStr, []rune(expanded)...)
 		}
 	}
 	p.currStrValid = true
diff --git a/vendor/github.com/cosiner/argv/scanner.go b/vendor/github.com/cosiner/argv/scanner.go
index 47497511..287061af 100644
--- a/vendor/github.com/cosiner/argv/scanner.go
+++ b/vendor/github.com/cosiner/argv/scanner.go
@@ -1,30 +1,24 @@
 package argv
 
-import "unicode"
+import (
+	"unicode"
+)
 
 // Scanner is a cmdline string scanner.
 //
 // It split cmdline string to tokens: space, string, pipe, reverse quote string.
 type Scanner struct {
-	env map[string]string
-
-	text      []rune
-	rpos      int
-	dollarBuf []rune
+	text []rune
+	rpos int
 }
 
 // NewScanner create a scanner and init it's internal states.
-func NewScanner(text []rune, env map[string]string) *Scanner {
+func NewScanner(text string) *Scanner {
 	return &Scanner{
-		text: text,
-		env:  env,
+		text: []rune(text),
 	}
 }
 
-func (s *Scanner) envs() map[string]string {
-	return s.env
-}
-
 const _RuneEOF = 0
 
 func (s *Scanner) nextRune() rune {
@@ -67,13 +61,6 @@ func (s *Scanner) isEscapeChars(r rune) (rune, bool) {
 	return r, false
 }
 
-func (s *Scanner) endEnv(r rune) bool {
-	if r == '_' || (r >= 'a' && r <= 'z') || (r >= 'A' && r <= 'Z') || (r >= '0' && r <= '9') {
-		return false
-	}
-	return true
-}
-
 // TokenType is the type of tokens recognized by the scanner.
 type TokenType uint32
 
@@ -84,61 +71,20 @@ type Token struct {
 }
 
 const (
-	// TokString for string, single quoted string and double quoted string
+	// TokString for string
 	TokString TokenType = iota + 1
+	TokStringSingleQuote
+	TokStringDoubleQuote
 	// TokPipe is the '|' character
 	TokPipe
-	// TokReversequote is reverse quoted string
-	TokReversequote
+	// TokBackQuote is reverse quoted string
+	TokBackQuote
 	// TokSpace represent space character sequence
 	TokSpace
 	// TokEOF means the input end.
 	TokEOF
 )
 
-func (s *Scanner) getEnv(name string) string {
-	return s.env[name]
-}
-
-func (s *Scanner) specialVar(r rune) (string, bool) {
-	switch r {
-	case '0', '*', '#', '@', '?', '$':
-		v, has := s.env[string(r)]
-		return v, has
-	default:
-		return "", false
-	}
-}
-
-func (s *Scanner) checkDollarStart(tok *Token, r rune, from, switchTo uint8) uint8 {
-	state := from
-	nr := s.nextRune()
-	if val, has := s.specialVar(nr); has {
-		if val != "" {
-			tok.Value = append(tok.Value, []rune(val)...)
-		}
-	} else if s.endEnv(nr) {
-		tok.Value = append(tok.Value, r)
-		s.unreadRune(nr)
-	} else {
-		state = switchTo
-		s.dollarBuf = append(s.dollarBuf[:0], nr)
-	}
-	return state
-}
-
-func (s *Scanner) checkDollarEnd(tok *Token, r rune, from, switchTo uint8) uint8 {
-	var state = from
-	if s.endEnv(r) {
-		tok.Value = append(tok.Value, []rune(s.getEnv(string(s.dollarBuf)))...)
-		state = switchTo
-		s.unreadRune(r)
-	} else {
-		s.dollarBuf = append(s.dollarBuf, r)
-	}
-	return state
-}
-
 // Next return next token, if it reach the end, TOK_EOF will be returned.
 //
 // Error is returned for invalid syntax such as unpaired quotes.
@@ -146,12 +92,10 @@ func (s *Scanner) Next() (Token, error) {
 	const (
 		Initial = iota + 1
 		Space
-		ReverseQuote
+		BackQuote
 		String
-		StringDollar
-		StringQuoteSingle
-		StringQuoteDouble
-		StringQuoteDoubleDollar
+		StringSingleQuote
+		StringDoubleQuote
 	)
 
 	var (
@@ -159,7 +103,6 @@ func (s *Scanner) Next() (Token, error) {
 
 		state uint8 = Initial
 	)
-	s.dollarBuf = s.dollarBuf[:0]
 	for {
 		r := s.nextRune()
 		switch state {
@@ -172,10 +115,14 @@ func (s *Scanner) Next() (Token, error) {
 				tok.Type = TokPipe
 				return tok, nil
 			case r == '`':
-				state = ReverseQuote
+				state = BackQuote
 			case unicode.IsSpace(r):
 				state = Space
 				s.unreadRune(r)
+			case r == '\'':
+				state = StringSingleQuote
+			case r == '"':
+				state = StringDoubleQuote
 			default:
 				state = String
 				s.unreadRune(r)
@@ -186,87 +133,64 @@ func (s *Scanner) Next() (Token, error) {
 				tok.Type = TokSpace
 				return tok, nil
 			}
-		case ReverseQuote:
+		case BackQuote:
 			switch r {
 			case _RuneEOF:
 				return tok, ErrInvalidSyntax
 			case '`':
-				tok.Type = TokReversequote
+				tok.Type = TokBackQuote
 				return tok, nil
 			default:
 				tok.Value = append(tok.Value, r)
 			}
 		case String:
 			switch {
-			case r == _RuneEOF || r == '|' || r == '`' || unicode.IsSpace(r):
+			case r == _RuneEOF, r == '|', r == '`', r == '\'', r == '"', unicode.IsSpace(r):
 				tok.Type = TokString
 				s.unreadRune(r)
 				return tok, nil
-			case r == '\'':
-				state = StringQuoteSingle
-			case r == '"':
-				state = StringQuoteDouble
 			case r == '\\':
 				nr := s.nextRune()
 				if nr == _RuneEOF {
 					return tok, ErrInvalidSyntax
 				}
 				tok.Value = append(tok.Value, nr)
-			case r == '$':
-				state = s.checkDollarStart(&tok, r, state, StringDollar)
 			default:
 				tok.Value = append(tok.Value, r)
 			}
-		case StringDollar:
-			state = s.checkDollarEnd(&tok, r, state, String)
-		case StringQuoteSingle:
+		case StringSingleQuote, StringDoubleQuote:
 			switch r {
 			case _RuneEOF:
 				return tok, ErrInvalidSyntax
-			case '\'':
-				state = String
-			case '\\':
-				nr := s.nextRune()
-				if escape, ok := s.isEscapeChars(nr); ok {
-					tok.Value = append(tok.Value, escape)
+			case '\'', '"':
+				if singleQuote := state == StringSingleQuote; singleQuote == (r == '\'') {
+					if singleQuote {
+						tok.Type = TokStringSingleQuote
+					} else {
+						tok.Type = TokStringDoubleQuote
+					}
+					return tok, nil
 				} else {
 					tok.Value = append(tok.Value, r)
-					s.unreadRune(nr)
 				}
-			default:
-				tok.Value = append(tok.Value, r)
-			}
-		case StringQuoteDouble:
-			switch r {
-			case _RuneEOF:
-				return tok, ErrInvalidSyntax
-			case '"':
-				state = String
 			case '\\':
 				nr := s.nextRune()
-				if nr == _RuneEOF {
-					return tok, ErrInvalidSyntax
-				}
 				if escape, ok := s.isEscapeChars(nr); ok {
 					tok.Value = append(tok.Value, escape)
 				} else {
 					tok.Value = append(tok.Value, r)
 					s.unreadRune(nr)
 				}
-			case '$':
-				state = s.checkDollarStart(&tok, r, state, StringQuoteDoubleDollar)
 			default:
 				tok.Value = append(tok.Value, r)
 			}
-		case StringQuoteDoubleDollar:
-			state = s.checkDollarEnd(&tok, r, state, StringQuoteDouble)
 		}
 	}
 }
 
 // Scan is a utility function help split input text as tokens.
-func Scan(text []rune, env map[string]string) ([]Token, error) {
-	s := NewScanner(text, env)
+func Scan(text string) ([]Token, error) {
+	s := NewScanner(text)
 	var tokens []Token
 	for {
 		tok, err := s.Next()
diff --git a/vendor/modules.txt b/vendor/modules.txt
index ccde4992..93bab999 100644
--- a/vendor/modules.txt
+++ b/vendor/modules.txt
@@ -1,4 +1,4 @@
-# github.com/cosiner/argv v0.0.0-20170225145430-13bacc38a0a5
+# github.com/cosiner/argv v0.1.0
 github.com/cosiner/argv
 # github.com/cpuguy83/go-md2man v1.0.10
 github.com/cpuguy83/go-md2man/md2man


Test patch:
diff --git a/pkg/proc/dwarf_export_test.go b/pkg/proc/dwarf_export_test.go
new file mode 100644
index 00000000..de8f505a
--- /dev/null
+++ b/pkg/proc/dwarf_export_test.go
@@ -0,0 +1,6 @@
+package proc
+
+// PackageVars returns bi.packageVars (for tests)
+func (bi *BinaryInfo) PackageVars() []packageVar {
+	return bi.packageVars
+}
diff --git a/pkg/proc/dwarf_expr_test.go b/pkg/proc/dwarf_expr_test.go
index 73abafd8..2410ecc5 100644
--- a/pkg/proc/dwarf_expr_test.go
+++ b/pkg/proc/dwarf_expr_test.go
@@ -323,3 +323,33 @@ func TestIssue1636_InlineWithoutOrigin(t *testing.T) {
 	dwb.TagClose()
 	fakeBinaryInfo(t, dwb)
 }
+
+func TestUnsupportedType(t *testing.T) {
+	// Tests that reading an unsupported type does not cause an error
+	dwb := dwarfbuilder.New()
+	dwb.AddCompileUnit("main", 0x0)
+	off := dwb.TagOpen(dwarf.TagReferenceType, "blah")
+	dwb.TagClose()
+	dwb.TagClose()
+	_, dw := fakeBinaryInfo(t, dwb)
+	_, err := godwarf.ReadType(dw, 0, off, make(map[dwarf.Offset]godwarf.Type))
+	if err != nil {
+		t.Errorf("unexpected error reading unsupported type: %#v", err)
+	}
+}
+
+func TestNestedCompileUnts(t *testing.T) {
+	// Tests that a compile unit with a nested entry that we don't care about
+	// (such as a DW_TAG_namespace) is read fully.
+	dwb := dwarfbuilder.New()
+	dwb.AddCompileUnit("main", 0x0)
+	dwb.TagOpen(dwarf.TagNamespace, "namespace")
+	dwb.AddVariable("var1", 0x0, uint64(0x0))
+	dwb.TagClose()
+	dwb.AddVariable("var2", 0x0, uint64(0x0))
+	dwb.TagClose()
+	bi, _ := fakeBinaryInfo(t, dwb)
+	if n := len(bi.PackageVars()); n != 2 {
+		t.Errorf("expected 2 variables, got %d", n)
+	}
+}
diff --git a/service/dap/daptest/client.go b/service/dap/daptest/client.go
index 67a70966..6b8fce3e 100644
--- a/service/dap/daptest/client.go
+++ b/service/dap/daptest/client.go
@@ -368,8 +368,11 @@ func (c *Client) ThreadsRequest() {
 }
 
 // StackTraceRequest sends a 'stackTrace' request.
-func (c *Client) StackTraceRequest() {
+func (c *Client) StackTraceRequest(threadID, startFrame, levels int) {
 	request := &dap.StackTraceRequest{Request: *c.newRequest("stackTrace")}
+	request.Arguments.ThreadId = threadID
+	request.Arguments.StartFrame = startFrame
+	request.Arguments.Levels = levels
 	c.send(request)
 }
 
diff --git a/service/dap/server_test.go b/service/dap/server_test.go
index ff86808b..4223cc6b 100644
--- a/service/dap/server_test.go
+++ b/service/dap/server_test.go
@@ -166,17 +166,17 @@ func TestStopOnEntry(t *testing.T) {
 		}
 
 		// 8 >> stackTrace, << stackTrace
-		client.StackTraceRequest()
+		client.StackTraceRequest(1, 0, 20)
 		stResp := client.ExpectErrorResponse(t)
-		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)
+		if stResp.Seq != 0 || stResp.RequestSeq != 8 || stResp.Body.Error.Format != "Unable to produce stack trace: unknown goroutine 1" {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=8 Format=\"Unable to produce stack trace: unknown goroutine 1\"", stResp)
 		}
 
 		// 9 >> stackTrace, << stackTrace
-		client.StackTraceRequest()
+		client.StackTraceRequest(1, 0, 20)
 		stResp = client.ExpectErrorResponse(t)
-		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)
+		if stResp.Seq != 0 || stResp.RequestSeq != 9 || stResp.Body.Error.Id != 2004 {
+			t.Errorf("\ngot %#v\nwant Seq=0, RequestSeq=9 Id=2004", stResp)
 		}
 
 		// 10 >> continue, << continue, << terminated
@@ -306,8 +306,34 @@ func TestSetBreakpoint(t *testing.T) {
 			}
 		}
 
+		client.StackTraceRequest(1, 0, 20)
+		stResp := client.ExpectStackTraceResponse(t)
+
+		if stResp.Body.TotalFrames != 6 {
+			t.Errorf("\ngot %#v\nwant TotalFrames=6", stResp.Body.TotalFrames)
+		}
+		if len(stResp.Body.StackFrames) != 6 {
+			t.Errorf("\ngot %#v\nwant len(StackFrames)=6", stResp.Body.StackFrames)
+		} else {
+			expectFrame := func(got dap.StackFrame, id int, name string, sourceName string, line int) {
+				t.Helper()
+				if got.Id != id || got.Name != name {
+					t.Errorf("\ngot  %#v\nwant Id=%d Name=%s", got, id, name)
+				}
+				if (sourceName != "" && got.Source.Name != sourceName) || (line > 0 && got.Line != line) {
+					t.Errorf("\ngot  %#v\nwant Source.Name=%s Line=%d", got, sourceName, line)
+				}
+			}
+			expectFrame(stResp.Body.StackFrames[0], 1000, "main.Increment", "increment.go", 8)
+			expectFrame(stResp.Body.StackFrames[1], 1001, "main.Increment", "increment.go", 11)
+			expectFrame(stResp.Body.StackFrames[2], 1002, "main.Increment", "increment.go", 11)
+			expectFrame(stResp.Body.StackFrames[3], 1003, "main.main", "increment.go", 17)
+			expectFrame(stResp.Body.StackFrames[4], 1004, "runtime.main", "proc.go", -1)
+			expectFrame(stResp.Body.StackFrames[5], 1005, "runtime.goexit", "", -1)
+		}
+
 		// TODO(polina): add other status checking requests
-		// that are not yet supported (stackTrace, scopes, variables)
+		// that are not yet supported (scopes, variables)
 
 		client.ContinueRequest(1)
 		client.ExpectContinueResponse(t)
@@ -319,10 +345,144 @@ func TestSetBreakpoint(t *testing.T) {
 	})
 }
 
-// runDebugSesion is a helper for executing the standard init and shutdown
+// expectStackFrames is a helper for verifying the values within StackTraceResponse.
+//     wantStartLine - file line of the first returned frame (non-positive values are ignored).
+//     wantStartID - id of the first frame returned (ignored if wantFrames is 0).
+//     wantFrames - number of frames returned.
+//     wantTotalFrames - total number of stack frames (StackTraceResponse.Body.TotalFrames).
+func expectStackFrames(t *testing.T, got *dap.StackTraceResponse,
+	wantStartLine, wantStartID, wantFrames, wantTotalFrames int) {
+	t.Helper()
+	if got.Body.TotalFrames != wantTotalFrames {
+		t.Errorf("\ngot  %#v\nwant TotalFrames=%d", got.Body.TotalFrames, wantTotalFrames)
+	}
+	if len(got.Body.StackFrames) != wantFrames {
+		t.Errorf("\ngot  len(StackFrames)=%d\nwant %d", len(got.Body.StackFrames), wantFrames)
+	} else {
+		// Verify that frame ids are consecutive numbers starting at wantStartID
+		for i := 0; i < wantFrames; i++ {
+			if got.Body.StackFrames[i].Id != wantStartID+i {
+				t.Errorf("\ngot  %#v\nwant Id=%d", got.Body.StackFrames[i], wantStartID+i)
+			}
+		}
+		// Verify the line corresponding to the first returned frame (if any).
+		// This is useful when the first frame is the frame corresponding to the breakpoint at
+		// a predefined line. Values < 0 are a signal to skip the check (which can be useful
+		// for frames in the third-party code, where we do not control the lines).
+		if wantFrames > 0 && wantStartLine > 0 && got.Body.StackFrames[0].Line != wantStartLine {
+			t.Errorf("\ngot  Line=%d\nwant %d", got.Body.StackFrames[0].Line, wantStartLine)
+		}
+	}
+}
+
+// TestStackTraceRequest executes to a breakpoint (similarly to TestSetBreakpoint
+// that includes more thorough checking of that sequence) and tests different
+// good and bad configurations of 'stackTrace' requests.
+func TestStackTraceRequest(t *testing.T) {
+	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
+		var stResp *dap.StackTraceResponse
+		runDebugSessionWithBPs(t, client,
+			// Launch
+			func() {
+				client.LaunchRequest("exec", fixture.Path, !stopOnEntry)
+			},
+			// Set breakpoints
+			fixture.Source, []int{8, 18},
+			[]func(){
+				// Stop at line 8
+				func() {
+					t.Helper()
+					client.StackTraceRequest(1, 0, 0)
+					stResp = client.ExpectStackTraceResponse(t)
+					expectStackFrames(t, stResp, 8, 1000, 6, 6)
+
+					// Even though the stack frames are the same,
+					// repeated requests at the same breakpoint,
+					// would assign unique ids to them each time.
+					client.StackTraceRequest(1, -100, 0) // Negative startFrame is treated as 0
+					stResp = client.ExpectStackTraceResponse(t)
+					expectStackFrames(t, stResp, 8, 1006, 6, 6)
+
+					client.StackTraceRequest(1, 3, 0)
+					stResp = client.ExpectStackTraceResponse(t)
+					expectStackFrames(t, stResp, 17, 1015, 3, 6)
+
+					client.StackTraceRequest(1, 6, 0)
+					stResp = client.ExpectStackTraceResponse(t)
+					expectStackFrames(t, stResp, -1, -1, 0, 6)
+
+					client.StackTraceRequest(1, 7, 0) // Out of bounds startFrame is capped at len
+					stResp = client.ExpectStackTraceResponse(t)
+					expectStackFrames(t, stResp, -1, -1, 0, 6)
+				},
+				// Stop at line 18
+				func() {
+					t.Helper()
+					// Frame ids get reset at each breakpoint.
+					client.StackTraceRequest(1, 0, 0)
+					stResp = client.ExpectStackTraceResponse(t)
+					expectStackFrames(t, stResp, 18, 1000, 3, 3)
+
+					client.StackTraceRequest(1, 0, -100) // Negative levels is treated as 0
+					stResp = client.ExpectStackTraceResponse(t)
+					expectStackFrames(t, stResp, 18, 1003, 3, 3)
+
+					client.StackTraceRequest(1, 0, 2)
+					stResp = client.ExpectStackTraceResponse(t)
+					expectStackFrames(t, stResp, 18, 1006, 2, 3)
+
+					client.StackTraceRequest(1, 0, 3)
+					stResp = client.ExpectStackTraceResponse(t)
+					expectStackFrames(t, stResp, 18, 1009, 3, 3)
+
+					client.StackTraceRequest(1, 0, 4) // Out of bounds levels is capped at len
+					stResp = client.ExpectStackTraceResponse(t)
+					expectStackFrames(t, stResp, 18, 1012, 3, 3)
+
+					client.StackTraceRequest(1, 1, 2)
+					stResp = client.ExpectStackTraceResponse(t)
+					expectStackFrames(t, stResp, -1, 1016, 2, 3) // Don't test for runtime line we don't control
+				}})
+	})
+}
+
+// Tests that 'stackTraceDepth' from LaunchRequest is parsed and passed to
+// stacktrace requests handlers.
+func TestLaunchRequestWithStackTraceDepth(t *testing.T) {
+	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
+		var stResp *dap.StackTraceResponse
+		runDebugSessionWithBPs(t, client,
+			// Launch
+			func() {
+				client.LaunchRequestWithArgs(map[string]interface{}{
+					"mode": "exec", "program": fixture.Path, "stackTraceDepth": 1,
+				})
+			},
+			// Set breakpoints
+			fixture.Source, []int{8},
+			[]func(){
+				// Stop at line 8
+				func() {
+					t.Helper()
+					client.StackTraceRequest(1, 0, 0)
+					stResp = client.ExpectStackTraceResponse(t)
+					expectStackFrames(t, stResp, 8, 1000, 2, 2)
+				}})
+	})
+}
+
+// runDebugSesionWithBPs is a helper for executing the common init and shutdown
 // sequences for a program that does not stop on entry
-// while specifying unique launch criteria via parameters.
-func runDebugSession(t *testing.T, client *daptest.Client, launchRequest func()) {
+// while specifying breakpoints and unique launch criteria via parameters.
+//     launchRequest - a function that sends a launch request, so the test author
+//                     has full control of its arguments. Note that he rest of
+//                     the test sequence assumes that stopOneEntry is false.
+//     breakpoints   - list of lines, where breakpoints are to be set
+//     onBreakpoints - list of functions to be called at each of the above breakpoints.
+//                     These can be used to simulate additional editor-driven or
+//                     user-driven requests that could occur at each stopped breakpoint.
+func runDebugSessionWithBPs(t *testing.T, client *daptest.Client, launchRequest func(), source string, breakpoints []int, onBreakpoints []func()) {
+	t.Helper()
 	client.InitializeRequest()
 	client.ExpectInitializeResponse(t)
 
@@ -330,19 +490,36 @@ func runDebugSession(t *testing.T, client *daptest.Client, launchRequest func())
 	client.ExpectInitializedEvent(t)
 	client.ExpectLaunchResponse(t)
 
-	// Skip no-op setBreakpoints
+	client.SetBreakpointsRequest(source, breakpoints)
+	client.ExpectSetBreakpointsResponse(t)
+
 	// Skip no-op setExceptionBreakpoints
 
 	client.ConfigurationDoneRequest()
 	client.ExpectConfigurationDoneResponse(t)
 
-	// Program automatically continues to completion
+	// Program automatically continues to breakpoint or completion
+
+	for _, onBP := range onBreakpoints {
+		client.ExpectStoppedEvent(t)
+		onBP()
+		client.ContinueRequest(1)
+		client.ExpectContinueResponse(t)
+		// "Continue" is triggered after the response is sent
+	}
 
 	client.ExpectTerminatedEvent(t)
 	client.DisconnectRequest()
 	client.ExpectDisconnectResponse(t)
 }
 
+// runDebugSesion is a helper for executing the standard init and shutdown
+// sequences for a program that does not stop on entry
+// while specifying unique launch criteria via parameters.
+func runDebugSession(t *testing.T, client *daptest.Client, launchRequest func()) {
+	runDebugSessionWithBPs(t, client, launchRequest, "", nil, nil)
+}
+
 func TestLaunchDebugRequest(t *testing.T) {
 	runTest(t, "increment", func(client *daptest.Client, fixture protest.Fixture) {
 		// We reuse the harness that builds, but ignore the built binary,
@@ -477,9 +654,6 @@ func TestRequiredNotYetImplementedResponses(t *testing.T) {
 		client.PauseRequest()
 		expectNotYetImplemented("pause")
 
-		client.StackTraceRequest()
-		expectNotYetImplemented("stackTrace")
-
 		client.ScopesRequest()
 		expectNotYetImplemented("scopes")