mirror of
https://github.com/beego/bee.git
synced 2024-11-22 10:10:53 +00:00
Merge pull request #392 from beego/hotfix-dlv-support
Use conditional build for Delve support
This commit is contained in:
commit
52a37a86a9
@ -13,7 +13,7 @@ install:
|
|||||||
script:
|
script:
|
||||||
- go vet $(go list ./... | grep -v /vendor/)
|
- go vet $(go list ./... | grep -v /vendor/)
|
||||||
- structcheck $(go list ./... | grep -v /vendor/)
|
- structcheck $(go list ./... | grep -v /vendor/)
|
||||||
- gosimple -ignore "github.com/beego/bee/cmd/commands/run/*.go:S1024" $(go list ./... | grep -v /vendor/)
|
- gosimple -ignore "$(cat gosimple.ignore)" $(go list ./... | grep -v /vendor/)
|
||||||
- staticcheck $(go list ./... | grep -v /vendor/)
|
- staticcheck $(go list ./... | grep -v /vendor/)
|
||||||
- unused $(go list ./... | grep -v /vendor/)
|
- unused $(go list ./... | grep -v /vendor/)
|
||||||
- unconvert $(go list ./... | grep -v /vendor/)
|
- unconvert $(go list ./... | grep -v /vendor/)
|
||||||
|
@ -14,122 +14,3 @@
|
|||||||
|
|
||||||
// Package dlv ...
|
// Package dlv ...
|
||||||
package dlv
|
package dlv
|
||||||
|
|
||||||
import (
|
|
||||||
"flag"
|
|
||||||
"fmt"
|
|
||||||
"net"
|
|
||||||
"os"
|
|
||||||
"path/filepath"
|
|
||||||
|
|
||||||
"github.com/beego/bee/cmd/commands"
|
|
||||||
"github.com/beego/bee/cmd/commands/version"
|
|
||||||
beeLogger "github.com/beego/bee/logger"
|
|
||||||
"github.com/beego/bee/utils"
|
|
||||||
"github.com/derekparker/delve/pkg/terminal"
|
|
||||||
"github.com/derekparker/delve/service"
|
|
||||||
"github.com/derekparker/delve/service/rpc2"
|
|
||||||
"github.com/derekparker/delve/service/rpccommon"
|
|
||||||
)
|
|
||||||
|
|
||||||
var cmdDlv = &commands.Command{
|
|
||||||
CustomFlags: true,
|
|
||||||
UsageLine: "dlv [-package=\"\"] [-port=8181]",
|
|
||||||
Short: "Start a debugging session using Delve",
|
|
||||||
Long: `dlv command start a debugging session using debugging tool Delve.
|
|
||||||
|
|
||||||
To debug your application using Delve, use: {{"$ bee dlv" | bold}}
|
|
||||||
|
|
||||||
For more information on Delve: https://github.com/derekparker/delve
|
|
||||||
`,
|
|
||||||
PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
|
|
||||||
Run: runDlv,
|
|
||||||
}
|
|
||||||
|
|
||||||
var (
|
|
||||||
packageName string
|
|
||||||
port int
|
|
||||||
)
|
|
||||||
|
|
||||||
func init() {
|
|
||||||
fs := flag.NewFlagSet("dlv", flag.ContinueOnError)
|
|
||||||
fs.IntVar(&port, "port", 8181, "Port to listen to for clients")
|
|
||||||
fs.StringVar(&packageName, "package", "", "The package to debug (Must have a main package)")
|
|
||||||
cmdDlv.Flag = *fs
|
|
||||||
commands.AvailableCommands = append(commands.AvailableCommands, cmdDlv)
|
|
||||||
}
|
|
||||||
|
|
||||||
func runDlv(cmd *commands.Command, args []string) int {
|
|
||||||
if err := cmd.Flag.Parse(args); err != nil {
|
|
||||||
beeLogger.Log.Fatalf("Error parsing flags: %v", err.Error())
|
|
||||||
}
|
|
||||||
|
|
||||||
debugname := "debug"
|
|
||||||
addr := fmt.Sprintf("127.0.0.1:%d", port)
|
|
||||||
return runDelve(addr, debugname)
|
|
||||||
}
|
|
||||||
|
|
||||||
// runDelve runs the Delve debugger server
|
|
||||||
func runDelve(addr, debugname string) int {
|
|
||||||
beeLogger.Log.Info("Starting Delve Debugger...")
|
|
||||||
|
|
||||||
err := gobuild(debugname, packageName)
|
|
||||||
if err != nil {
|
|
||||||
beeLogger.Log.Fatalf("%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
fp, err := filepath.Abs("./" + debugname)
|
|
||||||
if err != nil {
|
|
||||||
beeLogger.Log.Fatalf("%v", err)
|
|
||||||
}
|
|
||||||
defer os.Remove(fp)
|
|
||||||
|
|
||||||
abs, err := filepath.Abs(debugname)
|
|
||||||
if err != nil {
|
|
||||||
beeLogger.Log.Fatalf("%v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and start the debugger server
|
|
||||||
listener, err := net.Listen("tcp", addr)
|
|
||||||
if err != nil {
|
|
||||||
beeLogger.Log.Fatalf("Could not start listener: %s", err)
|
|
||||||
}
|
|
||||||
defer listener.Close()
|
|
||||||
|
|
||||||
server := rpccommon.NewServer(&service.Config{
|
|
||||||
Listener: listener,
|
|
||||||
AcceptMulti: true,
|
|
||||||
AttachPid: 0,
|
|
||||||
APIVersion: 2,
|
|
||||||
WorkingDir: "./",
|
|
||||||
ProcessArgs: []string{abs},
|
|
||||||
}, false)
|
|
||||||
if err := server.Run(); err != nil {
|
|
||||||
beeLogger.Log.Fatalf("Could not start debugger server: %v", err)
|
|
||||||
}
|
|
||||||
|
|
||||||
// Start the Delve client REPL
|
|
||||||
client := rpc2.NewClient(addr)
|
|
||||||
term := terminal.New(client, nil)
|
|
||||||
|
|
||||||
status, err := term.Run()
|
|
||||||
if err != nil {
|
|
||||||
beeLogger.Log.Fatalf("Could not start Delve REPL: %v", err)
|
|
||||||
}
|
|
||||||
defer term.Close()
|
|
||||||
|
|
||||||
// Stop and kill the debugger server once
|
|
||||||
// user quits the REPL
|
|
||||||
if err := server.Stop(true); err != nil {
|
|
||||||
beeLogger.Log.Fatalf("Could not stop Delve server: %v", err)
|
|
||||||
}
|
|
||||||
return status
|
|
||||||
}
|
|
||||||
|
|
||||||
// gobuild runs the "go build" command on the specified package
|
|
||||||
func gobuild(debugname, pkg string) error {
|
|
||||||
args := []string{"-gcflags", "-N -l", "-o", debugname}
|
|
||||||
args = append(args, utils.SplitQuotedFields("-ldflags='-linkmode internal'")...)
|
|
||||||
args = append(args, pkg)
|
|
||||||
return utils.GoCommand("build", args...)
|
|
||||||
}
|
|
||||||
|
245
cmd/commands/dlv/dlv_amd64.go
Normal file
245
cmd/commands/dlv/dlv_amd64.go
Normal file
@ -0,0 +1,245 @@
|
|||||||
|
// Copyright 2017 bee authors
|
||||||
|
//
|
||||||
|
// Licensed under the Apache License, Version 2.0 (the "License"): you may
|
||||||
|
// not use this file except in compliance with the License. You may obtain
|
||||||
|
// a copy of the License at
|
||||||
|
//
|
||||||
|
// http://www.apache.org/licenses/LICENSE-2.0
|
||||||
|
//
|
||||||
|
// Unless required by applicable law or agreed to in writing, software
|
||||||
|
// distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||||
|
// WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||||
|
// License for the specific language governing permissions and limitations
|
||||||
|
// under the License.
|
||||||
|
|
||||||
|
// Package dlv ...
|
||||||
|
package dlv
|
||||||
|
|
||||||
|
import (
|
||||||
|
"flag"
|
||||||
|
"fmt"
|
||||||
|
"net"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
"time"
|
||||||
|
|
||||||
|
"github.com/beego/bee/cmd/commands"
|
||||||
|
"github.com/beego/bee/cmd/commands/version"
|
||||||
|
beeLogger "github.com/beego/bee/logger"
|
||||||
|
"github.com/beego/bee/utils"
|
||||||
|
"github.com/derekparker/delve/service"
|
||||||
|
"github.com/derekparker/delve/service/rpc2"
|
||||||
|
"github.com/derekparker/delve/service/rpccommon"
|
||||||
|
"github.com/derekparker/delve/terminal"
|
||||||
|
"github.com/fsnotify/fsnotify"
|
||||||
|
)
|
||||||
|
|
||||||
|
var cmdDlv = &commands.Command{
|
||||||
|
CustomFlags: true,
|
||||||
|
UsageLine: "dlv [-package=\"\"] [-port=8181] [-verbose=false]",
|
||||||
|
Short: "Start a debugging session using Delve",
|
||||||
|
Long: `dlv command start a debugging session using debugging tool Delve.
|
||||||
|
|
||||||
|
To debug your application using Delve, use: {{"$ bee dlv" | bold}}
|
||||||
|
|
||||||
|
For more information on Delve: https://github.com/derekparker/delve
|
||||||
|
`,
|
||||||
|
PreRun: func(cmd *commands.Command, args []string) { version.ShowShortVersionBanner() },
|
||||||
|
Run: runDlv,
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
packageName string
|
||||||
|
verbose bool
|
||||||
|
port int
|
||||||
|
)
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
fs := flag.NewFlagSet("dlv", flag.ContinueOnError)
|
||||||
|
fs.StringVar(&packageName, "package", "", "The package to debug (Must have a main package)")
|
||||||
|
fs.BoolVar(&verbose, "verbose", false, "Enable verbose mode")
|
||||||
|
fs.IntVar(&port, "port", 8181, "Port to listen to for clients")
|
||||||
|
cmdDlv.Flag = *fs
|
||||||
|
commands.AvailableCommands = append(commands.AvailableCommands, cmdDlv)
|
||||||
|
}
|
||||||
|
|
||||||
|
func runDlv(cmd *commands.Command, args []string) int {
|
||||||
|
if err := cmd.Flag.Parse(args); err != nil {
|
||||||
|
beeLogger.Log.Fatalf("Error while parsing flags: %v", err.Error())
|
||||||
|
}
|
||||||
|
|
||||||
|
var (
|
||||||
|
addr = fmt.Sprintf("127.0.0.1:%d", port)
|
||||||
|
paths = make([]string, 0)
|
||||||
|
notifyChan = make(chan int)
|
||||||
|
)
|
||||||
|
|
||||||
|
if err := loadPathsToWatch(&paths); err != nil {
|
||||||
|
beeLogger.Log.Fatalf("Error while loading paths to watch: %v", err.Error())
|
||||||
|
}
|
||||||
|
go startWatcher(paths, notifyChan)
|
||||||
|
return startDelveDebugger(addr, notifyChan)
|
||||||
|
}
|
||||||
|
|
||||||
|
// buildDebug builds a debug binary in the current working directory
|
||||||
|
func buildDebug() (string, error) {
|
||||||
|
args := []string{"-gcflags", "-N -l", "-o", "debug"}
|
||||||
|
args = append(args, utils.SplitQuotedFields("-ldflags='-linkmode internal'")...)
|
||||||
|
args = append(args, packageName)
|
||||||
|
if err := utils.GoCommand("build", args...); err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
|
||||||
|
fp, err := filepath.Abs("./debug")
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
}
|
||||||
|
return fp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// loadPathsToWatch loads the paths that needs to be watched for changes
|
||||||
|
func loadPathsToWatch(paths *[]string) error {
|
||||||
|
directory, err := os.Getwd()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
filepath.Walk(directory, func(path string, info os.FileInfo, _ error) error {
|
||||||
|
if strings.HasSuffix(info.Name(), "docs") {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(info.Name(), "swagger") {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
if strings.HasSuffix(info.Name(), "vendor") {
|
||||||
|
return filepath.SkipDir
|
||||||
|
}
|
||||||
|
|
||||||
|
if filepath.Ext(info.Name()) == ".go" {
|
||||||
|
*paths = append(*paths, path)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
})
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// startDelveDebugger starts the Delve debugger server
|
||||||
|
func startDelveDebugger(addr string, ch chan int) int {
|
||||||
|
beeLogger.Log.Info("Starting Delve Debugger...")
|
||||||
|
|
||||||
|
fp, err := buildDebug()
|
||||||
|
if err != nil {
|
||||||
|
beeLogger.Log.Fatalf("Error while building debug binary: %v", err)
|
||||||
|
}
|
||||||
|
defer os.Remove(fp)
|
||||||
|
|
||||||
|
abs, err := filepath.Abs("./debug")
|
||||||
|
if err != nil {
|
||||||
|
beeLogger.Log.Fatalf("%v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Create and start the debugger server
|
||||||
|
listener, err := net.Listen("tcp", addr)
|
||||||
|
if err != nil {
|
||||||
|
beeLogger.Log.Fatalf("Could not start listener: %s", err)
|
||||||
|
}
|
||||||
|
defer listener.Close()
|
||||||
|
|
||||||
|
server := rpccommon.NewServer(&service.Config{
|
||||||
|
Listener: listener,
|
||||||
|
AcceptMulti: true,
|
||||||
|
AttachPid: 0,
|
||||||
|
APIVersion: 2,
|
||||||
|
WorkingDir: ".",
|
||||||
|
ProcessArgs: []string{abs},
|
||||||
|
}, false)
|
||||||
|
if err := server.Run(); err != nil {
|
||||||
|
beeLogger.Log.Fatalf("Could not start debugger server: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start the Delve client REPL
|
||||||
|
client := rpc2.NewClient(addr)
|
||||||
|
// Make sure the client is restarted when new changes are introduced
|
||||||
|
go func() {
|
||||||
|
for {
|
||||||
|
if val := <-ch; val == 0 {
|
||||||
|
if _, err := client.Restart(); err != nil {
|
||||||
|
utils.Notify("Error while restarting the client: "+err.Error(), "bee")
|
||||||
|
} else {
|
||||||
|
if verbose {
|
||||||
|
utils.Notify("Delve Debugger Restarted", "bee")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
|
||||||
|
// Create the terminal and connect it to the client debugger
|
||||||
|
term := terminal.New(client, nil)
|
||||||
|
status, err := term.Run()
|
||||||
|
if err != nil {
|
||||||
|
beeLogger.Log.Fatalf("Could not start Delve REPL: %v", err)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Stop and kill the debugger server once user quits the REPL
|
||||||
|
if err := server.Stop(true); err != nil {
|
||||||
|
beeLogger.Log.Fatalf("Could not stop Delve server: %v", err)
|
||||||
|
}
|
||||||
|
return status
|
||||||
|
}
|
||||||
|
|
||||||
|
var eventsModTime = make(map[string]int64)
|
||||||
|
|
||||||
|
// startWatcher starts the fsnotify watcher on the passed paths
|
||||||
|
func startWatcher(paths []string, ch chan int) {
|
||||||
|
watcher, err := fsnotify.NewWatcher()
|
||||||
|
if err != nil {
|
||||||
|
beeLogger.Log.Fatalf("Could not start the watcher: %v", err)
|
||||||
|
}
|
||||||
|
defer watcher.Close()
|
||||||
|
|
||||||
|
// Feed the paths to the watcher
|
||||||
|
for _, path := range paths {
|
||||||
|
if err := watcher.Add(path); err != nil {
|
||||||
|
beeLogger.Log.Fatalf("Could not set a watch on path: %v", err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
for {
|
||||||
|
select {
|
||||||
|
case evt := <-watcher.Events:
|
||||||
|
build := true
|
||||||
|
if filepath.Ext(evt.Name) != ".go" {
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
|
||||||
|
mt := utils.GetFileModTime(evt.Name)
|
||||||
|
if t := eventsModTime[evt.Name]; mt == t {
|
||||||
|
build = false
|
||||||
|
}
|
||||||
|
eventsModTime[evt.Name] = mt
|
||||||
|
|
||||||
|
if build {
|
||||||
|
go func() {
|
||||||
|
if verbose {
|
||||||
|
utils.Notify("Rebuilding application with the new changes", "bee")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Wait 1s before re-build until there is no file change
|
||||||
|
scheduleTime := time.Now().Add(1 * time.Second)
|
||||||
|
time.Sleep(scheduleTime.Sub(time.Now()))
|
||||||
|
_, err := buildDebug()
|
||||||
|
if err != nil {
|
||||||
|
utils.Notify("Build Failed: "+err.Error(), "bee")
|
||||||
|
} else {
|
||||||
|
ch <- 0 // Notify listeners
|
||||||
|
}
|
||||||
|
}()
|
||||||
|
}
|
||||||
|
case err := <-watcher.Errors:
|
||||||
|
if err != nil {
|
||||||
|
ch <- -1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
@ -71,7 +71,7 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
|
|
||||||
mt := getFileModTime(e.Name)
|
mt := utils.GetFileModTime(e.Name)
|
||||||
if t := eventTime[e.Name]; mt == t {
|
if t := eventTime[e.Name]; mt == t {
|
||||||
beeLogger.Log.Hintf(colors.Bold("Skipping: ")+"%s", e.String())
|
beeLogger.Log.Hintf(colors.Bold("Skipping: ")+"%s", e.String())
|
||||||
isBuild = false
|
isBuild = false
|
||||||
@ -104,25 +104,6 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// getFileModTime returns unix timestamp of `os.File.ModTime` for the given path.
|
|
||||||
func getFileModTime(path string) int64 {
|
|
||||||
path = strings.Replace(path, "\\", "/", -1)
|
|
||||||
f, err := os.Open(path)
|
|
||||||
if err != nil {
|
|
||||||
beeLogger.Log.Errorf("Failed to open file on '%s': %s", path, err)
|
|
||||||
return time.Now().Unix()
|
|
||||||
}
|
|
||||||
defer f.Close()
|
|
||||||
|
|
||||||
fi, err := f.Stat()
|
|
||||||
if err != nil {
|
|
||||||
beeLogger.Log.Errorf("Failed to get file stats: %s", err)
|
|
||||||
return time.Now().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
return fi.ModTime().Unix()
|
|
||||||
}
|
|
||||||
|
|
||||||
// AutoBuild builds the specified set of files
|
// AutoBuild builds the specified set of files
|
||||||
func AutoBuild(files []string, isgenerate bool) {
|
func AutoBuild(files []string, isgenerate bool) {
|
||||||
state.Lock()
|
state.Lock()
|
||||||
|
2
gosimple.ignore
Normal file
2
gosimple.ignore
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
github.com/beego/bee/cmd/commands/run/*.go:S1024
|
||||||
|
github.com/beego/bee/cmd/commands/dlv/*.go:S1024
|
@ -26,6 +26,7 @@ import (
|
|||||||
"runtime"
|
"runtime"
|
||||||
"strings"
|
"strings"
|
||||||
"text/template"
|
"text/template"
|
||||||
|
"time"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
beeLogger "github.com/beego/bee/logger"
|
beeLogger "github.com/beego/bee/logger"
|
||||||
@ -67,7 +68,7 @@ func IsInGOPATH(thePath string) bool {
|
|||||||
thePath = filepath.ToSlash(thePath)
|
thePath = filepath.ToSlash(thePath)
|
||||||
}
|
}
|
||||||
for _, gopath := range GetGOPATHs() {
|
for _, gopath := range GetGOPATHs() {
|
||||||
if strings.Contains(thePath, gopath + "/src") {
|
if strings.Contains(thePath, gopath+"/src") {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -412,3 +413,22 @@ func SplitQuotedFields(in string) []string {
|
|||||||
|
|
||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// GetFileModTime returns unix timestamp of `os.File.ModTime` for the given path.
|
||||||
|
func GetFileModTime(path string) int64 {
|
||||||
|
path = strings.Replace(path, "\\", "/", -1)
|
||||||
|
f, err := os.Open(path)
|
||||||
|
if err != nil {
|
||||||
|
beeLogger.Log.Errorf("Failed to open file on '%s': %s", path, err)
|
||||||
|
return time.Now().Unix()
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
beeLogger.Log.Errorf("Failed to get file stats: %s", err)
|
||||||
|
return time.Now().Unix()
|
||||||
|
}
|
||||||
|
|
||||||
|
return fi.ModTime().Unix()
|
||||||
|
}
|
||||||
|
@ -7,7 +7,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
"github.com/derekparker/delve/dwarf/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type parsefunc func(*parseContext) parsefunc
|
type parsefunc func(*parseContext) parsefunc
|
@ -5,7 +5,7 @@ import (
|
|||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
"github.com/derekparker/delve/dwarf/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type CurrentFrameAddress struct {
|
type CurrentFrameAddress struct {
|
@ -4,7 +4,7 @@ import (
|
|||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
"github.com/derekparker/delve/dwarf/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type DebugLinePrologue struct {
|
type DebugLinePrologue struct {
|
@ -6,7 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
"github.com/derekparker/delve/dwarf/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Location struct {
|
type Location struct {
|
||||||
@ -27,11 +27,6 @@ type StateMachine struct {
|
|||||||
endSeq bool
|
endSeq bool
|
||||||
lastWasStandard bool
|
lastWasStandard bool
|
||||||
lastDelta int
|
lastDelta int
|
||||||
// valid is true if the current value of the state machine is the address of
|
|
||||||
// an instruction (using the terminology used by DWARF spec the current
|
|
||||||
// value of the state machine should be appended to the matrix representing
|
|
||||||
// the compilation unit)
|
|
||||||
valid bool
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type opcodefn func(*StateMachine, *bytes.Buffer)
|
type opcodefn func(*StateMachine, *bytes.Buffer)
|
||||||
@ -96,9 +91,7 @@ func (dbl *DebugLines) AllPCsForFileLine(f string, l int) (pcs []uint64) {
|
|||||||
}
|
}
|
||||||
if sm.line == l && sm.file == f && sm.address != lastAddr {
|
if sm.line == l && sm.file == f && sm.address != lastAddr {
|
||||||
foundFile = true
|
foundFile = true
|
||||||
if sm.valid {
|
|
||||||
pcs = append(pcs, sm.address)
|
pcs = append(pcs, sm.address)
|
||||||
}
|
|
||||||
line := sm.line
|
line := sm.line
|
||||||
// Keep going until we're on a different line. We only care about
|
// Keep going until we're on a different line. We only care about
|
||||||
// when a line comes back around (i.e. for loop) so get to next line,
|
// when a line comes back around (i.e. for loop) so get to next line,
|
||||||
@ -130,9 +123,6 @@ func (dbl *DebugLines) AllPCsBetween(begin, end uint64, filename string) ([]uint
|
|||||||
|
|
||||||
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
|
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
|
||||||
findAndExecOpcode(sm, buf, b)
|
findAndExecOpcode(sm, buf, b)
|
||||||
if !sm.valid {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
if sm.address > end {
|
if sm.address > end {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -167,10 +157,9 @@ func execSpecialOpcode(sm *StateMachine, instr byte) {
|
|||||||
|
|
||||||
sm.lastDelta = int(sm.dbl.Prologue.LineBase + int8(decoded%sm.dbl.Prologue.LineRange))
|
sm.lastDelta = int(sm.dbl.Prologue.LineBase + int8(decoded%sm.dbl.Prologue.LineRange))
|
||||||
sm.line += sm.lastDelta
|
sm.line += sm.lastDelta
|
||||||
sm.address += uint64(decoded/sm.dbl.Prologue.LineRange) * uint64(sm.dbl.Prologue.MinInstrLength)
|
sm.address += uint64(decoded / sm.dbl.Prologue.LineRange)
|
||||||
sm.basicBlock = false
|
sm.basicBlock = false
|
||||||
sm.lastWasStandard = false
|
sm.lastWasStandard = false
|
||||||
sm.valid = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func execExtendedOpcode(sm *StateMachine, instr byte, buf *bytes.Buffer) {
|
func execExtendedOpcode(sm *StateMachine, instr byte, buf *bytes.Buffer) {
|
||||||
@ -181,7 +170,6 @@ func execExtendedOpcode(sm *StateMachine, instr byte, buf *bytes.Buffer) {
|
|||||||
panic(fmt.Sprintf("Encountered unknown extended opcode %#v\n", b))
|
panic(fmt.Sprintf("Encountered unknown extended opcode %#v\n", b))
|
||||||
}
|
}
|
||||||
sm.lastWasStandard = false
|
sm.lastWasStandard = false
|
||||||
sm.valid = false
|
|
||||||
|
|
||||||
fn(sm, buf)
|
fn(sm, buf)
|
||||||
}
|
}
|
||||||
@ -192,14 +180,12 @@ func execStandardOpcode(sm *StateMachine, instr byte, buf *bytes.Buffer) {
|
|||||||
panic(fmt.Sprintf("Encountered unknown standard opcode %#v\n", instr))
|
panic(fmt.Sprintf("Encountered unknown standard opcode %#v\n", instr))
|
||||||
}
|
}
|
||||||
sm.lastWasStandard = true
|
sm.lastWasStandard = true
|
||||||
sm.valid = false
|
|
||||||
|
|
||||||
fn(sm, buf)
|
fn(sm, buf)
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyfn(sm *StateMachine, buf *bytes.Buffer) {
|
func copyfn(sm *StateMachine, buf *bytes.Buffer) {
|
||||||
sm.basicBlock = false
|
sm.basicBlock = false
|
||||||
sm.valid = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func advancepc(sm *StateMachine, buf *bytes.Buffer) {
|
func advancepc(sm *StateMachine, buf *bytes.Buffer) {
|
||||||
@ -232,7 +218,7 @@ func setbasicblock(sm *StateMachine, buf *bytes.Buffer) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func constaddpc(sm *StateMachine, buf *bytes.Buffer) {
|
func constaddpc(sm *StateMachine, buf *bytes.Buffer) {
|
||||||
sm.address += uint64((255-sm.dbl.Prologue.OpcodeBase)/sm.dbl.Prologue.LineRange) * uint64(sm.dbl.Prologue.MinInstrLength)
|
sm.address += (255 / uint64(sm.dbl.Prologue.LineRange))
|
||||||
}
|
}
|
||||||
|
|
||||||
func fixedadvancepc(sm *StateMachine, buf *bytes.Buffer) {
|
func fixedadvancepc(sm *StateMachine, buf *bytes.Buffer) {
|
||||||
@ -244,7 +230,6 @@ func fixedadvancepc(sm *StateMachine, buf *bytes.Buffer) {
|
|||||||
|
|
||||||
func endsequence(sm *StateMachine, buf *bytes.Buffer) {
|
func endsequence(sm *StateMachine, buf *bytes.Buffer) {
|
||||||
sm.endSeq = true
|
sm.endSeq = true
|
||||||
sm.valid = true
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func setaddress(sm *StateMachine, buf *bytes.Buffer) {
|
func setaddress(sm *StateMachine, buf *bytes.Buffer) {
|
@ -6,7 +6,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
"github.com/derekparker/delve/dwarf/util"
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
@ -4,8 +4,8 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
|
||||||
"golang.org/x/debug/dwarf"
|
"golang.org/x/debug/dwarf"
|
||||||
|
"github.com/derekparker/delve/dwarf/op"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Reader struct {
|
type Reader struct {
|
84
vendor/github.com/derekparker/delve/pkg/target/target.go
generated
vendored
84
vendor/github.com/derekparker/delve/pkg/target/target.go
generated
vendored
@ -1,84 +0,0 @@
|
|||||||
package target
|
|
||||||
|
|
||||||
import (
|
|
||||||
"debug/gosym"
|
|
||||||
"go/ast"
|
|
||||||
"time"
|
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/proc"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Target represents the target of the debugger. This
|
|
||||||
// target could be a system process, core file, etc.
|
|
||||||
type Interface interface {
|
|
||||||
Info
|
|
||||||
ProcessManipulation
|
|
||||||
BreakpointManipulation
|
|
||||||
VariableEval
|
|
||||||
}
|
|
||||||
|
|
||||||
// Info is an interface that provides general information on the target.
|
|
||||||
type Info interface {
|
|
||||||
Pid() int
|
|
||||||
Exited() bool
|
|
||||||
Running() bool
|
|
||||||
|
|
||||||
BinaryInfo
|
|
||||||
ThreadInfo
|
|
||||||
GoroutineInfo
|
|
||||||
}
|
|
||||||
|
|
||||||
// BinaryInfo is an interface for accessing information on the binary file
|
|
||||||
// and the contents of binary sections.
|
|
||||||
type BinaryInfo interface {
|
|
||||||
LastModified() time.Time
|
|
||||||
Sources() map[string]*gosym.Obj
|
|
||||||
FindFileLocation(fileName string, lineNumber int) (uint64, error)
|
|
||||||
FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error)
|
|
||||||
Funcs() []gosym.Func
|
|
||||||
Types() ([]string, error)
|
|
||||||
PCToLine(uint64) (string, int, *gosym.Func)
|
|
||||||
FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ThreadInfo is an interface for getting information on active threads
|
|
||||||
// in the process.
|
|
||||||
type ThreadInfo interface {
|
|
||||||
Threads() map[int]*proc.Thread
|
|
||||||
CurrentThread() *proc.Thread
|
|
||||||
}
|
|
||||||
|
|
||||||
// GoroutineInfo is an interface for getting information on running goroutines.
|
|
||||||
type GoroutineInfo interface {
|
|
||||||
GoroutinesInfo() ([]*proc.G, error)
|
|
||||||
SelectedGoroutine() *proc.G
|
|
||||||
FindGoroutine(int) (*proc.G, error)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ProcessManipulation is an interface for changing the execution state of a process.
|
|
||||||
type ProcessManipulation interface {
|
|
||||||
Continue() error
|
|
||||||
Next() error
|
|
||||||
Step() error
|
|
||||||
StepOut() error
|
|
||||||
StepInstruction() error
|
|
||||||
SwitchThread(int) error
|
|
||||||
SwitchGoroutine(int) error
|
|
||||||
RequestManualStop() error
|
|
||||||
Halt() error
|
|
||||||
Kill() error
|
|
||||||
Detach(bool) error
|
|
||||||
}
|
|
||||||
|
|
||||||
// BreakpointManipulation is an interface for managing breakpoints.
|
|
||||||
type BreakpointManipulation interface {
|
|
||||||
Breakpoints() map[uint64]*proc.Breakpoint
|
|
||||||
SetBreakpoint(addr uint64, kind proc.BreakpointKind, cond ast.Expr) (*proc.Breakpoint, error)
|
|
||||||
ClearBreakpoint(addr uint64) (*proc.Breakpoint, error)
|
|
||||||
ClearInternalBreakpoints() error
|
|
||||||
}
|
|
||||||
|
|
||||||
// VariableEval is an interface for dealing with eval scopes.
|
|
||||||
type VariableEval interface {
|
|
||||||
ConvertEvalScope(gid, frame int) (*proc.EvalScope, error)
|
|
||||||
}
|
|
@ -41,7 +41,7 @@ func (thread *Thread) Disassemble(startPC, endPC uint64, currentGoroutine bool)
|
|||||||
}
|
}
|
||||||
|
|
||||||
for len(mem) > 0 {
|
for len(mem) > 0 {
|
||||||
bp, atbp := thread.dbp.breakpoints[pc]
|
bp, atbp := thread.dbp.Breakpoints[pc]
|
||||||
if atbp {
|
if atbp {
|
||||||
for i := range bp.OriginalData {
|
for i := range bp.OriginalData {
|
||||||
mem[i] = bp.OriginalData[i]
|
mem[i] = bp.OriginalData[i]
|
@ -3,7 +3,6 @@ package proc
|
|||||||
import (
|
import (
|
||||||
"debug/gosym"
|
"debug/gosym"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
|
|
||||||
"rsc.io/x86/x86asm"
|
"rsc.io/x86/x86asm"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -146,7 +145,7 @@ func init() {
|
|||||||
// FirstPCAfterPrologue returns the address of the first instruction after the prologue for function fn
|
// FirstPCAfterPrologue returns the address of the first instruction after the prologue for function fn
|
||||||
// If sameline is set FirstPCAfterPrologue will always return an address associated with the same line as fn.Entry
|
// If sameline is set FirstPCAfterPrologue will always return an address associated with the same line as fn.Entry
|
||||||
func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
|
func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
|
||||||
text, err := dbp.CurrentThread().Disassemble(fn.Entry, fn.End, false)
|
text, err := dbp.CurrentThread.Disassemble(fn.Entry, fn.End, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return fn.Entry, err
|
return fn.Entry, err
|
||||||
}
|
}
|
@ -3,7 +3,6 @@ package proc
|
|||||||
import (
|
import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"encoding/binary"
|
"encoding/binary"
|
||||||
"errors"
|
|
||||||
"fmt"
|
"fmt"
|
||||||
"go/ast"
|
"go/ast"
|
||||||
"go/constant"
|
"go/constant"
|
||||||
@ -12,12 +11,10 @@ import (
|
|||||||
"go/token"
|
"go/token"
|
||||||
"reflect"
|
"reflect"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
"github.com/derekparker/delve/dwarf/reader"
|
||||||
"golang.org/x/debug/dwarf"
|
"golang.org/x/debug/dwarf"
|
||||||
)
|
)
|
||||||
|
|
||||||
var OperationOnSpecialFloatError = errors.New("operations on non-finite floats not implemented")
|
|
||||||
|
|
||||||
// EvalExpression returns the value of the given expression.
|
// EvalExpression returns the value of the given expression.
|
||||||
func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable, error) {
|
func (scope *EvalScope) EvalExpression(expr string, cfg LoadConfig) (*Variable, error) {
|
||||||
t, err := parser.ParseExpr(expr)
|
t, err := parser.ParseExpr(expr)
|
||||||
@ -691,9 +688,6 @@ func (scope *EvalScope) evalUnary(node *ast.UnaryExpr) (*Variable, error) {
|
|||||||
if xv.Unreadable != nil {
|
if xv.Unreadable != nil {
|
||||||
return nil, xv.Unreadable
|
return nil, xv.Unreadable
|
||||||
}
|
}
|
||||||
if xv.FloatSpecial != 0 {
|
|
||||||
return nil, OperationOnSpecialFloatError
|
|
||||||
}
|
|
||||||
if xv.Value == nil {
|
if xv.Value == nil {
|
||||||
return nil, fmt.Errorf("operator %s can not be applied to \"%s\"", node.Op.String(), exprToString(node.X))
|
return nil, fmt.Errorf("operator %s can not be applied to \"%s\"", node.Op.String(), exprToString(node.X))
|
||||||
}
|
}
|
||||||
@ -800,10 +794,6 @@ func (scope *EvalScope) evalBinary(node *ast.BinaryExpr) (*Variable, error) {
|
|||||||
return nil, yv.Unreadable
|
return nil, yv.Unreadable
|
||||||
}
|
}
|
||||||
|
|
||||||
if xv.FloatSpecial != 0 || yv.FloatSpecial != 0 {
|
|
||||||
return nil, OperationOnSpecialFloatError
|
|
||||||
}
|
|
||||||
|
|
||||||
typ, err := negotiateType(node.Op, xv, yv)
|
typ, err := negotiateType(node.Op, xv, yv)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
@ -16,10 +16,6 @@ type GoVersion struct {
|
|||||||
RC int
|
RC int
|
||||||
}
|
}
|
||||||
|
|
||||||
var (
|
|
||||||
GoVer18Beta = GoVersion{1, 8, -1, 0, 0}
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseVersionString(ver string) (GoVersion, bool) {
|
func ParseVersionString(ver string) (GoVersion, bool) {
|
||||||
var r GoVersion
|
var r GoVersion
|
||||||
var err1, err2, err3 error
|
var err1, err2, err3 error
|
@ -13,7 +13,7 @@ type moduleData struct {
|
|||||||
|
|
||||||
func (dbp *Process) loadModuleData() (err error) {
|
func (dbp *Process) loadModuleData() (err error) {
|
||||||
dbp.loadModuleDataOnce.Do(func() {
|
dbp.loadModuleDataOnce.Do(func() {
|
||||||
scope := &EvalScope{Thread: dbp.currentThread, PC: 0, CFA: 0}
|
scope := &EvalScope{Thread: dbp.CurrentThread, PC: 0, CFA: 0}
|
||||||
var md *Variable
|
var md *Variable
|
||||||
md, err = scope.packageVarAddr("runtime.firstmoduledata")
|
md, err = scope.packageVarAddr("runtime.firstmoduledata")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -84,13 +84,13 @@ func (dbp *Process) resolveTypeOff(typeAddr uintptr, off uintptr) (*Variable, er
|
|||||||
return v.newVariable(v.Name, uintptr(addr), rtyp), nil
|
return v.newVariable(v.Name, uintptr(addr), rtyp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.currentThread)); t != nil {
|
if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.CurrentThread)); t != nil {
|
||||||
return t, nil
|
return t, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
res := md.types + uintptr(off)
|
res := md.types + uintptr(off)
|
||||||
|
|
||||||
return dbp.currentThread.newVariable("", res, rtyp), nil
|
return dbp.CurrentThread.newVariable("", res, rtyp), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) resolveNameOff(typeAddr uintptr, off uintptr) (name, tag string, pkgpathoff int32, err error) {
|
func (dbp *Process) resolveNameOff(typeAddr uintptr, off uintptr) (name, tag string, pkgpathoff int32, err error) {
|
||||||
@ -119,7 +119,7 @@ func (dbp *Process) resolveNameOff(typeAddr uintptr, off uintptr) (name, tag str
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) reflectOffsMapAccess(off uintptr) (*Variable, error) {
|
func (dbp *Process) reflectOffsMapAccess(off uintptr) (*Variable, error) {
|
||||||
scope := &EvalScope{Thread: dbp.currentThread, PC: 0, CFA: 0}
|
scope := &EvalScope{Thread: dbp.CurrentThread, PC: 0, CFA: 0}
|
||||||
reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs")
|
reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -130,7 +130,7 @@ func (dbp *Process) reflectOffsMapAccess(off uintptr) (*Variable, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.currentThread))
|
return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.CurrentThread))
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
@ -142,7 +142,7 @@ const (
|
|||||||
|
|
||||||
func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32, err error) {
|
func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32, err error) {
|
||||||
off := addr
|
off := addr
|
||||||
namedata, err := dbp.currentThread.readMemory(off, 3)
|
namedata, err := dbp.CurrentThread.readMemory(off, 3)
|
||||||
off += 3
|
off += 3
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", 0, err
|
return "", "", 0, err
|
||||||
@ -150,7 +150,7 @@ func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32,
|
|||||||
|
|
||||||
namelen := uint16(namedata[1]<<8) | uint16(namedata[2])
|
namelen := uint16(namedata[1]<<8) | uint16(namedata[2])
|
||||||
|
|
||||||
rawstr, err := dbp.currentThread.readMemory(off, int(namelen))
|
rawstr, err := dbp.CurrentThread.readMemory(off, int(namelen))
|
||||||
off += uintptr(namelen)
|
off += uintptr(namelen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", 0, err
|
return "", "", 0, err
|
||||||
@ -159,14 +159,14 @@ func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32,
|
|||||||
name = string(rawstr)
|
name = string(rawstr)
|
||||||
|
|
||||||
if namedata[0]&nameflagHasTag != 0 {
|
if namedata[0]&nameflagHasTag != 0 {
|
||||||
taglendata, err := dbp.currentThread.readMemory(off, 2)
|
taglendata, err := dbp.CurrentThread.readMemory(off, 2)
|
||||||
off += 2
|
off += 2
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", 0, err
|
return "", "", 0, err
|
||||||
}
|
}
|
||||||
taglen := uint16(taglendata[0]<<8) | uint16(taglendata[1])
|
taglen := uint16(taglendata[0]<<8) | uint16(taglendata[1])
|
||||||
|
|
||||||
rawstr, err := dbp.currentThread.readMemory(off, int(taglen))
|
rawstr, err := dbp.CurrentThread.readMemory(off, int(taglen))
|
||||||
off += uintptr(taglen)
|
off += uintptr(taglen)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", 0, err
|
return "", "", 0, err
|
||||||
@ -176,7 +176,7 @@ func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32,
|
|||||||
}
|
}
|
||||||
|
|
||||||
if namedata[0]&nameflagHasPkg != 0 {
|
if namedata[0]&nameflagHasPkg != 0 {
|
||||||
pkgdata, err := dbp.currentThread.readMemory(off, 4)
|
pkgdata, err := dbp.CurrentThread.readMemory(off, 4)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", "", 0, err
|
return "", "", 0, err
|
||||||
}
|
}
|
@ -16,32 +16,32 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
"github.com/derekparker/delve/dwarf/frame"
|
||||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
"github.com/derekparker/delve/dwarf/line"
|
||||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
"github.com/derekparker/delve/dwarf/reader"
|
||||||
"golang.org/x/debug/dwarf"
|
"golang.org/x/debug/dwarf"
|
||||||
)
|
)
|
||||||
|
|
||||||
// Process represents all of the information the debugger
|
// Process represents all of the information the debugger
|
||||||
// is holding onto regarding the process we are debugging.
|
// is holding onto regarding the process we are debugging.
|
||||||
type Process struct {
|
type Process struct {
|
||||||
pid int // Process Pid
|
Pid int // Process Pid
|
||||||
Process *os.Process // Pointer to process struct for the actual process we are debugging
|
Process *os.Process // Pointer to process struct for the actual process we are debugging
|
||||||
lastModified time.Time // Time the executable of this process was last modified
|
LastModified time.Time // Time the executable of this process was last modified
|
||||||
|
|
||||||
// Breakpoint table, holds information on breakpoints.
|
// Breakpoint table, holds information on breakpoints.
|
||||||
// Maps instruction address to Breakpoint struct.
|
// Maps instruction address to Breakpoint struct.
|
||||||
breakpoints map[uint64]*Breakpoint
|
Breakpoints map[uint64]*Breakpoint
|
||||||
|
|
||||||
// List of threads mapped as such: pid -> *Thread
|
// List of threads mapped as such: pid -> *Thread
|
||||||
threads map[int]*Thread
|
Threads map[int]*Thread
|
||||||
|
|
||||||
// Active thread
|
// Active thread
|
||||||
currentThread *Thread
|
CurrentThread *Thread
|
||||||
|
|
||||||
// Goroutine that will be used by default to set breakpoint, eval variables, etc...
|
// Goroutine that will be used by default to set breakpoint, eval variables, etc...
|
||||||
// Normally selectedGoroutine is currentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
|
// Normally SelectedGoroutine is CurrentThread.GetG, it will not be only if SwitchGoroutine is called with a goroutine that isn't attached to a thread
|
||||||
selectedGoroutine *G
|
SelectedGoroutine *G
|
||||||
|
|
||||||
// Maps package names to package paths, needed to lookup types inside DWARF info
|
// Maps package names to package paths, needed to lookup types inside DWARF info
|
||||||
packageMap map[string]string
|
packageMap map[string]string
|
||||||
@ -61,18 +61,12 @@ type Process struct {
|
|||||||
ptraceChan chan func()
|
ptraceChan chan func()
|
||||||
ptraceDoneChan chan interface{}
|
ptraceDoneChan chan interface{}
|
||||||
types map[string]dwarf.Offset
|
types map[string]dwarf.Offset
|
||||||
functions []functionDebugInfo
|
|
||||||
|
|
||||||
loadModuleDataOnce sync.Once
|
loadModuleDataOnce sync.Once
|
||||||
moduleData []moduleData
|
moduleData []moduleData
|
||||||
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
|
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
|
||||||
}
|
}
|
||||||
|
|
||||||
type functionDebugInfo struct {
|
|
||||||
lowpc, highpc uint64
|
|
||||||
offset dwarf.Offset
|
|
||||||
}
|
|
||||||
|
|
||||||
var NotExecutableErr = errors.New("not an executable file")
|
var NotExecutableErr = errors.New("not an executable file")
|
||||||
|
|
||||||
// New returns an initialized Process struct. Before returning,
|
// New returns an initialized Process struct. Before returning,
|
||||||
@ -81,9 +75,9 @@ var NotExecutableErr = errors.New("not an executable file")
|
|||||||
// `handlePtraceFuncs`.
|
// `handlePtraceFuncs`.
|
||||||
func New(pid int) *Process {
|
func New(pid int) *Process {
|
||||||
dbp := &Process{
|
dbp := &Process{
|
||||||
pid: pid,
|
Pid: pid,
|
||||||
threads: make(map[int]*Thread),
|
Threads: make(map[int]*Thread),
|
||||||
breakpoints: make(map[uint64]*Breakpoint),
|
Breakpoints: make(map[uint64]*Breakpoint),
|
||||||
firstStart: true,
|
firstStart: true,
|
||||||
os: new(OSProcessDetails),
|
os: new(OSProcessDetails),
|
||||||
ptraceChan: make(chan func()),
|
ptraceChan: make(chan func()),
|
||||||
@ -112,9 +106,6 @@ func (pe ProcessExitedError) Error() string {
|
|||||||
|
|
||||||
// Detach from the process being debugged, optionally killing it.
|
// Detach from the process being debugged, optionally killing it.
|
||||||
func (dbp *Process) Detach(kill bool) (err error) {
|
func (dbp *Process) Detach(kill bool) (err error) {
|
||||||
if dbp.exited {
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
if dbp.Running() {
|
if dbp.Running() {
|
||||||
if err = dbp.Halt(); err != nil {
|
if err = dbp.Halt(); err != nil {
|
||||||
return
|
return
|
||||||
@ -122,7 +113,7 @@ func (dbp *Process) Detach(kill bool) (err error) {
|
|||||||
}
|
}
|
||||||
if !kill {
|
if !kill {
|
||||||
// Clean up any breakpoints we've set.
|
// Clean up any breakpoints we've set.
|
||||||
for _, bp := range dbp.breakpoints {
|
for _, bp := range dbp.Breakpoints {
|
||||||
if bp != nil {
|
if bp != nil {
|
||||||
_, err := dbp.ClearBreakpoint(bp.Addr)
|
_, err := dbp.ClearBreakpoint(bp.Addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -132,12 +123,12 @@ func (dbp *Process) Detach(kill bool) (err error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
dbp.execPtraceFunc(func() {
|
dbp.execPtraceFunc(func() {
|
||||||
err = PtraceDetach(dbp.pid, 0)
|
err = PtraceDetach(dbp.Pid, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if kill {
|
if kill {
|
||||||
err = killProcess(dbp.pid)
|
err = killProcess(dbp.Pid)
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
return
|
return
|
||||||
@ -152,7 +143,7 @@ func (dbp *Process) Exited() bool {
|
|||||||
// Running returns whether the debugged
|
// Running returns whether the debugged
|
||||||
// process is currently executing.
|
// process is currently executing.
|
||||||
func (dbp *Process) Running() bool {
|
func (dbp *Process) Running() bool {
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.Threads {
|
||||||
if th.running {
|
if th.running {
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -160,30 +151,6 @@ func (dbp *Process) Running() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) LastModified() time.Time {
|
|
||||||
return dbp.lastModified
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dbp *Process) Pid() int {
|
|
||||||
return dbp.pid
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dbp *Process) SelectedGoroutine() *G {
|
|
||||||
return dbp.selectedGoroutine
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dbp *Process) Threads() map[int]*Thread {
|
|
||||||
return dbp.threads
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dbp *Process) CurrentThread() *Thread {
|
|
||||||
return dbp.currentThread
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dbp *Process) Breakpoints() map[uint64]*Breakpoint {
|
|
||||||
return dbp.breakpoints
|
|
||||||
}
|
|
||||||
|
|
||||||
// LoadInformation finds the executable and then uses it
|
// LoadInformation finds the executable and then uses it
|
||||||
// to parse the following information:
|
// to parse the following information:
|
||||||
// * Dwarf .debug_frame section
|
// * Dwarf .debug_frame section
|
||||||
@ -198,7 +165,7 @@ func (dbp *Process) LoadInformation(path string) error {
|
|||||||
}
|
}
|
||||||
fi, err := os.Stat(path)
|
fi, err := os.Stat(path)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
dbp.lastModified = fi.ModTime()
|
dbp.LastModified = fi.ModTime()
|
||||||
}
|
}
|
||||||
|
|
||||||
wg.Add(5)
|
wg.Add(5)
|
||||||
@ -206,7 +173,7 @@ func (dbp *Process) LoadInformation(path string) error {
|
|||||||
go dbp.parseDebugFrame(exe, &wg)
|
go dbp.parseDebugFrame(exe, &wg)
|
||||||
go dbp.obtainGoSymbols(exe, &wg)
|
go dbp.obtainGoSymbols(exe, &wg)
|
||||||
go dbp.parseDebugLineInfo(exe, &wg)
|
go dbp.parseDebugLineInfo(exe, &wg)
|
||||||
go dbp.loadDebugInfoMaps(&wg)
|
go dbp.loadTypeMap(&wg)
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
|
|
||||||
return nil
|
return nil
|
||||||
@ -250,7 +217,7 @@ func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOf
|
|||||||
|
|
||||||
// CurrentLocation returns the location of the current thread.
|
// CurrentLocation returns the location of the current thread.
|
||||||
func (dbp *Process) CurrentLocation() (*Location, error) {
|
func (dbp *Process) CurrentLocation() (*Location, error) {
|
||||||
return dbp.currentThread.Location()
|
return dbp.CurrentThread.Location()
|
||||||
}
|
}
|
||||||
|
|
||||||
// RequestManualStop sets the `halt` flag and
|
// RequestManualStop sets the `halt` flag and
|
||||||
@ -267,7 +234,7 @@ func (dbp *Process) RequestManualStop() error {
|
|||||||
// break point table. Setting a break point must be thread specific due to
|
// break point table. Setting a break point must be thread specific due to
|
||||||
// ptrace actions needing the thread to be in a signal-delivery-stop.
|
// ptrace actions needing the thread to be in a signal-delivery-stop.
|
||||||
func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) {
|
func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) {
|
||||||
tid := dbp.currentThread.ID
|
tid := dbp.CurrentThread.ID
|
||||||
|
|
||||||
if bp, ok := dbp.FindBreakpoint(addr); ok {
|
if bp, ok := dbp.FindBreakpoint(addr); ok {
|
||||||
return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
|
return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
|
||||||
@ -296,7 +263,7 @@ func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Exp
|
|||||||
newBreakpoint.ID = dbp.breakpointIDCounter
|
newBreakpoint.ID = dbp.breakpointIDCounter
|
||||||
}
|
}
|
||||||
|
|
||||||
thread := dbp.threads[tid]
|
thread := dbp.Threads[tid]
|
||||||
originalData, err := thread.readMemory(uintptr(addr), dbp.arch.BreakpointSize())
|
originalData, err := thread.readMemory(uintptr(addr), dbp.arch.BreakpointSize())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -305,7 +272,7 @@ func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Exp
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
newBreakpoint.OriginalData = originalData
|
newBreakpoint.OriginalData = originalData
|
||||||
dbp.breakpoints[addr] = newBreakpoint
|
dbp.Breakpoints[addr] = newBreakpoint
|
||||||
|
|
||||||
return newBreakpoint, nil
|
return newBreakpoint, nil
|
||||||
}
|
}
|
||||||
@ -320,18 +287,18 @@ func (dbp *Process) ClearBreakpoint(addr uint64) (*Breakpoint, error) {
|
|||||||
return nil, NoBreakpointError{addr: addr}
|
return nil, NoBreakpointError{addr: addr}
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, err := bp.Clear(dbp.currentThread); err != nil {
|
if _, err := bp.Clear(dbp.CurrentThread); err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
delete(dbp.breakpoints, addr)
|
delete(dbp.Breakpoints, addr)
|
||||||
|
|
||||||
return bp, nil
|
return bp, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Status returns the status of the current main thread context.
|
// Status returns the status of the current main thread context.
|
||||||
func (dbp *Process) Status() *WaitStatus {
|
func (dbp *Process) Status() *WaitStatus {
|
||||||
return dbp.currentThread.Status
|
return dbp.CurrentThread.Status
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next continues execution until the next source line.
|
// Next continues execution until the next source line.
|
||||||
@ -339,8 +306,8 @@ func (dbp *Process) Next() (err error) {
|
|||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
return &ProcessExitedError{}
|
return &ProcessExitedError{}
|
||||||
}
|
}
|
||||||
for i := range dbp.breakpoints {
|
for i := range dbp.Breakpoints {
|
||||||
if dbp.breakpoints[i].Internal() {
|
if dbp.Breakpoints[i].Internal() {
|
||||||
return fmt.Errorf("next while nexting")
|
return fmt.Errorf("next while nexting")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -370,7 +337,7 @@ func (dbp *Process) Continue() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbp.allGCache = nil
|
dbp.allGCache = nil
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.Threads {
|
||||||
th.clearBreakpointState()
|
th.clearBreakpointState()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -389,42 +356,34 @@ func (dbp *Process) Continue() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
switch {
|
switch {
|
||||||
case dbp.currentThread.CurrentBreakpoint == nil:
|
case dbp.CurrentThread.CurrentBreakpoint == nil:
|
||||||
// runtime.Breakpoint or manual stop
|
// runtime.Breakpoint or manual stop
|
||||||
if dbp.currentThread.onRuntimeBreakpoint() {
|
if dbp.CurrentThread.onRuntimeBreakpoint() {
|
||||||
// Single-step current thread until we exit runtime.breakpoint and
|
for i := 0; i < 2; i++ {
|
||||||
// runtime.Breakpoint.
|
if err = dbp.CurrentThread.StepInstruction(); err != nil {
|
||||||
// On go < 1.8 it was sufficient to single-step twice on go1.8 a change
|
|
||||||
// to the compiler requires 4 steps.
|
|
||||||
for {
|
|
||||||
if err = dbp.currentThread.StepInstruction(); err != nil {
|
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
loc, err := dbp.currentThread.Location()
|
|
||||||
if err != nil || loc.Fn == nil || (loc.Fn.Name != "runtime.breakpoint" && loc.Fn.Name != "runtime.Breakpoint") {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return dbp.conditionErrors()
|
return dbp.conditionErrors()
|
||||||
case dbp.currentThread.onTriggeredInternalBreakpoint():
|
case dbp.CurrentThread.onTriggeredInternalBreakpoint():
|
||||||
if dbp.currentThread.CurrentBreakpoint.Kind == StepBreakpoint {
|
if dbp.CurrentThread.CurrentBreakpoint.Kind == StepBreakpoint {
|
||||||
// See description of proc.(*Process).next for the meaning of StepBreakpoints
|
// See description of proc.(*Process).next for the meaning of StepBreakpoints
|
||||||
if err := dbp.conditionErrors(); err != nil {
|
if err := dbp.conditionErrors(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
pc, err := dbp.currentThread.PC()
|
pc, err := dbp.CurrentThread.PC()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
text, err := dbp.currentThread.Disassemble(pc, pc+maxInstructionLength, true)
|
text, err := dbp.CurrentThread.Disassemble(pc, pc+maxInstructionLength, true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
// here we either set a breakpoint into the destination of the CALL
|
// here we either set a breakpoint into the destination of the CALL
|
||||||
// instruction or we determined that the called function is hidden,
|
// instruction or we determined that the called function is hidden,
|
||||||
// either way we need to resume execution
|
// either way we need to resume execution
|
||||||
if err = dbp.setStepIntoBreakpoint(text, sameGoroutineCondition(dbp.selectedGoroutine)); err != nil {
|
if err = dbp.setStepIntoBreakpoint(text, sameGoroutineCondition(dbp.SelectedGoroutine)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@ -433,8 +392,8 @@ func (dbp *Process) Continue() error {
|
|||||||
}
|
}
|
||||||
return dbp.conditionErrors()
|
return dbp.conditionErrors()
|
||||||
}
|
}
|
||||||
case dbp.currentThread.onTriggeredBreakpoint():
|
case dbp.CurrentThread.onTriggeredBreakpoint():
|
||||||
onNextGoroutine, err := dbp.currentThread.onNextGoroutine()
|
onNextGoroutine, err := dbp.CurrentThread.onNextGoroutine()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -453,7 +412,7 @@ func (dbp *Process) Continue() error {
|
|||||||
|
|
||||||
func (dbp *Process) conditionErrors() error {
|
func (dbp *Process) conditionErrors() error {
|
||||||
var condErr error
|
var condErr error
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.Threads {
|
||||||
if th.CurrentBreakpoint != nil && th.BreakpointConditionError != nil {
|
if th.CurrentBreakpoint != nil && th.BreakpointConditionError != nil {
|
||||||
if condErr == nil {
|
if condErr == nil {
|
||||||
condErr = th.BreakpointConditionError
|
condErr = th.BreakpointConditionError
|
||||||
@ -465,12 +424,12 @@ func (dbp *Process) conditionErrors() error {
|
|||||||
return condErr
|
return condErr
|
||||||
}
|
}
|
||||||
|
|
||||||
// pick a new dbp.currentThread, with the following priority:
|
// pick a new dbp.CurrentThread, with the following priority:
|
||||||
// - a thread with onTriggeredInternalBreakpoint() == true
|
// - a thread with onTriggeredInternalBreakpoint() == true
|
||||||
// - a thread with onTriggeredBreakpoint() == true (prioritizing trapthread)
|
// - a thread with onTriggeredBreakpoint() == true (prioritizing trapthread)
|
||||||
// - trapthread
|
// - trapthread
|
||||||
func (dbp *Process) pickCurrentThread(trapthread *Thread) error {
|
func (dbp *Process) pickCurrentThread(trapthread *Thread) error {
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.Threads {
|
||||||
if th.onTriggeredInternalBreakpoint() {
|
if th.onTriggeredInternalBreakpoint() {
|
||||||
return dbp.SwitchThread(th.ID)
|
return dbp.SwitchThread(th.ID)
|
||||||
}
|
}
|
||||||
@ -478,7 +437,7 @@ func (dbp *Process) pickCurrentThread(trapthread *Thread) error {
|
|||||||
if trapthread.onTriggeredBreakpoint() {
|
if trapthread.onTriggeredBreakpoint() {
|
||||||
return dbp.SwitchThread(trapthread.ID)
|
return dbp.SwitchThread(trapthread.ID)
|
||||||
}
|
}
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.Threads {
|
||||||
if th.onTriggeredBreakpoint() {
|
if th.onTriggeredBreakpoint() {
|
||||||
return dbp.SwitchThread(th.ID)
|
return dbp.SwitchThread(th.ID)
|
||||||
}
|
}
|
||||||
@ -492,8 +451,8 @@ func (dbp *Process) Step() (err error) {
|
|||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
return &ProcessExitedError{}
|
return &ProcessExitedError{}
|
||||||
}
|
}
|
||||||
for i := range dbp.breakpoints {
|
for i := range dbp.Breakpoints {
|
||||||
if dbp.breakpoints[i].Internal() {
|
if dbp.Breakpoints[i].Internal() {
|
||||||
return fmt.Errorf("next while nexting")
|
return fmt.Errorf("next while nexting")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -533,12 +492,12 @@ func sameGoroutineCondition(g *G) ast.Expr {
|
|||||||
// asssociated with the selected goroutine. All other
|
// asssociated with the selected goroutine. All other
|
||||||
// threads will remain stopped.
|
// threads will remain stopped.
|
||||||
func (dbp *Process) StepInstruction() (err error) {
|
func (dbp *Process) StepInstruction() (err error) {
|
||||||
if dbp.selectedGoroutine == nil {
|
if dbp.SelectedGoroutine == nil {
|
||||||
return errors.New("cannot single step: no selected goroutine")
|
return errors.New("cannot single step: no selected goroutine")
|
||||||
}
|
}
|
||||||
if dbp.selectedGoroutine.thread == nil {
|
if dbp.SelectedGoroutine.thread == nil {
|
||||||
// Step called on parked goroutine
|
// Step called on parked goroutine
|
||||||
if _, err := dbp.SetBreakpoint(dbp.selectedGoroutine.PC, NextBreakpoint, sameGoroutineCondition(dbp.selectedGoroutine)); err != nil {
|
if _, err := dbp.SetBreakpoint(dbp.SelectedGoroutine.PC, NextBreakpoint, sameGoroutineCondition(dbp.SelectedGoroutine)); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return dbp.Continue()
|
return dbp.Continue()
|
||||||
@ -547,20 +506,20 @@ func (dbp *Process) StepInstruction() (err error) {
|
|||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
return &ProcessExitedError{}
|
return &ProcessExitedError{}
|
||||||
}
|
}
|
||||||
dbp.selectedGoroutine.thread.clearBreakpointState()
|
dbp.SelectedGoroutine.thread.clearBreakpointState()
|
||||||
err = dbp.selectedGoroutine.thread.StepInstruction()
|
err = dbp.SelectedGoroutine.thread.StepInstruction()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return dbp.selectedGoroutine.thread.SetCurrentBreakpoint()
|
return dbp.SelectedGoroutine.thread.SetCurrentBreakpoint()
|
||||||
}
|
}
|
||||||
|
|
||||||
// StepOut will continue until the current goroutine exits the
|
// StepOut will continue until the current goroutine exits the
|
||||||
// function currently being executed or a deferred function is executed
|
// function currently being executed or a deferred function is executed
|
||||||
func (dbp *Process) StepOut() error {
|
func (dbp *Process) StepOut() error {
|
||||||
cond := sameGoroutineCondition(dbp.selectedGoroutine)
|
cond := sameGoroutineCondition(dbp.SelectedGoroutine)
|
||||||
|
|
||||||
topframe, err := topframe(dbp.selectedGoroutine, dbp.currentThread)
|
topframe, err := topframe(dbp.SelectedGoroutine, dbp.CurrentThread)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -569,10 +528,8 @@ func (dbp *Process) StepOut() error {
|
|||||||
|
|
||||||
var deferpc uint64 = 0
|
var deferpc uint64 = 0
|
||||||
if filepath.Ext(topframe.Current.File) == ".go" {
|
if filepath.Ext(topframe.Current.File) == ".go" {
|
||||||
if dbp.selectedGoroutine != nil {
|
if dbp.SelectedGoroutine != nil && dbp.SelectedGoroutine.DeferPC != 0 {
|
||||||
deferPCEntry := dbp.selectedGoroutine.DeferPC()
|
_, _, deferfn := dbp.goSymTable.PCToLine(dbp.SelectedGoroutine.DeferPC)
|
||||||
if deferPCEntry != 0 {
|
|
||||||
_, _, deferfn := dbp.goSymTable.PCToLine(deferPCEntry)
|
|
||||||
deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
|
deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -580,7 +537,6 @@ func (dbp *Process) StepOut() error {
|
|||||||
pcs = append(pcs, deferpc)
|
pcs = append(pcs, deferpc)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
if topframe.Ret == 0 && deferpc == 0 {
|
if topframe.Ret == 0 && deferpc == 0 {
|
||||||
return errors.New("nothing to stepout to")
|
return errors.New("nothing to stepout to")
|
||||||
@ -616,9 +572,9 @@ func (dbp *Process) SwitchThread(tid int) error {
|
|||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
return &ProcessExitedError{}
|
return &ProcessExitedError{}
|
||||||
}
|
}
|
||||||
if th, ok := dbp.threads[tid]; ok {
|
if th, ok := dbp.Threads[tid]; ok {
|
||||||
dbp.currentThread = th
|
dbp.CurrentThread = th
|
||||||
dbp.selectedGoroutine, _ = dbp.currentThread.GetG()
|
dbp.SelectedGoroutine, _ = dbp.CurrentThread.GetG()
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
return fmt.Errorf("thread %d does not exist", tid)
|
return fmt.Errorf("thread %d does not exist", tid)
|
||||||
@ -635,13 +591,13 @@ func (dbp *Process) SwitchGoroutine(gid int) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if g == nil {
|
if g == nil {
|
||||||
// user specified -1 and selectedGoroutine is nil
|
// user specified -1 and SelectedGoroutine is nil
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if g.thread != nil {
|
if g.thread != nil {
|
||||||
return dbp.SwitchThread(g.thread.ID)
|
return dbp.SwitchThread(g.thread.ID)
|
||||||
}
|
}
|
||||||
dbp.selectedGoroutine = g
|
dbp.SelectedGoroutine = g
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -661,13 +617,13 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) {
|
|||||||
rdr = dbp.DwarfReader()
|
rdr = dbp.DwarfReader()
|
||||||
)
|
)
|
||||||
|
|
||||||
for i := range dbp.threads {
|
for i := range dbp.Threads {
|
||||||
if dbp.threads[i].blocked() {
|
if dbp.Threads[i].blocked() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
g, _ := dbp.threads[i].GetG()
|
g, _ := dbp.Threads[i].GetG()
|
||||||
if g != nil {
|
if g != nil {
|
||||||
threadg[g.ID] = dbp.threads[i]
|
threadg[g.ID] = dbp.Threads[i]
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -675,7 +631,7 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
allglenBytes, err := dbp.currentThread.readMemory(uintptr(addr), 8)
|
allglenBytes, err := dbp.CurrentThread.readMemory(uintptr(addr), 8)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -690,11 +646,11 @@ func (dbp *Process) GoroutinesInfo() ([]*G, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
faddr, err := dbp.currentThread.readMemory(uintptr(allgentryaddr), dbp.arch.PtrSize())
|
faddr, err := dbp.CurrentThread.readMemory(uintptr(allgentryaddr), dbp.arch.PtrSize())
|
||||||
allgptr := binary.LittleEndian.Uint64(faddr)
|
allgptr := binary.LittleEndian.Uint64(faddr)
|
||||||
|
|
||||||
for i := uint64(0); i < allglen; i++ {
|
for i := uint64(0); i < allglen; i++ {
|
||||||
gvar, err := dbp.currentThread.newGVariable(uintptr(allgptr+(i*uint64(dbp.arch.PtrSize()))), true)
|
gvar, err := dbp.CurrentThread.newGVariable(uintptr(allgptr+(i*uint64(dbp.arch.PtrSize()))), true)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -728,7 +684,7 @@ func (dbp *Process) Halt() (err error) {
|
|||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
return &ProcessExitedError{}
|
return &ProcessExitedError{}
|
||||||
}
|
}
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.Threads {
|
||||||
if err := th.Halt(); err != nil {
|
if err := th.Halt(); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -739,18 +695,18 @@ func (dbp *Process) Halt() (err error) {
|
|||||||
// Registers obtains register values from the
|
// Registers obtains register values from the
|
||||||
// "current" thread of the traced process.
|
// "current" thread of the traced process.
|
||||||
func (dbp *Process) Registers() (Registers, error) {
|
func (dbp *Process) Registers() (Registers, error) {
|
||||||
return dbp.currentThread.Registers(false)
|
return dbp.CurrentThread.Registers(false)
|
||||||
}
|
}
|
||||||
|
|
||||||
// PC returns the PC of the current thread.
|
// PC returns the PC of the current thread.
|
||||||
func (dbp *Process) PC() (uint64, error) {
|
func (dbp *Process) PC() (uint64, error) {
|
||||||
return dbp.currentThread.PC()
|
return dbp.CurrentThread.PC()
|
||||||
}
|
}
|
||||||
|
|
||||||
// CurrentBreakpoint returns the breakpoint the current thread
|
// CurrentBreakpoint returns the breakpoint the current thread
|
||||||
// is stopped at.
|
// is stopped at.
|
||||||
func (dbp *Process) CurrentBreakpoint() *Breakpoint {
|
func (dbp *Process) CurrentBreakpoint() *Breakpoint {
|
||||||
return dbp.currentThread.CurrentBreakpoint
|
return dbp.CurrentThread.CurrentBreakpoint
|
||||||
}
|
}
|
||||||
|
|
||||||
// DwarfReader returns a reader for the dwarf data
|
// DwarfReader returns a reader for the dwarf data
|
||||||
@ -784,7 +740,7 @@ func (dbp *Process) PCToLine(pc uint64) (string, int, *gosym.Func) {
|
|||||||
|
|
||||||
// FindBreakpointByID finds the breakpoint for the given ID.
|
// FindBreakpointByID finds the breakpoint for the given ID.
|
||||||
func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
|
func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
|
||||||
for _, bp := range dbp.breakpoints {
|
for _, bp := range dbp.Breakpoints {
|
||||||
if bp.ID == id {
|
if bp.ID == id {
|
||||||
return bp, true
|
return bp, true
|
||||||
}
|
}
|
||||||
@ -795,11 +751,11 @@ func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
|
|||||||
// FindBreakpoint finds the breakpoint for the given pc.
|
// FindBreakpoint finds the breakpoint for the given pc.
|
||||||
func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
|
func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
|
||||||
// Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
|
// Check to see if address is past the breakpoint, (i.e. breakpoint was hit).
|
||||||
if bp, ok := dbp.breakpoints[pc-uint64(dbp.arch.BreakpointSize())]; ok {
|
if bp, ok := dbp.Breakpoints[pc-uint64(dbp.arch.BreakpointSize())]; ok {
|
||||||
return bp, true
|
return bp, true
|
||||||
}
|
}
|
||||||
// Directly use addr to lookup breakpoint.
|
// Directly use addr to lookup breakpoint.
|
||||||
if bp, ok := dbp.breakpoints[pc]; ok {
|
if bp, ok := dbp.Breakpoints[pc]; ok {
|
||||||
return bp, true
|
return bp, true
|
||||||
}
|
}
|
||||||
return nil, false
|
return nil, false
|
||||||
@ -809,17 +765,17 @@ func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
|
|||||||
func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, error) {
|
func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, error) {
|
||||||
if attach {
|
if attach {
|
||||||
var err error
|
var err error
|
||||||
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
|
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.Pid) })
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
_, _, err = dbp.wait(dbp.pid, 0)
|
_, _, err = dbp.wait(dbp.Pid, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
proc, err := os.FindProcess(dbp.pid)
|
proc, err := os.FindProcess(dbp.Pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -840,11 +796,11 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbp.arch.SetGStructOffset(ver, isextld)
|
dbp.arch.SetGStructOffset(ver, isextld)
|
||||||
// selectedGoroutine can not be set correctly by the call to updateThreadList
|
// SelectedGoroutine can not be set correctly by the call to updateThreadList
|
||||||
// because without calling SetGStructOffset we can not read the G struct of currentThread
|
// because without calling SetGStructOffset we can not read the G struct of CurrentThread
|
||||||
// but without calling updateThreadList we can not examine memory to determine
|
// but without calling updateThreadList we can not examine memory to determine
|
||||||
// the offset of g struct inside TLS
|
// the offset of g struct inside TLS
|
||||||
dbp.selectedGoroutine, _ = dbp.currentThread.GetG()
|
dbp.SelectedGoroutine, _ = dbp.CurrentThread.GetG()
|
||||||
|
|
||||||
panicpc, err := dbp.FindFunctionLocation("runtime.startpanic", true, 0)
|
panicpc, err := dbp.FindFunctionLocation("runtime.startpanic", true, 0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
@ -860,7 +816,7 @@ func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, e
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) ClearInternalBreakpoints() error {
|
func (dbp *Process) ClearInternalBreakpoints() error {
|
||||||
for _, bp := range dbp.breakpoints {
|
for _, bp := range dbp.Breakpoints {
|
||||||
if !bp.Internal() {
|
if !bp.Internal() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -868,9 +824,9 @@ func (dbp *Process) ClearInternalBreakpoints() error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
for i := range dbp.threads {
|
for i := range dbp.Threads {
|
||||||
if dbp.threads[i].CurrentBreakpoint != nil && dbp.threads[i].CurrentBreakpoint.Internal() {
|
if dbp.Threads[i].CurrentBreakpoint != nil && dbp.Threads[i].CurrentBreakpoint.Internal() {
|
||||||
dbp.threads[i].CurrentBreakpoint = nil
|
dbp.Threads[i].CurrentBreakpoint = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
@ -928,7 +884,7 @@ func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error)
|
|||||||
// specified by `gid`.
|
// specified by `gid`.
|
||||||
func (dbp *Process) FindGoroutine(gid int) (*G, error) {
|
func (dbp *Process) FindGoroutine(gid int) (*G, error) {
|
||||||
if gid == -1 {
|
if gid == -1 {
|
||||||
return dbp.selectedGoroutine, nil
|
return dbp.SelectedGoroutine, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
gs, err := dbp.GoroutinesInfo()
|
gs, err := dbp.GoroutinesInfo()
|
||||||
@ -954,13 +910,13 @@ func (dbp *Process) ConvertEvalScope(gid, frame int) (*EvalScope, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if g == nil {
|
if g == nil {
|
||||||
return dbp.currentThread.Scope()
|
return dbp.CurrentThread.Scope()
|
||||||
}
|
}
|
||||||
|
|
||||||
var out EvalScope
|
var out EvalScope
|
||||||
|
|
||||||
if g.thread == nil {
|
if g.thread == nil {
|
||||||
out.Thread = dbp.currentThread
|
out.Thread = dbp.CurrentThread
|
||||||
} else {
|
} else {
|
||||||
out.Thread = g.thread
|
out.Thread = g.thread
|
||||||
}
|
}
|
@ -17,8 +17,8 @@ import (
|
|||||||
|
|
||||||
"golang.org/x/debug/macho"
|
"golang.org/x/debug/macho"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
"github.com/derekparker/delve/dwarf/frame"
|
||||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
"github.com/derekparker/delve/dwarf/line"
|
||||||
sys "golang.org/x/sys/unix"
|
sys "golang.org/x/sys/unix"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -77,7 +77,7 @@ func Launch(cmd []string, wd string) (*Process, error) {
|
|||||||
if pid <= 0 {
|
if pid <= 0 {
|
||||||
return nil, fmt.Errorf("could not fork/exec")
|
return nil, fmt.Errorf("could not fork/exec")
|
||||||
}
|
}
|
||||||
dbp.pid = pid
|
dbp.Pid = pid
|
||||||
for i := range argvSlice {
|
for i := range argvSlice {
|
||||||
C.free(unsafe.Pointer(argvSlice[i]))
|
C.free(unsafe.Pointer(argvSlice[i]))
|
||||||
}
|
}
|
||||||
@ -86,7 +86,7 @@ func Launch(cmd []string, wd string) (*Process, error) {
|
|||||||
// trapWait to wait until the child process calls execve.
|
// trapWait to wait until the child process calls execve.
|
||||||
|
|
||||||
for {
|
for {
|
||||||
err = dbp.updateThreadListForTask(C.get_task_for_pid(C.int(dbp.pid)))
|
err = dbp.updateThreadListForTask(C.get_task_for_pid(C.int(dbp.Pid)))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
@ -100,7 +100,7 @@ func Launch(cmd []string, wd string) (*Process, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbp.allGCache = nil
|
dbp.allGCache = nil
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.Threads {
|
||||||
th.clearBreakpointState()
|
th.clearBreakpointState()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -152,11 +152,11 @@ func (dbp *Process) Kill() (err error) {
|
|||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
err = sys.Kill(-dbp.pid, sys.SIGKILL)
|
err = sys.Kill(-dbp.Pid, sys.SIGKILL)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return errors.New("could not deliver signal: " + err.Error())
|
return errors.New("could not deliver signal: " + err.Error())
|
||||||
}
|
}
|
||||||
for port := range dbp.threads {
|
for port := range dbp.Threads {
|
||||||
if C.thread_resume(C.thread_act_t(port)) != C.KERN_SUCCESS {
|
if C.thread_resume(C.thread_act_t(port)) != C.KERN_SUCCESS {
|
||||||
return errors.New("could not resume task")
|
return errors.New("could not resume task")
|
||||||
}
|
}
|
||||||
@ -175,7 +175,7 @@ func (dbp *Process) Kill() (err error) {
|
|||||||
func (dbp *Process) requestManualStop() (err error) {
|
func (dbp *Process) requestManualStop() (err error) {
|
||||||
var (
|
var (
|
||||||
task = C.mach_port_t(dbp.os.task)
|
task = C.mach_port_t(dbp.os.task)
|
||||||
thread = C.mach_port_t(dbp.currentThread.os.threadAct)
|
thread = C.mach_port_t(dbp.CurrentThread.os.threadAct)
|
||||||
exceptionPort = C.mach_port_t(dbp.os.exceptionPort)
|
exceptionPort = C.mach_port_t(dbp.os.exceptionPort)
|
||||||
)
|
)
|
||||||
kret := C.raise_exception(task, thread, exceptionPort, C.EXC_BREAKPOINT)
|
kret := C.raise_exception(task, thread, exceptionPort, C.EXC_BREAKPOINT)
|
||||||
@ -218,12 +218,12 @@ func (dbp *Process) updateThreadListForTask(task C.task_t) error {
|
|||||||
return couldNotGetThreadList
|
return couldNotGetThreadList
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, thread := range dbp.threads {
|
for _, thread := range dbp.Threads {
|
||||||
thread.os.exists = false
|
thread.os.exists = false
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, port := range list {
|
for _, port := range list {
|
||||||
thread, ok := dbp.threads[int(port)]
|
thread, ok := dbp.Threads[int(port)]
|
||||||
if !ok {
|
if !ok {
|
||||||
thread, err = dbp.addThread(int(port), false)
|
thread, err = dbp.addThread(int(port), false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -233,9 +233,9 @@ func (dbp *Process) updateThreadListForTask(task C.task_t) error {
|
|||||||
thread.os.exists = true
|
thread.os.exists = true
|
||||||
}
|
}
|
||||||
|
|
||||||
for threadID, thread := range dbp.threads {
|
for threadID, thread := range dbp.Threads {
|
||||||
if !thread.os.exists {
|
if !thread.os.exists {
|
||||||
delete(dbp.threads, threadID)
|
delete(dbp.Threads, threadID)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -243,7 +243,7 @@ func (dbp *Process) updateThreadListForTask(task C.task_t) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) addThread(port int, attach bool) (*Thread, error) {
|
func (dbp *Process) addThread(port int, attach bool) (*Thread, error) {
|
||||||
if thread, ok := dbp.threads[port]; ok {
|
if thread, ok := dbp.Threads[port]; ok {
|
||||||
return thread, nil
|
return thread, nil
|
||||||
}
|
}
|
||||||
thread := &Thread{
|
thread := &Thread{
|
||||||
@ -251,9 +251,9 @@ func (dbp *Process) addThread(port int, attach bool) (*Thread, error) {
|
|||||||
dbp: dbp,
|
dbp: dbp,
|
||||||
os: new(OSSpecificDetails),
|
os: new(OSSpecificDetails),
|
||||||
}
|
}
|
||||||
dbp.threads[port] = thread
|
dbp.Threads[port] = thread
|
||||||
thread.os.threadAct = C.thread_act_t(port)
|
thread.os.threadAct = C.thread_act_t(port)
|
||||||
if dbp.currentThread == nil {
|
if dbp.CurrentThread == nil {
|
||||||
dbp.SwitchThread(thread.ID)
|
dbp.SwitchThread(thread.ID)
|
||||||
}
|
}
|
||||||
return thread, nil
|
return thread, nil
|
||||||
@ -338,7 +338,7 @@ var UnsupportedArchErr = errors.New("unsupported architecture - only darwin/amd6
|
|||||||
|
|
||||||
func (dbp *Process) findExecutable(path string) (*macho.File, string, error) {
|
func (dbp *Process) findExecutable(path string) (*macho.File, string, error) {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = C.GoString(C.find_executable(C.int(dbp.pid)))
|
path = C.GoString(C.find_executable(C.int(dbp.Pid)))
|
||||||
}
|
}
|
||||||
exe, err := macho.Open(path)
|
exe, err := macho.Open(path)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -369,16 +369,16 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if !dbp.os.initialized {
|
if !dbp.os.initialized {
|
||||||
if pidtask := C.get_task_for_pid(C.int(dbp.pid)); pidtask != 0 && dbp.os.task != pidtask {
|
if pidtask := C.get_task_for_pid(C.int(dbp.Pid)); pidtask != 0 && dbp.os.task != pidtask {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
_, status, err := dbp.wait(dbp.pid, 0)
|
_, status, err := dbp.wait(dbp.Pid, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dbp.postExit()
|
dbp.postExit()
|
||||||
return nil, ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
|
return nil, ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
|
||||||
|
|
||||||
case C.MACH_RCV_INTERRUPTED:
|
case C.MACH_RCV_INTERRUPTED:
|
||||||
if !dbp.halt {
|
if !dbp.halt {
|
||||||
@ -407,7 +407,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
|||||||
// Since we cannot be notified of new threads on OS X
|
// Since we cannot be notified of new threads on OS X
|
||||||
// this is as good a time as any to check for them.
|
// this is as good a time as any to check for them.
|
||||||
dbp.updateThreadList()
|
dbp.updateThreadList()
|
||||||
th, ok := dbp.threads[int(port)]
|
th, ok := dbp.Threads[int(port)]
|
||||||
if !ok {
|
if !ok {
|
||||||
if dbp.halt {
|
if dbp.halt {
|
||||||
dbp.halt = false
|
dbp.halt = false
|
||||||
@ -427,7 +427,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) waitForStop() ([]int, error) {
|
func (dbp *Process) waitForStop() ([]int, error) {
|
||||||
ports := make([]int, 0, len(dbp.threads))
|
ports := make([]int, 0, len(dbp.Threads))
|
||||||
count := 0
|
count := 0
|
||||||
for {
|
for {
|
||||||
var task C.task_t
|
var task C.task_t
|
||||||
@ -455,7 +455,7 @@ func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
|
|||||||
}
|
}
|
||||||
trapthread.SetCurrentBreakpoint()
|
trapthread.SetCurrentBreakpoint()
|
||||||
for _, port := range ports {
|
for _, port := range ports {
|
||||||
if th, ok := dbp.threads[port]; ok {
|
if th, ok := dbp.Threads[port]; ok {
|
||||||
err := th.SetCurrentBreakpoint()
|
err := th.SetCurrentBreakpoint()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -483,17 +483,17 @@ func (dbp *Process) exitGuard(err error) error {
|
|||||||
if err != ErrContinueThread {
|
if err != ErrContinueThread {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
_, status, werr := dbp.wait(dbp.pid, sys.WNOHANG)
|
_, status, werr := dbp.wait(dbp.Pid, sys.WNOHANG)
|
||||||
if werr == nil && status.Exited() {
|
if werr == nil && status.Exited() {
|
||||||
dbp.postExit()
|
dbp.postExit()
|
||||||
return ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
|
return ProcessExitedError{Pid: dbp.Pid, Status: status.ExitStatus()}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) resume() error {
|
func (dbp *Process) resume() error {
|
||||||
// all threads stopped over a breakpoint are made to step over it
|
// all threads stopped over a breakpoint are made to step over it
|
||||||
for _, thread := range dbp.threads {
|
for _, thread := range dbp.Threads {
|
||||||
if thread.CurrentBreakpoint != nil {
|
if thread.CurrentBreakpoint != nil {
|
||||||
if err := thread.StepInstruction(); err != nil {
|
if err := thread.StepInstruction(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -502,7 +502,7 @@ func (dbp *Process) resume() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// everything is resumed
|
// everything is resumed
|
||||||
for _, thread := range dbp.threads {
|
for _, thread := range dbp.Threads {
|
||||||
if err := thread.resume(); err != nil {
|
if err := thread.resume(); err != nil {
|
||||||
return dbp.exitGuard(err)
|
return dbp.exitGuard(err)
|
||||||
}
|
}
|
@ -18,8 +18,8 @@ import (
|
|||||||
|
|
||||||
sys "golang.org/x/sys/unix"
|
sys "golang.org/x/sys/unix"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
"github.com/derekparker/delve/dwarf/frame"
|
||||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
"github.com/derekparker/delve/dwarf/line"
|
||||||
"golang.org/x/debug/elf"
|
"golang.org/x/debug/elf"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -70,7 +70,7 @@ func Launch(cmd []string, wd string) (*Process, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
dbp.pid = proc.Process.Pid
|
dbp.Pid = proc.Process.Pid
|
||||||
_, _, err = dbp.wait(proc.Process.Pid, 0)
|
_, _, err = dbp.wait(proc.Process.Pid, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
|
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
|
||||||
@ -88,13 +88,13 @@ func (dbp *Process) Kill() (err error) {
|
|||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !dbp.threads[dbp.pid].Stopped() {
|
if !dbp.Threads[dbp.Pid].Stopped() {
|
||||||
return errors.New("process must be stopped in order to kill it")
|
return errors.New("process must be stopped in order to kill it")
|
||||||
}
|
}
|
||||||
if err = sys.Kill(-dbp.pid, sys.SIGKILL); err != nil {
|
if err = sys.Kill(-dbp.Pid, sys.SIGKILL); err != nil {
|
||||||
return errors.New("could not deliver signal " + err.Error())
|
return errors.New("could not deliver signal " + err.Error())
|
||||||
}
|
}
|
||||||
if _, _, err = dbp.wait(dbp.pid, 0); err != nil {
|
if _, _, err = dbp.wait(dbp.Pid, 0); err != nil {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
dbp.postExit()
|
dbp.postExit()
|
||||||
@ -102,13 +102,13 @@ func (dbp *Process) Kill() (err error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) requestManualStop() (err error) {
|
func (dbp *Process) requestManualStop() (err error) {
|
||||||
return sys.Kill(dbp.pid, sys.SIGTRAP)
|
return sys.Kill(dbp.Pid, sys.SIGTRAP)
|
||||||
}
|
}
|
||||||
|
|
||||||
// Attach to a newly created thread, and store that thread in our list of
|
// Attach to a newly created thread, and store that thread in our list of
|
||||||
// known threads.
|
// known threads.
|
||||||
func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
|
func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
|
||||||
if thread, ok := dbp.threads[tid]; ok {
|
if thread, ok := dbp.Threads[tid]; ok {
|
||||||
return thread, nil
|
return thread, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -145,26 +145,26 @@ func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
dbp.threads[tid] = &Thread{
|
dbp.Threads[tid] = &Thread{
|
||||||
ID: tid,
|
ID: tid,
|
||||||
dbp: dbp,
|
dbp: dbp,
|
||||||
os: new(OSSpecificDetails),
|
os: new(OSSpecificDetails),
|
||||||
}
|
}
|
||||||
if dbp.currentThread == nil {
|
if dbp.CurrentThread == nil {
|
||||||
dbp.SwitchThread(tid)
|
dbp.SwitchThread(tid)
|
||||||
}
|
}
|
||||||
return dbp.threads[tid], nil
|
return dbp.Threads[tid], nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) updateThreadList() error {
|
func (dbp *Process) updateThreadList() error {
|
||||||
tids, _ := filepath.Glob(fmt.Sprintf("/proc/%d/task/*", dbp.pid))
|
tids, _ := filepath.Glob(fmt.Sprintf("/proc/%d/task/*", dbp.Pid))
|
||||||
for _, tidpath := range tids {
|
for _, tidpath := range tids {
|
||||||
tidstr := filepath.Base(tidpath)
|
tidstr := filepath.Base(tidpath)
|
||||||
tid, err := strconv.Atoi(tidstr)
|
tid, err := strconv.Atoi(tidstr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if _, err := dbp.addThread(tid, tid != dbp.pid); err != nil {
|
if _, err := dbp.addThread(tid, tid != dbp.Pid); err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -175,7 +175,7 @@ var UnsupportedArchErr = errors.New("unsupported architecture - only linux/amd64
|
|||||||
|
|
||||||
func (dbp *Process) findExecutable(path string) (*elf.File, string, error) {
|
func (dbp *Process) findExecutable(path string) (*elf.File, string, error) {
|
||||||
if path == "" {
|
if path == "" {
|
||||||
path = fmt.Sprintf("/proc/%d/exe", dbp.pid)
|
path = fmt.Sprintf("/proc/%d/exe", dbp.Pid)
|
||||||
}
|
}
|
||||||
f, err := os.OpenFile(path, 0, os.ModePerm)
|
f, err := os.OpenFile(path, 0, os.ModePerm)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -279,16 +279,16 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
|||||||
if wpid == 0 {
|
if wpid == 0 {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
th, ok := dbp.threads[wpid]
|
th, ok := dbp.Threads[wpid]
|
||||||
if ok {
|
if ok {
|
||||||
th.Status = (*WaitStatus)(status)
|
th.Status = (*WaitStatus)(status)
|
||||||
}
|
}
|
||||||
if status.Exited() {
|
if status.Exited() {
|
||||||
if wpid == dbp.pid {
|
if wpid == dbp.Pid {
|
||||||
dbp.postExit()
|
dbp.postExit()
|
||||||
return nil, ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
|
return nil, ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
|
||||||
}
|
}
|
||||||
delete(dbp.threads, wpid)
|
delete(dbp.Threads, wpid)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
if status.StopSignal() == sys.SIGTRAP && status.TrapCause() == sys.PTRACE_EVENT_CLONE {
|
if status.StopSignal() == sys.SIGTRAP && status.TrapCause() == sys.PTRACE_EVENT_CLONE {
|
||||||
@ -314,12 +314,12 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
|||||||
if err = th.Continue(); err != nil {
|
if err = th.Continue(); err != nil {
|
||||||
if err == sys.ESRCH {
|
if err == sys.ESRCH {
|
||||||
// thread died while we were adding it
|
// thread died while we were adding it
|
||||||
delete(dbp.threads, th.ID)
|
delete(dbp.Threads, th.ID)
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
return nil, fmt.Errorf("could not continue new thread %d %s", cloned, err)
|
return nil, fmt.Errorf("could not continue new thread %d %s", cloned, err)
|
||||||
}
|
}
|
||||||
if err = dbp.threads[int(wpid)].Continue(); err != nil {
|
if err = dbp.Threads[int(wpid)].Continue(); err != nil {
|
||||||
if err != sys.ESRCH {
|
if err != sys.ESRCH {
|
||||||
return nil, fmt.Errorf("could not continue existing thread %d %s", wpid, err)
|
return nil, fmt.Errorf("could not continue existing thread %d %s", wpid, err)
|
||||||
}
|
}
|
||||||
@ -343,7 +343,7 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
|||||||
// TODO(dp) alert user about unexpected signals here.
|
// TODO(dp) alert user about unexpected signals here.
|
||||||
if err := th.resumeWithSig(int(status.StopSignal())); err != nil {
|
if err := th.resumeWithSig(int(status.StopSignal())); err != nil {
|
||||||
if err == sys.ESRCH {
|
if err == sys.ESRCH {
|
||||||
return nil, ProcessExitedError{Pid: dbp.pid}
|
return nil, ProcessExitedError{Pid: dbp.Pid}
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -354,19 +354,19 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
|||||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
|
|
||||||
comm, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", dbp.pid))
|
comm, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", dbp.Pid))
|
||||||
if err == nil {
|
if err == nil {
|
||||||
// removes newline character
|
// removes newline character
|
||||||
comm = bytes.TrimSuffix(comm, []byte("\n"))
|
comm = bytes.TrimSuffix(comm, []byte("\n"))
|
||||||
}
|
}
|
||||||
|
|
||||||
if comm == nil || len(comm) <= 0 {
|
if comm == nil || len(comm) <= 0 {
|
||||||
stat, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", dbp.pid))
|
stat, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", dbp.Pid))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Could not read proc stat: %v\n", err)
|
fmt.Printf("Could not read proc stat: %v\n", err)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
expr := fmt.Sprintf("%d\\s*\\((.*)\\)", dbp.pid)
|
expr := fmt.Sprintf("%d\\s*\\((.*)\\)", dbp.Pid)
|
||||||
rexp, err := regexp.Compile(expr)
|
rexp, err := regexp.Compile(expr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Printf("Regexp compile error: %v\n", err)
|
fmt.Printf("Regexp compile error: %v\n", err)
|
||||||
@ -374,7 +374,7 @@ func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
|||||||
}
|
}
|
||||||
match := rexp.FindSubmatch(stat)
|
match := rexp.FindSubmatch(stat)
|
||||||
if match == nil {
|
if match == nil {
|
||||||
fmt.Printf("No match found using regexp '%s' in /proc/%d/stat\n", expr, dbp.pid)
|
fmt.Printf("No match found using regexp '%s' in /proc/%d/stat\n", expr, dbp.Pid)
|
||||||
os.Exit(1)
|
os.Exit(1)
|
||||||
}
|
}
|
||||||
comm = match[1]
|
comm = match[1]
|
||||||
@ -402,16 +402,9 @@ func status(pid int, comm string) rune {
|
|||||||
return state
|
return state
|
||||||
}
|
}
|
||||||
|
|
||||||
// waitFast is like wait but does not handle process-exit correctly
|
|
||||||
func (dbp *Process) waitFast(pid int) (int, *sys.WaitStatus, error) {
|
|
||||||
var s sys.WaitStatus
|
|
||||||
wpid, err := sys.Wait4(pid, &s, sys.WALL, nil)
|
|
||||||
return wpid, &s, err
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||||
var s sys.WaitStatus
|
var s sys.WaitStatus
|
||||||
if (pid != dbp.pid) || (options != 0) {
|
if (pid != dbp.Pid) || (options != 0) {
|
||||||
wpid, err := sys.Wait4(pid, &s, sys.WALL|options, nil)
|
wpid, err := sys.Wait4(pid, &s, sys.WALL|options, nil)
|
||||||
return wpid, &s, err
|
return wpid, &s, err
|
||||||
}
|
}
|
||||||
@ -442,7 +435,7 @@ func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
|
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
|
||||||
for _, th := range dbp.threads {
|
for _, th := range dbp.Threads {
|
||||||
if th.CurrentBreakpoint == nil {
|
if th.CurrentBreakpoint == nil {
|
||||||
err := th.SetCurrentBreakpoint()
|
err := th.SetCurrentBreakpoint()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -457,7 +450,7 @@ func (dbp *Process) exitGuard(err error) error {
|
|||||||
if err != sys.ESRCH {
|
if err != sys.ESRCH {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if status(dbp.pid, dbp.os.comm) == StatusZombie {
|
if status(dbp.Pid, dbp.os.comm) == StatusZombie {
|
||||||
_, err := dbp.trapWait(-1)
|
_, err := dbp.trapWait(-1)
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -467,7 +460,7 @@ func (dbp *Process) exitGuard(err error) error {
|
|||||||
|
|
||||||
func (dbp *Process) resume() error {
|
func (dbp *Process) resume() error {
|
||||||
// all threads stopped over a breakpoint are made to step over it
|
// all threads stopped over a breakpoint are made to step over it
|
||||||
for _, thread := range dbp.threads {
|
for _, thread := range dbp.Threads {
|
||||||
if thread.CurrentBreakpoint != nil {
|
if thread.CurrentBreakpoint != nil {
|
||||||
if err := thread.StepInstruction(); err != nil {
|
if err := thread.StepInstruction(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -476,7 +469,7 @@ func (dbp *Process) resume() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
// everything is resumed
|
// everything is resumed
|
||||||
for _, thread := range dbp.threads {
|
for _, thread := range dbp.Threads {
|
||||||
if err := thread.resume(); err != nil && err != sys.ESRCH {
|
if err := thread.resume(); err != nil && err != sys.ESRCH {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
@ -14,8 +14,8 @@ import (
|
|||||||
|
|
||||||
sys "golang.org/x/sys/windows"
|
sys "golang.org/x/sys/windows"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
"github.com/derekparker/delve/dwarf/frame"
|
||||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
"github.com/derekparker/delve/dwarf/line"
|
||||||
"golang.org/x/debug/dwarf"
|
"golang.org/x/debug/dwarf"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -113,7 +113,7 @@ func Launch(cmd []string, wd string) (*Process, error) {
|
|||||||
sys.CloseHandle(sys.Handle(pi.Process))
|
sys.CloseHandle(sys.Handle(pi.Process))
|
||||||
sys.CloseHandle(sys.Handle(pi.Thread))
|
sys.CloseHandle(sys.Handle(pi.Thread))
|
||||||
|
|
||||||
dbp.pid = int(pi.ProcessId)
|
dbp.Pid = int(pi.ProcessId)
|
||||||
|
|
||||||
return newDebugProcess(dbp, argv0Go)
|
return newDebugProcess(dbp, argv0Go)
|
||||||
}
|
}
|
||||||
@ -135,11 +135,11 @@ func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
|
|||||||
}
|
}
|
||||||
if tid == 0 {
|
if tid == 0 {
|
||||||
dbp.postExit()
|
dbp.postExit()
|
||||||
return nil, ProcessExitedError{Pid: dbp.pid, Status: exitCode}
|
return nil, ProcessExitedError{Pid: dbp.Pid, Status: exitCode}
|
||||||
}
|
}
|
||||||
// Suspend all threads so that the call to _ContinueDebugEvent will
|
// Suspend all threads so that the call to _ContinueDebugEvent will
|
||||||
// not resume the target.
|
// not resume the target.
|
||||||
for _, thread := range dbp.threads {
|
for _, thread := range dbp.Threads {
|
||||||
_, err := _SuspendThread(thread.os.hThread)
|
_, err := _SuspendThread(thread.os.hThread)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -147,7 +147,7 @@ func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
dbp.execPtraceFunc(func() {
|
dbp.execPtraceFunc(func() {
|
||||||
err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
|
err = _ContinueDebugEvent(uint32(dbp.Pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -209,7 +209,7 @@ func (dbp *Process) Kill() error {
|
|||||||
if dbp.exited {
|
if dbp.exited {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
if !dbp.threads[dbp.pid].Stopped() {
|
if !dbp.Threads[dbp.Pid].Stopped() {
|
||||||
return errors.New("process must be stopped in order to kill it")
|
return errors.New("process must be stopped in order to kill it")
|
||||||
}
|
}
|
||||||
// TODO: Should not have to ignore failures here,
|
// TODO: Should not have to ignore failures here,
|
||||||
@ -231,7 +231,7 @@ func (dbp *Process) updateThreadList() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) addThread(hThread syscall.Handle, threadID int, attach, suspendNewThreads bool) (*Thread, error) {
|
func (dbp *Process) addThread(hThread syscall.Handle, threadID int, attach, suspendNewThreads bool) (*Thread, error) {
|
||||||
if thread, ok := dbp.threads[threadID]; ok {
|
if thread, ok := dbp.Threads[threadID]; ok {
|
||||||
return thread, nil
|
return thread, nil
|
||||||
}
|
}
|
||||||
thread := &Thread{
|
thread := &Thread{
|
||||||
@ -240,8 +240,8 @@ func (dbp *Process) addThread(hThread syscall.Handle, threadID int, attach, susp
|
|||||||
os: new(OSSpecificDetails),
|
os: new(OSSpecificDetails),
|
||||||
}
|
}
|
||||||
thread.os.hThread = hThread
|
thread.os.hThread = hThread
|
||||||
dbp.threads[threadID] = thread
|
dbp.Threads[threadID] = thread
|
||||||
if dbp.currentThread == nil {
|
if dbp.CurrentThread == nil {
|
||||||
dbp.SwitchThread(thread.ID)
|
dbp.SwitchThread(thread.ID)
|
||||||
}
|
}
|
||||||
if suspendNewThreads {
|
if suspendNewThreads {
|
||||||
@ -488,7 +488,7 @@ func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, e
|
|||||||
}
|
}
|
||||||
break
|
break
|
||||||
case _EXIT_THREAD_DEBUG_EVENT:
|
case _EXIT_THREAD_DEBUG_EVENT:
|
||||||
delete(dbp.threads, int(debugEvent.ThreadId))
|
delete(dbp.Threads, int(debugEvent.ThreadId))
|
||||||
break
|
break
|
||||||
case _OUTPUT_DEBUG_STRING_EVENT:
|
case _OUTPUT_DEBUG_STRING_EVENT:
|
||||||
//TODO: Handle debug output strings
|
//TODO: Handle debug output strings
|
||||||
@ -509,40 +509,11 @@ func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, e
|
|||||||
break
|
break
|
||||||
case _EXCEPTION_DEBUG_EVENT:
|
case _EXCEPTION_DEBUG_EVENT:
|
||||||
exception := (*_EXCEPTION_DEBUG_INFO)(unionPtr)
|
exception := (*_EXCEPTION_DEBUG_INFO)(unionPtr)
|
||||||
|
if code := exception.ExceptionRecord.ExceptionCode; code == _EXCEPTION_BREAKPOINT || code == _EXCEPTION_SINGLE_STEP {
|
||||||
tid := int(debugEvent.ThreadId)
|
tid := int(debugEvent.ThreadId)
|
||||||
|
|
||||||
switch code := exception.ExceptionRecord.ExceptionCode; code {
|
|
||||||
case _EXCEPTION_BREAKPOINT:
|
|
||||||
|
|
||||||
// check if the exception address really is a breakpoint instruction, if
|
|
||||||
// it isn't we already removed that breakpoint and we can't deal with
|
|
||||||
// this exception anymore.
|
|
||||||
atbp := true
|
|
||||||
if thread, found := dbp.threads[tid]; found {
|
|
||||||
if data, err := thread.readMemory(exception.ExceptionRecord.ExceptionAddress, dbp.arch.BreakpointSize()); err == nil {
|
|
||||||
instr := dbp.arch.BreakpointInstruction()
|
|
||||||
for i := range instr {
|
|
||||||
if data[i] != instr[i] {
|
|
||||||
atbp = false
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if !atbp {
|
|
||||||
thread.SetPC(uint64(exception.ExceptionRecord.ExceptionAddress))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if atbp {
|
|
||||||
dbp.os.breakThread = tid
|
dbp.os.breakThread = tid
|
||||||
return tid, 0, nil
|
return tid, 0, nil
|
||||||
} else {
|
} else {
|
||||||
continueStatus = _DBG_CONTINUE
|
|
||||||
}
|
|
||||||
case _EXCEPTION_SINGLE_STEP:
|
|
||||||
dbp.os.breakThread = tid
|
|
||||||
return tid, 0, nil
|
|
||||||
default:
|
|
||||||
continueStatus = _DBG_EXCEPTION_NOT_HANDLED
|
continueStatus = _DBG_EXCEPTION_NOT_HANDLED
|
||||||
}
|
}
|
||||||
case _EXIT_PROCESS_DEBUG_EVENT:
|
case _EXIT_PROCESS_DEBUG_EVENT:
|
||||||
@ -576,9 +547,9 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
|||||||
}
|
}
|
||||||
if tid == 0 {
|
if tid == 0 {
|
||||||
dbp.postExit()
|
dbp.postExit()
|
||||||
return nil, ProcessExitedError{Pid: dbp.pid, Status: exitCode}
|
return nil, ProcessExitedError{Pid: dbp.Pid, Status: exitCode}
|
||||||
}
|
}
|
||||||
th := dbp.threads[tid]
|
th := dbp.Threads[tid]
|
||||||
return th, nil
|
return th, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -606,7 +577,7 @@ func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, thread := range dbp.threads {
|
for _, thread := range dbp.Threads {
|
||||||
thread.running = false
|
thread.running = false
|
||||||
_, err := _SuspendThread(thread.os.hThread)
|
_, err := _SuspendThread(thread.os.hThread)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -618,7 +589,7 @@ func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
|
|||||||
var err error
|
var err error
|
||||||
var tid int
|
var tid int
|
||||||
dbp.execPtraceFunc(func() {
|
dbp.execPtraceFunc(func() {
|
||||||
err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
|
err = _ContinueDebugEvent(uint32(dbp.Pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
tid, _, _ = dbp.waitForDebugEvent(waitSuspendNewThreads)
|
tid, _, _ = dbp.waitForDebugEvent(waitSuspendNewThreads)
|
||||||
}
|
}
|
||||||
@ -629,7 +600,7 @@ func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
|
|||||||
if tid == 0 {
|
if tid == 0 {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
err = dbp.threads[tid].SetCurrentBreakpoint()
|
err = dbp.Threads[tid].SetCurrentBreakpoint()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -643,7 +614,7 @@ func (dbp *Process) exitGuard(err error) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) resume() error {
|
func (dbp *Process) resume() error {
|
||||||
for _, thread := range dbp.threads {
|
for _, thread := range dbp.Threads {
|
||||||
if thread.CurrentBreakpoint != nil {
|
if thread.CurrentBreakpoint != nil {
|
||||||
if err := thread.StepInstruction(); err != nil {
|
if err := thread.StepInstruction(); err != nil {
|
||||||
return err
|
return err
|
||||||
@ -652,7 +623,7 @@ func (dbp *Process) resume() error {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, thread := range dbp.threads {
|
for _, thread := range dbp.Threads {
|
||||||
thread.running = true
|
thread.running = true
|
||||||
_, err := _ResumeThread(thread.os.hThread)
|
_, err := _ResumeThread(thread.os.hThread)
|
||||||
if err != nil {
|
if err != nil {
|
@ -17,7 +17,6 @@ import (
|
|||||||
type Registers interface {
|
type Registers interface {
|
||||||
PC() uint64
|
PC() uint64
|
||||||
SP() uint64
|
SP() uint64
|
||||||
BP() uint64
|
|
||||||
CX() uint64
|
CX() uint64
|
||||||
TLS() uint64
|
TLS() uint64
|
||||||
Get(int) (uint64, error)
|
Get(int) (uint64, error)
|
@ -88,10 +88,6 @@ func (r *Regs) SP() uint64 {
|
|||||||
return r.rsp
|
return r.rsp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Regs) BP() uint64 {
|
|
||||||
return r.rbp
|
|
||||||
}
|
|
||||||
|
|
||||||
// CX returns the value of the RCX register.
|
// CX returns the value of the RCX register.
|
||||||
func (r *Regs) CX() uint64 {
|
func (r *Regs) CX() uint64 {
|
||||||
return r.rcx
|
return r.rcx
|
||||||
@ -134,7 +130,7 @@ func (r *Regs) Get(n int) (uint64, error) {
|
|||||||
case x86asm.AH:
|
case x86asm.AH:
|
||||||
return (r.rax >> 8) & mask8, nil
|
return (r.rax >> 8) & mask8, nil
|
||||||
case x86asm.CH:
|
case x86asm.CH:
|
||||||
return (r.rcx >> 8) & mask8, nil
|
return (r.rax >> 8) & mask8, nil
|
||||||
case x86asm.DH:
|
case x86asm.DH:
|
||||||
return (r.rdx >> 8) & mask8, nil
|
return (r.rdx >> 8) & mask8, nil
|
||||||
case x86asm.BH:
|
case x86asm.BH:
|
@ -69,10 +69,6 @@ func (r *Regs) SP() uint64 {
|
|||||||
return r.regs.Rsp
|
return r.regs.Rsp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Regs) BP() uint64 {
|
|
||||||
return r.regs.Rbp
|
|
||||||
}
|
|
||||||
|
|
||||||
// CX returns the value of RCX register.
|
// CX returns the value of RCX register.
|
||||||
func (r *Regs) CX() uint64 {
|
func (r *Regs) CX() uint64 {
|
||||||
return r.regs.Rcx
|
return r.regs.Rcx
|
||||||
@ -112,7 +108,7 @@ func (r *Regs) Get(n int) (uint64, error) {
|
|||||||
case x86asm.AH:
|
case x86asm.AH:
|
||||||
return (r.regs.Rax >> 8) & mask8, nil
|
return (r.regs.Rax >> 8) & mask8, nil
|
||||||
case x86asm.CH:
|
case x86asm.CH:
|
||||||
return (r.regs.Rcx >> 8) & mask8, nil
|
return (r.regs.Rax >> 8) & mask8, nil
|
||||||
case x86asm.DH:
|
case x86asm.DH:
|
||||||
return (r.regs.Rdx >> 8) & mask8, nil
|
return (r.regs.Rdx >> 8) & mask8, nil
|
||||||
case x86asm.BH:
|
case x86asm.BH:
|
@ -107,10 +107,6 @@ func (r *Regs) SP() uint64 {
|
|||||||
return r.rsp
|
return r.rsp
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *Regs) BP() uint64 {
|
|
||||||
return r.rbp
|
|
||||||
}
|
|
||||||
|
|
||||||
// CX returns the value of the RCX register.
|
// CX returns the value of the RCX register.
|
||||||
func (r *Regs) CX() uint64 {
|
func (r *Regs) CX() uint64 {
|
||||||
return r.rcx
|
return r.rcx
|
||||||
@ -159,7 +155,7 @@ func (r *Regs) Get(n int) (uint64, error) {
|
|||||||
case x86asm.AH:
|
case x86asm.AH:
|
||||||
return (r.rax >> 8) & mask8, nil
|
return (r.rax >> 8) & mask8, nil
|
||||||
case x86asm.CH:
|
case x86asm.CH:
|
||||||
return (r.rcx >> 8) & mask8, nil
|
return (r.rax >> 8) & mask8, nil
|
||||||
case x86asm.DH:
|
case x86asm.DH:
|
||||||
return (r.rdx >> 8) & mask8, nil
|
return (r.rdx >> 8) & mask8, nil
|
||||||
case x86asm.BH:
|
case x86asm.BH:
|
@ -1,17 +1,12 @@
|
|||||||
package proc
|
package proc
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"encoding/binary"
|
||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
"github.com/derekparker/delve/dwarf/frame"
|
||||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
|
||||||
)
|
)
|
||||||
|
|
||||||
// This code is partly adaped from runtime.gentraceback in
|
|
||||||
// $GOROOT/src/runtime/traceback.go
|
|
||||||
|
|
||||||
const runtimeStackBarrier = "runtime.stackBarrier"
|
|
||||||
|
|
||||||
// NoReturnAddr is returned when return address
|
// NoReturnAddr is returned when return address
|
||||||
// could not be found during stack trace.
|
// could not be found during stack trace.
|
||||||
type NoReturnAddr struct {
|
type NoReturnAddr struct {
|
||||||
@ -34,8 +29,6 @@ type Stackframe struct {
|
|||||||
FDE *frame.FrameDescriptionEntry
|
FDE *frame.FrameDescriptionEntry
|
||||||
// Return address for this stack frame (as read from the stack frame itself).
|
// Return address for this stack frame (as read from the stack frame itself).
|
||||||
Ret uint64
|
Ret uint64
|
||||||
// Address to the memory location containing the return address
|
|
||||||
addrret uint64
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Scope returns a new EvalScope using this frame.
|
// Scope returns a new EvalScope using this frame.
|
||||||
@ -56,18 +49,18 @@ func (t *Thread) ReturnAddress() (uint64, error) {
|
|||||||
return locations[1].Current.PC, nil
|
return locations[1].Current.PC, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) stackIterator(stkbar []savedLR, stkbarPos int) (*stackIterator, error) {
|
func (t *Thread) stackIterator() (*stackIterator, error) {
|
||||||
regs, err := t.Registers(false)
|
regs, err := t.Registers(false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
return newStackIterator(t.dbp, regs.PC(), regs.SP(), regs.BP(), stkbar, stkbarPos), nil
|
return newStackIterator(t.dbp, regs.PC(), regs.SP()), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stacktrace returns the stack trace for thread.
|
// Stacktrace returns the stack trace for thread.
|
||||||
// Note the locations in the array are return addresses not call addresses.
|
// Note the locations in the array are return addresses not call addresses.
|
||||||
func (t *Thread) Stacktrace(depth int) ([]Stackframe, error) {
|
func (t *Thread) Stacktrace(depth int) ([]Stackframe, error) {
|
||||||
it, err := t.stackIterator(nil, -1)
|
it, err := t.stackIterator()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -75,14 +68,10 @@ func (t *Thread) Stacktrace(depth int) ([]Stackframe, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (g *G) stackIterator() (*stackIterator, error) {
|
func (g *G) stackIterator() (*stackIterator, error) {
|
||||||
stkbar, err := g.stkbar()
|
|
||||||
if err != nil {
|
|
||||||
return nil, err
|
|
||||||
}
|
|
||||||
if g.thread != nil {
|
if g.thread != nil {
|
||||||
return g.thread.stackIterator(stkbar, g.stkbarPos)
|
return g.thread.stackIterator()
|
||||||
}
|
}
|
||||||
return newStackIterator(g.dbp, g.PC, g.SP, 0, stkbar, g.stkbarPos), nil
|
return newStackIterator(g.dbp, g.PC, g.SP), nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// Stacktrace returns the stack trace for a goroutine.
|
// Stacktrace returns the stack trace for a goroutine.
|
||||||
@ -113,43 +102,16 @@ func (n NullAddrError) Error() string {
|
|||||||
// required to iterate and walk the program
|
// required to iterate and walk the program
|
||||||
// stack.
|
// stack.
|
||||||
type stackIterator struct {
|
type stackIterator struct {
|
||||||
pc, sp, bp uint64
|
pc, sp uint64
|
||||||
top bool
|
top bool
|
||||||
atend bool
|
atend bool
|
||||||
frame Stackframe
|
frame Stackframe
|
||||||
dbp *Process
|
dbp *Process
|
||||||
err error
|
err error
|
||||||
|
|
||||||
stackBarrierPC uint64
|
|
||||||
stkbar []savedLR
|
|
||||||
}
|
}
|
||||||
|
|
||||||
type savedLR struct {
|
func newStackIterator(dbp *Process, pc, sp uint64) *stackIterator {
|
||||||
ptr uint64
|
return &stackIterator{pc: pc, sp: sp, top: true, dbp: dbp, err: nil, atend: false}
|
||||||
val uint64
|
|
||||||
}
|
|
||||||
|
|
||||||
func newStackIterator(dbp *Process, pc, sp, bp uint64, stkbar []savedLR, stkbarPos int) *stackIterator {
|
|
||||||
stackBarrierFunc := dbp.goSymTable.LookupFunc(runtimeStackBarrier) // stack barriers were removed in Go 1.9
|
|
||||||
var stackBarrierPC uint64
|
|
||||||
if stackBarrierFunc != nil && stkbar != nil {
|
|
||||||
stackBarrierPC = stackBarrierFunc.Entry
|
|
||||||
fn := dbp.goSymTable.PCToFunc(pc)
|
|
||||||
if fn != nil && fn.Name == runtimeStackBarrier {
|
|
||||||
// We caught the goroutine as it's executing the stack barrier, we must
|
|
||||||
// determine whether or not g.stackPos has already been incremented or not.
|
|
||||||
if len(stkbar) > 0 && stkbar[stkbarPos].ptr < sp {
|
|
||||||
// runtime.stackBarrier has not incremented stkbarPos.
|
|
||||||
} else if stkbarPos > 0 && stkbar[stkbarPos-1].ptr < sp {
|
|
||||||
// runtime.stackBarrier has incremented stkbarPos.
|
|
||||||
stkbarPos--
|
|
||||||
} else {
|
|
||||||
return &stackIterator{err: fmt.Errorf("failed to unwind through stackBarrier at SP %x", sp)}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
stkbar = stkbar[stkbarPos:]
|
|
||||||
}
|
|
||||||
return &stackIterator{pc: pc, sp: sp, bp: bp, top: true, dbp: dbp, err: nil, atend: false, stackBarrierPC: stackBarrierPC, stkbar: stkbar}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Next points the iterator to the next stack frame.
|
// Next points the iterator to the next stack frame.
|
||||||
@ -157,7 +119,7 @@ func (it *stackIterator) Next() bool {
|
|||||||
if it.err != nil || it.atend {
|
if it.err != nil || it.atend {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
it.frame, it.err = it.dbp.frameInfo(it.pc, it.sp, it.bp, it.top)
|
it.frame, it.err = it.dbp.frameInfo(it.pc, it.sp, it.top)
|
||||||
if it.err != nil {
|
if it.err != nil {
|
||||||
if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top {
|
if _, nofde := it.err.(*frame.NoFDEForPCError); nofde && !it.top {
|
||||||
it.frame = Stackframe{Current: Location{PC: it.pc, File: "?", Line: -1}, Call: Location{PC: it.pc, File: "?", Line: -1}, CFA: 0, Ret: 0}
|
it.frame = Stackframe{Current: Location{PC: it.pc, File: "?", Line: -1}, Call: Location{PC: it.pc, File: "?", Line: -1}, CFA: 0, Ret: 0}
|
||||||
@ -168,19 +130,19 @@ func (it *stackIterator) Next() bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if it.frame.Current.Fn == nil {
|
||||||
|
if it.top {
|
||||||
|
it.err = fmt.Errorf("PC not associated to any function")
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
if it.frame.Ret <= 0 {
|
if it.frame.Ret <= 0 {
|
||||||
it.atend = true
|
it.atend = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
if it.stkbar != nil && it.frame.Ret == it.stackBarrierPC && it.frame.addrret == it.stkbar[0].ptr {
|
|
||||||
// Skip stack barrier frames
|
|
||||||
it.frame.Ret = it.stkbar[0].val
|
|
||||||
it.stkbar = it.stkbar[1:]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Look for "top of stack" functions.
|
// Look for "top of stack" functions.
|
||||||
if it.frame.Current.Fn != nil && (it.frame.Current.Fn.Name == "runtime.goexit" || it.frame.Current.Fn.Name == "runtime.rt0_go" || it.frame.Current.Fn.Name == "runtime.mcall") {
|
if it.frame.Current.Fn.Name == "runtime.goexit" || it.frame.Current.Fn.Name == "runtime.rt0_go" || it.frame.Current.Fn.Name == "runtime.mcall" {
|
||||||
it.atend = true
|
it.atend = true
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
@ -188,7 +150,6 @@ func (it *stackIterator) Next() bool {
|
|||||||
it.top = false
|
it.top = false
|
||||||
it.pc = it.frame.Ret
|
it.pc = it.frame.Ret
|
||||||
it.sp = uint64(it.frame.CFA)
|
it.sp = uint64(it.frame.CFA)
|
||||||
it.bp, _ = readUintRaw(it.dbp.currentThread, uintptr(it.bp), int64(it.dbp.arch.PtrSize()))
|
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -205,38 +166,27 @@ func (it *stackIterator) Err() error {
|
|||||||
return it.err
|
return it.err
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, error) {
|
func (dbp *Process) frameInfo(pc, sp uint64, top bool) (Stackframe, error) {
|
||||||
|
f, l, fn := dbp.PCToLine(pc)
|
||||||
fde, err := dbp.frameEntries.FDEForPC(pc)
|
fde, err := dbp.frameEntries.FDEForPC(pc)
|
||||||
if _, nofde := err.(*frame.NoFDEForPCError); nofde {
|
if err != nil {
|
||||||
if bp == 0 {
|
|
||||||
return Stackframe{}, err
|
return Stackframe{}, err
|
||||||
}
|
}
|
||||||
// When no FDE is available attempt to use BP instead
|
|
||||||
retaddr := uintptr(int(bp) + dbp.arch.PtrSize())
|
|
||||||
cfa := int64(retaddr) + int64(dbp.arch.PtrSize())
|
|
||||||
return dbp.newStackframe(pc, cfa, retaddr, nil, top)
|
|
||||||
}
|
|
||||||
|
|
||||||
spoffset, retoffset := fde.ReturnAddressOffset(pc)
|
spoffset, retoffset := fde.ReturnAddressOffset(pc)
|
||||||
cfa := int64(sp) + spoffset
|
cfa := int64(sp) + spoffset
|
||||||
|
|
||||||
retaddr := uintptr(cfa + retoffset)
|
retaddr := uintptr(cfa + retoffset)
|
||||||
return dbp.newStackframe(pc, cfa, retaddr, fde, top)
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dbp *Process) newStackframe(pc uint64, cfa int64, retaddr uintptr, fde *frame.FrameDescriptionEntry, top bool) (Stackframe, error) {
|
|
||||||
if retaddr == 0 {
|
if retaddr == 0 {
|
||||||
return Stackframe{}, NullAddrError{}
|
return Stackframe{}, NullAddrError{}
|
||||||
}
|
}
|
||||||
f, l, fn := dbp.PCToLine(pc)
|
data, err := dbp.CurrentThread.readMemory(retaddr, dbp.arch.PtrSize())
|
||||||
ret, err := readUintRaw(dbp.currentThread, retaddr, int64(dbp.arch.PtrSize()))
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return Stackframe{}, err
|
return Stackframe{}, err
|
||||||
}
|
}
|
||||||
r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: ret, addrret: uint64(retaddr)}
|
r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: binary.LittleEndian.Uint64(data)}
|
||||||
if !top {
|
if !top {
|
||||||
r.Call.File, r.Call.Line, r.Call.Fn = dbp.PCToLine(pc - 1)
|
r.Call.File, r.Call.Line, r.Call.Fn = dbp.PCToLine(pc - 1)
|
||||||
r.Call.PC = r.Current.PC
|
r.Call.PC, _, _ = dbp.goSymTable.LineToPC(r.Call.File, r.Call.Line)
|
||||||
} else {
|
} else {
|
||||||
r.Call = r.Current
|
r.Call = r.Current
|
||||||
}
|
}
|
@ -154,7 +154,7 @@ func topframe(g *G, thread *Thread) (Stackframe, error) {
|
|||||||
// Continue will take care of setting a breakpoint to the destination
|
// Continue will take care of setting a breakpoint to the destination
|
||||||
// once the CALL is reached.
|
// once the CALL is reached.
|
||||||
func (dbp *Process) next(stepInto bool) error {
|
func (dbp *Process) next(stepInto bool) error {
|
||||||
topframe, err := topframe(dbp.selectedGoroutine, dbp.currentThread)
|
topframe, err := topframe(dbp.SelectedGoroutine, dbp.CurrentThread)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -167,10 +167,10 @@ func (dbp *Process) next(stepInto bool) error {
|
|||||||
}()
|
}()
|
||||||
|
|
||||||
csource := filepath.Ext(topframe.Current.File) != ".go"
|
csource := filepath.Ext(topframe.Current.File) != ".go"
|
||||||
thread := dbp.currentThread
|
thread := dbp.CurrentThread
|
||||||
currentGoroutine := false
|
currentGoroutine := false
|
||||||
if dbp.selectedGoroutine != nil && dbp.selectedGoroutine.thread != nil {
|
if dbp.SelectedGoroutine != nil && dbp.SelectedGoroutine.thread != nil {
|
||||||
thread = dbp.selectedGoroutine.thread
|
thread = dbp.SelectedGoroutine.thread
|
||||||
currentGoroutine = true
|
currentGoroutine = true
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -179,7 +179,7 @@ func (dbp *Process) next(stepInto bool) error {
|
|||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
cond := sameGoroutineCondition(dbp.selectedGoroutine)
|
cond := sameGoroutineCondition(dbp.SelectedGoroutine)
|
||||||
|
|
||||||
if stepInto {
|
if stepInto {
|
||||||
for _, instr := range text {
|
for _, instr := range text {
|
||||||
@ -215,17 +215,14 @@ func (dbp *Process) next(stepInto bool) error {
|
|||||||
|
|
||||||
// Set breakpoint on the most recently deferred function (if any)
|
// Set breakpoint on the most recently deferred function (if any)
|
||||||
var deferpc uint64 = 0
|
var deferpc uint64 = 0
|
||||||
if dbp.selectedGoroutine != nil {
|
if dbp.SelectedGoroutine != nil && dbp.SelectedGoroutine.DeferPC != 0 {
|
||||||
deferPCEntry := dbp.selectedGoroutine.DeferPC()
|
_, _, deferfn := dbp.goSymTable.PCToLine(dbp.SelectedGoroutine.DeferPC)
|
||||||
if deferPCEntry != 0 {
|
|
||||||
_, _, deferfn := dbp.goSymTable.PCToLine(deferPCEntry)
|
|
||||||
var err error
|
var err error
|
||||||
deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
|
deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if deferpc != 0 && deferpc != topframe.Current.PC {
|
if deferpc != 0 && deferpc != topframe.Current.PC {
|
||||||
bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, cond)
|
bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, cond)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -256,7 +253,7 @@ func (dbp *Process) next(stepInto bool) error {
|
|||||||
|
|
||||||
if !covered {
|
if !covered {
|
||||||
fn := dbp.goSymTable.PCToFunc(topframe.Ret)
|
fn := dbp.goSymTable.PCToFunc(topframe.Ret)
|
||||||
if dbp.selectedGoroutine != nil && fn != nil && fn.Name == "runtime.goexit" {
|
if dbp.SelectedGoroutine != nil && fn != nil && fn.Name == "runtime.goexit" {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -342,7 +339,7 @@ func (thread *Thread) getGVariable() (*Variable, error) {
|
|||||||
|
|
||||||
if thread.dbp.arch.GStructOffset() == 0 {
|
if thread.dbp.arch.GStructOffset() == 0 {
|
||||||
// GetG was called through SwitchThread / updateThreadList during initialization
|
// GetG was called through SwitchThread / updateThreadList during initialization
|
||||||
// thread.dbp.arch isn't setup yet (it needs a current thread to read global variables from)
|
// thread.dbp.arch isn't setup yet (it needs a CurrentThread to read global variables from)
|
||||||
return nil, fmt.Errorf("g struct offset not initialized")
|
return nil, fmt.Errorf("g struct offset not initialized")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -399,9 +396,6 @@ func (thread *Thread) GetG() (g *G, err error) {
|
|||||||
g, err = gaddr.parseG()
|
g, err = gaddr.parseG()
|
||||||
if err == nil {
|
if err == nil {
|
||||||
g.thread = thread
|
g.thread = thread
|
||||||
if loc, err := thread.Location(); err == nil {
|
|
||||||
g.CurrentLoc = *loc
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -490,9 +484,9 @@ func (thread *Thread) onRuntimeBreakpoint() bool {
|
|||||||
// onNextGorutine returns true if this thread is on the goroutine requested by the current 'next' command
|
// onNextGorutine returns true if this thread is on the goroutine requested by the current 'next' command
|
||||||
func (thread *Thread) onNextGoroutine() (bool, error) {
|
func (thread *Thread) onNextGoroutine() (bool, error) {
|
||||||
var bp *Breakpoint
|
var bp *Breakpoint
|
||||||
for i := range thread.dbp.breakpoints {
|
for i := range thread.dbp.Breakpoints {
|
||||||
if thread.dbp.breakpoints[i].Internal() {
|
if thread.dbp.Breakpoints[i].Internal() {
|
||||||
bp = thread.dbp.breakpoints[i]
|
bp = thread.dbp.Breakpoints[i]
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,9 +5,8 @@ package proc
|
|||||||
import "C"
|
import "C"
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"unsafe"
|
|
||||||
|
|
||||||
sys "golang.org/x/sys/unix"
|
sys "golang.org/x/sys/unix"
|
||||||
|
"unsafe"
|
||||||
)
|
)
|
||||||
|
|
||||||
// WaitStatus is a synonym for the platform-specific WaitStatus
|
// WaitStatus is a synonym for the platform-specific WaitStatus
|
||||||
@ -36,7 +35,7 @@ func (t *Thread) halt() (err error) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
if _, ok := t.dbp.threads[t.ID]; ok {
|
if _, ok := t.dbp.Threads[t.ID]; ok {
|
||||||
err = fmt.Errorf("could not suspend thread %d %s", t.ID, errStr)
|
err = fmt.Errorf("could not suspend thread %d %s", t.ID, errStr)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -50,7 +49,7 @@ func (t *Thread) singleStep() error {
|
|||||||
return fmt.Errorf("could not single step")
|
return fmt.Errorf("could not single step")
|
||||||
}
|
}
|
||||||
for {
|
for {
|
||||||
twthread, err := t.dbp.trapWait(t.dbp.pid)
|
twthread, err := t.dbp.trapWait(t.dbp.Pid)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -70,7 +69,7 @@ func (t *Thread) resume() error {
|
|||||||
t.running = true
|
t.running = true
|
||||||
// TODO(dp) set flag for ptrace stops
|
// TODO(dp) set flag for ptrace stops
|
||||||
var err error
|
var err error
|
||||||
t.dbp.execPtraceFunc(func() { err = PtraceCont(t.dbp.pid, 0) })
|
t.dbp.execPtraceFunc(func() { err = PtraceCont(t.dbp.Pid, 0) })
|
||||||
if err == nil {
|
if err == nil {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
@ -15,7 +15,7 @@ type OSSpecificDetails struct {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (t *Thread) halt() (err error) {
|
func (t *Thread) halt() (err error) {
|
||||||
err = sys.Tgkill(t.dbp.pid, t.ID, sys.SIGSTOP)
|
err = sys.Tgkill(t.dbp.Pid, t.ID, sys.SIGSTOP)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
err = fmt.Errorf("halt err %s on thread %d", err, t.ID)
|
err = fmt.Errorf("halt err %s on thread %d", err, t.ID)
|
||||||
return
|
return
|
||||||
@ -49,17 +49,17 @@ func (t *Thread) singleStep() (err error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
wpid, status, err := t.dbp.waitFast(t.ID)
|
wpid, status, err := t.dbp.wait(t.ID, 0)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
if (status == nil || status.Exited()) && wpid == t.dbp.pid {
|
if (status == nil || status.Exited()) && wpid == t.dbp.Pid {
|
||||||
t.dbp.postExit()
|
t.dbp.postExit()
|
||||||
rs := 0
|
rs := 0
|
||||||
if status != nil {
|
if status != nil {
|
||||||
rs = status.ExitStatus()
|
rs = status.ExitStatus()
|
||||||
}
|
}
|
||||||
return ProcessExitedError{Pid: t.dbp.pid, Status: rs}
|
return ProcessExitedError{Pid: t.dbp.Pid, Status: rs}
|
||||||
}
|
}
|
||||||
if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
|
if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
|
||||||
return nil
|
return nil
|
@ -57,7 +57,7 @@ func (t *Thread) singleStep() error {
|
|||||||
}
|
}
|
||||||
if tid == 0 {
|
if tid == 0 {
|
||||||
t.dbp.postExit()
|
t.dbp.postExit()
|
||||||
return ProcessExitedError{Pid: t.dbp.pid, Status: exitCode}
|
return ProcessExitedError{Pid: t.dbp.Pid, Status: exitCode}
|
||||||
}
|
}
|
||||||
|
|
||||||
if t.dbp.os.breakThread == t.ID {
|
if t.dbp.os.breakThread == t.ID {
|
||||||
@ -65,7 +65,7 @@ func (t *Thread) singleStep() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.dbp.execPtraceFunc(func() {
|
t.dbp.execPtraceFunc(func() {
|
||||||
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.dbp.os.breakThread), _DBG_CONTINUE)
|
err = _ContinueDebugEvent(uint32(t.dbp.Pid), uint32(t.dbp.os.breakThread), _DBG_CONTINUE)
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -75,7 +75,7 @@ func (t *Thread) singleStep() error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
t.dbp.execPtraceFunc(func() {
|
t.dbp.execPtraceFunc(func() {
|
||||||
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
|
err = _ContinueDebugEvent(uint32(t.dbp.Pid), uint32(t.ID), _DBG_CONTINUE)
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
@ -98,7 +98,7 @@ func (t *Thread) resume() error {
|
|||||||
t.dbp.execPtraceFunc(func() {
|
t.dbp.execPtraceFunc(func() {
|
||||||
//TODO: Note that we are ignoring the thread we were asked to continue and are continuing the
|
//TODO: Note that we are ignoring the thread we were asked to continue and are continuing the
|
||||||
//thread that we last broke on.
|
//thread that we last broke on.
|
||||||
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
|
err = _ContinueDebugEvent(uint32(t.dbp.Pid), uint32(t.ID), _DBG_CONTINUE)
|
||||||
})
|
})
|
||||||
return err
|
return err
|
||||||
}
|
}
|
@ -8,13 +8,12 @@ import (
|
|||||||
"go/constant"
|
"go/constant"
|
||||||
"go/token"
|
"go/token"
|
||||||
"reflect"
|
"reflect"
|
||||||
"sort"
|
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
"sync"
|
"sync"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
"github.com/derekparker/delve/dwarf/reader"
|
||||||
|
|
||||||
"golang.org/x/debug/dwarf"
|
"golang.org/x/debug/dwarf"
|
||||||
)
|
)
|
||||||
@ -119,27 +118,14 @@ func (dbp *Process) loadPackageMap() error {
|
|||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
type sortFunctionsDebugInfoByLowpc []functionDebugInfo
|
func (dbp *Process) loadTypeMap(wg *sync.WaitGroup) {
|
||||||
|
|
||||||
func (v sortFunctionsDebugInfoByLowpc) Len() int { return len(v) }
|
|
||||||
func (v sortFunctionsDebugInfoByLowpc) Less(i, j int) bool { return v[i].lowpc < v[j].lowpc }
|
|
||||||
func (v sortFunctionsDebugInfoByLowpc) Swap(i, j int) {
|
|
||||||
temp := v[i]
|
|
||||||
v[i] = v[j]
|
|
||||||
v[j] = temp
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dbp *Process) loadDebugInfoMaps(wg *sync.WaitGroup) {
|
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
dbp.types = make(map[string]dwarf.Offset)
|
dbp.types = make(map[string]dwarf.Offset)
|
||||||
dbp.functions = []functionDebugInfo{}
|
|
||||||
reader := dbp.DwarfReader()
|
reader := dbp.DwarfReader()
|
||||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
for entry, err := reader.NextType(); entry != nil; entry, err = reader.NextType() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
switch entry.Tag {
|
|
||||||
case dwarf.TagArrayType, dwarf.TagBaseType, dwarf.TagClassType, dwarf.TagStructType, dwarf.TagUnionType, dwarf.TagConstType, dwarf.TagVolatileType, dwarf.TagRestrictType, dwarf.TagEnumerationType, dwarf.TagPointerType, dwarf.TagSubroutineType, dwarf.TagTypedef, dwarf.TagUnspecifiedType:
|
|
||||||
name, ok := entry.Val(dwarf.AttrName).(string)
|
name, ok := entry.Val(dwarf.AttrName).(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
continue
|
continue
|
||||||
@ -147,33 +133,7 @@ func (dbp *Process) loadDebugInfoMaps(wg *sync.WaitGroup) {
|
|||||||
if _, exists := dbp.types[name]; !exists {
|
if _, exists := dbp.types[name]; !exists {
|
||||||
dbp.types[name] = entry.Offset
|
dbp.types[name] = entry.Offset
|
||||||
}
|
}
|
||||||
case dwarf.TagSubprogram:
|
|
||||||
lowpc, ok := entry.Val(dwarf.AttrLowpc).(uint64)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
}
|
||||||
highpc, ok := entry.Val(dwarf.AttrHighpc).(uint64)
|
|
||||||
if !ok {
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
dbp.functions = append(dbp.functions, functionDebugInfo{lowpc, highpc, entry.Offset})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
sort.Sort(sortFunctionsDebugInfoByLowpc(dbp.functions))
|
|
||||||
}
|
|
||||||
|
|
||||||
func (dbp *Process) findFunctionDebugInfo(pc uint64) (dwarf.Offset, error) {
|
|
||||||
i := sort.Search(len(dbp.functions), func(i int) bool {
|
|
||||||
fn := dbp.functions[i]
|
|
||||||
return pc <= fn.lowpc || (fn.lowpc <= pc && pc < fn.highpc)
|
|
||||||
})
|
|
||||||
if i != len(dbp.functions) {
|
|
||||||
fn := dbp.functions[i]
|
|
||||||
if fn.lowpc <= pc && pc < fn.highpc {
|
|
||||||
return fn.offset, nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return 0, errors.New("unable to find function context")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func (dbp *Process) expandPackagesInType(expr ast.Expr) {
|
func (dbp *Process) expandPackagesInType(expr ast.Expr) {
|
||||||
@ -227,10 +187,10 @@ func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error)
|
|||||||
|
|
||||||
var tflag int64
|
var tflag int64
|
||||||
|
|
||||||
if tflagField := _type.loadFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
|
if tflagField := _type.toFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
|
||||||
tflag, _ = constant.Int64Val(tflagField.Value)
|
tflag, _ = constant.Int64Val(tflagField.Value)
|
||||||
}
|
}
|
||||||
if kindField := _type.loadFieldNamed("kind"); kindField != nil && kindField.Value != nil {
|
if kindField := _type.toFieldNamed("kind"); kindField != nil && kindField.Value != nil {
|
||||||
kind, _ = constant.Int64Val(kindField.Value)
|
kind, _ = constant.Int64Val(kindField.Value)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +227,7 @@ func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error)
|
|||||||
// to a runtime.slicetype).
|
// to a runtime.slicetype).
|
||||||
func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string, err error) {
|
func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string, err error) {
|
||||||
var strOff int64
|
var strOff int64
|
||||||
if strField := _type.loadFieldNamed("str"); strField != nil && strField.Value != nil {
|
if strField := _type.toFieldNamed("str"); strField != nil && strField.Value != nil {
|
||||||
strOff, _ = constant.Int64Val(strField.Value)
|
strOff, _ = constant.Int64Val(strField.Value)
|
||||||
} else {
|
} else {
|
||||||
return "", errors.New("could not find str field")
|
return "", errors.New("could not find str field")
|
||||||
@ -301,7 +261,7 @@ func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string
|
|||||||
}
|
}
|
||||||
|
|
||||||
if ut := uncommon(_type, tflag); ut != nil {
|
if ut := uncommon(_type, tflag); ut != nil {
|
||||||
if pkgPathField := ut.loadFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil {
|
if pkgPathField := ut.toFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil {
|
||||||
pkgPathOff, _ := constant.Int64Val(pkgPathField.Value)
|
pkgPathOff, _ := constant.Int64Val(pkgPathField.Value)
|
||||||
pkgPath, _, _, err := _type.dbp.resolveNameOff(_type.Addr, uintptr(pkgPathOff))
|
pkgPath, _, _, err := _type.dbp.resolveNameOff(_type.Addr, uintptr(pkgPathOff))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -324,7 +284,7 @@ func nameOfUnnamedRuntimeType(_type *Variable, kind, tflag int64) (string, error
|
|||||||
switch reflect.Kind(kind & kindMask) {
|
switch reflect.Kind(kind & kindMask) {
|
||||||
case reflect.Array:
|
case reflect.Array:
|
||||||
var len int64
|
var len int64
|
||||||
if lenField := _type.loadFieldNamed("len"); lenField != nil && lenField.Value != nil {
|
if lenField := _type.toFieldNamed("len"); lenField != nil && lenField.Value != nil {
|
||||||
len, _ = constant.Int64Val(lenField.Value)
|
len, _ = constant.Int64Val(lenField.Value)
|
||||||
}
|
}
|
||||||
elemname, err := fieldToType(_type, "elem")
|
elemname, err := fieldToType(_type, "elem")
|
||||||
@ -388,10 +348,10 @@ func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string
|
|||||||
}
|
}
|
||||||
|
|
||||||
var inCount, outCount int64
|
var inCount, outCount int64
|
||||||
if inCountField := _type.loadFieldNamed("inCount"); inCountField != nil && inCountField.Value != nil {
|
if inCountField := _type.toFieldNamed("inCount"); inCountField != nil && inCountField.Value != nil {
|
||||||
inCount, _ = constant.Int64Val(inCountField.Value)
|
inCount, _ = constant.Int64Val(inCountField.Value)
|
||||||
}
|
}
|
||||||
if outCountField := _type.loadFieldNamed("outCount"); outCountField != nil && outCountField.Value != nil {
|
if outCountField := _type.toFieldNamed("outCount"); outCountField != nil && outCountField.Value != nil {
|
||||||
outCount, _ = constant.Int64Val(outCountField.Value)
|
outCount, _ = constant.Int64Val(outCountField.Value)
|
||||||
// only the lowest 15 bits of outCount are used, rest are flags
|
// only the lowest 15 bits of outCount are used, rest are flags
|
||||||
outCount = outCount & (1<<15 - 1)
|
outCount = outCount & (1<<15 - 1)
|
||||||
@ -489,7 +449,7 @@ func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, err
|
|||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
var tflag int64
|
var tflag int64
|
||||||
if tflagField := typ.loadFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
|
if tflagField := typ.toFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
|
||||||
tflag, _ = constant.Int64Val(tflagField.Value)
|
tflag, _ = constant.Int64Val(tflagField.Value)
|
||||||
}
|
}
|
||||||
methodtype, err = nameOfFuncRuntimeType(typ, tflag, false)
|
methodtype, err = nameOfFuncRuntimeType(typ, tflag, false)
|
@ -8,13 +8,12 @@ import (
|
|||||||
"go/constant"
|
"go/constant"
|
||||||
"go/parser"
|
"go/parser"
|
||||||
"go/token"
|
"go/token"
|
||||||
"math"
|
|
||||||
"reflect"
|
"reflect"
|
||||||
"strings"
|
"strings"
|
||||||
"unsafe"
|
"unsafe"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
"github.com/derekparker/delve/dwarf/op"
|
||||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
"github.com/derekparker/delve/dwarf/reader"
|
||||||
"golang.org/x/debug/dwarf"
|
"golang.org/x/debug/dwarf"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -30,15 +29,6 @@ const (
|
|||||||
hashMinTopHash = 4 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated
|
hashMinTopHash = 4 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated
|
||||||
)
|
)
|
||||||
|
|
||||||
type FloatSpecial uint8
|
|
||||||
|
|
||||||
const (
|
|
||||||
FloatIsNormal FloatSpecial = iota
|
|
||||||
FloatIsNaN
|
|
||||||
FloatIsPosInf
|
|
||||||
FloatIsNegInf
|
|
||||||
)
|
|
||||||
|
|
||||||
// Variable represents a variable. It contains the address, name,
|
// Variable represents a variable. It contains the address, name,
|
||||||
// type and other information parsed from both the Dwarf information
|
// type and other information parsed from both the Dwarf information
|
||||||
// and the memory of the debugged process.
|
// and the memory of the debugged process.
|
||||||
@ -54,7 +44,6 @@ type Variable struct {
|
|||||||
dbp *Process
|
dbp *Process
|
||||||
|
|
||||||
Value constant.Value
|
Value constant.Value
|
||||||
FloatSpecial FloatSpecial
|
|
||||||
|
|
||||||
Len int64
|
Len int64
|
||||||
Cap int64
|
Cap int64
|
||||||
@ -122,16 +111,16 @@ type G struct {
|
|||||||
GoPC uint64 // PC of 'go' statement that created this goroutine.
|
GoPC uint64 // PC of 'go' statement that created this goroutine.
|
||||||
WaitReason string // Reason for goroutine being parked.
|
WaitReason string // Reason for goroutine being parked.
|
||||||
Status uint64
|
Status uint64
|
||||||
stkbarVar *Variable // stkbar field of g struct
|
|
||||||
stkbarPos int // stkbarPos field of g struct
|
|
||||||
|
|
||||||
// Information on goroutine location
|
// Information on goroutine location
|
||||||
CurrentLoc Location
|
CurrentLoc Location
|
||||||
|
|
||||||
|
// PC of entry to top-most deferred function.
|
||||||
|
DeferPC uint64
|
||||||
|
|
||||||
// Thread that this goroutine is currently allocated to
|
// Thread that this goroutine is currently allocated to
|
||||||
thread *Thread
|
thread *Thread
|
||||||
|
|
||||||
variable *Variable
|
|
||||||
dbp *Process
|
dbp *Process
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -380,31 +369,24 @@ func (gvar *Variable) parseG() (*G, error) {
|
|||||||
}
|
}
|
||||||
return nil, NoGError{tid: id}
|
return nil, NoGError{tid: id}
|
||||||
}
|
}
|
||||||
for {
|
gvar.loadValue(loadFullValue)
|
||||||
if _, isptr := gvar.RealType.(*dwarf.PtrType); !isptr {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
gvar = gvar.maybeDereference()
|
|
||||||
}
|
|
||||||
gvar.loadValue(LoadConfig{false, 1, 64, 0, -1})
|
|
||||||
if gvar.Unreadable != nil {
|
if gvar.Unreadable != nil {
|
||||||
return nil, gvar.Unreadable
|
return nil, gvar.Unreadable
|
||||||
}
|
}
|
||||||
schedVar := gvar.fieldVariable("sched")
|
schedVar := gvar.toFieldNamed("sched")
|
||||||
pc, _ := constant.Int64Val(schedVar.fieldVariable("pc").Value)
|
pc, _ := constant.Int64Val(schedVar.toFieldNamed("pc").Value)
|
||||||
sp, _ := constant.Int64Val(schedVar.fieldVariable("sp").Value)
|
sp, _ := constant.Int64Val(schedVar.toFieldNamed("sp").Value)
|
||||||
id, _ := constant.Int64Val(gvar.fieldVariable("goid").Value)
|
id, _ := constant.Int64Val(gvar.toFieldNamed("goid").Value)
|
||||||
gopc, _ := constant.Int64Val(gvar.fieldVariable("gopc").Value)
|
gopc, _ := constant.Int64Val(gvar.toFieldNamed("gopc").Value)
|
||||||
waitReason := constant.StringVal(gvar.fieldVariable("waitreason").Value)
|
waitReason := constant.StringVal(gvar.toFieldNamed("waitreason").Value)
|
||||||
|
d := gvar.toFieldNamed("_defer")
|
||||||
stkbarVar, _ := gvar.structMember("stkbar")
|
deferPC := int64(0)
|
||||||
stkbarVarPosFld := gvar.fieldVariable("stkbarPos")
|
fnvar := d.toFieldNamed("fn")
|
||||||
var stkbarPos int64
|
if fnvar != nil {
|
||||||
if stkbarVarPosFld != nil { // stack barriers were removed in Go 1.9
|
fnvalvar := fnvar.toFieldNamed("fn")
|
||||||
stkbarPos, _ = constant.Int64Val(stkbarVarPosFld.Value)
|
deferPC, _ = constant.Int64Val(fnvalvar.Value)
|
||||||
}
|
}
|
||||||
|
status, _ := constant.Int64Val(gvar.toFieldNamed("atomicstatus").Value)
|
||||||
status, _ := constant.Int64Val(gvar.fieldVariable("atomicstatus").Value)
|
|
||||||
f, l, fn := gvar.dbp.goSymTable.PCToLine(uint64(pc))
|
f, l, fn := gvar.dbp.goSymTable.PCToLine(uint64(pc))
|
||||||
g := &G{
|
g := &G{
|
||||||
ID: int(id),
|
ID: int(id),
|
||||||
@ -412,17 +394,15 @@ func (gvar *Variable) parseG() (*G, error) {
|
|||||||
PC: uint64(pc),
|
PC: uint64(pc),
|
||||||
SP: uint64(sp),
|
SP: uint64(sp),
|
||||||
WaitReason: waitReason,
|
WaitReason: waitReason,
|
||||||
|
DeferPC: uint64(deferPC),
|
||||||
Status: uint64(status),
|
Status: uint64(status),
|
||||||
CurrentLoc: Location{PC: uint64(pc), File: f, Line: l, Fn: fn},
|
CurrentLoc: Location{PC: uint64(pc), File: f, Line: l, Fn: fn},
|
||||||
variable: gvar,
|
|
||||||
stkbarVar: stkbarVar,
|
|
||||||
stkbarPos: int(stkbarPos),
|
|
||||||
dbp: gvar.dbp,
|
dbp: gvar.dbp,
|
||||||
}
|
}
|
||||||
return g, nil
|
return g, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Variable) loadFieldNamed(name string) *Variable {
|
func (v *Variable) toFieldNamed(name string) *Variable {
|
||||||
v, err := v.structMember(name)
|
v, err := v.structMember(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil
|
return nil
|
||||||
@ -434,40 +414,6 @@ func (v *Variable) loadFieldNamed(name string) *Variable {
|
|||||||
return v
|
return v
|
||||||
}
|
}
|
||||||
|
|
||||||
func (v *Variable) fieldVariable(name string) *Variable {
|
|
||||||
for i := range v.Children {
|
|
||||||
if child := &v.Children[i]; child.Name == name {
|
|
||||||
return child
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// PC of entry to top-most deferred function.
|
|
||||||
func (g *G) DeferPC() uint64 {
|
|
||||||
if g.variable.Unreadable != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
d := g.variable.fieldVariable("_defer").maybeDereference()
|
|
||||||
if d.Addr == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
d.loadValue(LoadConfig{false, 1, 64, 0, -1})
|
|
||||||
if d.Unreadable != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
fnvar := d.fieldVariable("fn").maybeDereference()
|
|
||||||
if fnvar.Addr == 0 {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
fnvar.loadValue(LoadConfig{false, 1, 64, 0, -1})
|
|
||||||
if fnvar.Unreadable != nil {
|
|
||||||
return 0
|
|
||||||
}
|
|
||||||
deferPC, _ := constant.Int64Val(fnvar.fieldVariable("fn").Value)
|
|
||||||
return uint64(deferPC)
|
|
||||||
}
|
|
||||||
|
|
||||||
// From $GOROOT/src/runtime/traceback.go:597
|
// From $GOROOT/src/runtime/traceback.go:597
|
||||||
// isExportedRuntime reports whether name is an exported runtime function.
|
// isExportedRuntime reports whether name is an exported runtime function.
|
||||||
// It is only for runtime functions, so ASCII A-Z is fine.
|
// It is only for runtime functions, so ASCII A-Z is fine.
|
||||||
@ -502,31 +448,6 @@ func (g *G) Go() Location {
|
|||||||
return Location{PC: g.GoPC, File: f, Line: l, Fn: fn}
|
return Location{PC: g.GoPC, File: f, Line: l, Fn: fn}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Returns the list of saved return addresses used by stack barriers
|
|
||||||
func (g *G) stkbar() ([]savedLR, error) {
|
|
||||||
if g.stkbarVar == nil { // stack barriers were removed in Go 1.9
|
|
||||||
return nil, nil
|
|
||||||
}
|
|
||||||
g.stkbarVar.loadValue(LoadConfig{false, 1, 0, int(g.stkbarVar.Len), 3})
|
|
||||||
if g.stkbarVar.Unreadable != nil {
|
|
||||||
return nil, fmt.Errorf("unreadable stkbar: %v\n", g.stkbarVar.Unreadable)
|
|
||||||
}
|
|
||||||
r := make([]savedLR, len(g.stkbarVar.Children))
|
|
||||||
for i, child := range g.stkbarVar.Children {
|
|
||||||
for _, field := range child.Children {
|
|
||||||
switch field.Name {
|
|
||||||
case "savedLRPtr":
|
|
||||||
ptr, _ := constant.Int64Val(field.Value)
|
|
||||||
r[i].ptr = uint64(ptr)
|
|
||||||
case "savedLRVal":
|
|
||||||
val, _ := constant.Int64Val(field.Value)
|
|
||||||
r[i].val = uint64(val)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return r, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// EvalVariable returns the value of the given expression (backwards compatibility).
|
// EvalVariable returns the value of the given expression (backwards compatibility).
|
||||||
func (scope *EvalScope) EvalVariable(name string, cfg LoadConfig) (*Variable, error) {
|
func (scope *EvalScope) EvalVariable(name string, cfg LoadConfig) (*Variable, error) {
|
||||||
return scope.EvalExpression(name, cfg)
|
return scope.EvalExpression(name, cfg)
|
||||||
@ -581,25 +502,22 @@ func (scope *EvalScope) extractVariableFromEntry(entry *dwarf.Entry, cfg LoadCon
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
v.loadValue(cfg)
|
||||||
return v, nil
|
return v, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (scope *EvalScope) extractVarInfo(varName string) (*Variable, error) {
|
func (scope *EvalScope) extractVarInfo(varName string) (*Variable, error) {
|
||||||
reader := scope.DwarfReader()
|
reader := scope.DwarfReader()
|
||||||
off, err := scope.Thread.dbp.findFunctionDebugInfo(scope.PC)
|
|
||||||
|
_, err := reader.SeekToFunction(scope.PC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
reader.Seek(off)
|
|
||||||
reader.Next()
|
|
||||||
|
|
||||||
for entry, err := reader.NextScopeVariable(); entry != nil; entry, err = reader.NextScopeVariable() {
|
for entry, err := reader.NextScopeVariable(); entry != nil; entry, err = reader.NextScopeVariable() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if entry.Tag == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
n, ok := entry.Val(dwarf.AttrName).(string)
|
n, ok := entry.Val(dwarf.AttrName).(string)
|
||||||
if !ok {
|
if !ok {
|
||||||
@ -648,7 +566,6 @@ func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
val.loadValue(cfg)
|
|
||||||
vars = append(vars, val)
|
vars = append(vars, val)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -658,7 +575,7 @@ func (scope *EvalScope) PackageVariables(cfg LoadConfig) ([]*Variable, error) {
|
|||||||
// EvalPackageVariable will evaluate the package level variable
|
// EvalPackageVariable will evaluate the package level variable
|
||||||
// specified by 'name'.
|
// specified by 'name'.
|
||||||
func (dbp *Process) EvalPackageVariable(name string, cfg LoadConfig) (*Variable, error) {
|
func (dbp *Process) EvalPackageVariable(name string, cfg LoadConfig) (*Variable, error) {
|
||||||
scope := &EvalScope{Thread: dbp.currentThread, PC: 0, CFA: 0}
|
scope := &EvalScope{Thread: dbp.CurrentThread, PC: 0, CFA: 0}
|
||||||
|
|
||||||
v, err := scope.packageVarAddr(name)
|
v, err := scope.packageVarAddr(name)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -821,12 +738,7 @@ func (v *Variable) loadValueInternal(recurseLevel int, cfg LoadConfig) {
|
|||||||
v.Children = []Variable{*v.maybeDereference()}
|
v.Children = []Variable{*v.maybeDereference()}
|
||||||
if cfg.FollowPointers {
|
if cfg.FollowPointers {
|
||||||
// Don't increase the recursion level when dereferencing pointers
|
// Don't increase the recursion level when dereferencing pointers
|
||||||
// unless this is a pointer to interface (which could cause an infinite loop)
|
v.Children[0].loadValueInternal(recurseLevel, cfg)
|
||||||
nextLvl := recurseLevel
|
|
||||||
if v.Children[0].Kind == reflect.Interface {
|
|
||||||
nextLvl++
|
|
||||||
}
|
|
||||||
v.Children[0].loadValueInternal(nextLvl, cfg)
|
|
||||||
} else {
|
} else {
|
||||||
v.Children[0].OnlyAddr = true
|
v.Children[0].OnlyAddr = true
|
||||||
}
|
}
|
||||||
@ -896,14 +808,6 @@ func (v *Variable) loadValueInternal(recurseLevel int, cfg LoadConfig) {
|
|||||||
var val float64
|
var val float64
|
||||||
val, v.Unreadable = v.readFloatRaw(v.RealType.(*dwarf.FloatType).ByteSize)
|
val, v.Unreadable = v.readFloatRaw(v.RealType.(*dwarf.FloatType).ByteSize)
|
||||||
v.Value = constant.MakeFloat64(val)
|
v.Value = constant.MakeFloat64(val)
|
||||||
switch {
|
|
||||||
case math.IsInf(val, +1):
|
|
||||||
v.FloatSpecial = FloatIsPosInf
|
|
||||||
case math.IsInf(val, -1):
|
|
||||||
v.FloatSpecial = FloatIsNegInf
|
|
||||||
case math.IsNaN(val):
|
|
||||||
v.FloatSpecial = FloatIsNaN
|
|
||||||
}
|
|
||||||
case reflect.Func:
|
case reflect.Func:
|
||||||
v.readFunctionPtr()
|
v.readFunctionPtr()
|
||||||
default:
|
default:
|
||||||
@ -1333,18 +1237,9 @@ func (v *Variable) mapIterator() *mapIterator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if it.buckets.Kind != reflect.Struct || it.oldbuckets.Kind != reflect.Struct {
|
|
||||||
v.Unreadable = mapBucketsNotStructErr
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
return it
|
return it
|
||||||
}
|
}
|
||||||
|
|
||||||
var mapBucketContentsNotArrayErr = errors.New("malformed map type: keys, values or tophash of a bucket is not an array")
|
|
||||||
var mapBucketContentsInconsistentLenErr = errors.New("malformed map type: inconsistent array length in bucket")
|
|
||||||
var mapBucketsNotStructErr = errors.New("malformed map type: buckets, oldbuckets or overflow field not a struct")
|
|
||||||
|
|
||||||
func (it *mapIterator) nextBucket() bool {
|
func (it *mapIterator) nextBucket() bool {
|
||||||
if it.overflow != nil && it.overflow.Addr > 0 {
|
if it.overflow != nil && it.overflow.Addr > 0 {
|
||||||
it.b = it.overflow
|
it.b = it.overflow
|
||||||
@ -1433,17 +1328,12 @@ func (it *mapIterator) nextBucket() bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
if it.tophashes.Kind != reflect.Array || it.keys.Kind != reflect.Array || it.values.Kind != reflect.Array {
|
if it.tophashes.Kind != reflect.Array || it.keys.Kind != reflect.Array || it.values.Kind != reflect.Array {
|
||||||
it.v.Unreadable = mapBucketContentsNotArrayErr
|
it.v.Unreadable = fmt.Errorf("malformed map type: keys, values or tophash of a bucket is not an array")
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
if it.tophashes.Len != it.keys.Len || it.tophashes.Len != it.values.Len {
|
if it.tophashes.Len != it.keys.Len || it.tophashes.Len != it.values.Len {
|
||||||
it.v.Unreadable = mapBucketContentsInconsistentLenErr
|
it.v.Unreadable = fmt.Errorf("malformed map type: inconsistent array length in bucket")
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
if it.overflow.Kind != reflect.Struct {
|
|
||||||
it.v.Unreadable = mapBucketsNotStructErr
|
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1644,7 +1534,7 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
|
|||||||
data = data.newVariable("data", data.Addr, typ)
|
data = data.newVariable("data", data.Addr, typ)
|
||||||
|
|
||||||
v.Children = []Variable{*data}
|
v.Children = []Variable{*data}
|
||||||
if loadData && recurseLevel <= cfg.MaxVariableRecurse {
|
if loadData {
|
||||||
v.Children[0].loadValueInternal(recurseLevel, cfg)
|
v.Children[0].loadValueInternal(recurseLevel, cfg)
|
||||||
} else {
|
} else {
|
||||||
v.Children[0].OnlyAddr = true
|
v.Children[0].OnlyAddr = true
|
||||||
@ -1655,21 +1545,17 @@ func (v *Variable) loadInterface(recurseLevel int, loadData bool, cfg LoadConfig
|
|||||||
// Fetches all variables of a specific type in the current function scope
|
// Fetches all variables of a specific type in the current function scope
|
||||||
func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variable, error) {
|
func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variable, error) {
|
||||||
reader := scope.DwarfReader()
|
reader := scope.DwarfReader()
|
||||||
off, err := scope.Thread.dbp.findFunctionDebugInfo(scope.PC)
|
|
||||||
|
_, err := reader.SeekToFunction(scope.PC)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
reader.Seek(off)
|
|
||||||
reader.Next()
|
|
||||||
|
|
||||||
var vars []*Variable
|
var vars []*Variable
|
||||||
for entry, err := reader.NextScopeVariable(); entry != nil; entry, err = reader.NextScopeVariable() {
|
for entry, err := reader.NextScopeVariable(); entry != nil; entry, err = reader.NextScopeVariable() {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if entry.Tag == 0 {
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
if entry.Tag == tag {
|
if entry.Tag == tag {
|
||||||
val, err := scope.extractVariableFromEntry(entry, cfg)
|
val, err := scope.extractVariableFromEntry(entry, cfg)
|
||||||
@ -1681,43 +1567,6 @@ func (scope *EvalScope) variablesByTag(tag dwarf.Tag, cfg LoadConfig) ([]*Variab
|
|||||||
vars = append(vars, val)
|
vars = append(vars, val)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if len(vars) <= 0 {
|
|
||||||
return vars, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
// prefetch the whole chunk of memory relative to these variables
|
|
||||||
|
|
||||||
minaddr := vars[0].Addr
|
|
||||||
var maxaddr uintptr
|
|
||||||
var size int64
|
|
||||||
|
|
||||||
for _, v := range vars {
|
|
||||||
if v.Addr < minaddr {
|
|
||||||
minaddr = v.Addr
|
|
||||||
}
|
|
||||||
|
|
||||||
size += v.DwarfType.Size()
|
|
||||||
|
|
||||||
if end := v.Addr + uintptr(v.DwarfType.Size()); end > maxaddr {
|
|
||||||
maxaddr = end
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// check that we aren't trying to cache too much memory: we shouldn't
|
|
||||||
// exceed the real size of the variables by more than the number of
|
|
||||||
// variables times the size of an architecture pointer (to allow for memory
|
|
||||||
// alignment).
|
|
||||||
if int64(maxaddr-minaddr)-size <= int64(len(vars))*int64(scope.PtrSize()) {
|
|
||||||
mem := cacheMemory(vars[0].mem, minaddr, int(maxaddr-minaddr))
|
|
||||||
|
|
||||||
for _, v := range vars {
|
|
||||||
v.mem = mem
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
for _, v := range vars {
|
|
||||||
v.loadValue(cfg)
|
|
||||||
}
|
|
||||||
|
|
||||||
return vars, nil
|
return vars, nil
|
||||||
}
|
}
|
22
vendor/github.com/derekparker/delve/service/api/conversions.go
generated
vendored
22
vendor/github.com/derekparker/delve/service/api/conversions.go
generated
vendored
@ -9,8 +9,7 @@ import (
|
|||||||
"reflect"
|
"reflect"
|
||||||
"strconv"
|
"strconv"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/proc"
|
"github.com/derekparker/delve/proc"
|
||||||
|
|
||||||
"golang.org/x/debug/dwarf"
|
"golang.org/x/debug/dwarf"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -99,19 +98,6 @@ func prettyTypeName(typ dwarf.Type) string {
|
|||||||
return r
|
return r
|
||||||
}
|
}
|
||||||
|
|
||||||
func convertFloatValue(v *proc.Variable, sz int) string {
|
|
||||||
switch v.FloatSpecial {
|
|
||||||
case proc.FloatIsPosInf:
|
|
||||||
return "+Inf"
|
|
||||||
case proc.FloatIsNegInf:
|
|
||||||
return "-Inf"
|
|
||||||
case proc.FloatIsNaN:
|
|
||||||
return "NaN"
|
|
||||||
}
|
|
||||||
f, _ := constant.Float64Val(v.Value)
|
|
||||||
return strconv.FormatFloat(f, 'f', -1, sz)
|
|
||||||
}
|
|
||||||
|
|
||||||
// ConvertVar converts from proc.Variable to api.Variable.
|
// ConvertVar converts from proc.Variable to api.Variable.
|
||||||
func ConvertVar(v *proc.Variable) *Variable {
|
func ConvertVar(v *proc.Variable) *Variable {
|
||||||
r := Variable{
|
r := Variable{
|
||||||
@ -133,9 +119,11 @@ func ConvertVar(v *proc.Variable) *Variable {
|
|||||||
if v.Value != nil {
|
if v.Value != nil {
|
||||||
switch v.Kind {
|
switch v.Kind {
|
||||||
case reflect.Float32:
|
case reflect.Float32:
|
||||||
r.Value = convertFloatValue(v, 32)
|
f, _ := constant.Float64Val(v.Value)
|
||||||
|
r.Value = strconv.FormatFloat(f, 'f', -1, 32)
|
||||||
case reflect.Float64:
|
case reflect.Float64:
|
||||||
r.Value = convertFloatValue(v, 64)
|
f, _ := constant.Float64Val(v.Value)
|
||||||
|
r.Value = strconv.FormatFloat(f, 'f', -1, 64)
|
||||||
case reflect.String, reflect.Func:
|
case reflect.String, reflect.Func:
|
||||||
r.Value = constant.StringVal(v.Value)
|
r.Value = constant.StringVal(v.Value)
|
||||||
default:
|
default:
|
||||||
|
4
vendor/github.com/derekparker/delve/service/api/prettyprint.go
generated
vendored
4
vendor/github.com/derekparker/delve/service/api/prettyprint.go
generated
vendored
@ -87,9 +87,7 @@ func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, inden
|
|||||||
}
|
}
|
||||||
data := v.Children[0]
|
data := v.Children[0]
|
||||||
if data.Kind == reflect.Ptr {
|
if data.Kind == reflect.Ptr {
|
||||||
if len(data.Children) == 0 {
|
if data.Children[0].Addr == 0 {
|
||||||
fmt.Fprintf(buf, "...")
|
|
||||||
} else if data.Children[0].Addr == 0 {
|
|
||||||
fmt.Fprintf(buf, "nil")
|
fmt.Fprintf(buf, "nil")
|
||||||
} else if data.Children[0].OnlyAddr {
|
} else if data.Children[0].OnlyAddr {
|
||||||
fmt.Fprintf(buf, "0x%x", v.Children[0].Addr)
|
fmt.Fprintf(buf, "0x%x", v.Children[0].Addr)
|
||||||
|
2
vendor/github.com/derekparker/delve/service/api/types.go
generated
vendored
2
vendor/github.com/derekparker/delve/service/api/types.go
generated
vendored
@ -8,7 +8,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"unicode"
|
"unicode"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/proc"
|
"github.com/derekparker/delve/proc"
|
||||||
)
|
)
|
||||||
|
|
||||||
var NotExecutableErr = proc.NotExecutableErr
|
var NotExecutableErr = proc.NotExecutableErr
|
||||||
|
130
vendor/github.com/derekparker/delve/service/debugger/debugger.go
generated
vendored
130
vendor/github.com/derekparker/delve/service/debugger/debugger.go
generated
vendored
@ -13,8 +13,7 @@ import (
|
|||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/proc"
|
"github.com/derekparker/delve/proc"
|
||||||
"github.com/derekparker/delve/pkg/target"
|
|
||||||
"github.com/derekparker/delve/service/api"
|
"github.com/derekparker/delve/service/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -28,9 +27,8 @@ import (
|
|||||||
// lower lever packages such as proc.
|
// lower lever packages such as proc.
|
||||||
type Debugger struct {
|
type Debugger struct {
|
||||||
config *Config
|
config *Config
|
||||||
// TODO(DO NOT MERGE WITHOUT) rename to targetMutex
|
|
||||||
processMutex sync.Mutex
|
processMutex sync.Mutex
|
||||||
target target.Interface
|
process *proc.Process
|
||||||
}
|
}
|
||||||
|
|
||||||
// Config provides the configuration to start a Debugger.
|
// Config provides the configuration to start a Debugger.
|
||||||
@ -63,7 +61,7 @@ func New(config *Config) (*Debugger, error) {
|
|||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, attachErrorMessage(d.config.AttachPid, err)
|
return nil, attachErrorMessage(d.config.AttachPid, err)
|
||||||
}
|
}
|
||||||
d.target = p
|
d.process = p
|
||||||
} else {
|
} else {
|
||||||
log.Printf("launching process with args: %v", d.config.ProcessArgs)
|
log.Printf("launching process with args: %v", d.config.ProcessArgs)
|
||||||
p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir)
|
p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir)
|
||||||
@ -73,7 +71,7 @@ func New(config *Config) (*Debugger, error) {
|
|||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
d.target = p
|
d.process = p
|
||||||
}
|
}
|
||||||
return d, nil
|
return d, nil
|
||||||
}
|
}
|
||||||
@ -81,13 +79,13 @@ func New(config *Config) (*Debugger, error) {
|
|||||||
// ProcessPid returns the PID of the process
|
// ProcessPid returns the PID of the process
|
||||||
// the debugger is debugging.
|
// the debugger is debugging.
|
||||||
func (d *Debugger) ProcessPid() int {
|
func (d *Debugger) ProcessPid() int {
|
||||||
return d.target.Pid()
|
return d.process.Pid
|
||||||
}
|
}
|
||||||
|
|
||||||
// LastModified returns the time that the process' executable was last
|
// LastModified returns the time that the process' executable was last
|
||||||
// modified.
|
// modified.
|
||||||
func (d *Debugger) LastModified() time.Time {
|
func (d *Debugger) LastModified() time.Time {
|
||||||
return d.target.LastModified()
|
return d.process.LastModified
|
||||||
}
|
}
|
||||||
|
|
||||||
// Detach detaches from the target process.
|
// Detach detaches from the target process.
|
||||||
@ -102,9 +100,9 @@ func (d *Debugger) Detach(kill bool) error {
|
|||||||
|
|
||||||
func (d *Debugger) detach(kill bool) error {
|
func (d *Debugger) detach(kill bool) error {
|
||||||
if d.config.AttachPid != 0 {
|
if d.config.AttachPid != 0 {
|
||||||
return d.target.Detach(kill)
|
return d.process.Detach(kill)
|
||||||
}
|
}
|
||||||
return d.target.Kill()
|
return d.process.Kill()
|
||||||
}
|
}
|
||||||
|
|
||||||
// Restart will restart the target process, first killing
|
// Restart will restart the target process, first killing
|
||||||
@ -113,9 +111,9 @@ func (d *Debugger) Restart() ([]api.DiscardedBreakpoint, error) {
|
|||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
if !d.target.Exited() {
|
if !d.process.Exited() {
|
||||||
if d.target.Running() {
|
if d.process.Running() {
|
||||||
d.target.Halt()
|
d.process.Halt()
|
||||||
}
|
}
|
||||||
// Ensure the process is in a PTRACE_STOP.
|
// Ensure the process is in a PTRACE_STOP.
|
||||||
if err := stopProcess(d.ProcessPid()); err != nil {
|
if err := stopProcess(d.ProcessPid()); err != nil {
|
||||||
@ -149,7 +147,7 @@ func (d *Debugger) Restart() ([]api.DiscardedBreakpoint, error) {
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
d.target = p
|
d.process = p
|
||||||
return discarded, nil
|
return discarded, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -161,7 +159,7 @@ func (d *Debugger) State() (*api.DebuggerState, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Debugger) state() (*api.DebuggerState, error) {
|
func (d *Debugger) state() (*api.DebuggerState, error) {
|
||||||
if d.target.Exited() {
|
if d.process.Exited() {
|
||||||
return nil, proc.ProcessExitedError{Pid: d.ProcessPid()}
|
return nil, proc.ProcessExitedError{Pid: d.ProcessPid()}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -170,24 +168,24 @@ func (d *Debugger) state() (*api.DebuggerState, error) {
|
|||||||
goroutine *api.Goroutine
|
goroutine *api.Goroutine
|
||||||
)
|
)
|
||||||
|
|
||||||
if d.target.SelectedGoroutine() != nil {
|
if d.process.SelectedGoroutine != nil {
|
||||||
goroutine = api.ConvertGoroutine(d.target.SelectedGoroutine())
|
goroutine = api.ConvertGoroutine(d.process.SelectedGoroutine)
|
||||||
}
|
}
|
||||||
|
|
||||||
state = &api.DebuggerState{
|
state = &api.DebuggerState{
|
||||||
SelectedGoroutine: goroutine,
|
SelectedGoroutine: goroutine,
|
||||||
Exited: d.target.Exited(),
|
Exited: d.process.Exited(),
|
||||||
}
|
}
|
||||||
|
|
||||||
for i := range d.target.Threads() {
|
for i := range d.process.Threads {
|
||||||
th := api.ConvertThread(d.target.Threads()[i])
|
th := api.ConvertThread(d.process.Threads[i])
|
||||||
state.Threads = append(state.Threads, th)
|
state.Threads = append(state.Threads, th)
|
||||||
if i == d.target.CurrentThread().ID {
|
if i == d.process.CurrentThread.ID {
|
||||||
state.CurrentThread = th
|
state.CurrentThread = th
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, bp := range d.target.Breakpoints() {
|
for _, bp := range d.process.Breakpoints {
|
||||||
if bp.Internal() {
|
if bp.Internal() {
|
||||||
state.NextInProgress = true
|
state.NextInProgress = true
|
||||||
break
|
break
|
||||||
@ -223,19 +221,19 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
|
|||||||
if runtime.GOOS == "windows" {
|
if runtime.GOOS == "windows" {
|
||||||
// Accept fileName which is case-insensitive and slash-insensitive match
|
// Accept fileName which is case-insensitive and slash-insensitive match
|
||||||
fileNameNormalized := strings.ToLower(filepath.ToSlash(fileName))
|
fileNameNormalized := strings.ToLower(filepath.ToSlash(fileName))
|
||||||
for symFile := range d.target.Sources() {
|
for symFile := range d.process.Sources() {
|
||||||
if fileNameNormalized == strings.ToLower(filepath.ToSlash(symFile)) {
|
if fileNameNormalized == strings.ToLower(filepath.ToSlash(symFile)) {
|
||||||
fileName = symFile
|
fileName = symFile
|
||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
addr, err = d.target.FindFileLocation(fileName, requestedBp.Line)
|
addr, err = d.process.FindFileLocation(fileName, requestedBp.Line)
|
||||||
case len(requestedBp.FunctionName) > 0:
|
case len(requestedBp.FunctionName) > 0:
|
||||||
if requestedBp.Line >= 0 {
|
if requestedBp.Line >= 0 {
|
||||||
addr, err = d.target.FindFunctionLocation(requestedBp.FunctionName, false, requestedBp.Line)
|
addr, err = d.process.FindFunctionLocation(requestedBp.FunctionName, false, requestedBp.Line)
|
||||||
} else {
|
} else {
|
||||||
addr, err = d.target.FindFunctionLocation(requestedBp.FunctionName, true, 0)
|
addr, err = d.process.FindFunctionLocation(requestedBp.FunctionName, true, 0)
|
||||||
}
|
}
|
||||||
default:
|
default:
|
||||||
addr = requestedBp.Addr
|
addr = requestedBp.Addr
|
||||||
@ -245,12 +243,12 @@ func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoin
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
bp, err := d.target.SetBreakpoint(addr, proc.UserBreakpoint, nil)
|
bp, err := d.process.SetBreakpoint(addr, proc.UserBreakpoint, nil)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
if err := copyBreakpointInfo(bp, requestedBp); err != nil {
|
if err := copyBreakpointInfo(bp, requestedBp); err != nil {
|
||||||
if _, err1 := d.target.ClearBreakpoint(bp.Addr); err1 != nil {
|
if _, err1 := d.process.ClearBreakpoint(bp.Addr); err1 != nil {
|
||||||
err = fmt.Errorf("error while creating breakpoint: %v, additionally the breakpoint could not be properly rolled back: %v", err, err1)
|
err = fmt.Errorf("error while creating breakpoint: %v, additionally the breakpoint could not be properly rolled back: %v", err, err1)
|
||||||
}
|
}
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -275,7 +273,7 @@ func (d *Debugger) AmendBreakpoint(amend *api.Breakpoint) error {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Debugger) CancelNext() error {
|
func (d *Debugger) CancelNext() error {
|
||||||
return d.target.ClearInternalBreakpoints()
|
return d.process.ClearInternalBreakpoints()
|
||||||
}
|
}
|
||||||
|
|
||||||
func copyBreakpointInfo(bp *proc.Breakpoint, requested *api.Breakpoint) (err error) {
|
func copyBreakpointInfo(bp *proc.Breakpoint, requested *api.Breakpoint) (err error) {
|
||||||
@ -299,7 +297,7 @@ func (d *Debugger) ClearBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint
|
|||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
var clearedBp *api.Breakpoint
|
var clearedBp *api.Breakpoint
|
||||||
bp, err := d.target.ClearBreakpoint(requestedBp.Addr)
|
bp, err := d.process.ClearBreakpoint(requestedBp.Addr)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, fmt.Errorf("Can't clear breakpoint @%x: %s", requestedBp.Addr, err)
|
return nil, fmt.Errorf("Can't clear breakpoint @%x: %s", requestedBp.Addr, err)
|
||||||
}
|
}
|
||||||
@ -317,7 +315,7 @@ func (d *Debugger) Breakpoints() []*api.Breakpoint {
|
|||||||
|
|
||||||
func (d *Debugger) breakpoints() []*api.Breakpoint {
|
func (d *Debugger) breakpoints() []*api.Breakpoint {
|
||||||
bps := []*api.Breakpoint{}
|
bps := []*api.Breakpoint{}
|
||||||
for _, bp := range d.target.Breakpoints() {
|
for _, bp := range d.process.Breakpoints {
|
||||||
if bp.Internal() {
|
if bp.Internal() {
|
||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
@ -339,7 +337,7 @@ func (d *Debugger) FindBreakpoint(id int) *api.Breakpoint {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (d *Debugger) findBreakpoint(id int) *proc.Breakpoint {
|
func (d *Debugger) findBreakpoint(id int) *proc.Breakpoint {
|
||||||
for _, bp := range d.target.Breakpoints() {
|
for _, bp := range d.process.Breakpoints {
|
||||||
if bp.ID == id {
|
if bp.ID == id {
|
||||||
return bp
|
return bp
|
||||||
}
|
}
|
||||||
@ -368,11 +366,11 @@ func (d *Debugger) Threads() ([]*api.Thread, error) {
|
|||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
if d.target.Exited() {
|
if d.process.Exited() {
|
||||||
return nil, &proc.ProcessExitedError{}
|
return nil, &proc.ProcessExitedError{}
|
||||||
}
|
}
|
||||||
threads := []*api.Thread{}
|
threads := []*api.Thread{}
|
||||||
for _, th := range d.target.Threads() {
|
for _, th := range d.process.Threads {
|
||||||
threads = append(threads, api.ConvertThread(th))
|
threads = append(threads, api.ConvertThread(th))
|
||||||
}
|
}
|
||||||
return threads, nil
|
return threads, nil
|
||||||
@ -383,11 +381,11 @@ func (d *Debugger) FindThread(id int) (*api.Thread, error) {
|
|||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
if d.target.Exited() {
|
if d.process.Exited() {
|
||||||
return nil, &proc.ProcessExitedError{}
|
return nil, &proc.ProcessExitedError{}
|
||||||
}
|
}
|
||||||
|
|
||||||
for _, th := range d.target.Threads() {
|
for _, th := range d.process.Threads {
|
||||||
if th.ID == id {
|
if th.ID == id {
|
||||||
return api.ConvertThread(th), nil
|
return api.ConvertThread(th), nil
|
||||||
}
|
}
|
||||||
@ -403,7 +401,7 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
|
|||||||
// RequestManualStop does not invoke any ptrace syscalls, so it's safe to
|
// RequestManualStop does not invoke any ptrace syscalls, so it's safe to
|
||||||
// access the process directly.
|
// access the process directly.
|
||||||
log.Print("halting")
|
log.Print("halting")
|
||||||
err = d.target.RequestManualStop()
|
err = d.process.RequestManualStop()
|
||||||
}
|
}
|
||||||
|
|
||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
@ -412,7 +410,7 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
|
|||||||
switch command.Name {
|
switch command.Name {
|
||||||
case api.Continue:
|
case api.Continue:
|
||||||
log.Print("continuing")
|
log.Print("continuing")
|
||||||
err = d.target.Continue()
|
err = d.process.Continue()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
if exitedErr, exited := err.(proc.ProcessExitedError); exited {
|
if exitedErr, exited := err.(proc.ProcessExitedError); exited {
|
||||||
state := &api.DebuggerState{}
|
state := &api.DebuggerState{}
|
||||||
@ -432,22 +430,22 @@ func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, er
|
|||||||
|
|
||||||
case api.Next:
|
case api.Next:
|
||||||
log.Print("nexting")
|
log.Print("nexting")
|
||||||
err = d.target.Next()
|
err = d.process.Next()
|
||||||
case api.Step:
|
case api.Step:
|
||||||
log.Print("stepping")
|
log.Print("stepping")
|
||||||
err = d.target.Step()
|
err = d.process.Step()
|
||||||
case api.StepInstruction:
|
case api.StepInstruction:
|
||||||
log.Print("single stepping")
|
log.Print("single stepping")
|
||||||
err = d.target.StepInstruction()
|
err = d.process.StepInstruction()
|
||||||
case api.StepOut:
|
case api.StepOut:
|
||||||
log.Print("step out")
|
log.Print("step out")
|
||||||
err = d.target.StepOut()
|
err = d.process.StepOut()
|
||||||
case api.SwitchThread:
|
case api.SwitchThread:
|
||||||
log.Printf("switching to thread %d", command.ThreadID)
|
log.Printf("switching to thread %d", command.ThreadID)
|
||||||
err = d.target.SwitchThread(command.ThreadID)
|
err = d.process.SwitchThread(command.ThreadID)
|
||||||
case api.SwitchGoroutine:
|
case api.SwitchGoroutine:
|
||||||
log.Printf("switching to goroutine %d", command.GoroutineID)
|
log.Printf("switching to goroutine %d", command.GoroutineID)
|
||||||
err = d.target.SwitchGoroutine(command.GoroutineID)
|
err = d.process.SwitchGoroutine(command.GoroutineID)
|
||||||
case api.Halt:
|
case api.Halt:
|
||||||
// RequestManualStop already called
|
// RequestManualStop already called
|
||||||
}
|
}
|
||||||
@ -472,7 +470,7 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
|||||||
state.Threads[i].BreakpointInfo = bpi
|
state.Threads[i].BreakpointInfo = bpi
|
||||||
|
|
||||||
if bp.Goroutine {
|
if bp.Goroutine {
|
||||||
g, err := d.target.CurrentThread().GetG()
|
g, err := d.process.CurrentThread.GetG()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -480,7 +478,7 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
|||||||
}
|
}
|
||||||
|
|
||||||
if bp.Stacktrace > 0 {
|
if bp.Stacktrace > 0 {
|
||||||
rawlocs, err := d.target.CurrentThread().Stacktrace(bp.Stacktrace)
|
rawlocs, err := d.process.CurrentThread.Stacktrace(bp.Stacktrace)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -490,7 +488,7 @@ func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
s, err := d.target.Threads()[state.Threads[i].ID].Scope()
|
s, err := d.process.Threads[state.Threads[i].ID].Scope()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -531,7 +529,7 @@ func (d *Debugger) Sources(filter string) ([]string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
files := []string{}
|
files := []string{}
|
||||||
for f := range d.target.Sources() {
|
for f := range d.process.Sources() {
|
||||||
if regex.Match([]byte(f)) {
|
if regex.Match([]byte(f)) {
|
||||||
files = append(files, f)
|
files = append(files, f)
|
||||||
}
|
}
|
||||||
@ -544,7 +542,7 @@ func (d *Debugger) Functions(filter string) ([]string, error) {
|
|||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
return regexFilterFuncs(filter, d.target.Funcs())
|
return regexFilterFuncs(filter, d.process.Funcs())
|
||||||
}
|
}
|
||||||
|
|
||||||
func (d *Debugger) Types(filter string) ([]string, error) {
|
func (d *Debugger) Types(filter string) ([]string, error) {
|
||||||
@ -556,7 +554,7 @@ func (d *Debugger) Types(filter string) ([]string, error) {
|
|||||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||||
}
|
}
|
||||||
|
|
||||||
types, err := d.target.Types()
|
types, err := d.process.Types()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -598,7 +596,7 @@ func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadCo
|
|||||||
}
|
}
|
||||||
|
|
||||||
vars := []api.Variable{}
|
vars := []api.Variable{}
|
||||||
thread, found := d.target.Threads()[threadID]
|
thread, found := d.process.Threads[threadID]
|
||||||
if !found {
|
if !found {
|
||||||
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
||||||
}
|
}
|
||||||
@ -623,7 +621,7 @@ func (d *Debugger) Registers(threadID int, floatingPoint bool) (api.Registers, e
|
|||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
thread, found := d.target.Threads()[threadID]
|
thread, found := d.process.Threads[threadID]
|
||||||
if !found {
|
if !found {
|
||||||
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
||||||
}
|
}
|
||||||
@ -647,7 +645,7 @@ func (d *Debugger) LocalVariables(scope api.EvalScope, cfg proc.LoadConfig) ([]a
|
|||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
s, err := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -663,7 +661,7 @@ func (d *Debugger) FunctionArguments(scope api.EvalScope, cfg proc.LoadConfig) (
|
|||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
s, err := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -680,7 +678,7 @@ func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string, cfg p
|
|||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
s, err := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -697,7 +695,7 @@ func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol, value string)
|
|||||||
d.processMutex.Lock()
|
d.processMutex.Lock()
|
||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
s, err := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
@ -710,7 +708,7 @@ func (d *Debugger) Goroutines() ([]*api.Goroutine, error) {
|
|||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
goroutines := []*api.Goroutine{}
|
goroutines := []*api.Goroutine{}
|
||||||
gs, err := d.target.GoroutinesInfo()
|
gs, err := d.process.GoroutinesInfo()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -729,13 +727,13 @@ func (d *Debugger) Stacktrace(goroutineID, depth int, cfg *proc.LoadConfig) ([]a
|
|||||||
|
|
||||||
var rawlocs []proc.Stackframe
|
var rawlocs []proc.Stackframe
|
||||||
|
|
||||||
g, err := d.target.FindGoroutine(goroutineID)
|
g, err := d.process.FindGoroutine(goroutineID)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
if g == nil {
|
if g == nil {
|
||||||
rawlocs, err = d.target.CurrentThread().Stacktrace(depth)
|
rawlocs, err = d.process.CurrentThread.Stacktrace(depth)
|
||||||
} else {
|
} else {
|
||||||
rawlocs, err = g.Stacktrace(depth)
|
rawlocs, err = g.Stacktrace(depth)
|
||||||
}
|
}
|
||||||
@ -750,9 +748,9 @@ func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadCo
|
|||||||
locations := make([]api.Stackframe, 0, len(rawlocs))
|
locations := make([]api.Stackframe, 0, len(rawlocs))
|
||||||
for i := range rawlocs {
|
for i := range rawlocs {
|
||||||
frame := api.Stackframe{Location: api.ConvertLocation(rawlocs[i].Call)}
|
frame := api.Stackframe{Location: api.ConvertLocation(rawlocs[i].Call)}
|
||||||
if cfg != nil && rawlocs[i].Current.Fn != nil {
|
if cfg != nil {
|
||||||
var err error
|
var err error
|
||||||
scope := rawlocs[i].Scope(d.target.CurrentThread())
|
scope := rawlocs[i].Scope(d.process.CurrentThread)
|
||||||
locals, err := scope.LocalVariables(*cfg)
|
locals, err := scope.LocalVariables(*cfg)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
@ -781,11 +779,11 @@ func (d *Debugger) FindLocation(scope api.EvalScope, locStr string) ([]api.Locat
|
|||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
|
|
||||||
s, _ := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
s, _ := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||||
|
|
||||||
locs, err := loc.Find(d, s, locStr)
|
locs, err := loc.Find(d, s, locStr)
|
||||||
for i := range locs {
|
for i := range locs {
|
||||||
file, line, fn := d.target.PCToLine(locs[i].PC)
|
file, line, fn := d.process.PCToLine(locs[i].PC)
|
||||||
locs[i].File = file
|
locs[i].File = file
|
||||||
locs[i].Line = line
|
locs[i].Line = line
|
||||||
locs[i].Function = api.ConvertFunction(fn)
|
locs[i].Function = api.ConvertFunction(fn)
|
||||||
@ -800,7 +798,7 @@ func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavo
|
|||||||
defer d.processMutex.Unlock()
|
defer d.processMutex.Unlock()
|
||||||
|
|
||||||
if endPC == 0 {
|
if endPC == 0 {
|
||||||
_, _, fn := d.target.PCToLine(startPC)
|
_, _, fn := d.process.PCToLine(startPC)
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
return nil, fmt.Errorf("Address 0x%x does not belong to any function", startPC)
|
return nil, fmt.Errorf("Address 0x%x does not belong to any function", startPC)
|
||||||
}
|
}
|
||||||
@ -809,9 +807,9 @@ func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavo
|
|||||||
}
|
}
|
||||||
|
|
||||||
currentGoroutine := true
|
currentGoroutine := true
|
||||||
thread := d.target.CurrentThread()
|
thread := d.process.CurrentThread
|
||||||
|
|
||||||
if s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame); err == nil {
|
if s, err := d.process.ConvertEvalScope(scope.GoroutineID, scope.Frame); err == nil {
|
||||||
thread = s.Thread
|
thread = s.Thread
|
||||||
if scope.GoroutineID != -1 {
|
if scope.GoroutineID != -1 {
|
||||||
g, _ := s.Thread.GetG()
|
g, _ := s.Thread.GetG()
|
||||||
|
28
vendor/github.com/derekparker/delve/service/debugger/locations.go
generated
vendored
28
vendor/github.com/derekparker/delve/service/debugger/locations.go
generated
vendored
@ -10,7 +10,7 @@ import (
|
|||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
"strings"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/proc"
|
"github.com/derekparker/delve/proc"
|
||||||
"github.com/derekparker/delve/service/api"
|
"github.com/derekparker/delve/service/api"
|
||||||
)
|
)
|
||||||
|
|
||||||
@ -243,14 +243,14 @@ func (spec *FuncLocationSpec) Match(sym *gosym.Sym) bool {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (loc *RegexLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
func (loc *RegexLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||||
funcs := d.target.Funcs()
|
funcs := d.process.Funcs()
|
||||||
matches, err := regexFilterFuncs(loc.FuncRegex, funcs)
|
matches, err := regexFilterFuncs(loc.FuncRegex, funcs)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
r := make([]api.Location, 0, len(matches))
|
r := make([]api.Location, 0, len(matches))
|
||||||
for i := range matches {
|
for i := range matches {
|
||||||
addr, err := d.target.FindFunctionLocation(matches[i], true, 0)
|
addr, err := d.process.FindFunctionLocation(matches[i], true, 0)
|
||||||
if err == nil {
|
if err == nil {
|
||||||
r = append(r, api.Location{PC: addr})
|
r = append(r, api.Location{PC: addr})
|
||||||
}
|
}
|
||||||
@ -278,8 +278,8 @@ func (loc *AddrLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr str
|
|||||||
addr, _ := constant.Uint64Val(v.Value)
|
addr, _ := constant.Uint64Val(v.Value)
|
||||||
return []api.Location{{PC: addr}}, nil
|
return []api.Location{{PC: addr}}, nil
|
||||||
case reflect.Func:
|
case reflect.Func:
|
||||||
_, _, fn := d.target.PCToLine(uint64(v.Base))
|
_, _, fn := d.process.PCToLine(uint64(v.Base))
|
||||||
pc, err := d.target.FirstPCAfterPrologue(fn, false)
|
pc, err := d.process.FirstPCAfterPrologue(fn, false)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return nil, err
|
return nil, err
|
||||||
}
|
}
|
||||||
@ -327,8 +327,8 @@ func (ale AmbiguousLocationError) Error() string {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||||
funcs := d.target.Funcs()
|
funcs := d.process.Funcs()
|
||||||
files := d.target.Sources()
|
files := d.process.Sources()
|
||||||
|
|
||||||
candidates := []string{}
|
candidates := []string{}
|
||||||
for file := range files {
|
for file := range files {
|
||||||
@ -366,12 +366,12 @@ func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr s
|
|||||||
if loc.LineOffset < 0 {
|
if loc.LineOffset < 0 {
|
||||||
return nil, fmt.Errorf("Malformed breakpoint location, no line offset specified")
|
return nil, fmt.Errorf("Malformed breakpoint location, no line offset specified")
|
||||||
}
|
}
|
||||||
addr, err = d.target.FindFileLocation(candidates[0], loc.LineOffset)
|
addr, err = d.process.FindFileLocation(candidates[0], loc.LineOffset)
|
||||||
} else {
|
} else {
|
||||||
if loc.LineOffset < 0 {
|
if loc.LineOffset < 0 {
|
||||||
addr, err = d.target.FindFunctionLocation(candidates[0], true, 0)
|
addr, err = d.process.FindFunctionLocation(candidates[0], true, 0)
|
||||||
} else {
|
} else {
|
||||||
addr, err = d.target.FindFunctionLocation(candidates[0], false, loc.LineOffset)
|
addr, err = d.process.FindFunctionLocation(candidates[0], false, loc.LineOffset)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -390,11 +390,11 @@ func (loc *OffsetLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr s
|
|||||||
if scope == nil {
|
if scope == nil {
|
||||||
return nil, fmt.Errorf("could not determine current location (scope is nil)")
|
return nil, fmt.Errorf("could not determine current location (scope is nil)")
|
||||||
}
|
}
|
||||||
file, line, fn := d.target.PCToLine(scope.PC)
|
file, line, fn := d.process.PCToLine(scope.PC)
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
return nil, fmt.Errorf("could not determine current location")
|
return nil, fmt.Errorf("could not determine current location")
|
||||||
}
|
}
|
||||||
addr, err := d.target.FindFileLocation(file, line+loc.Offset)
|
addr, err := d.process.FindFileLocation(file, line+loc.Offset)
|
||||||
return []api.Location{{PC: addr}}, err
|
return []api.Location{{PC: addr}}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -402,10 +402,10 @@ func (loc *LineLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr str
|
|||||||
if scope == nil {
|
if scope == nil {
|
||||||
return nil, fmt.Errorf("could not determine current location (scope is nil)")
|
return nil, fmt.Errorf("could not determine current location (scope is nil)")
|
||||||
}
|
}
|
||||||
file, _, fn := d.target.PCToLine(scope.PC)
|
file, _, fn := d.process.PCToLine(scope.PC)
|
||||||
if fn == nil {
|
if fn == nil {
|
||||||
return nil, fmt.Errorf("could not determine current location")
|
return nil, fmt.Errorf("could not determine current location")
|
||||||
}
|
}
|
||||||
addr, err := d.target.FindFileLocation(file, loc.Line)
|
addr, err := d.process.FindFileLocation(file, loc.Line)
|
||||||
return []api.Location{{PC: addr}}, err
|
return []api.Location{{PC: addr}}, err
|
||||||
}
|
}
|
||||||
|
2
vendor/github.com/derekparker/delve/service/rpc1/server.go
generated
vendored
2
vendor/github.com/derekparker/delve/service/rpc1/server.go
generated
vendored
@ -4,7 +4,7 @@ import (
|
|||||||
"errors"
|
"errors"
|
||||||
"fmt"
|
"fmt"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/proc"
|
"github.com/derekparker/delve/proc"
|
||||||
"github.com/derekparker/delve/service"
|
"github.com/derekparker/delve/service"
|
||||||
"github.com/derekparker/delve/service/api"
|
"github.com/derekparker/delve/service/api"
|
||||||
"github.com/derekparker/delve/service/debugger"
|
"github.com/derekparker/delve/service/debugger"
|
||||||
|
4
vendor/github.com/derekparker/delve/service/rpccommon/server.go
generated
vendored
4
vendor/github.com/derekparker/delve/service/rpccommon/server.go
generated
vendored
@ -16,12 +16,12 @@ import (
|
|||||||
"unicode"
|
"unicode"
|
||||||
"unicode/utf8"
|
"unicode/utf8"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/version"
|
|
||||||
"github.com/derekparker/delve/service"
|
"github.com/derekparker/delve/service"
|
||||||
"github.com/derekparker/delve/service/api"
|
"github.com/derekparker/delve/service/api"
|
||||||
"github.com/derekparker/delve/service/debugger"
|
"github.com/derekparker/delve/service/debugger"
|
||||||
"github.com/derekparker/delve/service/rpc1"
|
"github.com/derekparker/delve/service/rpc1"
|
||||||
"github.com/derekparker/delve/service/rpc2"
|
"github.com/derekparker/delve/service/rpc2"
|
||||||
|
"github.com/derekparker/delve/version"
|
||||||
)
|
)
|
||||||
|
|
||||||
// ServerImpl implements a JSON-RPC server that can switch between two
|
// ServerImpl implements a JSON-RPC server that can switch between two
|
||||||
@ -403,7 +403,7 @@ func (err *internalError) Error() string {
|
|||||||
var out bytes.Buffer
|
var out bytes.Buffer
|
||||||
fmt.Fprintf(&out, "Internal debugger error: %v\n", err.Err)
|
fmt.Fprintf(&out, "Internal debugger error: %v\n", err.Err)
|
||||||
for _, frame := range err.Stack {
|
for _, frame := range err.Stack {
|
||||||
fmt.Fprintf(&out, "%s (%#x)\n\t%s:%d\n", frame.Func, frame.Pc, frame.File, frame.Line)
|
fmt.Fprintf(&out, "%s (%#x)\n\t%s%d\n", frame.Func, frame.Pc, frame.File, frame.Line)
|
||||||
}
|
}
|
||||||
return out.String()
|
return out.String()
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,7 @@ import (
|
|||||||
|
|
||||||
"github.com/peterh/liner"
|
"github.com/peterh/liner"
|
||||||
|
|
||||||
"github.com/derekparker/delve/pkg/config"
|
"github.com/derekparker/delve/config"
|
||||||
"github.com/derekparker/delve/service"
|
"github.com/derekparker/delve/service"
|
||||||
)
|
)
|
||||||
|
|
118
vendor/vendor.json
vendored
118
vendor/vendor.json
vendored
@ -15,100 +15,94 @@
|
|||||||
"revisionTime": "2017-03-06T13:59:04Z"
|
"revisionTime": "2017-03-06T13:59:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "Ay6dg3VVgggBHMsytbc+U4Wutyc=",
|
"checksumSHA1": "7o4Bn5wmWhqiC6QERVE+eFuMVxE=",
|
||||||
"path": "github.com/derekparker/delve/pkg/config",
|
"path": "github.com/derekparker/delve/config",
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "i4N90QPS76SMH2GjkJKjuS+z4xI=",
|
"checksumSHA1": "z9dNXc4EocXzA7shhyD9ICYzqMU=",
|
||||||
"path": "github.com/derekparker/delve/pkg/dwarf/frame",
|
"path": "github.com/derekparker/delve/dwarf/frame",
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "3pNKXngpbU4tnDOZc1ez6ywk7GA=",
|
"checksumSHA1": "1fsK0IHP5Gdnj2jdJqWGb+IiSbg=",
|
||||||
"path": "github.com/derekparker/delve/pkg/dwarf/line",
|
"path": "github.com/derekparker/delve/dwarf/line",
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "mOPdy13ztM3W5WMt3vVUq00AXfg=",
|
"checksumSHA1": "qGcXmO+L1RuCsl1rNxvBfHFF7xQ=",
|
||||||
"path": "github.com/derekparker/delve/pkg/dwarf/op",
|
"path": "github.com/derekparker/delve/dwarf/op",
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "hqg+xjH/qCO0CQokj5L1qD5lBmE=",
|
"checksumSHA1": "B4ANlFg/tsjxgw6VGKvymnqmFSs=",
|
||||||
"path": "github.com/derekparker/delve/pkg/dwarf/reader",
|
"path": "github.com/derekparker/delve/dwarf/reader",
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "QkHSoN99PlCYaEgChgPaEBmUWbk=",
|
"checksumSHA1": "sfJBUZjAjg9jlWnYvxxpXSteEfw=",
|
||||||
"path": "github.com/derekparker/delve/pkg/dwarf/util",
|
"path": "github.com/derekparker/delve/dwarf/util",
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "KH9MVb6MX5iPgRjySRZHQa324Sw=",
|
"checksumSHA1": "EV76KO6c8eV4x6qu7feUxpBbIyQ=",
|
||||||
"path": "github.com/derekparker/delve/pkg/proc",
|
"path": "github.com/derekparker/delve/proc",
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
},
|
|
||||||
{
|
|
||||||
"checksumSHA1": "9dbHlIWbcf3jXVu6zRjZvJnDq4c=",
|
|
||||||
"path": "github.com/derekparker/delve/pkg/target",
|
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"checksumSHA1": "nqWjcE4SeTBNSuYlh6MJ8akB0cM=",
|
|
||||||
"path": "github.com/derekparker/delve/pkg/terminal",
|
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
|
||||||
},
|
|
||||||
{
|
|
||||||
"checksumSHA1": "xRa64kuzCXVktmGscs1vCz9jIHs=",
|
|
||||||
"path": "github.com/derekparker/delve/pkg/version",
|
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "BPHre0My8iszpkoNcX4ljtrz1dA=",
|
"checksumSHA1": "BPHre0My8iszpkoNcX4ljtrz1dA=",
|
||||||
"path": "github.com/derekparker/delve/service",
|
"path": "github.com/derekparker/delve/service",
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "xMPjvGhTumLbAUApyLI/7UnC7sQ=",
|
"checksumSHA1": "EaIRvHNXgLb3yU/nwEkM48sA4Xc=",
|
||||||
"path": "github.com/derekparker/delve/service/api",
|
"path": "github.com/derekparker/delve/service/api",
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "/EXcw7FytgML9fdbMV0pxDBJJE0=",
|
"checksumSHA1": "zi0XsumHd/tKG0fIMNQsmZHSkM8=",
|
||||||
"path": "github.com/derekparker/delve/service/debugger",
|
"path": "github.com/derekparker/delve/service/debugger",
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "d73mTsiCuYYpDaVs9QtozhzMDU0=",
|
"checksumSHA1": "MVw0jbqnWHLX41UsSftGP7BZ6xU=",
|
||||||
"path": "github.com/derekparker/delve/service/rpc1",
|
"path": "github.com/derekparker/delve/service/rpc1",
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "B+J0KRNuX0anRlif3mWXhBQhnVo=",
|
"checksumSHA1": "B+J0KRNuX0anRlif3mWXhBQhnVo=",
|
||||||
"path": "github.com/derekparker/delve/service/rpc2",
|
"path": "github.com/derekparker/delve/service/rpc2",
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "PsNfnfv8GS79AG225Q/h57q/6F4=",
|
"checksumSHA1": "qCMUUT6PtR1jsrZcqkDkQHycBXM=",
|
||||||
"path": "github.com/derekparker/delve/service/rpccommon",
|
"path": "github.com/derekparker/delve/service/rpccommon",
|
||||||
"revision": "ab7367ed2bf15044f7bca97147a802f77b875797",
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
"revisionTime": "2017-03-13T17:59:34Z"
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "KUK2wagVoGOYEIy2vpH7up+DpXA=",
|
||||||
|
"path": "github.com/derekparker/delve/terminal",
|
||||||
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"checksumSHA1": "013Reixyv4BwEJF/J8vHeaSTtSI=",
|
||||||
|
"path": "github.com/derekparker/delve/version",
|
||||||
|
"revision": "449b276fe1ce7e20a0c7ffffbdd47d97f40022a1",
|
||||||
|
"revisionTime": "2017-01-11T19:37:04Z"
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
"checksumSHA1": "hveFTNQ9YEyYRs6SWuXM+XU9qRI=",
|
"checksumSHA1": "hveFTNQ9YEyYRs6SWuXM+XU9qRI=",
|
||||||
|
Loading…
Reference in New Issue
Block a user