Repository: go-delve/delve
Pull request #2054 (2020-05-15 12:30:23)

gdbserial: ensure registers are loaded in SetDX/SetSP/SetPC
```
gdbserial: ensure registers are loaded in SetDX/SetSP/SetPC

These methods only work if registers have been loaded once after the
last resume, there's probably no code path that calls SetXX before
Thread.Registers but lets make sure it can't happen anyway.

```



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/proc/gdbserial/gdbserver.go b/pkg/proc/gdbserial/gdbserver.go
index dbfb82c8..2c7a5be9 100644
--- a/pkg/proc/gdbserial/gdbserver.go
+++ b/pkg/proc/gdbserial/gdbserver.go
@@ -1840,6 +1840,7 @@ func (r *gdbRegisters) FloatLoadError() error {
 
 // SetPC will set the value of the PC register to the given value.
 func (t *gdbThread) SetPC(pc uint64) error {
+	_, _ = t.Registers() // Registes must be loaded first
 	t.regs.setPC(pc)
 	if t.p.gcmdok {
 		return t.p.conn.writeRegisters(t.strID, t.regs.buf)
@@ -1850,6 +1851,7 @@ func (t *gdbThread) SetPC(pc uint64) error {
 
 // SetSP will set the value of the SP register to the given value.
 func (t *gdbThread) SetSP(sp uint64) error {
+	_, _ = t.Registers() // Registes must be loaded first
 	t.regs.setSP(sp)
 	if t.p.gcmdok {
 		return t.p.conn.writeRegisters(t.strID, t.regs.buf)
@@ -1860,6 +1862,7 @@ func (t *gdbThread) SetSP(sp uint64) error {
 
 // SetDX will set the value of the DX register to the given value.
 func (t *gdbThread) SetDX(dx uint64) error {
+	_, _ = t.Registers() // Registes must be loaded first
 	t.regs.setDX(dx)
 	if t.p.gcmdok {
 		return t.p.conn.writeRegisters(t.strID, t.regs.buf)
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")