mirror of
https://github.com/beego/bee.git
synced 2024-12-22 10:00:50 +00:00
Merge pull request #384 from amrfaissal/dlv-support
Add support for Delve debugger
This commit is contained in:
commit
8c0742c140
49
README.md
49
README.md
@ -1,3 +1,4 @@
|
||||
|
||||
bee
|
||||
===
|
||||
|
||||
@ -34,17 +35,20 @@ go get -u github.com/beego/bee
|
||||
Bee provides a variety of commands which can be helpful at various stages of development. The top level commands include:
|
||||
|
||||
```
|
||||
new Creates a Beego application
|
||||
run Run the application by starting a local development server
|
||||
pack Compresses a Beego application into a single file
|
||||
api Creates a Beego API application
|
||||
hprose Creates an RPC application based on Hprose and Beego frameworks
|
||||
bale Transforms non-Go files to Go source files
|
||||
version Prints the current Bee version
|
||||
generate Source code generator
|
||||
migrate Runs database migrations
|
||||
api Creates a Beego API application
|
||||
bale Transforms non-Go files to Go source files
|
||||
fix Fixes your application by making it compatible with newer versions of Beego
|
||||
dlv Start a debugging session using Delve
|
||||
dockerize Generates a Dockerfile for your Beego application
|
||||
generate Source code generator
|
||||
hprose Creates an RPC application based on Hprose and Beego frameworks
|
||||
new Creates a Beego application
|
||||
pack Compresses a Beego application into a single file
|
||||
rs Run customized scripts
|
||||
run Run the application by starting a local development server
|
||||
|
||||
```
|
||||
|
||||
### bee version
|
||||
@ -298,6 +302,37 @@ ______
|
||||
|
||||
For more information on the usage, run `bee help dockerize`.
|
||||
|
||||
### bee dlv
|
||||
|
||||
Bee can also help with debugging your application. To start a debugging session:
|
||||
|
||||
```bash
|
||||
______
|
||||
| ___ \
|
||||
| |_/ / ___ ___
|
||||
| ___ \ / _ \ / _ \
|
||||
| |_/ /| __/| __/
|
||||
\____/ \___| \___| v1.8.0
|
||||
2017/03/22 11:17:05 INFO ▶ 0001 Starting Delve Debugger...
|
||||
Type 'help' for list of commands.
|
||||
(dlv) break main.main
|
||||
Breakpoint 1 set at 0x40100f for main.main() ./main.go:8
|
||||
|
||||
(dlv) continue
|
||||
> main.main() ./main.go:8 (hits goroutine(1):1 total:1) (PC: 0x40100f)
|
||||
3: import (
|
||||
4: _ "github.com/user/myapp/routers"
|
||||
5: "github.com/astaxie/beego"
|
||||
6: )
|
||||
7:
|
||||
=> 8: func main() {
|
||||
9: beego.Run()
|
||||
10: }
|
||||
11:
|
||||
```
|
||||
|
||||
For more information on the usage, run `bee help dlv`.
|
||||
|
||||
## Shortcuts
|
||||
|
||||
Because you'll likely type these generator commands over and over, it makes sense to create aliases:
|
||||
|
@ -12,7 +12,7 @@
|
||||
// License for the specific language governing permissions and limitations
|
||||
// under the License.
|
||||
|
||||
// Bee is a tool for developing applications based on beego framework.
|
||||
// Package cmd ...
|
||||
package cmd
|
||||
|
||||
import (
|
||||
@ -20,6 +20,7 @@ import (
|
||||
_ "github.com/beego/bee/cmd/commands/api"
|
||||
_ "github.com/beego/bee/cmd/commands/bale"
|
||||
_ "github.com/beego/bee/cmd/commands/beefix"
|
||||
_ "github.com/beego/bee/cmd/commands/dlv"
|
||||
_ "github.com/beego/bee/cmd/commands/dockerize"
|
||||
_ "github.com/beego/bee/cmd/commands/generate"
|
||||
_ "github.com/beego/bee/cmd/commands/hprose"
|
||||
|
135
cmd/commands/dlv/dlv.go
Normal file
135
cmd/commands/dlv/dlv.go
Normal file
@ -0,0 +1,135 @@
|
||||
// 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"
|
||||
|
||||
"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...)
|
||||
}
|
@ -26,6 +26,7 @@ import (
|
||||
"runtime"
|
||||
"strings"
|
||||
"text/template"
|
||||
"unicode"
|
||||
|
||||
beeLogger "github.com/beego/bee/logger"
|
||||
"github.com/beego/bee/logger/colors"
|
||||
@ -332,3 +333,69 @@ func PrintErrorAndExit(message, errorTemplate string) {
|
||||
Tmpl(fmt.Sprintf(errorTemplate, message), nil)
|
||||
os.Exit(2)
|
||||
}
|
||||
|
||||
// GoCommand executes the passed command using Go tool
|
||||
func GoCommand(command string, args ...string) error {
|
||||
allargs := []string{command}
|
||||
allargs = append(allargs, args...)
|
||||
goBuild := exec.Command("go", allargs...)
|
||||
goBuild.Stderr = os.Stderr
|
||||
return goBuild.Run()
|
||||
}
|
||||
|
||||
// SplitQuotedFields is like strings.Fields but ignores spaces
|
||||
// inside areas surrounded by single quotes.
|
||||
// To specify a single quote use backslash to escape it: '\''
|
||||
func SplitQuotedFields(in string) []string {
|
||||
type stateEnum int
|
||||
const (
|
||||
inSpace stateEnum = iota
|
||||
inField
|
||||
inQuote
|
||||
inQuoteEscaped
|
||||
)
|
||||
state := inSpace
|
||||
r := []string{}
|
||||
var buf bytes.Buffer
|
||||
|
||||
for _, ch := range in {
|
||||
switch state {
|
||||
case inSpace:
|
||||
if ch == '\'' {
|
||||
state = inQuote
|
||||
} else if !unicode.IsSpace(ch) {
|
||||
buf.WriteRune(ch)
|
||||
state = inField
|
||||
}
|
||||
|
||||
case inField:
|
||||
if ch == '\'' {
|
||||
state = inQuote
|
||||
} else if unicode.IsSpace(ch) {
|
||||
r = append(r, buf.String())
|
||||
buf.Reset()
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
case inQuote:
|
||||
if ch == '\'' {
|
||||
state = inField
|
||||
} else if ch == '\\' {
|
||||
state = inQuoteEscaped
|
||||
} else {
|
||||
buf.WriteRune(ch)
|
||||
}
|
||||
|
||||
case inQuoteEscaped:
|
||||
buf.WriteRune(ch)
|
||||
state = inQuote
|
||||
}
|
||||
}
|
||||
|
||||
if buf.Len() != 0 {
|
||||
r = append(r, buf.String())
|
||||
}
|
||||
|
||||
return r
|
||||
}
|
||||
|
20
vendor/github.com/derekparker/delve/LICENSE
generated
vendored
Normal file
20
vendor/github.com/derekparker/delve/LICENSE
generated
vendored
Normal file
@ -0,0 +1,20 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2014 Derek Parker
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
||||
this software and associated documentation files (the "Software"), to deal in
|
||||
the Software without restriction, including without limitation the rights to
|
||||
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
||||
the Software, and to permit persons to whom the Software is furnished to do so,
|
||||
subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
||||
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
||||
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
134
vendor/github.com/derekparker/delve/pkg/config/config.go
generated
vendored
Normal file
134
vendor/github.com/derekparker/delve/pkg/config/config.go
generated
vendored
Normal file
@ -0,0 +1,134 @@
|
||||
package config
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/user"
|
||||
"path"
|
||||
|
||||
yaml "gopkg.in/yaml.v2"
|
||||
)
|
||||
|
||||
const (
|
||||
configDir string = ".dlv"
|
||||
configFile string = "config.yml"
|
||||
)
|
||||
|
||||
// Describes a rule for substitution of path to source code file.
|
||||
type SubstitutePathRule struct {
|
||||
// Directory path will be substituted if it matches `From`.
|
||||
From string
|
||||
// Path to which substitution is performed.
|
||||
To string
|
||||
}
|
||||
|
||||
// Slice of source code path substitution rules.
|
||||
type SubstitutePathRules []SubstitutePathRule
|
||||
|
||||
// Config defines all configuration options available to be set through the config file.
|
||||
type Config struct {
|
||||
// Commands aliases.
|
||||
Aliases map[string][]string
|
||||
// Source code path substitution rules.
|
||||
SubstitutePath SubstitutePathRules `yaml:"substitute-path"`
|
||||
}
|
||||
|
||||
// LoadConfig attempts to populate a Config object from the config.yml file.
|
||||
func LoadConfig() *Config {
|
||||
err := createConfigPath()
|
||||
if err != nil {
|
||||
fmt.Printf("Could not create config directory: %v.", err)
|
||||
return nil
|
||||
}
|
||||
fullConfigFile, err := GetConfigFilePath(configFile)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to get config file path: %v.", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
f, err := os.Open(fullConfigFile)
|
||||
if err != nil {
|
||||
createDefaultConfig(fullConfigFile)
|
||||
return nil
|
||||
}
|
||||
defer func() {
|
||||
err := f.Close()
|
||||
if err != nil {
|
||||
fmt.Printf("Closing config file failed: %v.", err)
|
||||
}
|
||||
}()
|
||||
|
||||
data, err := ioutil.ReadAll(f)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to read config data: %v.", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
var c Config
|
||||
err = yaml.Unmarshal(data, &c)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to decode config file: %v.", err)
|
||||
return nil
|
||||
}
|
||||
|
||||
return &c
|
||||
}
|
||||
|
||||
func createDefaultConfig(path string) {
|
||||
f, err := os.Create(path)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to create config file: %v.", err)
|
||||
return
|
||||
}
|
||||
defer func() {
|
||||
err := f.Close()
|
||||
if err != nil {
|
||||
fmt.Printf("Closing config file failed: %v.", err)
|
||||
}
|
||||
}()
|
||||
err = writeDefaultConfig(f)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to write default configuration: %v.", err)
|
||||
}
|
||||
}
|
||||
|
||||
func writeDefaultConfig(f *os.File) error {
|
||||
_, err := f.WriteString(
|
||||
`# Configuration file for the delve debugger.
|
||||
|
||||
# This is the default configuration file. Available options are provided, but disabled.
|
||||
# Delete the leading hash mark to enable an item.
|
||||
|
||||
# Provided aliases will be added to the default aliases for a given command.
|
||||
aliases:
|
||||
# command: ["alias1", "alias2"]
|
||||
|
||||
# Define sources path substitution rules. Can be used to rewrite a source path stored
|
||||
# in program's debug information, if the sources were moved to a different place
|
||||
# between compilation and debugging.
|
||||
# Note that substitution rules will not be used for paths passed to "break" and "trace"
|
||||
# commands.
|
||||
substitute-path:
|
||||
# - {from: path, to: path}
|
||||
`)
|
||||
return err
|
||||
}
|
||||
|
||||
// createConfigPath creates the directory structure at which all config files are saved.
|
||||
func createConfigPath() error {
|
||||
path, err := GetConfigFilePath("")
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return os.MkdirAll(path, 0700)
|
||||
}
|
||||
|
||||
// GetConfigFilePath gets the full path to the given config file name.
|
||||
func GetConfigFilePath(file string) (string, error) {
|
||||
usr, err := user.Current()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return path.Join(usr.HomeDir, configDir, file), nil
|
||||
}
|
95
vendor/github.com/derekparker/delve/pkg/dwarf/frame/entries.go
generated
vendored
Normal file
95
vendor/github.com/derekparker/delve/pkg/dwarf/frame/entries.go
generated
vendored
Normal file
@ -0,0 +1,95 @@
|
||||
package frame
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"sort"
|
||||
)
|
||||
|
||||
// Represents a Common Information Entry in
|
||||
// the Dwarf .debug_frame section.
|
||||
type CommonInformationEntry struct {
|
||||
Length uint32
|
||||
CIE_id uint32
|
||||
Version uint8
|
||||
Augmentation string
|
||||
CodeAlignmentFactor uint64
|
||||
DataAlignmentFactor int64
|
||||
ReturnAddressRegister uint64
|
||||
InitialInstructions []byte
|
||||
}
|
||||
|
||||
// Represents a Frame Descriptor Entry in the
|
||||
// Dwarf .debug_frame section.
|
||||
type FrameDescriptionEntry struct {
|
||||
Length uint32
|
||||
CIE *CommonInformationEntry
|
||||
Instructions []byte
|
||||
begin, end uint64
|
||||
order binary.ByteOrder
|
||||
}
|
||||
|
||||
// Returns whether or not the given address is within the
|
||||
// bounds of this frame.
|
||||
func (fde *FrameDescriptionEntry) Cover(addr uint64) bool {
|
||||
if (addr - fde.begin) < fde.end {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Address of first location for this frame.
|
||||
func (fde *FrameDescriptionEntry) Begin() uint64 {
|
||||
return fde.begin
|
||||
}
|
||||
|
||||
// Address of last location for this frame.
|
||||
func (fde *FrameDescriptionEntry) End() uint64 {
|
||||
return fde.begin + fde.end
|
||||
}
|
||||
|
||||
// Set up frame for the given PC.
|
||||
func (fde *FrameDescriptionEntry) EstablishFrame(pc uint64) *FrameContext {
|
||||
return executeDwarfProgramUntilPC(fde, pc)
|
||||
}
|
||||
|
||||
// Return the offset from the current SP that the return address is stored at.
|
||||
func (fde *FrameDescriptionEntry) ReturnAddressOffset(pc uint64) (frameOffset, returnAddressOffset int64) {
|
||||
frame := fde.EstablishFrame(pc)
|
||||
return frame.cfa.offset, frame.regs[fde.CIE.ReturnAddressRegister].offset
|
||||
}
|
||||
|
||||
type FrameDescriptionEntries []*FrameDescriptionEntry
|
||||
|
||||
func NewFrameIndex() FrameDescriptionEntries {
|
||||
return make(FrameDescriptionEntries, 0, 1000)
|
||||
}
|
||||
|
||||
type NoFDEForPCError struct {
|
||||
PC uint64
|
||||
}
|
||||
|
||||
func (err *NoFDEForPCError) Error() string {
|
||||
return fmt.Sprintf("could not find FDE for PC %#v", err.PC)
|
||||
}
|
||||
|
||||
// Returns the Frame Description Entry for the given PC.
|
||||
func (fdes FrameDescriptionEntries) FDEForPC(pc uint64) (*FrameDescriptionEntry, error) {
|
||||
idx := sort.Search(len(fdes), func(i int) bool {
|
||||
if fdes[i].Cover(pc) {
|
||||
return true
|
||||
}
|
||||
if fdes[i].LessThan(pc) {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
if idx == len(fdes) {
|
||||
return nil, &NoFDEForPCError{pc}
|
||||
}
|
||||
return fdes[idx], nil
|
||||
}
|
||||
|
||||
func (frame *FrameDescriptionEntry) LessThan(pc uint64) bool {
|
||||
return frame.End() <= pc
|
||||
}
|
164
vendor/github.com/derekparker/delve/pkg/dwarf/frame/expression_constants.go
generated
vendored
Normal file
164
vendor/github.com/derekparker/delve/pkg/dwarf/frame/expression_constants.go
generated
vendored
Normal file
@ -0,0 +1,164 @@
|
||||
package frame
|
||||
|
||||
// Operation opcodes
|
||||
const (
|
||||
DW_OP_addr = 0x03
|
||||
DW_OP_const1s = 0x09
|
||||
)
|
||||
|
||||
const (
|
||||
DW_OP_const2u = 0x0a
|
||||
DW_OP_const2s = 0x0b
|
||||
DW_OP_const4u = iota
|
||||
DW_OP_const4s
|
||||
DW_OP_const8u
|
||||
DW_OP_const8s
|
||||
DW_OP_constu
|
||||
DW_OP_consts
|
||||
DW_OP_dup
|
||||
DW_OP_drop
|
||||
DW_OP_over
|
||||
DW_OP_pick
|
||||
DW_OP_swap
|
||||
DW_OP_rot
|
||||
DW_OP_xderef
|
||||
DW_OP_abs
|
||||
DW_OP_and
|
||||
DW_OP_div
|
||||
DW_OP_minus
|
||||
DW_OP_mod
|
||||
DW_OP_mul
|
||||
DW_OP_neg
|
||||
DW_OP_not
|
||||
DW_OP_or
|
||||
DW_OP_plus
|
||||
DW_OP_plus_uconst
|
||||
DW_OP_shl
|
||||
DW_OP_shr
|
||||
DW_OP_shra
|
||||
DW_OP_xor
|
||||
DW_OP_skip
|
||||
DW_OP_bra
|
||||
DW_OP_eq
|
||||
DW_OP_ge
|
||||
DW_OP_gt
|
||||
DW_OP_le
|
||||
DW_OP_lt
|
||||
DW_OP_ne
|
||||
)
|
||||
|
||||
const (
|
||||
DW_OP_lit0 = 0x30
|
||||
DW_OP_lit1 = 0x31
|
||||
DW_OP_lit2 = iota
|
||||
DW_OP_lit3
|
||||
DW_OP_lit4
|
||||
DW_OP_lit5
|
||||
DW_OP_lit6
|
||||
DW_OP_lit7
|
||||
DW_OP_lit8
|
||||
DW_OP_lit9
|
||||
DW_OP_lit10
|
||||
DW_OP_lit11
|
||||
DW_OP_lit12
|
||||
DW_OP_lit13
|
||||
DW_OP_lit14
|
||||
DW_OP_lit15
|
||||
DW_OP_lit16
|
||||
DW_OP_lit17
|
||||
DW_OP_lit18
|
||||
DW_OP_lit19
|
||||
DW_OP_lit20
|
||||
DW_OP_lit21
|
||||
DW_OP_lit22
|
||||
DW_OP_lit23
|
||||
DW_OP_lit24
|
||||
DW_OP_lit25
|
||||
DW_OP_lit26
|
||||
DW_OP_lit27
|
||||
DW_OP_lit28
|
||||
DW_OP_lit29
|
||||
DW_OP_lit30
|
||||
DW_OP_lit31
|
||||
DW_OP_reg0
|
||||
DW_OP_reg1
|
||||
DW_OP_reg2
|
||||
DW_OP_reg3
|
||||
DW_OP_reg4
|
||||
DW_OP_reg5
|
||||
DW_OP_reg6
|
||||
DW_OP_reg7
|
||||
DW_OP_reg8
|
||||
DW_OP_reg9
|
||||
DW_OP_reg10
|
||||
DW_OP_reg11
|
||||
DW_OP_reg12
|
||||
DW_OP_reg13
|
||||
DW_OP_reg14
|
||||
DW_OP_reg15
|
||||
DW_OP_reg16
|
||||
DW_OP_reg17
|
||||
DW_OP_reg18
|
||||
DW_OP_reg19
|
||||
DW_OP_reg20
|
||||
DW_OP_reg21
|
||||
DW_OP_reg22
|
||||
DW_OP_reg23
|
||||
DW_OP_reg24
|
||||
DW_OP_reg25
|
||||
DW_OP_reg26
|
||||
DW_OP_reg27
|
||||
DW_OP_reg28
|
||||
DW_OP_reg29
|
||||
DW_OP_reg30
|
||||
DW_OP_reg31
|
||||
DW_OP_breg0
|
||||
DW_OP_breg1
|
||||
DW_OP_breg2
|
||||
DW_OP_breg3
|
||||
DW_OP_breg4
|
||||
DW_OP_breg5
|
||||
DW_OP_breg6
|
||||
DW_OP_breg7
|
||||
DW_OP_breg8
|
||||
DW_OP_breg9
|
||||
DW_OP_breg10
|
||||
DW_OP_breg11
|
||||
DW_OP_breg12
|
||||
DW_OP_breg13
|
||||
DW_OP_breg14
|
||||
DW_OP_breg15
|
||||
DW_OP_breg16
|
||||
DW_OP_breg17
|
||||
DW_OP_breg18
|
||||
DW_OP_breg19
|
||||
DW_OP_breg20
|
||||
DW_OP_breg21
|
||||
DW_OP_breg22
|
||||
DW_OP_breg23
|
||||
DW_OP_breg24
|
||||
DW_OP_breg25
|
||||
DW_OP_breg26
|
||||
DW_OP_breg27
|
||||
DW_OP_breg28
|
||||
DW_OP_breg29
|
||||
DW_OP_breg30
|
||||
DW_OP_breg31
|
||||
DW_OP_regx
|
||||
DW_OP_fbreg
|
||||
DW_OP_bregx
|
||||
DW_OP_piece
|
||||
DW_OP_deref_size
|
||||
DW_OP_xderef_size
|
||||
DW_OP_nop
|
||||
DW_OP_push_object_address
|
||||
DW_OP_call2
|
||||
DW_OP_call4
|
||||
DW_OP_call_ref
|
||||
DW_OP_form_tls_address
|
||||
DW_OP_call_frame_cfa
|
||||
DW_OP_bit_piece
|
||||
|
||||
DW_OP_lo_user = 0xe0
|
||||
DW_OP_hi_user = 0xff
|
||||
)
|
125
vendor/github.com/derekparker/delve/pkg/dwarf/frame/parser.go
generated
vendored
Normal file
125
vendor/github.com/derekparker/delve/pkg/dwarf/frame/parser.go
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
// Package frame contains data structures and
|
||||
// related functions for parsing and searching
|
||||
// through Dwarf .debug_frame data.
|
||||
package frame
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
type parsefunc func(*parseContext) parsefunc
|
||||
|
||||
type parseContext struct {
|
||||
buf *bytes.Buffer
|
||||
entries FrameDescriptionEntries
|
||||
common *CommonInformationEntry
|
||||
frame *FrameDescriptionEntry
|
||||
length uint32
|
||||
}
|
||||
|
||||
// Parse takes in data (a byte slice) and returns a slice of
|
||||
// commonInformationEntry structures. Each commonInformationEntry
|
||||
// has a slice of frameDescriptionEntry structures.
|
||||
func Parse(data []byte, order binary.ByteOrder) FrameDescriptionEntries {
|
||||
var (
|
||||
buf = bytes.NewBuffer(data)
|
||||
pctx = &parseContext{buf: buf, entries: NewFrameIndex()}
|
||||
)
|
||||
|
||||
for fn := parselength; buf.Len() != 0; {
|
||||
fn = fn(pctx)
|
||||
}
|
||||
|
||||
for i := range pctx.entries {
|
||||
pctx.entries[i].order = order
|
||||
}
|
||||
|
||||
return pctx.entries
|
||||
}
|
||||
|
||||
func cieEntry(data []byte) bool {
|
||||
return bytes.Equal(data, []byte{0xff, 0xff, 0xff, 0xff})
|
||||
}
|
||||
|
||||
func parselength(ctx *parseContext) parsefunc {
|
||||
var data = ctx.buf.Next(8)
|
||||
|
||||
ctx.length = binary.LittleEndian.Uint32(data[:4]) - 4 // take off the length of the CIE id / CIE pointer.
|
||||
|
||||
if cieEntry(data[4:]) {
|
||||
ctx.common = &CommonInformationEntry{Length: ctx.length}
|
||||
return parseCIE
|
||||
}
|
||||
|
||||
ctx.frame = &FrameDescriptionEntry{Length: ctx.length, CIE: ctx.common}
|
||||
return parseFDE
|
||||
}
|
||||
|
||||
func parseFDE(ctx *parseContext) parsefunc {
|
||||
r := ctx.buf.Next(int(ctx.length))
|
||||
|
||||
ctx.frame.begin = binary.LittleEndian.Uint64(r[:8])
|
||||
ctx.frame.end = binary.LittleEndian.Uint64(r[8:16])
|
||||
|
||||
// Insert into the tree after setting address range begin
|
||||
// otherwise compares won't work.
|
||||
ctx.entries = append(ctx.entries, ctx.frame)
|
||||
|
||||
// The rest of this entry consists of the instructions
|
||||
// so we can just grab all of the data from the buffer
|
||||
// cursor to length.
|
||||
ctx.frame.Instructions = r[16:]
|
||||
ctx.length = 0
|
||||
|
||||
return parselength
|
||||
}
|
||||
|
||||
func parseCIE(ctx *parseContext) parsefunc {
|
||||
data := ctx.buf.Next(int(ctx.length))
|
||||
buf := bytes.NewBuffer(data)
|
||||
// parse version
|
||||
ctx.common.Version = data[0]
|
||||
|
||||
// parse augmentation
|
||||
ctx.common.Augmentation, _ = util.ParseString(buf)
|
||||
|
||||
// parse code alignment factor
|
||||
ctx.common.CodeAlignmentFactor, _ = util.DecodeULEB128(buf)
|
||||
|
||||
// parse data alignment factor
|
||||
ctx.common.DataAlignmentFactor, _ = util.DecodeSLEB128(buf)
|
||||
|
||||
// parse return address register
|
||||
ctx.common.ReturnAddressRegister, _ = util.DecodeULEB128(buf)
|
||||
|
||||
// parse initial instructions
|
||||
// The rest of this entry consists of the instructions
|
||||
// so we can just grab all of the data from the buffer
|
||||
// cursor to length.
|
||||
ctx.common.InitialInstructions = buf.Bytes() //ctx.buf.Next(int(ctx.length))
|
||||
ctx.length = 0
|
||||
|
||||
return parselength
|
||||
}
|
||||
|
||||
// DwarfEndian determines the endianness of the DWARF by using the version number field in the debug_info section
|
||||
// Trick borrowed from "debug/dwarf".New()
|
||||
func DwarfEndian(infoSec []byte) binary.ByteOrder {
|
||||
if len(infoSec) < 6 {
|
||||
return binary.BigEndian
|
||||
}
|
||||
x, y := infoSec[4], infoSec[5]
|
||||
switch {
|
||||
case x == 0 && y == 0:
|
||||
return binary.BigEndian
|
||||
case x == 0:
|
||||
return binary.BigEndian
|
||||
case y == 0:
|
||||
return binary.LittleEndian
|
||||
default:
|
||||
return binary.BigEndian
|
||||
}
|
||||
}
|
429
vendor/github.com/derekparker/delve/pkg/dwarf/frame/table.go
generated
vendored
Normal file
429
vendor/github.com/derekparker/delve/pkg/dwarf/frame/table.go
generated
vendored
Normal file
@ -0,0 +1,429 @@
|
||||
package frame
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
type CurrentFrameAddress struct {
|
||||
register uint64
|
||||
offset int64
|
||||
expression []byte
|
||||
rule byte
|
||||
}
|
||||
|
||||
type DWRule struct {
|
||||
rule byte
|
||||
offset int64
|
||||
newreg uint64
|
||||
expression []byte
|
||||
}
|
||||
|
||||
type FrameContext struct {
|
||||
loc uint64
|
||||
order binary.ByteOrder
|
||||
address uint64
|
||||
cfa CurrentFrameAddress
|
||||
regs map[uint64]DWRule
|
||||
initialRegs map[uint64]DWRule
|
||||
prevRegs map[uint64]DWRule
|
||||
buf *bytes.Buffer
|
||||
cie *CommonInformationEntry
|
||||
codeAlignment uint64
|
||||
dataAlignment int64
|
||||
}
|
||||
|
||||
func (fctx *FrameContext) CFAOffset() int64 {
|
||||
return fctx.cfa.offset
|
||||
}
|
||||
|
||||
// Instructions used to recreate the table from the .debug_frame data.
|
||||
const (
|
||||
DW_CFA_nop = 0x0 // No ops
|
||||
DW_CFA_set_loc = 0x01 // op1: address
|
||||
DW_CFA_advance_loc1 = iota // op1: 1-bytes delta
|
||||
DW_CFA_advance_loc2 // op1: 2-byte delta
|
||||
DW_CFA_advance_loc4 // op1: 4-byte delta
|
||||
DW_CFA_offset_extended // op1: ULEB128 register, op2: ULEB128 offset
|
||||
DW_CFA_restore_extended // op1: ULEB128 register
|
||||
DW_CFA_undefined // op1: ULEB128 register
|
||||
DW_CFA_same_value // op1: ULEB128 register
|
||||
DW_CFA_register // op1: ULEB128 register, op2: ULEB128 register
|
||||
DW_CFA_remember_state // No ops
|
||||
DW_CFA_restore_state // No ops
|
||||
DW_CFA_def_cfa // op1: ULEB128 register, op2: ULEB128 offset
|
||||
DW_CFA_def_cfa_register // op1: ULEB128 register
|
||||
DW_CFA_def_cfa_offset // op1: ULEB128 offset
|
||||
DW_CFA_def_cfa_expression // op1: BLOCK
|
||||
DW_CFA_expression // op1: ULEB128 register, op2: BLOCK
|
||||
DW_CFA_offset_extended_sf // op1: ULEB128 register, op2: SLEB128 BLOCK
|
||||
DW_CFA_def_cfa_sf // op1: ULEB128 register, op2: SLEB128 offset
|
||||
DW_CFA_def_cfa_offset_sf // op1: SLEB128 offset
|
||||
DW_CFA_val_offset // op1: ULEB128, op2: ULEB128
|
||||
DW_CFA_val_offset_sf // op1: ULEB128, op2: SLEB128
|
||||
DW_CFA_val_expression // op1: ULEB128, op2: BLOCK
|
||||
DW_CFA_lo_user = 0x1c // op1: BLOCK
|
||||
DW_CFA_hi_user = 0x3f // op1: ULEB128 register, op2: BLOCK
|
||||
DW_CFA_advance_loc = (0x1 << 6) // High 2 bits: 0x1, low 6: delta
|
||||
DW_CFA_offset = (0x2 << 6) // High 2 bits: 0x2, low 6: register
|
||||
DW_CFA_restore = (0x3 << 6) // High 2 bits: 0x3, low 6: register
|
||||
)
|
||||
|
||||
// Rules defined for register values.
|
||||
const (
|
||||
rule_undefined = iota
|
||||
rule_sameval
|
||||
rule_offset
|
||||
rule_valoffset
|
||||
rule_register
|
||||
rule_expression
|
||||
rule_valexpression
|
||||
rule_architectural
|
||||
)
|
||||
|
||||
const low_6_offset = 0x3f
|
||||
|
||||
type instruction func(frame *FrameContext)
|
||||
|
||||
// // Mapping from DWARF opcode to function.
|
||||
var fnlookup = map[byte]instruction{
|
||||
DW_CFA_advance_loc: advanceloc,
|
||||
DW_CFA_offset: offset,
|
||||
DW_CFA_restore: restore,
|
||||
DW_CFA_set_loc: setloc,
|
||||
DW_CFA_advance_loc1: advanceloc1,
|
||||
DW_CFA_advance_loc2: advanceloc2,
|
||||
DW_CFA_advance_loc4: advanceloc4,
|
||||
DW_CFA_offset_extended: offsetextended,
|
||||
DW_CFA_restore_extended: restoreextended,
|
||||
DW_CFA_undefined: undefined,
|
||||
DW_CFA_same_value: samevalue,
|
||||
DW_CFA_register: register,
|
||||
DW_CFA_remember_state: rememberstate,
|
||||
DW_CFA_restore_state: restorestate,
|
||||
DW_CFA_def_cfa: defcfa,
|
||||
DW_CFA_def_cfa_register: defcfaregister,
|
||||
DW_CFA_def_cfa_offset: defcfaoffset,
|
||||
DW_CFA_def_cfa_expression: defcfaexpression,
|
||||
DW_CFA_expression: expression,
|
||||
DW_CFA_offset_extended_sf: offsetextendedsf,
|
||||
DW_CFA_def_cfa_sf: defcfasf,
|
||||
DW_CFA_def_cfa_offset_sf: defcfaoffsetsf,
|
||||
DW_CFA_val_offset: valoffset,
|
||||
DW_CFA_val_offset_sf: valoffsetsf,
|
||||
DW_CFA_val_expression: valexpression,
|
||||
DW_CFA_lo_user: louser,
|
||||
DW_CFA_hi_user: hiuser,
|
||||
}
|
||||
|
||||
func executeCIEInstructions(cie *CommonInformationEntry) *FrameContext {
|
||||
initialInstructions := make([]byte, len(cie.InitialInstructions))
|
||||
copy(initialInstructions, cie.InitialInstructions)
|
||||
frame := &FrameContext{
|
||||
cie: cie,
|
||||
regs: make(map[uint64]DWRule),
|
||||
initialRegs: make(map[uint64]DWRule),
|
||||
prevRegs: make(map[uint64]DWRule),
|
||||
codeAlignment: cie.CodeAlignmentFactor,
|
||||
dataAlignment: cie.DataAlignmentFactor,
|
||||
buf: bytes.NewBuffer(initialInstructions),
|
||||
}
|
||||
|
||||
frame.ExecuteDwarfProgram()
|
||||
return frame
|
||||
}
|
||||
|
||||
// Unwind the stack to find the return address register.
|
||||
func executeDwarfProgramUntilPC(fde *FrameDescriptionEntry, pc uint64) *FrameContext {
|
||||
frame := executeCIEInstructions(fde.CIE)
|
||||
frame.order = fde.order
|
||||
frame.loc = fde.Begin()
|
||||
frame.address = pc
|
||||
fdeInstructions := make([]byte, len(fde.Instructions))
|
||||
copy(fdeInstructions, fde.Instructions)
|
||||
frame.ExecuteUntilPC(fdeInstructions)
|
||||
|
||||
return frame
|
||||
}
|
||||
|
||||
func (frame *FrameContext) ExecuteDwarfProgram() {
|
||||
for frame.buf.Len() > 0 {
|
||||
executeDwarfInstruction(frame)
|
||||
}
|
||||
}
|
||||
|
||||
// Execute dwarf instructions.
|
||||
func (frame *FrameContext) ExecuteUntilPC(instructions []byte) {
|
||||
frame.buf.Truncate(0)
|
||||
frame.buf.Write(instructions)
|
||||
|
||||
// We only need to execute the instructions until
|
||||
// ctx.loc > ctx.addess (which is the address we
|
||||
// are currently at in the traced process).
|
||||
for frame.address >= frame.loc && frame.buf.Len() > 0 {
|
||||
executeDwarfInstruction(frame)
|
||||
}
|
||||
}
|
||||
|
||||
func executeDwarfInstruction(frame *FrameContext) {
|
||||
instruction, err := frame.buf.ReadByte()
|
||||
if err != nil {
|
||||
panic("Could not read from instruction buffer")
|
||||
}
|
||||
|
||||
if instruction == DW_CFA_nop {
|
||||
return
|
||||
}
|
||||
|
||||
fn := lookupFunc(instruction, frame.buf)
|
||||
|
||||
fn(frame)
|
||||
}
|
||||
|
||||
func lookupFunc(instruction byte, buf *bytes.Buffer) instruction {
|
||||
const high_2_bits = 0xc0
|
||||
var restore bool
|
||||
|
||||
// Special case the 3 opcodes that have their argument encoded in the opcode itself.
|
||||
switch instruction & high_2_bits {
|
||||
case DW_CFA_advance_loc:
|
||||
instruction = DW_CFA_advance_loc
|
||||
restore = true
|
||||
|
||||
case DW_CFA_offset:
|
||||
instruction = DW_CFA_offset
|
||||
restore = true
|
||||
|
||||
case DW_CFA_restore:
|
||||
instruction = DW_CFA_restore
|
||||
restore = true
|
||||
}
|
||||
|
||||
if restore {
|
||||
// Restore the last byte as it actually contains the argument for the opcode.
|
||||
err := buf.UnreadByte()
|
||||
if err != nil {
|
||||
panic("Could not unread byte")
|
||||
}
|
||||
}
|
||||
|
||||
fn, ok := fnlookup[instruction]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Encountered an unexpected DWARF CFA opcode: %#v", instruction))
|
||||
}
|
||||
|
||||
return fn
|
||||
}
|
||||
|
||||
func advanceloc(frame *FrameContext) {
|
||||
b, err := frame.buf.ReadByte()
|
||||
if err != nil {
|
||||
panic("Could not read byte")
|
||||
}
|
||||
|
||||
delta := b & low_6_offset
|
||||
frame.loc += uint64(delta) * frame.codeAlignment
|
||||
}
|
||||
|
||||
func advanceloc1(frame *FrameContext) {
|
||||
delta, err := frame.buf.ReadByte()
|
||||
if err != nil {
|
||||
panic("Could not read byte")
|
||||
}
|
||||
|
||||
frame.loc += uint64(delta) * frame.codeAlignment
|
||||
}
|
||||
|
||||
func advanceloc2(frame *FrameContext) {
|
||||
var delta uint16
|
||||
binary.Read(frame.buf, frame.order, &delta)
|
||||
|
||||
frame.loc += uint64(delta) * frame.codeAlignment
|
||||
}
|
||||
|
||||
func advanceloc4(frame *FrameContext) {
|
||||
var delta uint32
|
||||
binary.Read(frame.buf, frame.order, &delta)
|
||||
|
||||
frame.loc += uint64(delta) * frame.codeAlignment
|
||||
}
|
||||
|
||||
func offset(frame *FrameContext) {
|
||||
b, err := frame.buf.ReadByte()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
var (
|
||||
reg = b & low_6_offset
|
||||
offset, _ = util.DecodeULEB128(frame.buf)
|
||||
)
|
||||
|
||||
frame.regs[uint64(reg)] = DWRule{offset: int64(offset) * frame.dataAlignment, rule: rule_offset}
|
||||
}
|
||||
|
||||
func restore(frame *FrameContext) {
|
||||
b, err := frame.buf.ReadByte()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
reg := uint64(b & low_6_offset)
|
||||
oldrule, ok := frame.initialRegs[reg]
|
||||
if ok {
|
||||
frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset}
|
||||
} else {
|
||||
frame.regs[reg] = DWRule{rule: rule_undefined}
|
||||
}
|
||||
}
|
||||
|
||||
func setloc(frame *FrameContext) {
|
||||
var loc uint64
|
||||
binary.Read(frame.buf, frame.order, &loc)
|
||||
|
||||
frame.loc = loc
|
||||
}
|
||||
|
||||
func offsetextended(frame *FrameContext) {
|
||||
var (
|
||||
reg, _ = util.DecodeULEB128(frame.buf)
|
||||
offset, _ = util.DecodeULEB128(frame.buf)
|
||||
)
|
||||
|
||||
frame.regs[reg] = DWRule{offset: int64(offset) * frame.dataAlignment, rule: rule_offset}
|
||||
}
|
||||
|
||||
func undefined(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.regs[reg] = DWRule{rule: rule_undefined}
|
||||
}
|
||||
|
||||
func samevalue(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.regs[reg] = DWRule{rule: rule_sameval}
|
||||
}
|
||||
|
||||
func register(frame *FrameContext) {
|
||||
reg1, _ := util.DecodeULEB128(frame.buf)
|
||||
reg2, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.regs[reg1] = DWRule{newreg: reg2, rule: rule_register}
|
||||
}
|
||||
|
||||
func rememberstate(frame *FrameContext) {
|
||||
frame.prevRegs = frame.regs
|
||||
}
|
||||
|
||||
func restorestate(frame *FrameContext) {
|
||||
frame.regs = frame.prevRegs
|
||||
}
|
||||
|
||||
func restoreextended(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
|
||||
oldrule, ok := frame.initialRegs[reg]
|
||||
if ok {
|
||||
frame.regs[reg] = DWRule{offset: oldrule.offset, rule: rule_offset}
|
||||
} else {
|
||||
frame.regs[reg] = DWRule{rule: rule_undefined}
|
||||
}
|
||||
}
|
||||
|
||||
func defcfa(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
offset, _ := util.DecodeULEB128(frame.buf)
|
||||
|
||||
frame.cfa.register = reg
|
||||
frame.cfa.offset = int64(offset)
|
||||
}
|
||||
|
||||
func defcfaregister(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.cfa.register = reg
|
||||
}
|
||||
|
||||
func defcfaoffset(frame *FrameContext) {
|
||||
offset, _ := util.DecodeULEB128(frame.buf)
|
||||
frame.cfa.offset = int64(offset)
|
||||
}
|
||||
|
||||
func defcfasf(frame *FrameContext) {
|
||||
reg, _ := util.DecodeULEB128(frame.buf)
|
||||
offset, _ := util.DecodeSLEB128(frame.buf)
|
||||
|
||||
frame.cfa.register = reg
|
||||
frame.cfa.offset = offset * frame.dataAlignment
|
||||
}
|
||||
|
||||
func defcfaoffsetsf(frame *FrameContext) {
|
||||
offset, _ := util.DecodeSLEB128(frame.buf)
|
||||
offset *= frame.dataAlignment
|
||||
frame.cfa.offset = offset
|
||||
}
|
||||
|
||||
func defcfaexpression(frame *FrameContext) {
|
||||
var (
|
||||
l, _ = util.DecodeULEB128(frame.buf)
|
||||
expr = frame.buf.Next(int(l))
|
||||
)
|
||||
|
||||
frame.cfa.expression = expr
|
||||
frame.cfa.rule = rule_expression
|
||||
}
|
||||
|
||||
func expression(frame *FrameContext) {
|
||||
var (
|
||||
reg, _ = util.DecodeULEB128(frame.buf)
|
||||
l, _ = util.DecodeULEB128(frame.buf)
|
||||
expr = frame.buf.Next(int(l))
|
||||
)
|
||||
|
||||
frame.regs[reg] = DWRule{rule: rule_expression, expression: expr}
|
||||
}
|
||||
|
||||
func offsetextendedsf(frame *FrameContext) {
|
||||
var (
|
||||
reg, _ = util.DecodeULEB128(frame.buf)
|
||||
offset, _ = util.DecodeSLEB128(frame.buf)
|
||||
)
|
||||
|
||||
frame.regs[reg] = DWRule{offset: offset * frame.dataAlignment, rule: rule_offset}
|
||||
}
|
||||
|
||||
func valoffset(frame *FrameContext) {
|
||||
var (
|
||||
reg, _ = util.DecodeULEB128(frame.buf)
|
||||
offset, _ = util.DecodeULEB128(frame.buf)
|
||||
)
|
||||
|
||||
frame.regs[reg] = DWRule{offset: int64(offset), rule: rule_valoffset}
|
||||
}
|
||||
|
||||
func valoffsetsf(frame *FrameContext) {
|
||||
var (
|
||||
reg, _ = util.DecodeULEB128(frame.buf)
|
||||
offset, _ = util.DecodeSLEB128(frame.buf)
|
||||
)
|
||||
|
||||
frame.regs[reg] = DWRule{offset: offset * frame.dataAlignment, rule: rule_valoffset}
|
||||
}
|
||||
|
||||
func valexpression(frame *FrameContext) {
|
||||
var (
|
||||
reg, _ = util.DecodeULEB128(frame.buf)
|
||||
l, _ = util.DecodeULEB128(frame.buf)
|
||||
expr = frame.buf.Next(int(l))
|
||||
)
|
||||
|
||||
frame.regs[reg] = DWRule{rule: rule_valexpression, expression: expr}
|
||||
}
|
||||
|
||||
func louser(frame *FrameContext) {
|
||||
frame.buf.Next(1)
|
||||
}
|
||||
|
||||
func hiuser(frame *FrameContext) {
|
||||
frame.buf.Next(1)
|
||||
}
|
122
vendor/github.com/derekparker/delve/pkg/dwarf/line/line_parser.go
generated
vendored
Normal file
122
vendor/github.com/derekparker/delve/pkg/dwarf/line/line_parser.go
generated
vendored
Normal file
@ -0,0 +1,122 @@
|
||||
package line
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
type DebugLinePrologue struct {
|
||||
UnitLength uint32
|
||||
Version uint16
|
||||
Length uint32
|
||||
MinInstrLength uint8
|
||||
InitialIsStmt uint8
|
||||
LineBase int8
|
||||
LineRange uint8
|
||||
OpcodeBase uint8
|
||||
StdOpLengths []uint8
|
||||
}
|
||||
|
||||
type DebugLineInfo struct {
|
||||
Prologue *DebugLinePrologue
|
||||
IncludeDirs []string
|
||||
FileNames []*FileEntry
|
||||
Instructions []byte
|
||||
Lookup map[string]*FileEntry
|
||||
}
|
||||
|
||||
type FileEntry struct {
|
||||
Name string
|
||||
DirIdx uint64
|
||||
LastModTime uint64
|
||||
Length uint64
|
||||
}
|
||||
|
||||
type DebugLines []*DebugLineInfo
|
||||
|
||||
func (d *DebugLines) GetLineInfo(name string) *DebugLineInfo {
|
||||
// Find in which table file exists and return it.
|
||||
for _, l := range *d {
|
||||
if _, ok := l.Lookup[name]; ok {
|
||||
return l
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func Parse(data []byte) DebugLines {
|
||||
var (
|
||||
lines = make(DebugLines, 0)
|
||||
buf = bytes.NewBuffer(data)
|
||||
)
|
||||
|
||||
// We have to parse multiple file name tables here.
|
||||
for buf.Len() > 0 {
|
||||
dbl := new(DebugLineInfo)
|
||||
dbl.Lookup = make(map[string]*FileEntry)
|
||||
|
||||
parseDebugLinePrologue(dbl, buf)
|
||||
parseIncludeDirs(dbl, buf)
|
||||
parseFileEntries(dbl, buf)
|
||||
|
||||
// Instructions size calculation breakdown:
|
||||
// - dbl.Prologue.UnitLength is the length of the entire unit, not including the 4 bytes to represent that length.
|
||||
// - dbl.Prologue.Length is the length of the prologue not including unit length, version or prologue length itself.
|
||||
// - So you have UnitLength - PrologueLength - (version_length_bytes(2) + prologue_length_bytes(4)).
|
||||
dbl.Instructions = buf.Next(int(dbl.Prologue.UnitLength - dbl.Prologue.Length - 6))
|
||||
|
||||
lines = append(lines, dbl)
|
||||
}
|
||||
|
||||
return lines
|
||||
}
|
||||
|
||||
func parseDebugLinePrologue(dbl *DebugLineInfo, buf *bytes.Buffer) {
|
||||
p := new(DebugLinePrologue)
|
||||
|
||||
p.UnitLength = binary.LittleEndian.Uint32(buf.Next(4))
|
||||
p.Version = binary.LittleEndian.Uint16(buf.Next(2))
|
||||
p.Length = binary.LittleEndian.Uint32(buf.Next(4))
|
||||
p.MinInstrLength = uint8(buf.Next(1)[0])
|
||||
p.InitialIsStmt = uint8(buf.Next(1)[0])
|
||||
p.LineBase = int8(buf.Next(1)[0])
|
||||
p.LineRange = uint8(buf.Next(1)[0])
|
||||
p.OpcodeBase = uint8(buf.Next(1)[0])
|
||||
|
||||
p.StdOpLengths = make([]uint8, p.OpcodeBase-1)
|
||||
binary.Read(buf, binary.LittleEndian, &p.StdOpLengths)
|
||||
|
||||
dbl.Prologue = p
|
||||
}
|
||||
|
||||
func parseIncludeDirs(info *DebugLineInfo, buf *bytes.Buffer) {
|
||||
for {
|
||||
str, _ := util.ParseString(buf)
|
||||
if str == "" {
|
||||
break
|
||||
}
|
||||
|
||||
info.IncludeDirs = append(info.IncludeDirs, str)
|
||||
}
|
||||
}
|
||||
|
||||
func parseFileEntries(info *DebugLineInfo, buf *bytes.Buffer) {
|
||||
for {
|
||||
entry := new(FileEntry)
|
||||
|
||||
name, _ := util.ParseString(buf)
|
||||
if name == "" {
|
||||
break
|
||||
}
|
||||
|
||||
entry.Name = name
|
||||
entry.DirIdx, _ = util.DecodeULEB128(buf)
|
||||
entry.LastModTime, _ = util.DecodeULEB128(buf)
|
||||
entry.Length, _ = util.DecodeULEB128(buf)
|
||||
|
||||
info.FileNames = append(info.FileNames, entry)
|
||||
info.Lookup[name] = entry
|
||||
}
|
||||
}
|
267
vendor/github.com/derekparker/delve/pkg/dwarf/line/state_machine.go
generated
vendored
Normal file
267
vendor/github.com/derekparker/delve/pkg/dwarf/line/state_machine.go
generated
vendored
Normal file
@ -0,0 +1,267 @@
|
||||
package line
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
type Location struct {
|
||||
File string
|
||||
Line int
|
||||
Address uint64
|
||||
Delta int
|
||||
}
|
||||
|
||||
type StateMachine struct {
|
||||
dbl *DebugLineInfo
|
||||
file string
|
||||
line int
|
||||
address uint64
|
||||
column uint
|
||||
isStmt bool
|
||||
basicBlock bool
|
||||
endSeq bool
|
||||
lastWasStandard bool
|
||||
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)
|
||||
|
||||
// Special opcodes
|
||||
const (
|
||||
DW_LNS_copy = 1
|
||||
DW_LNS_advance_pc = 2
|
||||
DW_LNS_advance_line = 3
|
||||
DW_LNS_set_file = 4
|
||||
DW_LNS_set_column = 5
|
||||
DW_LNS_negate_stmt = 6
|
||||
DW_LNS_set_basic_block = 7
|
||||
DW_LNS_const_add_pc = 8
|
||||
DW_LNS_fixed_advance_pc = 9
|
||||
)
|
||||
|
||||
// Extended opcodes
|
||||
const (
|
||||
DW_LINE_end_sequence = 1
|
||||
DW_LINE_set_address = 2
|
||||
DW_LINE_define_file = 3
|
||||
)
|
||||
|
||||
var standardopcodes = map[byte]opcodefn{
|
||||
DW_LNS_copy: copyfn,
|
||||
DW_LNS_advance_pc: advancepc,
|
||||
DW_LNS_advance_line: advanceline,
|
||||
DW_LNS_set_file: setfile,
|
||||
DW_LNS_set_column: setcolumn,
|
||||
DW_LNS_negate_stmt: negatestmt,
|
||||
DW_LNS_set_basic_block: setbasicblock,
|
||||
DW_LNS_const_add_pc: constaddpc,
|
||||
DW_LNS_fixed_advance_pc: fixedadvancepc,
|
||||
}
|
||||
|
||||
var extendedopcodes = map[byte]opcodefn{
|
||||
DW_LINE_end_sequence: endsequence,
|
||||
DW_LINE_set_address: setaddress,
|
||||
DW_LINE_define_file: definefile,
|
||||
}
|
||||
|
||||
func newStateMachine(dbl *DebugLineInfo) *StateMachine {
|
||||
return &StateMachine{dbl: dbl, file: dbl.FileNames[0].Name, line: 1}
|
||||
}
|
||||
|
||||
// Returns all PCs for a given file/line. Useful for loops where the 'for' line
|
||||
// could be split amongst 2 PCs.
|
||||
func (dbl *DebugLines) AllPCsForFileLine(f string, l int) (pcs []uint64) {
|
||||
var (
|
||||
foundFile bool
|
||||
lastAddr uint64
|
||||
lineInfo = dbl.GetLineInfo(f)
|
||||
sm = newStateMachine(lineInfo)
|
||||
buf = bytes.NewBuffer(lineInfo.Instructions)
|
||||
)
|
||||
|
||||
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
|
||||
findAndExecOpcode(sm, buf, b)
|
||||
if foundFile && sm.file != f {
|
||||
return
|
||||
}
|
||||
if sm.line == l && sm.file == f && sm.address != lastAddr {
|
||||
foundFile = true
|
||||
if sm.valid {
|
||||
pcs = append(pcs, sm.address)
|
||||
}
|
||||
line := sm.line
|
||||
// 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,
|
||||
// and try to find the line we care about again.
|
||||
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
|
||||
findAndExecOpcode(sm, buf, b)
|
||||
if line < sm.line {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var NoSourceError = errors.New("no source available")
|
||||
|
||||
func (dbl *DebugLines) AllPCsBetween(begin, end uint64, filename string) ([]uint64, error) {
|
||||
lineInfo := dbl.GetLineInfo(filename)
|
||||
if lineInfo == nil {
|
||||
return nil, NoSourceError
|
||||
}
|
||||
var (
|
||||
pcs []uint64
|
||||
lastaddr uint64
|
||||
sm = newStateMachine(lineInfo)
|
||||
buf = bytes.NewBuffer(lineInfo.Instructions)
|
||||
)
|
||||
|
||||
for b, err := buf.ReadByte(); err == nil; b, err = buf.ReadByte() {
|
||||
findAndExecOpcode(sm, buf, b)
|
||||
if !sm.valid {
|
||||
continue
|
||||
}
|
||||
if sm.address > end {
|
||||
break
|
||||
}
|
||||
if sm.address >= begin && sm.address > lastaddr {
|
||||
lastaddr = sm.address
|
||||
pcs = append(pcs, sm.address)
|
||||
}
|
||||
}
|
||||
return pcs, nil
|
||||
}
|
||||
|
||||
func findAndExecOpcode(sm *StateMachine, buf *bytes.Buffer, b byte) {
|
||||
switch {
|
||||
case b == 0:
|
||||
execExtendedOpcode(sm, b, buf)
|
||||
case b < sm.dbl.Prologue.OpcodeBase:
|
||||
execStandardOpcode(sm, b, buf)
|
||||
default:
|
||||
execSpecialOpcode(sm, b)
|
||||
}
|
||||
}
|
||||
|
||||
func execSpecialOpcode(sm *StateMachine, instr byte) {
|
||||
var (
|
||||
opcode = uint8(instr)
|
||||
decoded = opcode - sm.dbl.Prologue.OpcodeBase
|
||||
)
|
||||
|
||||
if sm.dbl.Prologue.InitialIsStmt == uint8(1) {
|
||||
sm.isStmt = true
|
||||
}
|
||||
|
||||
sm.lastDelta = int(sm.dbl.Prologue.LineBase + int8(decoded%sm.dbl.Prologue.LineRange))
|
||||
sm.line += sm.lastDelta
|
||||
sm.address += uint64(decoded/sm.dbl.Prologue.LineRange) * uint64(sm.dbl.Prologue.MinInstrLength)
|
||||
sm.basicBlock = false
|
||||
sm.lastWasStandard = false
|
||||
sm.valid = true
|
||||
}
|
||||
|
||||
func execExtendedOpcode(sm *StateMachine, instr byte, buf *bytes.Buffer) {
|
||||
_, _ = util.DecodeULEB128(buf)
|
||||
b, _ := buf.ReadByte()
|
||||
fn, ok := extendedopcodes[b]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Encountered unknown extended opcode %#v\n", b))
|
||||
}
|
||||
sm.lastWasStandard = false
|
||||
sm.valid = false
|
||||
|
||||
fn(sm, buf)
|
||||
}
|
||||
|
||||
func execStandardOpcode(sm *StateMachine, instr byte, buf *bytes.Buffer) {
|
||||
fn, ok := standardopcodes[instr]
|
||||
if !ok {
|
||||
panic(fmt.Sprintf("Encountered unknown standard opcode %#v\n", instr))
|
||||
}
|
||||
sm.lastWasStandard = true
|
||||
sm.valid = false
|
||||
|
||||
fn(sm, buf)
|
||||
}
|
||||
|
||||
func copyfn(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.basicBlock = false
|
||||
sm.valid = true
|
||||
}
|
||||
|
||||
func advancepc(sm *StateMachine, buf *bytes.Buffer) {
|
||||
addr, _ := util.DecodeULEB128(buf)
|
||||
sm.address += addr * uint64(sm.dbl.Prologue.MinInstrLength)
|
||||
}
|
||||
|
||||
func advanceline(sm *StateMachine, buf *bytes.Buffer) {
|
||||
line, _ := util.DecodeSLEB128(buf)
|
||||
sm.line += int(line)
|
||||
sm.lastDelta = int(line)
|
||||
}
|
||||
|
||||
func setfile(sm *StateMachine, buf *bytes.Buffer) {
|
||||
i, _ := util.DecodeULEB128(buf)
|
||||
sm.file = sm.dbl.FileNames[i-1].Name
|
||||
}
|
||||
|
||||
func setcolumn(sm *StateMachine, buf *bytes.Buffer) {
|
||||
c, _ := util.DecodeULEB128(buf)
|
||||
sm.column = uint(c)
|
||||
}
|
||||
|
||||
func negatestmt(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.isStmt = !sm.isStmt
|
||||
}
|
||||
|
||||
func setbasicblock(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.basicBlock = true
|
||||
}
|
||||
|
||||
func constaddpc(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.address += uint64((255-sm.dbl.Prologue.OpcodeBase)/sm.dbl.Prologue.LineRange) * uint64(sm.dbl.Prologue.MinInstrLength)
|
||||
}
|
||||
|
||||
func fixedadvancepc(sm *StateMachine, buf *bytes.Buffer) {
|
||||
var operand uint16
|
||||
binary.Read(buf, binary.LittleEndian, &operand)
|
||||
|
||||
sm.address += uint64(operand)
|
||||
}
|
||||
|
||||
func endsequence(sm *StateMachine, buf *bytes.Buffer) {
|
||||
sm.endSeq = true
|
||||
sm.valid = true
|
||||
}
|
||||
|
||||
func setaddress(sm *StateMachine, buf *bytes.Buffer) {
|
||||
var addr uint64
|
||||
|
||||
binary.Read(buf, binary.LittleEndian, &addr)
|
||||
|
||||
sm.address = addr
|
||||
}
|
||||
|
||||
func definefile(sm *StateMachine, buf *bytes.Buffer) {
|
||||
var (
|
||||
_, _ = util.ParseString(buf)
|
||||
_, _ = util.DecodeULEB128(buf)
|
||||
_, _ = util.DecodeULEB128(buf)
|
||||
_, _ = util.DecodeULEB128(buf)
|
||||
)
|
||||
|
||||
// Don't do anything here yet.
|
||||
}
|
84
vendor/github.com/derekparker/delve/pkg/dwarf/op/op.go
generated
vendored
Normal file
84
vendor/github.com/derekparker/delve/pkg/dwarf/op/op.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
package op
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/util"
|
||||
)
|
||||
|
||||
const (
|
||||
DW_OP_addr = 0x3
|
||||
DW_OP_call_frame_cfa = 0x9c
|
||||
DW_OP_plus = 0x22
|
||||
DW_OP_consts = 0x11
|
||||
DW_OP_plus_uconsts = 0x23
|
||||
)
|
||||
|
||||
type stackfn func(*bytes.Buffer, []int64, int64) ([]int64, error)
|
||||
|
||||
var oplut = map[byte]stackfn{
|
||||
DW_OP_call_frame_cfa: callframecfa,
|
||||
DW_OP_plus: plus,
|
||||
DW_OP_consts: consts,
|
||||
DW_OP_addr: addr,
|
||||
DW_OP_plus_uconsts: plusuconsts,
|
||||
}
|
||||
|
||||
func ExecuteStackProgram(cfa int64, instructions []byte) (int64, error) {
|
||||
stack := make([]int64, 0, 3)
|
||||
buf := bytes.NewBuffer(instructions)
|
||||
|
||||
for opcode, err := buf.ReadByte(); err == nil; opcode, err = buf.ReadByte() {
|
||||
fn, ok := oplut[opcode]
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("invalid instruction %#v", opcode)
|
||||
}
|
||||
|
||||
stack, err = fn(buf, stack, cfa)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
}
|
||||
|
||||
if len(stack) == 0 {
|
||||
return 0, errors.New("empty OP stack")
|
||||
}
|
||||
|
||||
return stack[len(stack)-1], nil
|
||||
}
|
||||
|
||||
func callframecfa(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
|
||||
if cfa == 0 {
|
||||
return stack, fmt.Errorf("Could not retrieve CFA for current PC")
|
||||
}
|
||||
return append(stack, int64(cfa)), nil
|
||||
}
|
||||
|
||||
func addr(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
|
||||
return append(stack, int64(binary.LittleEndian.Uint64(buf.Next(8)))), nil
|
||||
}
|
||||
|
||||
func plus(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
|
||||
var (
|
||||
slen = len(stack)
|
||||
digits = stack[slen-2 : slen]
|
||||
st = stack[:slen-2]
|
||||
)
|
||||
|
||||
return append(st, digits[0]+digits[1]), nil
|
||||
}
|
||||
|
||||
func plusuconsts(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
|
||||
slen := len(stack)
|
||||
num, _ := util.DecodeULEB128(buf)
|
||||
stack[slen-1] = stack[slen-1] + int64(num)
|
||||
return stack, nil
|
||||
}
|
||||
|
||||
func consts(buf *bytes.Buffer, stack []int64, cfa int64) ([]int64, error) {
|
||||
num, _ := util.DecodeSLEB128(buf)
|
||||
return append(stack, num), nil
|
||||
}
|
345
vendor/github.com/derekparker/delve/pkg/dwarf/reader/reader.go
generated
vendored
Executable file
345
vendor/github.com/derekparker/delve/pkg/dwarf/reader/reader.go
generated
vendored
Executable file
@ -0,0 +1,345 @@
|
||||
package reader
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/op"
|
||||
"golang.org/x/debug/dwarf"
|
||||
)
|
||||
|
||||
type Reader struct {
|
||||
*dwarf.Reader
|
||||
depth int
|
||||
}
|
||||
|
||||
// New returns a reader for the specified dwarf data.
|
||||
func New(data *dwarf.Data) *Reader {
|
||||
return &Reader{data.Reader(), 0}
|
||||
}
|
||||
|
||||
// Seek moves the reader to an arbitrary offset.
|
||||
func (reader *Reader) Seek(off dwarf.Offset) {
|
||||
reader.depth = 0
|
||||
reader.Reader.Seek(off)
|
||||
}
|
||||
|
||||
// SeekToEntry moves the reader to an arbitrary entry.
|
||||
func (reader *Reader) SeekToEntry(entry *dwarf.Entry) error {
|
||||
reader.Seek(entry.Offset)
|
||||
// Consume the current entry so .Next works as intended
|
||||
_, err := reader.Next()
|
||||
return err
|
||||
}
|
||||
|
||||
// SeekToFunctionEntry moves the reader to the function that includes the
|
||||
// specified program counter.
|
||||
func (reader *Reader) SeekToFunction(pc uint64) (*dwarf.Entry, error) {
|
||||
reader.Seek(0)
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if entry.Tag != dwarf.TagSubprogram {
|
||||
continue
|
||||
}
|
||||
|
||||
lowpc, ok := entry.Val(dwarf.AttrLowpc).(uint64)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
highpc, ok := entry.Val(dwarf.AttrHighpc).(uint64)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if lowpc <= pc && highpc > pc {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, fmt.Errorf("unable to find function context")
|
||||
}
|
||||
|
||||
// Returns the address for the named entry.
|
||||
func (reader *Reader) AddrFor(name string) (uint64, error) {
|
||||
entry, err := reader.FindEntryNamed(name, false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
|
||||
if !ok {
|
||||
return 0, fmt.Errorf("type assertion failed")
|
||||
}
|
||||
addr, err := op.ExecuteStackProgram(0, instructions)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return uint64(addr), nil
|
||||
}
|
||||
|
||||
// Returns the address for the named struct member. Expects the reader to be at the parent entry
|
||||
// or one of the parents children, thus does not seek to parent by itself.
|
||||
func (reader *Reader) AddrForMember(member string, initialInstructions []byte) (uint64, error) {
|
||||
for {
|
||||
entry, err := reader.NextMemberVariable()
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if entry == nil {
|
||||
return 0, fmt.Errorf("nil entry for member named %s", member)
|
||||
}
|
||||
name, ok := entry.Val(dwarf.AttrName).(string)
|
||||
if !ok || name != member {
|
||||
continue
|
||||
}
|
||||
instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
addr, err := op.ExecuteStackProgram(0, append(initialInstructions, instructions...))
|
||||
return uint64(addr), err
|
||||
}
|
||||
}
|
||||
|
||||
var TypeNotFoundErr = errors.New("no type entry found, use 'types' for a list of valid types")
|
||||
|
||||
// SeekToType moves the reader to the type specified by the entry,
|
||||
// optionally resolving typedefs and pointer types. If the reader is set
|
||||
// to a struct type the NextMemberVariable call can be used to walk all member data.
|
||||
func (reader *Reader) SeekToType(entry *dwarf.Entry, resolveTypedefs bool, resolvePointerTypes bool) (*dwarf.Entry, error) {
|
||||
offset, ok := entry.Val(dwarf.AttrType).(dwarf.Offset)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("entry does not have a type attribute")
|
||||
}
|
||||
|
||||
// Seek to the first type offset
|
||||
reader.Seek(offset)
|
||||
|
||||
// Walk the types to the base
|
||||
for typeEntry, err := reader.Next(); typeEntry != nil; typeEntry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if typeEntry.Tag == dwarf.TagTypedef && !resolveTypedefs {
|
||||
return typeEntry, nil
|
||||
}
|
||||
|
||||
if typeEntry.Tag == dwarf.TagPointerType && !resolvePointerTypes {
|
||||
return typeEntry, nil
|
||||
}
|
||||
|
||||
offset, ok = typeEntry.Val(dwarf.AttrType).(dwarf.Offset)
|
||||
if !ok {
|
||||
return typeEntry, nil
|
||||
}
|
||||
|
||||
reader.Seek(offset)
|
||||
}
|
||||
|
||||
return nil, TypeNotFoundErr
|
||||
}
|
||||
|
||||
func (reader *Reader) NextType() (*dwarf.Entry, error) {
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
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:
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// SeekToTypeNamed moves the reader to the type specified by the name.
|
||||
// If the reader is set to a struct type the NextMemberVariable call
|
||||
// can be used to walk all member data.
|
||||
func (reader *Reader) SeekToTypeNamed(name string) (*dwarf.Entry, error) {
|
||||
// Walk the types to the base
|
||||
for entry, err := reader.NextType(); entry != nil; entry, err = reader.NextType() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
n, ok := entry.Val(dwarf.AttrName).(string)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
|
||||
if n == name {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, TypeNotFoundErr
|
||||
}
|
||||
|
||||
// Finds the entry for 'name'.
|
||||
func (reader *Reader) FindEntryNamed(name string, member bool) (*dwarf.Entry, error) {
|
||||
depth := 1
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if entry.Children {
|
||||
depth++
|
||||
}
|
||||
|
||||
if entry.Tag == 0 {
|
||||
depth--
|
||||
if depth <= 0 {
|
||||
return nil, fmt.Errorf("could not find symbol value for %s", name)
|
||||
}
|
||||
}
|
||||
|
||||
if member {
|
||||
if entry.Tag != dwarf.TagMember {
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
if entry.Tag != dwarf.TagVariable && entry.Tag != dwarf.TagFormalParameter && entry.Tag != dwarf.TagStructType {
|
||||
continue
|
||||
}
|
||||
}
|
||||
|
||||
n, ok := entry.Val(dwarf.AttrName).(string)
|
||||
if !ok || n != name {
|
||||
continue
|
||||
}
|
||||
return entry, nil
|
||||
}
|
||||
return nil, fmt.Errorf("could not find symbol value for %s", name)
|
||||
}
|
||||
|
||||
func (reader *Reader) InstructionsForEntryNamed(name string, member bool) ([]byte, error) {
|
||||
entry, err := reader.FindEntryNamed(name, member)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
var attr dwarf.Attr
|
||||
if member {
|
||||
attr = dwarf.AttrDataMemberLoc
|
||||
} else {
|
||||
attr = dwarf.AttrLocation
|
||||
}
|
||||
instr, ok := entry.Val(attr).([]byte)
|
||||
if !ok {
|
||||
return nil, errors.New("invalid typecast for Dwarf instructions")
|
||||
}
|
||||
return instr, nil
|
||||
}
|
||||
|
||||
func (reader *Reader) InstructionsForEntry(entry *dwarf.Entry) ([]byte, error) {
|
||||
if entry.Tag == dwarf.TagMember {
|
||||
instructions, ok := entry.Val(dwarf.AttrDataMemberLoc).([]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("member data has no data member location attribute")
|
||||
}
|
||||
// clone slice to prevent stomping on the dwarf data
|
||||
return append([]byte{}, instructions...), nil
|
||||
}
|
||||
|
||||
// non-member
|
||||
instructions, ok := entry.Val(dwarf.AttrLocation).([]byte)
|
||||
if !ok {
|
||||
return nil, fmt.Errorf("entry has no location attribute")
|
||||
}
|
||||
|
||||
// clone slice to prevent stomping on the dwarf data
|
||||
return append([]byte{}, instructions...), nil
|
||||
}
|
||||
|
||||
// NextScopeVariable moves the reader to the next debug entry that describes a local variable and returns the entry.
|
||||
func (reader *Reader) NextScopeVariable() (*dwarf.Entry, error) {
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// All scope variables will be at the same depth
|
||||
reader.SkipChildren()
|
||||
|
||||
// End of the current depth
|
||||
if entry.Tag == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if entry.Tag == dwarf.TagVariable || entry.Tag == dwarf.TagFormalParameter {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
// No more items
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NextMememberVariable moves the reader to the next debug entry that describes a member variable and returns the entry.
|
||||
func (reader *Reader) NextMemberVariable() (*dwarf.Entry, error) {
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// All member variables will be at the same depth
|
||||
reader.SkipChildren()
|
||||
|
||||
// End of the current depth
|
||||
if entry.Tag == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
if entry.Tag == dwarf.TagMember {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
// No more items
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// NextPackageVariable moves the reader to the next debug entry that describes a package variable.
|
||||
// Any TagVariable entry that is not inside a sub prgram entry and is marked external is considered a package variable.
|
||||
func (reader *Reader) NextPackageVariable() (*dwarf.Entry, error) {
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if entry.Tag == dwarf.TagVariable {
|
||||
ext, ok := entry.Val(dwarf.AttrExternal).(bool)
|
||||
if ok && ext {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
// Ignore everything inside sub programs
|
||||
if entry.Tag == dwarf.TagSubprogram {
|
||||
reader.SkipChildren()
|
||||
}
|
||||
}
|
||||
|
||||
// No more items
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
func (reader *Reader) NextCompileUnit() (*dwarf.Entry, error) {
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if entry.Tag == dwarf.TagCompileUnit {
|
||||
return entry, nil
|
||||
}
|
||||
}
|
||||
|
||||
return nil, nil
|
||||
}
|
81
vendor/github.com/derekparker/delve/pkg/dwarf/util/util.go
generated
vendored
Normal file
81
vendor/github.com/derekparker/delve/pkg/dwarf/util/util.go
generated
vendored
Normal file
@ -0,0 +1,81 @@
|
||||
package util
|
||||
|
||||
import "bytes"
|
||||
|
||||
// DecodeULEB128 decodes an unsigned Little Endian Base 128
|
||||
// represented number.
|
||||
func DecodeULEB128(buf *bytes.Buffer) (uint64, uint32) {
|
||||
var (
|
||||
result uint64
|
||||
shift uint64
|
||||
length uint32
|
||||
)
|
||||
|
||||
if buf.Len() == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
for {
|
||||
b, err := buf.ReadByte()
|
||||
if err != nil {
|
||||
panic("Could not parse ULEB128 value")
|
||||
}
|
||||
length++
|
||||
|
||||
result |= uint64((uint(b) & 0x7f) << shift)
|
||||
|
||||
// If high order bit is 1.
|
||||
if b&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
|
||||
shift += 7
|
||||
}
|
||||
|
||||
return result, length
|
||||
}
|
||||
|
||||
// DecodeSLEB128 decodes a signed Little Endian Base 128
|
||||
// represented number.
|
||||
func DecodeSLEB128(buf *bytes.Buffer) (int64, uint32) {
|
||||
var (
|
||||
b byte
|
||||
err error
|
||||
result int64
|
||||
shift uint64
|
||||
length uint32
|
||||
)
|
||||
|
||||
if buf.Len() == 0 {
|
||||
return 0, 0
|
||||
}
|
||||
|
||||
for {
|
||||
b, err = buf.ReadByte()
|
||||
if err != nil {
|
||||
panic("Could not parse SLEB128 value")
|
||||
}
|
||||
length++
|
||||
|
||||
result |= int64((int64(b) & 0x7f) << shift)
|
||||
shift += 7
|
||||
if b&0x80 == 0 {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if (shift < 8*uint64(length)) && (b&0x40 > 0) {
|
||||
result |= -(1 << shift)
|
||||
}
|
||||
|
||||
return result, length
|
||||
}
|
||||
|
||||
func ParseString(data *bytes.Buffer) (string, uint32) {
|
||||
str, err := data.ReadString(0x0)
|
||||
if err != nil {
|
||||
panic("Could not parse string")
|
||||
}
|
||||
|
||||
return str[:len(str)-1], uint32(len(str))
|
||||
}
|
79
vendor/github.com/derekparker/delve/pkg/proc/arch.go
generated
vendored
Normal file
79
vendor/github.com/derekparker/delve/pkg/proc/arch.go
generated
vendored
Normal file
@ -0,0 +1,79 @@
|
||||
package proc
|
||||
|
||||
import "runtime"
|
||||
|
||||
// Arch defines an interface for representing a
|
||||
// CPU architecture.
|
||||
type Arch interface {
|
||||
SetGStructOffset(ver GoVersion, iscgo bool)
|
||||
PtrSize() int
|
||||
BreakpointInstruction() []byte
|
||||
BreakpointSize() int
|
||||
GStructOffset() uint64
|
||||
}
|
||||
|
||||
// AMD64 represents the AMD64 CPU architecture.
|
||||
type AMD64 struct {
|
||||
ptrSize int
|
||||
breakInstruction []byte
|
||||
breakInstructionLen int
|
||||
gStructOffset uint64
|
||||
hardwareBreakpointUsage []bool
|
||||
}
|
||||
|
||||
// AMD64Arch returns an initialized AMD64
|
||||
// struct.
|
||||
func AMD64Arch() *AMD64 {
|
||||
var breakInstr = []byte{0xCC}
|
||||
|
||||
return &AMD64{
|
||||
ptrSize: 8,
|
||||
breakInstruction: breakInstr,
|
||||
breakInstructionLen: len(breakInstr),
|
||||
hardwareBreakpointUsage: make([]bool, 4),
|
||||
}
|
||||
}
|
||||
|
||||
// SetGStructOffset sets the offset of the G struct on the AMD64
|
||||
// arch struct. The offset is dependent on the Go compiler Version
|
||||
// and whether or not the target program was externally linked.
|
||||
func (a *AMD64) SetGStructOffset(ver GoVersion, isextld bool) {
|
||||
switch runtime.GOOS {
|
||||
case "darwin":
|
||||
a.gStructOffset = 0x8a0
|
||||
case "linux":
|
||||
a.gStructOffset = 0xfffffffffffffff0
|
||||
if isextld || ver.AfterOrEqual(GoVersion{1, 5, -1, 2, 0}) || ver.IsDevel() {
|
||||
a.gStructOffset += 8
|
||||
}
|
||||
case "windows":
|
||||
// Use ArbitraryUserPointer (0x28) as pointer to pointer
|
||||
// to G struct per:
|
||||
// https://golang.org/src/runtime/cgo/gcc_windows_amd64.c
|
||||
a.gStructOffset = 0x28
|
||||
}
|
||||
}
|
||||
|
||||
// PtrSize returns the size of a pointer
|
||||
// on this architecture.
|
||||
func (a *AMD64) PtrSize() int {
|
||||
return a.ptrSize
|
||||
}
|
||||
|
||||
// BreakpointInstruction returns the Breakpoint
|
||||
// instruction for this architecture.
|
||||
func (a *AMD64) BreakpointInstruction() []byte {
|
||||
return a.breakInstruction
|
||||
}
|
||||
|
||||
// BreakpointSize returns the size of the
|
||||
// breakpoint instruction on this architecture.
|
||||
func (a *AMD64) BreakpointSize() int {
|
||||
return a.breakInstructionLen
|
||||
}
|
||||
|
||||
// GStructOffset returns the offset of the G
|
||||
// struct in thread local storage.
|
||||
func (a *AMD64) GStructOffset() uint64 {
|
||||
return a.gStructOffset
|
||||
}
|
163
vendor/github.com/derekparker/delve/pkg/proc/breakpoints.go
generated
vendored
Normal file
163
vendor/github.com/derekparker/delve/pkg/proc/breakpoints.go
generated
vendored
Normal file
@ -0,0 +1,163 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// Breakpoint represents a breakpoint. Stores information on the break
|
||||
// point including the byte of data that originally was stored at that
|
||||
// address.
|
||||
type Breakpoint struct {
|
||||
// File & line information for printing.
|
||||
FunctionName string
|
||||
File string
|
||||
Line int
|
||||
|
||||
Addr uint64 // Address breakpoint is set for.
|
||||
OriginalData []byte // If software breakpoint, the data we replace with breakpoint instruction.
|
||||
Name string // User defined name of the breakpoint
|
||||
ID int // Monotonically increasing ID.
|
||||
Kind BreakpointKind // Whether this is an internal breakpoint (for next'ing or stepping).
|
||||
|
||||
// Breakpoint information
|
||||
Tracepoint bool // Tracepoint flag
|
||||
Goroutine bool // Retrieve goroutine information
|
||||
Stacktrace int // Number of stack frames to retrieve
|
||||
Variables []string // Variables to evaluate
|
||||
LoadArgs *LoadConfig
|
||||
LoadLocals *LoadConfig
|
||||
HitCount map[int]uint64 // Number of times a breakpoint has been reached in a certain goroutine
|
||||
TotalHitCount uint64 // Number of times a breakpoint has been reached
|
||||
|
||||
// DeferReturns: when kind == NextDeferBreakpoint this breakpoint
|
||||
// will also check if the caller is runtime.gopanic or if the return
|
||||
// address is in the DeferReturns array.
|
||||
// Next uses NextDeferBreakpoints for the breakpoint it sets on the
|
||||
// deferred function, DeferReturns is populated with the
|
||||
// addresses of calls to runtime.deferreturn in the current
|
||||
// function. This insures that the breakpoint on the deferred
|
||||
// function only triggers on panic or on the defer call to
|
||||
// the function, not when the function is called directly
|
||||
DeferReturns []uint64
|
||||
// Cond: if not nil the breakpoint will be triggered only if evaluating Cond returns true
|
||||
Cond ast.Expr
|
||||
}
|
||||
|
||||
// Breakpoint Kind determines the behavior of delve when the
|
||||
// breakpoint is reached.
|
||||
type BreakpointKind int
|
||||
|
||||
const (
|
||||
// UserBreakpoint is a user set breakpoint
|
||||
UserBreakpoint BreakpointKind = iota
|
||||
// NextBreakpoint is a breakpoint set by Next, Continue
|
||||
// will stop on it and delete it
|
||||
NextBreakpoint
|
||||
// NextDeferBreakpoint is a breakpoint set by Next on the
|
||||
// first deferred function. In addition to checking their condition
|
||||
// breakpoints of this kind will also check that the function has been
|
||||
// called by runtime.gopanic or through runtime.deferreturn.
|
||||
NextDeferBreakpoint
|
||||
// StepBreakpoint is a breakpoint set by Step on a CALL instruction,
|
||||
// Continue will set a new breakpoint (of NextBreakpoint kind) on the
|
||||
// destination of CALL, delete this breakpoint and then continue again
|
||||
StepBreakpoint
|
||||
)
|
||||
|
||||
func (bp *Breakpoint) String() string {
|
||||
return fmt.Sprintf("Breakpoint %d at %#v %s:%d (%d)", bp.ID, bp.Addr, bp.File, bp.Line, bp.TotalHitCount)
|
||||
}
|
||||
|
||||
// Clear this breakpoint appropriately depending on whether it is a
|
||||
// hardware or software breakpoint.
|
||||
func (bp *Breakpoint) Clear(thread *Thread) (*Breakpoint, error) {
|
||||
if _, err := thread.writeMemory(uintptr(bp.Addr), bp.OriginalData); err != nil {
|
||||
return nil, fmt.Errorf("could not clear breakpoint %s", err)
|
||||
}
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
// BreakpointExistsError is returned when trying to set a breakpoint at
|
||||
// an address that already has a breakpoint set for it.
|
||||
type BreakpointExistsError struct {
|
||||
file string
|
||||
line int
|
||||
addr uint64
|
||||
}
|
||||
|
||||
func (bpe BreakpointExistsError) Error() string {
|
||||
return fmt.Sprintf("Breakpoint exists at %s:%d at %x", bpe.file, bpe.line, bpe.addr)
|
||||
}
|
||||
|
||||
// InvalidAddressError represents the result of
|
||||
// attempting to set a breakpoint at an invalid address.
|
||||
type InvalidAddressError struct {
|
||||
address uint64
|
||||
}
|
||||
|
||||
func (iae InvalidAddressError) Error() string {
|
||||
return fmt.Sprintf("Invalid address %#v\n", iae.address)
|
||||
}
|
||||
|
||||
func (dbp *Process) writeSoftwareBreakpoint(thread *Thread, addr uint64) error {
|
||||
_, err := thread.writeMemory(uintptr(addr), dbp.arch.BreakpointInstruction())
|
||||
return err
|
||||
}
|
||||
|
||||
func (bp *Breakpoint) checkCondition(thread *Thread) (bool, error) {
|
||||
if bp.Cond == nil {
|
||||
return true, nil
|
||||
}
|
||||
if bp.Kind == NextDeferBreakpoint {
|
||||
frames, err := thread.Stacktrace(2)
|
||||
if err == nil {
|
||||
ispanic := len(frames) >= 3 && frames[2].Current.Fn != nil && frames[2].Current.Fn.Name == "runtime.gopanic"
|
||||
isdeferreturn := false
|
||||
if len(frames) >= 1 {
|
||||
for _, pc := range bp.DeferReturns {
|
||||
if frames[0].Ret == pc {
|
||||
isdeferreturn = true
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
if !ispanic && !isdeferreturn {
|
||||
return false, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
scope, err := thread.Scope()
|
||||
if err != nil {
|
||||
return true, err
|
||||
}
|
||||
v, err := scope.evalAST(bp.Cond)
|
||||
if err != nil {
|
||||
return true, fmt.Errorf("error evaluating expression: %v", err)
|
||||
}
|
||||
if v.Unreadable != nil {
|
||||
return true, fmt.Errorf("condition expression unreadable: %v", v.Unreadable)
|
||||
}
|
||||
if v.Kind != reflect.Bool {
|
||||
return true, errors.New("condition expression not boolean")
|
||||
}
|
||||
return constant.BoolVal(v.Value), nil
|
||||
}
|
||||
|
||||
// Internal returns true for breakpoints not set directly by the user.
|
||||
func (bp *Breakpoint) Internal() bool {
|
||||
return bp.Kind != UserBreakpoint
|
||||
}
|
||||
|
||||
// NoBreakpointError is returned when trying to
|
||||
// clear a breakpoint that does not exist.
|
||||
type NoBreakpointError struct {
|
||||
addr uint64
|
||||
}
|
||||
|
||||
func (nbp NoBreakpointError) Error() string {
|
||||
return fmt.Sprintf("no breakpoint at %#v", nbp.addr)
|
||||
}
|
67
vendor/github.com/derekparker/delve/pkg/proc/disasm.go
generated
vendored
Normal file
67
vendor/github.com/derekparker/delve/pkg/proc/disasm.go
generated
vendored
Normal file
@ -0,0 +1,67 @@
|
||||
package proc
|
||||
|
||||
type AsmInstruction struct {
|
||||
Loc Location
|
||||
DestLoc *Location
|
||||
Bytes []byte
|
||||
Breakpoint bool
|
||||
AtPC bool
|
||||
Inst *ArchInst
|
||||
}
|
||||
|
||||
type AssemblyFlavour int
|
||||
|
||||
const (
|
||||
GNUFlavour = AssemblyFlavour(iota)
|
||||
IntelFlavour
|
||||
)
|
||||
|
||||
// Disassemble disassembles target memory between startPC and endPC
|
||||
// If currentGoroutine is set and thread is stopped at a CALL instruction Disassemble will evaluate the argument of the CALL instruction using the thread's registers
|
||||
// Be aware that the Bytes field of each returned instruction is a slice of a larger array of size endPC - startPC
|
||||
func (thread *Thread) Disassemble(startPC, endPC uint64, currentGoroutine bool) ([]AsmInstruction, error) {
|
||||
if thread.dbp.exited {
|
||||
return nil, &ProcessExitedError{}
|
||||
}
|
||||
mem, err := thread.readMemory(uintptr(startPC), int(endPC-startPC))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := make([]AsmInstruction, 0, len(mem)/15)
|
||||
pc := startPC
|
||||
|
||||
var curpc uint64
|
||||
var regs Registers
|
||||
if currentGoroutine {
|
||||
regs, _ = thread.Registers(false)
|
||||
if regs != nil {
|
||||
curpc = regs.PC()
|
||||
}
|
||||
}
|
||||
|
||||
for len(mem) > 0 {
|
||||
bp, atbp := thread.dbp.breakpoints[pc]
|
||||
if atbp {
|
||||
for i := range bp.OriginalData {
|
||||
mem[i] = bp.OriginalData[i]
|
||||
}
|
||||
}
|
||||
file, line, fn := thread.dbp.PCToLine(pc)
|
||||
loc := Location{PC: pc, File: file, Line: line, Fn: fn}
|
||||
inst, err := asmDecode(mem, pc)
|
||||
if err == nil {
|
||||
atpc := currentGoroutine && (curpc == pc)
|
||||
destloc := thread.resolveCallArg(inst, atpc, regs)
|
||||
r = append(r, AsmInstruction{Loc: loc, DestLoc: destloc, Bytes: mem[:inst.Len], Breakpoint: atbp, AtPC: atpc, Inst: inst})
|
||||
|
||||
pc += uint64(inst.Size())
|
||||
mem = mem[inst.Size():]
|
||||
} else {
|
||||
r = append(r, AsmInstruction{Loc: loc, Bytes: mem[:1], Breakpoint: atbp, Inst: nil})
|
||||
pc++
|
||||
mem = mem[1:]
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
184
vendor/github.com/derekparker/delve/pkg/proc/disasm_amd64.go
generated
vendored
Normal file
184
vendor/github.com/derekparker/delve/pkg/proc/disasm_amd64.go
generated
vendored
Normal file
@ -0,0 +1,184 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"encoding/binary"
|
||||
|
||||
"rsc.io/x86/x86asm"
|
||||
)
|
||||
|
||||
var maxInstructionLength uint64 = 15
|
||||
|
||||
type ArchInst x86asm.Inst
|
||||
|
||||
func asmDecode(mem []byte, pc uint64) (*ArchInst, error) {
|
||||
inst, err := x86asm.Decode(mem, 64)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
patchPCRel(pc, &inst)
|
||||
r := ArchInst(inst)
|
||||
return &r, nil
|
||||
}
|
||||
|
||||
func (inst *ArchInst) Size() int {
|
||||
return inst.Len
|
||||
}
|
||||
|
||||
// converts PC relative arguments to absolute addresses
|
||||
func patchPCRel(pc uint64, inst *x86asm.Inst) {
|
||||
for i := range inst.Args {
|
||||
rel, isrel := inst.Args[i].(x86asm.Rel)
|
||||
if isrel {
|
||||
inst.Args[i] = x86asm.Imm(int64(pc) + int64(rel) + int64(inst.Len))
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (inst *AsmInstruction) Text(flavour AssemblyFlavour) string {
|
||||
if inst.Inst == nil {
|
||||
return "?"
|
||||
}
|
||||
|
||||
var text string
|
||||
|
||||
switch flavour {
|
||||
case GNUFlavour:
|
||||
text = x86asm.GNUSyntax(x86asm.Inst(*inst.Inst))
|
||||
case IntelFlavour:
|
||||
fallthrough
|
||||
default:
|
||||
text = x86asm.IntelSyntax(x86asm.Inst(*inst.Inst))
|
||||
}
|
||||
|
||||
if inst.IsCall() && inst.DestLoc != nil && inst.DestLoc.Fn != nil {
|
||||
text += " " + inst.DestLoc.Fn.Name
|
||||
}
|
||||
|
||||
return text
|
||||
}
|
||||
|
||||
func (inst *AsmInstruction) IsCall() bool {
|
||||
return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL
|
||||
}
|
||||
|
||||
func (thread *Thread) resolveCallArg(inst *ArchInst, currentGoroutine bool, regs Registers) *Location {
|
||||
if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
|
||||
return nil
|
||||
}
|
||||
|
||||
var pc uint64
|
||||
var err error
|
||||
|
||||
switch arg := inst.Args[0].(type) {
|
||||
case x86asm.Imm:
|
||||
pc = uint64(arg)
|
||||
case x86asm.Reg:
|
||||
if !currentGoroutine || regs == nil {
|
||||
return nil
|
||||
}
|
||||
pc, err = regs.Get(int(arg))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
case x86asm.Mem:
|
||||
if !currentGoroutine || regs == nil {
|
||||
return nil
|
||||
}
|
||||
if arg.Segment != 0 {
|
||||
return nil
|
||||
}
|
||||
regs, err := thread.Registers(false)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
base, err1 := regs.Get(int(arg.Base))
|
||||
index, err2 := regs.Get(int(arg.Index))
|
||||
if err1 != nil || err2 != nil {
|
||||
return nil
|
||||
}
|
||||
addr := uintptr(int64(base) + int64(index*uint64(arg.Scale)) + arg.Disp)
|
||||
//TODO: should this always be 64 bits instead of inst.MemBytes?
|
||||
pcbytes, err := thread.readMemory(addr, inst.MemBytes)
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
pc = binary.LittleEndian.Uint64(pcbytes)
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
file, line, fn := thread.dbp.PCToLine(pc)
|
||||
if fn == nil {
|
||||
return nil
|
||||
}
|
||||
return &Location{PC: pc, File: file, Line: line, Fn: fn}
|
||||
}
|
||||
|
||||
type instrseq []x86asm.Op
|
||||
|
||||
// Possible stacksplit prologues are inserted by stacksplit in
|
||||
// $GOROOT/src/cmd/internal/obj/x86/obj6.go.
|
||||
// The stacksplit prologue will always begin with loading curg in CX, this
|
||||
// instruction is added by load_g_cx in the same file and is either 1 or 2
|
||||
// MOVs.
|
||||
var prologues []instrseq
|
||||
|
||||
func init() {
|
||||
var tinyStacksplit = instrseq{x86asm.CMP, x86asm.JBE}
|
||||
var smallStacksplit = instrseq{x86asm.LEA, x86asm.CMP, x86asm.JBE}
|
||||
var bigStacksplit = instrseq{x86asm.MOV, x86asm.CMP, x86asm.JE, x86asm.LEA, x86asm.SUB, x86asm.CMP, x86asm.JBE}
|
||||
var unixGetG = instrseq{x86asm.MOV}
|
||||
var windowsGetG = instrseq{x86asm.MOV, x86asm.MOV}
|
||||
|
||||
prologues = make([]instrseq, 0, 2*3)
|
||||
for _, getG := range []instrseq{unixGetG, windowsGetG} {
|
||||
for _, stacksplit := range []instrseq{tinyStacksplit, smallStacksplit, bigStacksplit} {
|
||||
prologue := make(instrseq, 0, len(getG)+len(stacksplit))
|
||||
prologue = append(prologue, getG...)
|
||||
prologue = append(prologue, stacksplit...)
|
||||
prologues = append(prologues, prologue)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
func (dbp *Process) FirstPCAfterPrologue(fn *gosym.Func, sameline bool) (uint64, error) {
|
||||
text, err := dbp.CurrentThread().Disassemble(fn.Entry, fn.End, false)
|
||||
if err != nil {
|
||||
return fn.Entry, err
|
||||
}
|
||||
|
||||
if len(text) <= 0 {
|
||||
return fn.Entry, nil
|
||||
}
|
||||
|
||||
for _, prologue := range prologues {
|
||||
if len(prologue) >= len(text) {
|
||||
continue
|
||||
}
|
||||
if checkPrologue(text, prologue) {
|
||||
r := &text[len(prologue)]
|
||||
if sameline {
|
||||
if r.Loc.Line != text[0].Loc.Line {
|
||||
return fn.Entry, nil
|
||||
}
|
||||
}
|
||||
return r.Loc.PC, nil
|
||||
}
|
||||
}
|
||||
|
||||
return fn.Entry, nil
|
||||
}
|
||||
|
||||
func checkPrologue(s []AsmInstruction, prologuePattern instrseq) bool {
|
||||
line := s[0].Loc.Line
|
||||
for i, op := range prologuePattern {
|
||||
if s[i].Inst.Op != op || s[i].Loc.Line != line {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
9
vendor/github.com/derekparker/delve/pkg/proc/doc.go
generated
vendored
Normal file
9
vendor/github.com/derekparker/delve/pkg/proc/doc.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// Package proc is a low-level package that provides methods to manipulate
|
||||
// the process we are debugging.
|
||||
//
|
||||
// proc implements all core functionality including:
|
||||
// * creating / attaching to a process
|
||||
// * process manipulation (step, next, continue, halt)
|
||||
// * methods to explore the memory of the process
|
||||
//
|
||||
package proc
|
1131
vendor/github.com/derekparker/delve/pkg/proc/eval.go
generated
vendored
Normal file
1131
vendor/github.com/derekparker/delve/pkg/proc/eval.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
283
vendor/github.com/derekparker/delve/pkg/proc/exc.h
generated
vendored
Normal file
283
vendor/github.com/derekparker/delve/pkg/proc/exc.h
generated
vendored
Normal file
@ -0,0 +1,283 @@
|
||||
#ifndef _exc_user_
|
||||
#define _exc_user_
|
||||
|
||||
/* Module exc */
|
||||
|
||||
#include <string.h>
|
||||
#include <mach/ndr.h>
|
||||
#include <mach/boolean.h>
|
||||
#include <mach/kern_return.h>
|
||||
#include <mach/notify.h>
|
||||
#include <mach/mach_types.h>
|
||||
#include <mach/message.h>
|
||||
#include <mach/mig_errors.h>
|
||||
#include <mach/port.h>
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifndef KERNEL
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<mach/mig_voucher_support.h>)
|
||||
#ifndef USING_VOUCHERS
|
||||
#define USING_VOUCHERS
|
||||
#endif
|
||||
#ifndef __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#define __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern boolean_t voucher_mach_msg_set(mach_msg_header_t *msg) __attribute__((weak_import));
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#endif // __has_include(<mach/mach_voucher_types.h>)
|
||||
#endif // __has_include
|
||||
#endif // !KERNEL
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
|
||||
#ifdef AUTOTEST
|
||||
#ifndef FUNCTION_PTR_T
|
||||
#define FUNCTION_PTR_T
|
||||
typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t);
|
||||
typedef struct {
|
||||
char *name;
|
||||
function_ptr_t function;
|
||||
} function_table_entry;
|
||||
typedef function_table_entry *function_table_t;
|
||||
#endif /* FUNCTION_PTR_T */
|
||||
#endif /* AUTOTEST */
|
||||
|
||||
#ifndef exc_MSG_COUNT
|
||||
#define exc_MSG_COUNT 3
|
||||
#endif /* exc_MSG_COUNT */
|
||||
|
||||
#include <mach/std_types.h>
|
||||
#include <mach/mig.h>
|
||||
#include <mach/mig.h>
|
||||
#include <mach/mach_types.h>
|
||||
|
||||
#ifdef __BeforeMigUserHeader
|
||||
__BeforeMigUserHeader
|
||||
#endif /* __BeforeMigUserHeader */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__BEGIN_DECLS
|
||||
|
||||
|
||||
/* Routine exception_raise */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t exception_raise
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt
|
||||
);
|
||||
|
||||
/* Routine exception_raise_state */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t exception_raise_state
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
|
||||
/* Routine exception_raise_state_identity */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t exception_raise_state_identity
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
/********************** Caution **************************/
|
||||
/* The following data types should be used to calculate */
|
||||
/* maximum message sizes only. The actual message may be */
|
||||
/* smaller, and the position of the arguments within the */
|
||||
/* message layout may vary from what is presented here. */
|
||||
/* For example, if any of the arguments are variable- */
|
||||
/* sized, and less than the maximum is sent, the data */
|
||||
/* will be packed tight in the actual message to reduce */
|
||||
/* the presence of holes. */
|
||||
/********************** Caution **************************/
|
||||
|
||||
/* typedefs for all requests */
|
||||
|
||||
#ifndef __Request__exc_subsystem__defined
|
||||
#define __Request__exc_subsystem__defined
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
} __Request__exception_raise_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} __Request__exception_raise_state_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} __Request__exception_raise_state_identity_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
#endif /* !__Request__exc_subsystem__defined */
|
||||
|
||||
/* union of all requests */
|
||||
|
||||
#ifndef __RequestUnion__exc_subsystem__defined
|
||||
#define __RequestUnion__exc_subsystem__defined
|
||||
union __RequestUnion__exc_subsystem {
|
||||
__Request__exception_raise_t Request_exception_raise;
|
||||
__Request__exception_raise_state_t Request_exception_raise_state;
|
||||
__Request__exception_raise_state_identity_t Request_exception_raise_state_identity;
|
||||
};
|
||||
#endif /* !__RequestUnion__exc_subsystem__defined */
|
||||
/* typedefs for all replies */
|
||||
|
||||
#ifndef __Reply__exc_subsystem__defined
|
||||
#define __Reply__exc_subsystem__defined
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
} __Reply__exception_raise_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply__exception_raise_state_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply__exception_raise_state_identity_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
#endif /* !__Reply__exc_subsystem__defined */
|
||||
|
||||
/* union of all replies */
|
||||
|
||||
#ifndef __ReplyUnion__exc_subsystem__defined
|
||||
#define __ReplyUnion__exc_subsystem__defined
|
||||
union __ReplyUnion__exc_subsystem {
|
||||
__Reply__exception_raise_t Reply_exception_raise;
|
||||
__Reply__exception_raise_state_t Reply_exception_raise_state;
|
||||
__Reply__exception_raise_state_identity_t Reply_exception_raise_state_identity;
|
||||
};
|
||||
#endif /* !__RequestUnion__exc_subsystem__defined */
|
||||
|
||||
#ifndef subsystem_to_name_map_exc
|
||||
#define subsystem_to_name_map_exc \
|
||||
{ "exception_raise", 2401 },\
|
||||
{ "exception_raise_state", 2402 },\
|
||||
{ "exception_raise_state_identity", 2403 }
|
||||
#endif
|
||||
|
||||
#ifdef __AfterMigUserHeader
|
||||
__AfterMigUserHeader
|
||||
#endif /* __AfterMigUserHeader */
|
||||
|
||||
#endif /* _exc_user_ */
|
768
vendor/github.com/derekparker/delve/pkg/proc/exc_user_darwin.c
generated
vendored
Normal file
768
vendor/github.com/derekparker/delve/pkg/proc/exc_user_darwin.c
generated
vendored
Normal file
@ -0,0 +1,768 @@
|
||||
/*
|
||||
* IDENTIFICATION:
|
||||
* stub generated Sun Feb 22 20:54:31 2015
|
||||
* with a MiG generated by bootstrap_cmds-91
|
||||
* OPTIONS:
|
||||
*/
|
||||
#define __MIG_check__Reply__exc_subsystem__ 1
|
||||
|
||||
#include "exc.h"
|
||||
|
||||
|
||||
#ifndef mig_internal
|
||||
#define mig_internal static __inline__
|
||||
#endif /* mig_internal */
|
||||
|
||||
#ifndef mig_external
|
||||
#define mig_external
|
||||
#endif /* mig_external */
|
||||
|
||||
#if !defined(__MigTypeCheck) && defined(TypeCheck)
|
||||
#define __MigTypeCheck TypeCheck /* Legacy setting */
|
||||
#endif /* !defined(__MigTypeCheck) */
|
||||
|
||||
#if !defined(__MigKernelSpecificCode) && defined(_MIG_KERNEL_SPECIFIC_CODE_)
|
||||
#define __MigKernelSpecificCode _MIG_KERNEL_SPECIFIC_CODE_ /* Legacy setting */
|
||||
#endif /* !defined(__MigKernelSpecificCode) */
|
||||
|
||||
#ifndef LimitCheck
|
||||
#define LimitCheck 0
|
||||
#endif /* LimitCheck */
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ( ((a) < (b))? (a): (b) )
|
||||
#endif /* min */
|
||||
|
||||
#if !defined(_WALIGN_)
|
||||
#define _WALIGN_(x) (((x) + 3) & ~3)
|
||||
#endif /* !defined(_WALIGN_) */
|
||||
|
||||
#if !defined(_WALIGNSZ_)
|
||||
#define _WALIGNSZ_(x) _WALIGN_(sizeof(x))
|
||||
#endif /* !defined(_WALIGNSZ_) */
|
||||
|
||||
#ifndef UseStaticTemplates
|
||||
#define UseStaticTemplates 0
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#ifndef __MachMsgErrorWithTimeout
|
||||
#define __MachMsgErrorWithTimeout(_R_) { \
|
||||
switch (_R_) { \
|
||||
case MACH_SEND_INVALID_DATA: \
|
||||
case MACH_SEND_INVALID_DEST: \
|
||||
case MACH_SEND_INVALID_HEADER: \
|
||||
mig_put_reply_port(InP->Head.msgh_reply_port); \
|
||||
break; \
|
||||
case MACH_SEND_TIMED_OUT: \
|
||||
case MACH_RCV_TIMED_OUT: \
|
||||
default: \
|
||||
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
|
||||
} \
|
||||
}
|
||||
#endif /* __MachMsgErrorWithTimeout */
|
||||
|
||||
#ifndef __MachMsgErrorWithoutTimeout
|
||||
#define __MachMsgErrorWithoutTimeout(_R_) { \
|
||||
switch (_R_) { \
|
||||
case MACH_SEND_INVALID_DATA: \
|
||||
case MACH_SEND_INVALID_DEST: \
|
||||
case MACH_SEND_INVALID_HEADER: \
|
||||
mig_put_reply_port(InP->Head.msgh_reply_port); \
|
||||
break; \
|
||||
default: \
|
||||
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
|
||||
} \
|
||||
}
|
||||
#endif /* __MachMsgErrorWithoutTimeout */
|
||||
|
||||
#ifndef __DeclareSendRpc
|
||||
#define __DeclareSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __DeclareSendRpc */
|
||||
|
||||
#ifndef __BeforeSendRpc
|
||||
#define __BeforeSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __BeforeSendRpc */
|
||||
|
||||
#ifndef __AfterSendRpc
|
||||
#define __AfterSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __AfterSendRpc */
|
||||
|
||||
#ifndef __DeclareSendSimple
|
||||
#define __DeclareSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __DeclareSendSimple */
|
||||
|
||||
#ifndef __BeforeSendSimple
|
||||
#define __BeforeSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __BeforeSendSimple */
|
||||
|
||||
#ifndef __AfterSendSimple
|
||||
#define __AfterSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __AfterSendSimple */
|
||||
|
||||
#define msgh_request_port msgh_remote_port
|
||||
#define msgh_reply_port msgh_local_port
|
||||
|
||||
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__exception_raise_t__defined)
|
||||
#define __MIG_check__Reply__exception_raise_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__exception_raise_t(__Reply__exception_raise_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__exception_raise_t __Reply;
|
||||
if (Out0P->Head.msgh_id != 2501) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
(Out0P->Head.msgh_size != (mach_msg_size_t)sizeof(__Reply)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
{
|
||||
return Out0P->RetCode;
|
||||
}
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__exception_raise_t__defined) */
|
||||
#endif /* __MIG_check__Reply__exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine exception_raise */
|
||||
mig_external kern_return_t exception_raise
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
|
||||
#ifdef __MIG_check__Reply__exception_raise_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__exception_raise_t__defined */
|
||||
|
||||
__DeclareSendRpc(2401, "exception_raise")
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t threadTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t taskTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->msgh_body.msgh_descriptor_count = 2;
|
||||
#if UseStaticTemplates
|
||||
InP->thread = threadTemplate;
|
||||
InP->thread.name = thread;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->thread.name = thread;
|
||||
InP->thread.disposition = 19;
|
||||
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
InP->task = taskTemplate;
|
||||
InP->task.name = task;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->task.name = task;
|
||||
InP->task.disposition = 19;
|
||||
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 4 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 8) + ((4 * codeCnt));
|
||||
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2401;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2401, "exception_raise")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2401, "exception_raise")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__exception_raise_t__defined)
|
||||
check_result = __MIG_check__Reply__exception_raise_t((__Reply__exception_raise_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__exception_raise_t__defined) */
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__exception_raise_state_t__defined)
|
||||
#define __MIG_check__Reply__exception_raise_state_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__exception_raise_state_t(__Reply__exception_raise_state_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__exception_raise_state_t __Reply;
|
||||
#if __MigTypeCheck
|
||||
unsigned int msgh_size;
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->Head.msgh_id != 2502) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
msgh_size = Out0P->Head.msgh_size;
|
||||
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
|
||||
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
|
||||
Out0P->RetCode == KERN_SUCCESS)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->RetCode != KERN_SUCCESS) {
|
||||
return ((mig_reply_error_t *)Out0P)->RetCode;
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ( Out0P->new_stateCnt > 224 )
|
||||
return MIG_TYPE_ERROR;
|
||||
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
|
||||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
return MACH_MSG_SUCCESS;
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__exception_raise_state_t__defined) */
|
||||
#endif /* __MIG_check__Reply__exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine exception_raise_state */
|
||||
mig_external kern_return_t exception_raise_state
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
unsigned int msgh_size_delta;
|
||||
|
||||
|
||||
#ifdef __MIG_check__Reply__exception_raise_state_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__exception_raise_state_t__defined */
|
||||
|
||||
__DeclareSendRpc(2402, "exception_raise_state")
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 4 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size_delta = (4 * codeCnt);
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 904) + msgh_size_delta;
|
||||
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 8);
|
||||
|
||||
InP->flavor = *flavor;
|
||||
|
||||
if (old_stateCnt > 224) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
|
||||
|
||||
InP->old_stateCnt = old_stateCnt;
|
||||
|
||||
msgh_size += (4 * old_stateCnt);
|
||||
InP = &Mess.In;
|
||||
InP->Head.msgh_bits =
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2402;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2402, "exception_raise_state")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2402, "exception_raise_state")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__exception_raise_state_t__defined)
|
||||
check_result = __MIG_check__Reply__exception_raise_state_t((__Reply__exception_raise_state_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__exception_raise_state_t__defined) */
|
||||
|
||||
*flavor = Out0P->flavor;
|
||||
|
||||
if (Out0P->new_stateCnt > 224) {
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
|
||||
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__exception_raise_state_identity_t__defined)
|
||||
#define __MIG_check__Reply__exception_raise_state_identity_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__exception_raise_state_identity_t(__Reply__exception_raise_state_identity_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__exception_raise_state_identity_t __Reply;
|
||||
#if __MigTypeCheck
|
||||
unsigned int msgh_size;
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->Head.msgh_id != 2503) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
msgh_size = Out0P->Head.msgh_size;
|
||||
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
|
||||
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
|
||||
Out0P->RetCode == KERN_SUCCESS)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->RetCode != KERN_SUCCESS) {
|
||||
return ((mig_reply_error_t *)Out0P)->RetCode;
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ( Out0P->new_stateCnt > 224 )
|
||||
return MIG_TYPE_ERROR;
|
||||
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
|
||||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
return MACH_MSG_SUCCESS;
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__exception_raise_state_identity_t__defined) */
|
||||
#endif /* __MIG_check__Reply__exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine exception_raise_state_identity */
|
||||
mig_external kern_return_t exception_raise_state_identity
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
integer_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
unsigned int msgh_size_delta;
|
||||
|
||||
|
||||
#ifdef __MIG_check__Reply__exception_raise_state_identity_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__exception_raise_state_identity_t__defined */
|
||||
|
||||
__DeclareSendRpc(2403, "exception_raise_state_identity")
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t threadTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t taskTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->msgh_body.msgh_descriptor_count = 2;
|
||||
#if UseStaticTemplates
|
||||
InP->thread = threadTemplate;
|
||||
InP->thread.name = thread;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->thread.name = thread;
|
||||
InP->thread.disposition = 19;
|
||||
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
InP->task = taskTemplate;
|
||||
InP->task.name = task;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->task.name = task;
|
||||
InP->task.disposition = 19;
|
||||
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 4 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size_delta = (4 * codeCnt);
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 904) + msgh_size_delta;
|
||||
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 8);
|
||||
|
||||
InP->flavor = *flavor;
|
||||
|
||||
if (old_stateCnt > 224) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
|
||||
|
||||
InP->old_stateCnt = old_stateCnt;
|
||||
|
||||
msgh_size += (4 * old_stateCnt);
|
||||
InP = &Mess.In;
|
||||
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2403;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2403, "exception_raise_state_identity")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2403, "exception_raise_state_identity")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__exception_raise_state_identity_t__defined)
|
||||
check_result = __MIG_check__Reply__exception_raise_state_identity_t((__Reply__exception_raise_state_identity_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__exception_raise_state_identity_t__defined) */
|
||||
|
||||
*flavor = Out0P->flavor;
|
||||
|
||||
if (Out0P->new_stateCnt > 224) {
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
|
||||
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
112
vendor/github.com/derekparker/delve/pkg/proc/exec_darwin.c
generated
vendored
Normal file
112
vendor/github.com/derekparker/delve/pkg/proc/exec_darwin.c
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
#include "exec_darwin.h"
|
||||
#include "stdio.h"
|
||||
|
||||
extern char** environ;
|
||||
|
||||
int
|
||||
close_exec_pipe(int fd[2]) {
|
||||
if (pipe(fd) < 0) return -1;
|
||||
if (fcntl(fd[0], F_SETFD, FD_CLOEXEC) < 0) return -1;
|
||||
if (fcntl(fd[1], F_SETFD, FD_CLOEXEC) < 0) return -1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
fork_exec(char *argv0, char **argv, int size,
|
||||
char *wd,
|
||||
task_t *task,
|
||||
mach_port_t *port_set,
|
||||
mach_port_t *exception_port,
|
||||
mach_port_t *notification_port)
|
||||
{
|
||||
// Since we're using mach exceptions instead of signals,
|
||||
// we need to coordinate between parent and child via pipes
|
||||
// to ensure that the parent has set the exception ports on
|
||||
// the child task before it execs.
|
||||
int fd[2];
|
||||
if (close_exec_pipe(fd) < 0) return -1;
|
||||
|
||||
// Create another pipe to signal the parent on exec.
|
||||
int efd[2];
|
||||
if (close_exec_pipe(efd) < 0) return -1;
|
||||
|
||||
kern_return_t kret;
|
||||
pid_t pid = fork();
|
||||
if (pid > 0) {
|
||||
// In parent.
|
||||
close(fd[0]);
|
||||
close(efd[1]);
|
||||
kret = acquire_mach_task(pid, task, port_set, exception_port, notification_port);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
char msg = 'c';
|
||||
write(fd[1], &msg, 1);
|
||||
close(fd[1]);
|
||||
|
||||
char w;
|
||||
size_t n = read(efd[0], &w, 1);
|
||||
close(efd[0]);
|
||||
if (n != 0) {
|
||||
// Child died, reap it.
|
||||
waitpid(pid, NULL, 0);
|
||||
return -1;
|
||||
}
|
||||
return pid;
|
||||
}
|
||||
|
||||
// Fork succeeded, we are in the child.
|
||||
int pret, cret;
|
||||
char sig;
|
||||
|
||||
close(fd[1]);
|
||||
read(fd[0], &sig, 1);
|
||||
close(fd[0]);
|
||||
|
||||
// Create a new process group.
|
||||
if (setpgid(0, 0) < 0) {
|
||||
perror("setpgid");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Set errno to zero before a call to ptrace.
|
||||
// It is documented that ptrace can return -1 even
|
||||
// for successful calls.
|
||||
errno = 0;
|
||||
pret = ptrace(PT_TRACE_ME, 0, 0, 0);
|
||||
if (pret != 0 && errno != 0) {
|
||||
perror("ptrace");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
// Change working directory if wd is not empty.
|
||||
if (wd && wd[0]) {
|
||||
errno = 0;
|
||||
cret = chdir(wd);
|
||||
if (cret != 0 && errno != 0) {
|
||||
char *error_msg;
|
||||
asprintf(&error_msg, "%s '%s'", "chdir", wd);
|
||||
perror(error_msg);
|
||||
exit(1);
|
||||
}
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
pret = ptrace(PT_SIGEXC, 0, 0, 0);
|
||||
if (pret != 0 && errno != 0) {
|
||||
perror("ptrace");
|
||||
exit(1);
|
||||
}
|
||||
|
||||
sleep(1);
|
||||
|
||||
// Create the child process.
|
||||
execve(argv0, argv, environ);
|
||||
|
||||
// We should never reach here, but if we did something went wrong.
|
||||
// Write a message to parent to alert that exec failed.
|
||||
char msg = 'd';
|
||||
write(efd[1], &msg, 1);
|
||||
close(efd[1]);
|
||||
|
||||
exit(1);
|
||||
}
|
10
vendor/github.com/derekparker/delve/pkg/proc/exec_darwin.h
generated
vendored
Normal file
10
vendor/github.com/derekparker/delve/pkg/proc/exec_darwin.h
generated
vendored
Normal file
@ -0,0 +1,10 @@
|
||||
#include "proc_darwin.h"
|
||||
|
||||
#include <unistd.h>
|
||||
#include <sys/ptrace.h>
|
||||
#include <errno.h>
|
||||
#include <stdlib.h>
|
||||
#include <fcntl.h>
|
||||
|
||||
int
|
||||
fork_exec(char *, char **, int, char *, task_t*, mach_port_t*, mach_port_t*, mach_port_t*);
|
117
vendor/github.com/derekparker/delve/pkg/proc/go_version.go
generated
vendored
Normal file
117
vendor/github.com/derekparker/delve/pkg/proc/go_version.go
generated
vendored
Normal file
@ -0,0 +1,117 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// GoVersion represents the Go version of
|
||||
// the Go compiler version used to compile
|
||||
// the target binary.
|
||||
type GoVersion struct {
|
||||
Major int
|
||||
Minor int
|
||||
Rev int
|
||||
Beta int
|
||||
RC int
|
||||
}
|
||||
|
||||
var (
|
||||
GoVer18Beta = GoVersion{1, 8, -1, 0, 0}
|
||||
)
|
||||
|
||||
func ParseVersionString(ver string) (GoVersion, bool) {
|
||||
var r GoVersion
|
||||
var err1, err2, err3 error
|
||||
|
||||
if strings.HasPrefix(ver, "devel") {
|
||||
return GoVersion{-1, 0, 0, 0, 0}, true
|
||||
}
|
||||
|
||||
if strings.HasPrefix(ver, "go") {
|
||||
ver := strings.Split(ver, " ")[0]
|
||||
v := strings.SplitN(ver[2:], ".", 3)
|
||||
switch len(v) {
|
||||
case 2:
|
||||
r.Major, err1 = strconv.Atoi(v[0])
|
||||
vr := strings.SplitN(v[1], "beta", 2)
|
||||
if len(vr) == 2 {
|
||||
r.Beta, err3 = strconv.Atoi(vr[1])
|
||||
} else {
|
||||
vr = strings.SplitN(v[1], "rc", 2)
|
||||
if len(vr) == 2 {
|
||||
r.RC, err3 = strconv.Atoi(vr[1])
|
||||
} else {
|
||||
r.Minor, err2 = strconv.Atoi(v[1])
|
||||
if err2 != nil {
|
||||
return GoVersion{}, false
|
||||
}
|
||||
return r, true
|
||||
}
|
||||
}
|
||||
|
||||
r.Minor, err2 = strconv.Atoi(vr[0])
|
||||
r.Rev = -1
|
||||
|
||||
if err1 != nil || err2 != nil || err3 != nil {
|
||||
return GoVersion{}, false
|
||||
}
|
||||
|
||||
return r, true
|
||||
|
||||
case 3:
|
||||
|
||||
r.Major, err1 = strconv.Atoi(v[0])
|
||||
r.Minor, err2 = strconv.Atoi(v[1])
|
||||
r.Rev, err3 = strconv.Atoi(v[2])
|
||||
if err1 != nil || err2 != nil || err3 != nil {
|
||||
return GoVersion{}, false
|
||||
}
|
||||
|
||||
return r, true
|
||||
|
||||
default:
|
||||
return GoVersion{}, false
|
||||
}
|
||||
}
|
||||
|
||||
return GoVersion{}, false
|
||||
}
|
||||
|
||||
// AfterOrEqual returns whether one GoVersion is after or
|
||||
// equal to the other.
|
||||
func (v *GoVersion) AfterOrEqual(b GoVersion) bool {
|
||||
if v.Major < b.Major {
|
||||
return false
|
||||
} else if v.Major > b.Major {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Minor < b.Minor {
|
||||
return false
|
||||
} else if v.Minor > b.Minor {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Rev < b.Rev {
|
||||
return false
|
||||
} else if v.Rev > b.Rev {
|
||||
return true
|
||||
}
|
||||
|
||||
if v.Beta < b.Beta {
|
||||
return false
|
||||
}
|
||||
|
||||
if v.RC < b.RC {
|
||||
return false
|
||||
}
|
||||
|
||||
return true
|
||||
}
|
||||
|
||||
// IsDevel returns whether the GoVersion
|
||||
// is a development version.
|
||||
func (v *GoVersion) IsDevel() bool {
|
||||
return v.Major < 0
|
||||
}
|
119
vendor/github.com/derekparker/delve/pkg/proc/mach_exc.defs
generated
vendored
Normal file
119
vendor/github.com/derekparker/delve/pkg/proc/mach_exc.defs
generated
vendored
Normal file
@ -0,0 +1,119 @@
|
||||
/*
|
||||
* Copyright (c) 2006 Apple Computer, Inc. All rights reserved.
|
||||
*
|
||||
* @APPLE_OSREFERENCE_LICENSE_HEADER_START@
|
||||
*
|
||||
* This file contains Original Code and/or Modifications of Original Code
|
||||
* as defined in and that are subject to the Apple Public Source License
|
||||
* Version 2.0 (the 'License'). You may not use this file except in
|
||||
* compliance with the License. The rights granted to you under the License
|
||||
* may not be used to create, or enable the creation or redistribution of,
|
||||
* unlawful or unlicensed copies of an Apple operating system, or to
|
||||
* circumvent, violate, or enable the circumvention or violation of, any
|
||||
* terms of an Apple operating system software license agreement.
|
||||
*
|
||||
* Please obtain a copy of the License at
|
||||
* http://www.opensource.apple.com/apsl/ and read it before using this file.
|
||||
*
|
||||
* The Original Code and all software distributed under the License are
|
||||
* distributed on an 'AS IS' basis, WITHOUT WARRANTY OF ANY KIND, EITHER
|
||||
* EXPRESS OR IMPLIED, AND APPLE HEREBY DISCLAIMS ALL SUCH WARRANTIES,
|
||||
* INCLUDING WITHOUT LIMITATION, ANY WARRANTIES OF MERCHANTABILITY,
|
||||
* FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT OR NON-INFRINGEMENT.
|
||||
* Please see the License for the specific language governing rights and
|
||||
* limitations under the License.
|
||||
*
|
||||
* @APPLE_OSREFERENCE_LICENSE_HEADER_END@
|
||||
*/
|
||||
/*
|
||||
* @OSF_COPYRIGHT@
|
||||
*/
|
||||
/*
|
||||
* Mach Operating System
|
||||
* Copyright (c) 1991,1990,1989,1988,1987 Carnegie Mellon University
|
||||
* All Rights Reserved.
|
||||
*
|
||||
* Permission to use, copy, modify and distribute this software and its
|
||||
* documentation is hereby granted, provided that both the copyright
|
||||
* notice and this permission notice appear in all copies of the
|
||||
* software, derivative works or modified versions, and any portions
|
||||
* thereof, and that both notices appear in supporting documentation.
|
||||
*
|
||||
* CARNEGIE MELLON ALLOWS FREE USE OF THIS SOFTWARE IN ITS "AS IS"
|
||||
* CONDITION. CARNEGIE MELLON DISCLAIMS ANY LIABILITY OF ANY KIND FOR
|
||||
* ANY DAMAGES WHATSOEVER RESULTING FROM THE USE OF THIS SOFTWARE.
|
||||
*
|
||||
* Carnegie Mellon requests users of this software to return to
|
||||
*
|
||||
* Software Distribution Coordinator or Software.Distribution@CS.CMU.EDU
|
||||
* School of Computer Science
|
||||
* Carnegie Mellon University
|
||||
* Pittsburgh PA 15213-3890
|
||||
*
|
||||
* any improvements or extensions that they make and grant Carnegie Mellon
|
||||
* the rights to redistribute these changes.
|
||||
*/
|
||||
/*
|
||||
*/
|
||||
/*
|
||||
* Abstract:
|
||||
* MiG definitions file for Mach exception interface.
|
||||
*/
|
||||
|
||||
subsystem
|
||||
#if KERNEL_USER
|
||||
KernelUser
|
||||
#endif
|
||||
mach_exc 2405;
|
||||
|
||||
#include <mach/std_types.defs>
|
||||
#include <mach/mach_types.defs>
|
||||
|
||||
ServerPrefix catch_;
|
||||
|
||||
type mach_exception_data_t = array[*:2] of int64_t;
|
||||
type exception_type_t = int;
|
||||
|
||||
routine mach_exception_raise(
|
||||
#if KERNEL_USER
|
||||
exception_port : mach_port_move_send_t;
|
||||
thread : mach_port_move_send_t;
|
||||
task : mach_port_move_send_t;
|
||||
#else /* KERNEL_USER */
|
||||
exception_port : mach_port_t;
|
||||
thread : mach_port_t;
|
||||
task : mach_port_t;
|
||||
#endif /* KERNEL_USER */
|
||||
exception : exception_type_t;
|
||||
code : mach_exception_data_t
|
||||
);
|
||||
|
||||
routine mach_exception_raise_state(
|
||||
#if KERNEL_USER
|
||||
exception_port : mach_port_move_send_t;
|
||||
#else /* KERNEL_USER */
|
||||
exception_port : mach_port_t;
|
||||
#endif /* KERNEL_USER */
|
||||
exception : exception_type_t;
|
||||
code : mach_exception_data_t, const;
|
||||
inout flavor : int;
|
||||
old_state : thread_state_t, const;
|
||||
out new_state : thread_state_t);
|
||||
|
||||
routine mach_exception_raise_state_identity(
|
||||
#if KERNEL_USER
|
||||
exception_port : mach_port_move_send_t;
|
||||
thread : mach_port_move_send_t;
|
||||
task : mach_port_move_send_t;
|
||||
#else /* KERNEL_USER */
|
||||
exception_port : mach_port_t;
|
||||
thread : mach_port_t;
|
||||
task : mach_port_t;
|
||||
#endif /* KERNEL_USER */
|
||||
exception : exception_type_t;
|
||||
code : mach_exception_data_t;
|
||||
inout flavor : int;
|
||||
old_state : thread_state_t;
|
||||
out new_state : thread_state_t);
|
||||
|
||||
/* vim: set ft=c : */
|
283
vendor/github.com/derekparker/delve/pkg/proc/mach_exc.h
generated
vendored
Normal file
283
vendor/github.com/derekparker/delve/pkg/proc/mach_exc.h
generated
vendored
Normal file
@ -0,0 +1,283 @@
|
||||
#ifndef _mach_exc_user_
|
||||
#define _mach_exc_user_
|
||||
|
||||
/* Module mach_exc */
|
||||
|
||||
#include <string.h>
|
||||
#include <mach/ndr.h>
|
||||
#include <mach/boolean.h>
|
||||
#include <mach/kern_return.h>
|
||||
#include <mach/notify.h>
|
||||
#include <mach/mach_types.h>
|
||||
#include <mach/message.h>
|
||||
#include <mach/mig_errors.h>
|
||||
#include <mach/port.h>
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifndef KERNEL
|
||||
#if defined(__has_include)
|
||||
#if __has_include(<mach/mig_voucher_support.h>)
|
||||
#ifndef USING_VOUCHERS
|
||||
#define USING_VOUCHERS
|
||||
#endif
|
||||
#ifndef __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#define __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
extern boolean_t voucher_mach_msg_set(mach_msg_header_t *msg) __attribute__((weak_import));
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
#endif // __VOUCHER_FORWARD_TYPE_DECLS__
|
||||
#endif // __has_include(<mach/mach_voucher_types.h>)
|
||||
#endif // __has_include
|
||||
#endif // !KERNEL
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
|
||||
#ifdef AUTOTEST
|
||||
#ifndef FUNCTION_PTR_T
|
||||
#define FUNCTION_PTR_T
|
||||
typedef void (*function_ptr_t)(mach_port_t, char *, mach_msg_type_number_t);
|
||||
typedef struct {
|
||||
char *name;
|
||||
function_ptr_t function;
|
||||
} function_table_entry;
|
||||
typedef function_table_entry *function_table_t;
|
||||
#endif /* FUNCTION_PTR_T */
|
||||
#endif /* AUTOTEST */
|
||||
|
||||
#ifndef mach_exc_MSG_COUNT
|
||||
#define mach_exc_MSG_COUNT 3
|
||||
#endif /* mach_exc_MSG_COUNT */
|
||||
|
||||
#include <mach/std_types.h>
|
||||
#include <mach/mig.h>
|
||||
#include <mach/mig.h>
|
||||
#include <mach/mach_types.h>
|
||||
|
||||
#ifdef __BeforeMigUserHeader
|
||||
__BeforeMigUserHeader
|
||||
#endif /* __BeforeMigUserHeader */
|
||||
|
||||
#include <sys/cdefs.h>
|
||||
__BEGIN_DECLS
|
||||
|
||||
|
||||
/* Routine mach_exception_raise */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t mach_exception_raise
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt
|
||||
);
|
||||
|
||||
/* Routine mach_exception_raise_state */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t mach_exception_raise_state
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
|
||||
/* Routine mach_exception_raise_state_identity */
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
kern_return_t mach_exception_raise_state_identity
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
);
|
||||
|
||||
__END_DECLS
|
||||
|
||||
/********************** Caution **************************/
|
||||
/* The following data types should be used to calculate */
|
||||
/* maximum message sizes only. The actual message may be */
|
||||
/* smaller, and the position of the arguments within the */
|
||||
/* message layout may vary from what is presented here. */
|
||||
/* For example, if any of the arguments are variable- */
|
||||
/* sized, and less than the maximum is sent, the data */
|
||||
/* will be packed tight in the actual message to reduce */
|
||||
/* the presence of holes. */
|
||||
/********************** Caution **************************/
|
||||
|
||||
/* typedefs for all requests */
|
||||
|
||||
#ifndef __Request__mach_exc_subsystem__defined
|
||||
#define __Request__mach_exc_subsystem__defined
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
} __Request__mach_exception_raise_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} __Request__mach_exception_raise_state_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} __Request__mach_exception_raise_state_identity_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
#endif /* !__Request__mach_exc_subsystem__defined */
|
||||
|
||||
/* union of all requests */
|
||||
|
||||
#ifndef __RequestUnion__mach_exc_subsystem__defined
|
||||
#define __RequestUnion__mach_exc_subsystem__defined
|
||||
union __RequestUnion__mach_exc_subsystem {
|
||||
__Request__mach_exception_raise_t Request_mach_exception_raise;
|
||||
__Request__mach_exception_raise_state_t Request_mach_exception_raise_state;
|
||||
__Request__mach_exception_raise_state_identity_t Request_mach_exception_raise_state_identity;
|
||||
};
|
||||
#endif /* !__RequestUnion__mach_exc_subsystem__defined */
|
||||
/* typedefs for all replies */
|
||||
|
||||
#ifndef __Reply__mach_exc_subsystem__defined
|
||||
#define __Reply__mach_exc_subsystem__defined
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
} __Reply__mach_exception_raise_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply__mach_exception_raise_state_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply__mach_exception_raise_state_identity_t;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
#endif /* !__Reply__mach_exc_subsystem__defined */
|
||||
|
||||
/* union of all replies */
|
||||
|
||||
#ifndef __ReplyUnion__mach_exc_subsystem__defined
|
||||
#define __ReplyUnion__mach_exc_subsystem__defined
|
||||
union __ReplyUnion__mach_exc_subsystem {
|
||||
__Reply__mach_exception_raise_t Reply_mach_exception_raise;
|
||||
__Reply__mach_exception_raise_state_t Reply_mach_exception_raise_state;
|
||||
__Reply__mach_exception_raise_state_identity_t Reply_mach_exception_raise_state_identity;
|
||||
};
|
||||
#endif /* !__RequestUnion__mach_exc_subsystem__defined */
|
||||
|
||||
#ifndef subsystem_to_name_map_mach_exc
|
||||
#define subsystem_to_name_map_mach_exc \
|
||||
{ "mach_exception_raise", 2401 },\
|
||||
{ "mach_exception_raise_state", 2402 },\
|
||||
{ "mach_exception_raise_state_identity", 2403 }
|
||||
#endif
|
||||
|
||||
#ifdef __AfterMigUserHeader
|
||||
__AfterMigUserHeader
|
||||
#endif /* __AfterMigUserHeader */
|
||||
|
||||
#endif /* _mach_exc_user_ */
|
768
vendor/github.com/derekparker/delve/pkg/proc/mach_exc_user_darwin.c
generated
vendored
Normal file
768
vendor/github.com/derekparker/delve/pkg/proc/mach_exc_user_darwin.c
generated
vendored
Normal file
@ -0,0 +1,768 @@
|
||||
/*
|
||||
* IDENTIFICATION:
|
||||
* stub generated Sat Feb 21 18:10:52 2015
|
||||
* with a MiG generated by bootstrap_cmds-91
|
||||
* OPTIONS:
|
||||
*/
|
||||
#define __MIG_check__Reply__mach_exc_subsystem__ 1
|
||||
|
||||
#include "mach_exc.h"
|
||||
|
||||
|
||||
#ifndef mig_internal
|
||||
#define mig_internal static __inline__
|
||||
#endif /* mig_internal */
|
||||
|
||||
#ifndef mig_external
|
||||
#define mig_external
|
||||
#endif /* mig_external */
|
||||
|
||||
#if !defined(__MigTypeCheck) && defined(TypeCheck)
|
||||
#define __MigTypeCheck TypeCheck /* Legacy setting */
|
||||
#endif /* !defined(__MigTypeCheck) */
|
||||
|
||||
#if !defined(__MigKernelSpecificCode) && defined(_MIG_KERNEL_SPECIFIC_CODE_)
|
||||
#define __MigKernelSpecificCode _MIG_KERNEL_SPECIFIC_CODE_ /* Legacy setting */
|
||||
#endif /* !defined(__MigKernelSpecificCode) */
|
||||
|
||||
#ifndef LimitCheck
|
||||
#define LimitCheck 0
|
||||
#endif /* LimitCheck */
|
||||
|
||||
#ifndef min
|
||||
#define min(a,b) ( ((a) < (b))? (a): (b) )
|
||||
#endif /* min */
|
||||
|
||||
#if !defined(_WALIGN_)
|
||||
#define _WALIGN_(x) (((x) + 3) & ~3)
|
||||
#endif /* !defined(_WALIGN_) */
|
||||
|
||||
#if !defined(_WALIGNSZ_)
|
||||
#define _WALIGNSZ_(x) _WALIGN_(sizeof(x))
|
||||
#endif /* !defined(_WALIGNSZ_) */
|
||||
|
||||
#ifndef UseStaticTemplates
|
||||
#define UseStaticTemplates 0
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#ifndef __MachMsgErrorWithTimeout
|
||||
#define __MachMsgErrorWithTimeout(_R_) { \
|
||||
switch (_R_) { \
|
||||
case MACH_SEND_INVALID_DATA: \
|
||||
case MACH_SEND_INVALID_DEST: \
|
||||
case MACH_SEND_INVALID_HEADER: \
|
||||
mig_put_reply_port(InP->Head.msgh_reply_port); \
|
||||
break; \
|
||||
case MACH_SEND_TIMED_OUT: \
|
||||
case MACH_RCV_TIMED_OUT: \
|
||||
default: \
|
||||
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
|
||||
} \
|
||||
}
|
||||
#endif /* __MachMsgErrorWithTimeout */
|
||||
|
||||
#ifndef __MachMsgErrorWithoutTimeout
|
||||
#define __MachMsgErrorWithoutTimeout(_R_) { \
|
||||
switch (_R_) { \
|
||||
case MACH_SEND_INVALID_DATA: \
|
||||
case MACH_SEND_INVALID_DEST: \
|
||||
case MACH_SEND_INVALID_HEADER: \
|
||||
mig_put_reply_port(InP->Head.msgh_reply_port); \
|
||||
break; \
|
||||
default: \
|
||||
mig_dealloc_reply_port(InP->Head.msgh_reply_port); \
|
||||
} \
|
||||
}
|
||||
#endif /* __MachMsgErrorWithoutTimeout */
|
||||
|
||||
#ifndef __DeclareSendRpc
|
||||
#define __DeclareSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __DeclareSendRpc */
|
||||
|
||||
#ifndef __BeforeSendRpc
|
||||
#define __BeforeSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __BeforeSendRpc */
|
||||
|
||||
#ifndef __AfterSendRpc
|
||||
#define __AfterSendRpc(_NUM_, _NAME_)
|
||||
#endif /* __AfterSendRpc */
|
||||
|
||||
#ifndef __DeclareSendSimple
|
||||
#define __DeclareSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __DeclareSendSimple */
|
||||
|
||||
#ifndef __BeforeSendSimple
|
||||
#define __BeforeSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __BeforeSendSimple */
|
||||
|
||||
#ifndef __AfterSendSimple
|
||||
#define __AfterSendSimple(_NUM_, _NAME_)
|
||||
#endif /* __AfterSendSimple */
|
||||
|
||||
#define msgh_request_port msgh_remote_port
|
||||
#define msgh_reply_port msgh_local_port
|
||||
|
||||
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__mach_exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__mach_exception_raise_t__defined)
|
||||
#define __MIG_check__Reply__mach_exception_raise_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__mach_exception_raise_t(__Reply__mach_exception_raise_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__mach_exception_raise_t __Reply;
|
||||
if (Out0P->Head.msgh_id != 2505) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
(Out0P->Head.msgh_size != (mach_msg_size_t)sizeof(__Reply)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
{
|
||||
return Out0P->RetCode;
|
||||
}
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__mach_exception_raise_t__defined) */
|
||||
#endif /* __MIG_check__Reply__mach_exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine mach_exception_raise */
|
||||
mig_external kern_return_t mach_exception_raise
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
|
||||
#ifdef __MIG_check__Reply__mach_exception_raise_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__mach_exception_raise_t__defined */
|
||||
|
||||
__DeclareSendRpc(2405, "mach_exception_raise")
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t threadTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t taskTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->msgh_body.msgh_descriptor_count = 2;
|
||||
#if UseStaticTemplates
|
||||
InP->thread = threadTemplate;
|
||||
InP->thread.name = thread;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->thread.name = thread;
|
||||
InP->thread.disposition = 19;
|
||||
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
InP->task = taskTemplate;
|
||||
InP->task.name = task;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->task.name = task;
|
||||
InP->task.disposition = 19;
|
||||
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 8 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 16) + ((8 * codeCnt));
|
||||
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2405;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2405, "mach_exception_raise")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2405, "mach_exception_raise")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__mach_exception_raise_t__defined)
|
||||
check_result = __MIG_check__Reply__mach_exception_raise_t((__Reply__mach_exception_raise_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__mach_exception_raise_t__defined) */
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__mach_exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__mach_exception_raise_state_t__defined)
|
||||
#define __MIG_check__Reply__mach_exception_raise_state_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__mach_exception_raise_state_t(__Reply__mach_exception_raise_state_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__mach_exception_raise_state_t __Reply;
|
||||
#if __MigTypeCheck
|
||||
unsigned int msgh_size;
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->Head.msgh_id != 2506) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
msgh_size = Out0P->Head.msgh_size;
|
||||
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
|
||||
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
|
||||
Out0P->RetCode == KERN_SUCCESS)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->RetCode != KERN_SUCCESS) {
|
||||
return ((mig_reply_error_t *)Out0P)->RetCode;
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ( Out0P->new_stateCnt > 224 )
|
||||
return MIG_TYPE_ERROR;
|
||||
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
|
||||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
return MACH_MSG_SUCCESS;
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__mach_exception_raise_state_t__defined) */
|
||||
#endif /* __MIG_check__Reply__mach_exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine mach_exception_raise_state */
|
||||
mig_external kern_return_t mach_exception_raise_state
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
exception_type_t exception,
|
||||
const mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
const thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
unsigned int msgh_size_delta;
|
||||
|
||||
|
||||
#ifdef __MIG_check__Reply__mach_exception_raise_state_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__mach_exception_raise_state_t__defined */
|
||||
|
||||
__DeclareSendRpc(2406, "mach_exception_raise_state")
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 8 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size_delta = (8 * codeCnt);
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 912) + msgh_size_delta;
|
||||
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 16);
|
||||
|
||||
InP->flavor = *flavor;
|
||||
|
||||
if (old_stateCnt > 224) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
|
||||
|
||||
InP->old_stateCnt = old_stateCnt;
|
||||
|
||||
msgh_size += (4 * old_stateCnt);
|
||||
InP = &Mess.In;
|
||||
InP->Head.msgh_bits =
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2406;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2406, "mach_exception_raise_state")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2406, "mach_exception_raise_state")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__mach_exception_raise_state_t__defined)
|
||||
check_result = __MIG_check__Reply__mach_exception_raise_state_t((__Reply__mach_exception_raise_state_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__mach_exception_raise_state_t__defined) */
|
||||
|
||||
*flavor = Out0P->flavor;
|
||||
|
||||
if (Out0P->new_stateCnt > 224) {
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
|
||||
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
#if ( __MigTypeCheck )
|
||||
#if __MIG_check__Reply__mach_exc_subsystem__
|
||||
#if !defined(__MIG_check__Reply__mach_exception_raise_state_identity_t__defined)
|
||||
#define __MIG_check__Reply__mach_exception_raise_state_identity_t__defined
|
||||
|
||||
mig_internal kern_return_t __MIG_check__Reply__mach_exception_raise_state_identity_t(__Reply__mach_exception_raise_state_identity_t *Out0P)
|
||||
{
|
||||
|
||||
typedef __Reply__mach_exception_raise_state_identity_t __Reply;
|
||||
#if __MigTypeCheck
|
||||
unsigned int msgh_size;
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->Head.msgh_id != 2507) {
|
||||
if (Out0P->Head.msgh_id == MACH_NOTIFY_SEND_ONCE)
|
||||
{ return MIG_SERVER_DIED; }
|
||||
else
|
||||
{ return MIG_REPLY_MISMATCH; }
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
msgh_size = Out0P->Head.msgh_size;
|
||||
|
||||
if ((Out0P->Head.msgh_bits & MACH_MSGH_BITS_COMPLEX) ||
|
||||
((msgh_size > (mach_msg_size_t)sizeof(__Reply) || msgh_size < (mach_msg_size_t)(sizeof(__Reply) - 896)) &&
|
||||
(msgh_size != (mach_msg_size_t)sizeof(mig_reply_error_t) ||
|
||||
Out0P->RetCode == KERN_SUCCESS)))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
if (Out0P->RetCode != KERN_SUCCESS) {
|
||||
return ((mig_reply_error_t *)Out0P)->RetCode;
|
||||
}
|
||||
|
||||
#if __MigTypeCheck
|
||||
if ( Out0P->new_stateCnt > 224 )
|
||||
return MIG_TYPE_ERROR;
|
||||
if (((msgh_size - (mach_msg_size_t)(sizeof(__Reply) - 896)) / 4< Out0P->new_stateCnt) ||
|
||||
(msgh_size != (mach_msg_size_t)(sizeof(__Reply) - 896) + Out0P->new_stateCnt * 4))
|
||||
{ return MIG_TYPE_ERROR ; }
|
||||
#endif /* __MigTypeCheck */
|
||||
|
||||
return MACH_MSG_SUCCESS;
|
||||
}
|
||||
#endif /* !defined(__MIG_check__Reply__mach_exception_raise_state_identity_t__defined) */
|
||||
#endif /* __MIG_check__Reply__mach_exc_subsystem__ */
|
||||
#endif /* ( __MigTypeCheck ) */
|
||||
|
||||
|
||||
/* Routine mach_exception_raise_state_identity */
|
||||
mig_external kern_return_t mach_exception_raise_state_identity
|
||||
(
|
||||
mach_port_t exception_port,
|
||||
mach_port_t thread,
|
||||
mach_port_t task,
|
||||
exception_type_t exception,
|
||||
mach_exception_data_t code,
|
||||
mach_msg_type_number_t codeCnt,
|
||||
int *flavor,
|
||||
thread_state_t old_state,
|
||||
mach_msg_type_number_t old_stateCnt,
|
||||
thread_state_t new_state,
|
||||
mach_msg_type_number_t *new_stateCnt
|
||||
)
|
||||
{
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
/* start of the kernel processed data */
|
||||
mach_msg_body_t msgh_body;
|
||||
mach_msg_port_descriptor_t thread;
|
||||
mach_msg_port_descriptor_t task;
|
||||
/* end of the kernel processed data */
|
||||
NDR_record_t NDR;
|
||||
exception_type_t exception;
|
||||
mach_msg_type_number_t codeCnt;
|
||||
int64_t code[2];
|
||||
int flavor;
|
||||
mach_msg_type_number_t old_stateCnt;
|
||||
natural_t old_state[224];
|
||||
} Request;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
mach_msg_trailer_t trailer;
|
||||
} Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack(4)
|
||||
#endif
|
||||
typedef struct {
|
||||
mach_msg_header_t Head;
|
||||
NDR_record_t NDR;
|
||||
kern_return_t RetCode;
|
||||
int flavor;
|
||||
mach_msg_type_number_t new_stateCnt;
|
||||
natural_t new_state[224];
|
||||
} __Reply;
|
||||
#ifdef __MigPackStructs
|
||||
#pragma pack()
|
||||
#endif
|
||||
/*
|
||||
* typedef struct {
|
||||
* mach_msg_header_t Head;
|
||||
* NDR_record_t NDR;
|
||||
* kern_return_t RetCode;
|
||||
* } mig_reply_error_t;
|
||||
*/
|
||||
|
||||
union {
|
||||
Request In;
|
||||
Reply Out;
|
||||
} Mess;
|
||||
|
||||
Request *InP = &Mess.In;
|
||||
Reply *Out0P = &Mess.Out;
|
||||
|
||||
mach_msg_return_t msg_result;
|
||||
unsigned int msgh_size;
|
||||
unsigned int msgh_size_delta;
|
||||
|
||||
|
||||
#ifdef __MIG_check__Reply__mach_exception_raise_state_identity_t__defined
|
||||
kern_return_t check_result;
|
||||
#endif /* __MIG_check__Reply__mach_exception_raise_state_identity_t__defined */
|
||||
|
||||
__DeclareSendRpc(2407, "mach_exception_raise_state_identity")
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t threadTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
const static mach_msg_port_descriptor_t taskTemplate = {
|
||||
/* name = */ MACH_PORT_NULL,
|
||||
/* pad1 = */ 0,
|
||||
/* pad2 = */ 0,
|
||||
/* disp = */ 19,
|
||||
/* type = */ MACH_MSG_PORT_DESCRIPTOR,
|
||||
};
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->msgh_body.msgh_descriptor_count = 2;
|
||||
#if UseStaticTemplates
|
||||
InP->thread = threadTemplate;
|
||||
InP->thread.name = thread;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->thread.name = thread;
|
||||
InP->thread.disposition = 19;
|
||||
InP->thread.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
#if UseStaticTemplates
|
||||
InP->task = taskTemplate;
|
||||
InP->task.name = task;
|
||||
#else /* UseStaticTemplates */
|
||||
InP->task.name = task;
|
||||
InP->task.disposition = 19;
|
||||
InP->task.type = MACH_MSG_PORT_DESCRIPTOR;
|
||||
#endif /* UseStaticTemplates */
|
||||
|
||||
InP->NDR = NDR_record;
|
||||
|
||||
InP->exception = exception;
|
||||
|
||||
if (codeCnt > 2) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->code, (const char *) code, 8 * codeCnt);
|
||||
|
||||
InP->codeCnt = codeCnt;
|
||||
|
||||
msgh_size_delta = (8 * codeCnt);
|
||||
msgh_size = (mach_msg_size_t)(sizeof(Request) - 912) + msgh_size_delta;
|
||||
InP = (Request *) ((pointer_t) InP + msgh_size_delta - 16);
|
||||
|
||||
InP->flavor = *flavor;
|
||||
|
||||
if (old_stateCnt > 224) {
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) InP->old_state, (const char *) old_state, 4 * old_stateCnt);
|
||||
|
||||
InP->old_stateCnt = old_stateCnt;
|
||||
|
||||
msgh_size += (4 * old_stateCnt);
|
||||
InP = &Mess.In;
|
||||
InP->Head.msgh_bits = MACH_MSGH_BITS_COMPLEX|
|
||||
MACH_MSGH_BITS(19, MACH_MSG_TYPE_MAKE_SEND_ONCE);
|
||||
/* msgh_size passed as argument */
|
||||
InP->Head.msgh_request_port = exception_port;
|
||||
InP->Head.msgh_reply_port = mig_get_reply_port();
|
||||
InP->Head.msgh_id = 2407;
|
||||
|
||||
/* BEGIN VOUCHER CODE */
|
||||
|
||||
#ifdef USING_VOUCHERS
|
||||
if (voucher_mach_msg_set != NULL) {
|
||||
voucher_mach_msg_set(&InP->Head);
|
||||
}
|
||||
#endif // USING_VOUCHERS
|
||||
|
||||
/* END VOUCHER CODE */
|
||||
|
||||
__BeforeSendRpc(2407, "mach_exception_raise_state_identity")
|
||||
msg_result = mach_msg(&InP->Head, MACH_SEND_MSG|MACH_RCV_MSG|MACH_MSG_OPTION_NONE, msgh_size, (mach_msg_size_t)sizeof(Reply), InP->Head.msgh_reply_port, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
__AfterSendRpc(2407, "mach_exception_raise_state_identity")
|
||||
if (msg_result != MACH_MSG_SUCCESS) {
|
||||
__MachMsgErrorWithoutTimeout(msg_result);
|
||||
{ return msg_result; }
|
||||
}
|
||||
|
||||
|
||||
#if defined(__MIG_check__Reply__mach_exception_raise_state_identity_t__defined)
|
||||
check_result = __MIG_check__Reply__mach_exception_raise_state_identity_t((__Reply__mach_exception_raise_state_identity_t *)Out0P);
|
||||
if (check_result != MACH_MSG_SUCCESS)
|
||||
{ return check_result; }
|
||||
#endif /* defined(__MIG_check__Reply__mach_exception_raise_state_identity_t__defined) */
|
||||
|
||||
*flavor = Out0P->flavor;
|
||||
|
||||
if (Out0P->new_stateCnt > 224) {
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * 224);
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
{ return MIG_ARRAY_TOO_LARGE; }
|
||||
}
|
||||
(void)memcpy((char *) new_state, (const char *) Out0P->new_state, 4 * Out0P->new_stateCnt);
|
||||
|
||||
*new_stateCnt = Out0P->new_stateCnt;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
57
vendor/github.com/derekparker/delve/pkg/proc/mem.go
generated
vendored
Normal file
57
vendor/github.com/derekparker/delve/pkg/proc/mem.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
package proc
|
||||
|
||||
const cacheEnabled = true
|
||||
|
||||
type memoryReadWriter interface {
|
||||
readMemory(addr uintptr, size int) (data []byte, err error)
|
||||
writeMemory(addr uintptr, data []byte) (written int, err error)
|
||||
}
|
||||
|
||||
type memCache struct {
|
||||
cacheAddr uintptr
|
||||
cache []byte
|
||||
mem memoryReadWriter
|
||||
}
|
||||
|
||||
func (m *memCache) contains(addr uintptr, size int) bool {
|
||||
return addr >= m.cacheAddr && addr <= (m.cacheAddr+uintptr(len(m.cache)-size))
|
||||
}
|
||||
|
||||
func (m *memCache) readMemory(addr uintptr, size int) (data []byte, err error) {
|
||||
if m.contains(addr, size) {
|
||||
d := make([]byte, size)
|
||||
copy(d, m.cache[addr-m.cacheAddr:])
|
||||
return d, nil
|
||||
}
|
||||
|
||||
return m.mem.readMemory(addr, size)
|
||||
}
|
||||
|
||||
func (m *memCache) writeMemory(addr uintptr, data []byte) (written int, err error) {
|
||||
return m.mem.writeMemory(addr, data)
|
||||
}
|
||||
|
||||
func cacheMemory(mem memoryReadWriter, addr uintptr, size int) memoryReadWriter {
|
||||
if !cacheEnabled {
|
||||
return mem
|
||||
}
|
||||
if size <= 0 {
|
||||
return mem
|
||||
}
|
||||
if cacheMem, isCache := mem.(*memCache); isCache {
|
||||
if cacheMem.contains(addr, size) {
|
||||
return mem
|
||||
} else {
|
||||
cache, err := cacheMem.mem.readMemory(addr, size)
|
||||
if err != nil {
|
||||
return mem
|
||||
}
|
||||
return &memCache{addr, cache, mem}
|
||||
}
|
||||
}
|
||||
cache, err := mem.readMemory(addr, size)
|
||||
if err != nil {
|
||||
return mem
|
||||
}
|
||||
return &memCache{addr, cache, mem}
|
||||
}
|
189
vendor/github.com/derekparker/delve/pkg/proc/moduledata.go
generated
vendored
Normal file
189
vendor/github.com/derekparker/delve/pkg/proc/moduledata.go
generated
vendored
Normal file
@ -0,0 +1,189 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"go/constant"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// delve counterpart to runtime.moduledata
|
||||
type moduleData struct {
|
||||
types, etypes uintptr
|
||||
typemapVar *Variable
|
||||
}
|
||||
|
||||
func (dbp *Process) loadModuleData() (err error) {
|
||||
dbp.loadModuleDataOnce.Do(func() {
|
||||
scope := &EvalScope{Thread: dbp.currentThread, PC: 0, CFA: 0}
|
||||
var md *Variable
|
||||
md, err = scope.packageVarAddr("runtime.firstmoduledata")
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
for md.Addr != 0 {
|
||||
var typesVar, etypesVar, nextVar, typemapVar *Variable
|
||||
var types, etypes uint64
|
||||
|
||||
if typesVar, err = md.structMember("types"); err != nil {
|
||||
return
|
||||
}
|
||||
if etypesVar, err = md.structMember("etypes"); err != nil {
|
||||
return
|
||||
}
|
||||
if nextVar, err = md.structMember("next"); err != nil {
|
||||
return
|
||||
}
|
||||
if typemapVar, err = md.structMember("typemap"); err != nil {
|
||||
return
|
||||
}
|
||||
if types, err = typesVar.asUint(); err != nil {
|
||||
return
|
||||
}
|
||||
if etypes, err = etypesVar.asUint(); err != nil {
|
||||
return
|
||||
}
|
||||
|
||||
dbp.moduleData = append(dbp.moduleData, moduleData{uintptr(types), uintptr(etypes), typemapVar})
|
||||
|
||||
md = nextVar.maybeDereference()
|
||||
if md.Unreadable != nil {
|
||||
err = md.Unreadable
|
||||
return
|
||||
}
|
||||
}
|
||||
})
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
func (dbp *Process) resolveTypeOff(typeAddr uintptr, off uintptr) (*Variable, error) {
|
||||
// See runtime.(*_type).typeOff in $GOROOT/src/runtime/type.go
|
||||
if err := dbp.loadModuleData(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var md *moduleData
|
||||
for i := range dbp.moduleData {
|
||||
if typeAddr >= dbp.moduleData[i].types && typeAddr < dbp.moduleData[i].etypes {
|
||||
md = &dbp.moduleData[i]
|
||||
}
|
||||
}
|
||||
|
||||
rtyp, err := dbp.findType("runtime._type")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if md == nil {
|
||||
v, err := dbp.reflectOffsMapAccess(off)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v.loadValue(LoadConfig{false, 1, 0, 0, -1})
|
||||
addr, _ := constant.Int64Val(v.Value)
|
||||
return v.newVariable(v.Name, uintptr(addr), rtyp), nil
|
||||
}
|
||||
|
||||
if t, _ := md.typemapVar.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.currentThread)); t != nil {
|
||||
return t, nil
|
||||
}
|
||||
|
||||
res := md.types + uintptr(off)
|
||||
|
||||
return dbp.currentThread.newVariable("", res, rtyp), nil
|
||||
}
|
||||
|
||||
func (dbp *Process) resolveNameOff(typeAddr uintptr, off uintptr) (name, tag string, pkgpathoff int32, err error) {
|
||||
// See runtime.resolveNameOff in $GOROOT/src/runtime/type.go
|
||||
if err = dbp.loadModuleData(); err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
for _, md := range dbp.moduleData {
|
||||
if typeAddr >= md.types && typeAddr < md.etypes {
|
||||
return dbp.loadName(md.types + off)
|
||||
}
|
||||
}
|
||||
|
||||
v, err := dbp.reflectOffsMapAccess(off)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
resv := v.maybeDereference()
|
||||
if resv.Unreadable != nil {
|
||||
return "", "", 0, resv.Unreadable
|
||||
}
|
||||
|
||||
return dbp.loadName(resv.Addr)
|
||||
}
|
||||
|
||||
func (dbp *Process) reflectOffsMapAccess(off uintptr) (*Variable, error) {
|
||||
scope := &EvalScope{Thread: dbp.currentThread, PC: 0, CFA: 0}
|
||||
reflectOffs, err := scope.packageVarAddr("runtime.reflectOffs")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
reflectOffsm, err := reflectOffs.structMember("m")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return reflectOffsm.mapAccess(newConstant(constant.MakeUint64(uint64(off)), dbp.currentThread))
|
||||
}
|
||||
|
||||
const (
|
||||
// flags for the name struct (see 'type name struct' in $GOROOT/src/reflect/type.go)
|
||||
nameflagExported = 1 << 0
|
||||
nameflagHasTag = 1 << 1
|
||||
nameflagHasPkg = 1 << 2
|
||||
)
|
||||
|
||||
func (dbp *Process) loadName(addr uintptr) (name, tag string, pkgpathoff int32, err error) {
|
||||
off := addr
|
||||
namedata, err := dbp.currentThread.readMemory(off, 3)
|
||||
off += 3
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
namelen := uint16(namedata[1]<<8) | uint16(namedata[2])
|
||||
|
||||
rawstr, err := dbp.currentThread.readMemory(off, int(namelen))
|
||||
off += uintptr(namelen)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
name = string(rawstr)
|
||||
|
||||
if namedata[0]&nameflagHasTag != 0 {
|
||||
taglendata, err := dbp.currentThread.readMemory(off, 2)
|
||||
off += 2
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
taglen := uint16(taglendata[0]<<8) | uint16(taglendata[1])
|
||||
|
||||
rawstr, err := dbp.currentThread.readMemory(off, int(taglen))
|
||||
off += uintptr(taglen)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
tag = string(rawstr)
|
||||
}
|
||||
|
||||
if namedata[0]&nameflagHasPkg != 0 {
|
||||
pkgdata, err := dbp.currentThread.readMemory(off, 4)
|
||||
if err != nil {
|
||||
return "", "", 0, err
|
||||
}
|
||||
|
||||
// see func pkgPath in $GOROOT/src/reflect/type.go
|
||||
copy((*[4]byte)(unsafe.Pointer(&pkgpathoff))[:], pkgdata)
|
||||
}
|
||||
|
||||
return name, tag, pkgpathoff, nil
|
||||
}
|
986
vendor/github.com/derekparker/delve/pkg/proc/proc.go
generated
vendored
Normal file
986
vendor/github.com/derekparker/delve/pkg/proc/proc.go
generated
vendored
Normal file
@ -0,0 +1,986 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
||||
"golang.org/x/debug/dwarf"
|
||||
)
|
||||
|
||||
// Process represents all of the information the debugger
|
||||
// is holding onto regarding the process we are debugging.
|
||||
type Process struct {
|
||||
pid int // Process Pid
|
||||
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
|
||||
|
||||
// Breakpoint table, holds information on breakpoints.
|
||||
// Maps instruction address to Breakpoint struct.
|
||||
breakpoints map[uint64]*Breakpoint
|
||||
|
||||
// List of threads mapped as such: pid -> *Thread
|
||||
threads map[int]*Thread
|
||||
|
||||
// Active thread
|
||||
currentThread *Thread
|
||||
|
||||
// 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
|
||||
selectedGoroutine *G
|
||||
|
||||
// Maps package names to package paths, needed to lookup types inside DWARF info
|
||||
packageMap map[string]string
|
||||
|
||||
allGCache []*G
|
||||
dwarf *dwarf.Data
|
||||
goSymTable *gosym.Table
|
||||
frameEntries frame.FrameDescriptionEntries
|
||||
lineInfo line.DebugLines
|
||||
os *OSProcessDetails
|
||||
arch Arch
|
||||
breakpointIDCounter int
|
||||
internalBreakpointIDCounter int
|
||||
firstStart bool
|
||||
halt bool
|
||||
exited bool
|
||||
ptraceChan chan func()
|
||||
ptraceDoneChan chan interface{}
|
||||
types map[string]dwarf.Offset
|
||||
functions []functionDebugInfo
|
||||
|
||||
loadModuleDataOnce sync.Once
|
||||
moduleData []moduleData
|
||||
nameOfRuntimeType map[uintptr]nameOfRuntimeTypeEntry
|
||||
}
|
||||
|
||||
type functionDebugInfo struct {
|
||||
lowpc, highpc uint64
|
||||
offset dwarf.Offset
|
||||
}
|
||||
|
||||
var NotExecutableErr = errors.New("not an executable file")
|
||||
|
||||
// New returns an initialized Process struct. Before returning,
|
||||
// it will also launch a goroutine in order to handle ptrace(2)
|
||||
// functions. For more information, see the documentation on
|
||||
// `handlePtraceFuncs`.
|
||||
func New(pid int) *Process {
|
||||
dbp := &Process{
|
||||
pid: pid,
|
||||
threads: make(map[int]*Thread),
|
||||
breakpoints: make(map[uint64]*Breakpoint),
|
||||
firstStart: true,
|
||||
os: new(OSProcessDetails),
|
||||
ptraceChan: make(chan func()),
|
||||
ptraceDoneChan: make(chan interface{}),
|
||||
nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry),
|
||||
}
|
||||
// TODO: find better way to determine proc arch (perhaps use executable file info)
|
||||
switch runtime.GOARCH {
|
||||
case "amd64":
|
||||
dbp.arch = AMD64Arch()
|
||||
}
|
||||
go dbp.handlePtraceFuncs()
|
||||
return dbp
|
||||
}
|
||||
|
||||
// ProcessExitedError indicates that the process has exited and contains both
|
||||
// process id and exit status.
|
||||
type ProcessExitedError struct {
|
||||
Pid int
|
||||
Status int
|
||||
}
|
||||
|
||||
func (pe ProcessExitedError) Error() string {
|
||||
return fmt.Sprintf("Process %d has exited with status %d", pe.Pid, pe.Status)
|
||||
}
|
||||
|
||||
// Detach from the process being debugged, optionally killing it.
|
||||
func (dbp *Process) Detach(kill bool) (err error) {
|
||||
if dbp.exited {
|
||||
return nil
|
||||
}
|
||||
if dbp.Running() {
|
||||
if err = dbp.Halt(); err != nil {
|
||||
return
|
||||
}
|
||||
}
|
||||
if !kill {
|
||||
// Clean up any breakpoints we've set.
|
||||
for _, bp := range dbp.breakpoints {
|
||||
if bp != nil {
|
||||
_, err := dbp.ClearBreakpoint(bp.Addr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
dbp.execPtraceFunc(func() {
|
||||
err = PtraceDetach(dbp.pid, 0)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
if kill {
|
||||
err = killProcess(dbp.pid)
|
||||
}
|
||||
})
|
||||
return
|
||||
}
|
||||
|
||||
// Exited returns whether the debugged
|
||||
// process has exited.
|
||||
func (dbp *Process) Exited() bool {
|
||||
return dbp.exited
|
||||
}
|
||||
|
||||
// Running returns whether the debugged
|
||||
// process is currently executing.
|
||||
func (dbp *Process) Running() bool {
|
||||
for _, th := range dbp.threads {
|
||||
if th.running {
|
||||
return true
|
||||
}
|
||||
}
|
||||
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
|
||||
// to parse the following information:
|
||||
// * Dwarf .debug_frame section
|
||||
// * Dwarf .debug_line section
|
||||
// * Go symbol table.
|
||||
func (dbp *Process) LoadInformation(path string) error {
|
||||
var wg sync.WaitGroup
|
||||
|
||||
exe, path, err := dbp.findExecutable(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
fi, err := os.Stat(path)
|
||||
if err == nil {
|
||||
dbp.lastModified = fi.ModTime()
|
||||
}
|
||||
|
||||
wg.Add(5)
|
||||
go dbp.loadProcessInformation(&wg)
|
||||
go dbp.parseDebugFrame(exe, &wg)
|
||||
go dbp.obtainGoSymbols(exe, &wg)
|
||||
go dbp.parseDebugLineInfo(exe, &wg)
|
||||
go dbp.loadDebugInfoMaps(&wg)
|
||||
wg.Wait()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindFileLocation returns the PC for a given file:line.
|
||||
// Assumes that `file` is normailzed to lower case and '/' on Windows.
|
||||
func (dbp *Process) FindFileLocation(fileName string, lineno int) (uint64, error) {
|
||||
pc, fn, err := dbp.goSymTable.LineToPC(fileName, lineno)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if fn.Entry == pc {
|
||||
pc, _ = dbp.FirstPCAfterPrologue(fn, true)
|
||||
}
|
||||
return pc, nil
|
||||
}
|
||||
|
||||
// FindFunctionLocation finds address of a function's line
|
||||
// If firstLine == true is passed FindFunctionLocation will attempt to find the first line of the function
|
||||
// If lineOffset is passed FindFunctionLocation will return the address of that line
|
||||
// Pass lineOffset == 0 and firstLine == false if you want the address for the function's entry point
|
||||
// Note that setting breakpoints at that address will cause surprising behavior:
|
||||
// https://github.com/derekparker/delve/issues/170
|
||||
func (dbp *Process) FindFunctionLocation(funcName string, firstLine bool, lineOffset int) (uint64, error) {
|
||||
origfn := dbp.goSymTable.LookupFunc(funcName)
|
||||
if origfn == nil {
|
||||
return 0, fmt.Errorf("Could not find function %s\n", funcName)
|
||||
}
|
||||
|
||||
if firstLine {
|
||||
return dbp.FirstPCAfterPrologue(origfn, false)
|
||||
} else if lineOffset > 0 {
|
||||
filename, lineno, _ := dbp.goSymTable.PCToLine(origfn.Entry)
|
||||
breakAddr, _, err := dbp.goSymTable.LineToPC(filename, lineno+lineOffset)
|
||||
return breakAddr, err
|
||||
}
|
||||
|
||||
return origfn.Entry, nil
|
||||
}
|
||||
|
||||
// CurrentLocation returns the location of the current thread.
|
||||
func (dbp *Process) CurrentLocation() (*Location, error) {
|
||||
return dbp.currentThread.Location()
|
||||
}
|
||||
|
||||
// RequestManualStop sets the `halt` flag and
|
||||
// sends SIGSTOP to all threads.
|
||||
func (dbp *Process) RequestManualStop() error {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
dbp.halt = true
|
||||
return dbp.requestManualStop()
|
||||
}
|
||||
|
||||
// SetBreakpoint sets a breakpoint at addr, and stores it in the process wide
|
||||
// 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.
|
||||
func (dbp *Process) SetBreakpoint(addr uint64, kind BreakpointKind, cond ast.Expr) (*Breakpoint, error) {
|
||||
tid := dbp.currentThread.ID
|
||||
|
||||
if bp, ok := dbp.FindBreakpoint(addr); ok {
|
||||
return nil, BreakpointExistsError{bp.File, bp.Line, bp.Addr}
|
||||
}
|
||||
|
||||
f, l, fn := dbp.goSymTable.PCToLine(uint64(addr))
|
||||
if fn == nil {
|
||||
return nil, InvalidAddressError{address: addr}
|
||||
}
|
||||
|
||||
newBreakpoint := &Breakpoint{
|
||||
FunctionName: fn.Name,
|
||||
File: f,
|
||||
Line: l,
|
||||
Addr: addr,
|
||||
Kind: kind,
|
||||
Cond: cond,
|
||||
HitCount: map[int]uint64{},
|
||||
}
|
||||
|
||||
if kind != UserBreakpoint {
|
||||
dbp.internalBreakpointIDCounter++
|
||||
newBreakpoint.ID = dbp.internalBreakpointIDCounter
|
||||
} else {
|
||||
dbp.breakpointIDCounter++
|
||||
newBreakpoint.ID = dbp.breakpointIDCounter
|
||||
}
|
||||
|
||||
thread := dbp.threads[tid]
|
||||
originalData, err := thread.readMemory(uintptr(addr), dbp.arch.BreakpointSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := dbp.writeSoftwareBreakpoint(thread, addr); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
newBreakpoint.OriginalData = originalData
|
||||
dbp.breakpoints[addr] = newBreakpoint
|
||||
|
||||
return newBreakpoint, nil
|
||||
}
|
||||
|
||||
// ClearBreakpoint clears the breakpoint at addr.
|
||||
func (dbp *Process) ClearBreakpoint(addr uint64) (*Breakpoint, error) {
|
||||
if dbp.exited {
|
||||
return nil, &ProcessExitedError{}
|
||||
}
|
||||
bp, ok := dbp.FindBreakpoint(addr)
|
||||
if !ok {
|
||||
return nil, NoBreakpointError{addr: addr}
|
||||
}
|
||||
|
||||
if _, err := bp.Clear(dbp.currentThread); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
delete(dbp.breakpoints, addr)
|
||||
|
||||
return bp, nil
|
||||
}
|
||||
|
||||
// Status returns the status of the current main thread context.
|
||||
func (dbp *Process) Status() *WaitStatus {
|
||||
return dbp.currentThread.Status
|
||||
}
|
||||
|
||||
// Next continues execution until the next source line.
|
||||
func (dbp *Process) Next() (err error) {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
for i := range dbp.breakpoints {
|
||||
if dbp.breakpoints[i].Internal() {
|
||||
return fmt.Errorf("next while nexting")
|
||||
}
|
||||
}
|
||||
|
||||
if err = dbp.next(false); err != nil {
|
||||
switch err.(type) {
|
||||
case ThreadBlockedError, NoReturnAddr: // Noop
|
||||
default:
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return dbp.Continue()
|
||||
}
|
||||
|
||||
// Continue continues execution of the debugged
|
||||
// process. It will continue until it hits a breakpoint
|
||||
// or is otherwise stopped.
|
||||
func (dbp *Process) Continue() error {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
for {
|
||||
if err := dbp.resume(); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
dbp.allGCache = nil
|
||||
for _, th := range dbp.threads {
|
||||
th.clearBreakpointState()
|
||||
}
|
||||
|
||||
trapthread, err := dbp.trapWait(-1)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dbp.Halt(); err != nil {
|
||||
return dbp.exitGuard(err)
|
||||
}
|
||||
if err := dbp.setCurrentBreakpoints(trapthread); err != nil {
|
||||
return err
|
||||
}
|
||||
if err := dbp.pickCurrentThread(trapthread); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
switch {
|
||||
case dbp.currentThread.CurrentBreakpoint == nil:
|
||||
// runtime.Breakpoint or manual stop
|
||||
if dbp.currentThread.onRuntimeBreakpoint() {
|
||||
// Single-step current thread until we exit runtime.breakpoint and
|
||||
// runtime.Breakpoint.
|
||||
// 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
|
||||
}
|
||||
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()
|
||||
case dbp.currentThread.onTriggeredInternalBreakpoint():
|
||||
if dbp.currentThread.CurrentBreakpoint.Kind == StepBreakpoint {
|
||||
// See description of proc.(*Process).next for the meaning of StepBreakpoints
|
||||
if err := dbp.conditionErrors(); err != nil {
|
||||
return err
|
||||
}
|
||||
pc, err := dbp.currentThread.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
text, err := dbp.currentThread.Disassemble(pc, pc+maxInstructionLength, true)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// here we either set a breakpoint into the destination of the CALL
|
||||
// instruction or we determined that the called function is hidden,
|
||||
// either way we need to resume execution
|
||||
if err = dbp.setStepIntoBreakpoint(text, sameGoroutineCondition(dbp.selectedGoroutine)); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
if err := dbp.ClearInternalBreakpoints(); err != nil {
|
||||
return err
|
||||
}
|
||||
return dbp.conditionErrors()
|
||||
}
|
||||
case dbp.currentThread.onTriggeredBreakpoint():
|
||||
onNextGoroutine, err := dbp.currentThread.onNextGoroutine()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if onNextGoroutine {
|
||||
err := dbp.ClearInternalBreakpoints()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return dbp.conditionErrors()
|
||||
default:
|
||||
// not a manual stop, not on runtime.Breakpoint, not on a breakpoint, just repeat
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) conditionErrors() error {
|
||||
var condErr error
|
||||
for _, th := range dbp.threads {
|
||||
if th.CurrentBreakpoint != nil && th.BreakpointConditionError != nil {
|
||||
if condErr == nil {
|
||||
condErr = th.BreakpointConditionError
|
||||
} else {
|
||||
return fmt.Errorf("multiple errors evaluating conditions")
|
||||
}
|
||||
}
|
||||
}
|
||||
return condErr
|
||||
}
|
||||
|
||||
// pick a new dbp.currentThread, with the following priority:
|
||||
// - a thread with onTriggeredInternalBreakpoint() == true
|
||||
// - a thread with onTriggeredBreakpoint() == true (prioritizing trapthread)
|
||||
// - trapthread
|
||||
func (dbp *Process) pickCurrentThread(trapthread *Thread) error {
|
||||
for _, th := range dbp.threads {
|
||||
if th.onTriggeredInternalBreakpoint() {
|
||||
return dbp.SwitchThread(th.ID)
|
||||
}
|
||||
}
|
||||
if trapthread.onTriggeredBreakpoint() {
|
||||
return dbp.SwitchThread(trapthread.ID)
|
||||
}
|
||||
for _, th := range dbp.threads {
|
||||
if th.onTriggeredBreakpoint() {
|
||||
return dbp.SwitchThread(th.ID)
|
||||
}
|
||||
}
|
||||
return dbp.SwitchThread(trapthread.ID)
|
||||
}
|
||||
|
||||
// Step will continue until another source line is reached.
|
||||
// Will step into functions.
|
||||
func (dbp *Process) Step() (err error) {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
for i := range dbp.breakpoints {
|
||||
if dbp.breakpoints[i].Internal() {
|
||||
return fmt.Errorf("next while nexting")
|
||||
}
|
||||
}
|
||||
|
||||
if err = dbp.next(true); err != nil {
|
||||
switch err.(type) {
|
||||
case ThreadBlockedError, NoReturnAddr: // Noop
|
||||
default:
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
return dbp.Continue()
|
||||
}
|
||||
|
||||
// Returns an expression that evaluates to true when the current goroutine is g
|
||||
func sameGoroutineCondition(g *G) ast.Expr {
|
||||
if g == nil {
|
||||
return nil
|
||||
}
|
||||
return &ast.BinaryExpr{
|
||||
Op: token.EQL,
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.SelectorExpr{
|
||||
X: &ast.Ident{Name: "runtime"},
|
||||
Sel: &ast.Ident{Name: "curg"},
|
||||
},
|
||||
Sel: &ast.Ident{Name: "goid"},
|
||||
},
|
||||
Y: &ast.BasicLit{Kind: token.INT, Value: strconv.Itoa(g.ID)},
|
||||
}
|
||||
}
|
||||
|
||||
// StepInstruction will continue the current thread for exactly
|
||||
// one instruction. This method affects only the thread
|
||||
// asssociated with the selected goroutine. All other
|
||||
// threads will remain stopped.
|
||||
func (dbp *Process) StepInstruction() (err error) {
|
||||
if dbp.selectedGoroutine == nil {
|
||||
return errors.New("cannot single step: no selected goroutine")
|
||||
}
|
||||
if dbp.selectedGoroutine.thread == nil {
|
||||
// Step called on parked goroutine
|
||||
if _, err := dbp.SetBreakpoint(dbp.selectedGoroutine.PC, NextBreakpoint, sameGoroutineCondition(dbp.selectedGoroutine)); err != nil {
|
||||
return err
|
||||
}
|
||||
return dbp.Continue()
|
||||
}
|
||||
dbp.allGCache = nil
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
dbp.selectedGoroutine.thread.clearBreakpointState()
|
||||
err = dbp.selectedGoroutine.thread.StepInstruction()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return dbp.selectedGoroutine.thread.SetCurrentBreakpoint()
|
||||
}
|
||||
|
||||
// StepOut will continue until the current goroutine exits the
|
||||
// function currently being executed or a deferred function is executed
|
||||
func (dbp *Process) StepOut() error {
|
||||
cond := sameGoroutineCondition(dbp.selectedGoroutine)
|
||||
|
||||
topframe, err := topframe(dbp.selectedGoroutine, dbp.currentThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
pcs := []uint64{}
|
||||
|
||||
var deferpc uint64 = 0
|
||||
if filepath.Ext(topframe.Current.File) == ".go" {
|
||||
if dbp.selectedGoroutine != nil {
|
||||
deferPCEntry := dbp.selectedGoroutine.DeferPC()
|
||||
if deferPCEntry != 0 {
|
||||
_, _, deferfn := dbp.goSymTable.PCToLine(deferPCEntry)
|
||||
deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
pcs = append(pcs, deferpc)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if topframe.Ret == 0 && deferpc == 0 {
|
||||
return errors.New("nothing to stepout to")
|
||||
}
|
||||
|
||||
if deferpc != 0 && deferpc != topframe.Current.PC {
|
||||
bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, cond)
|
||||
if err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return err
|
||||
}
|
||||
}
|
||||
if bp != nil {
|
||||
// For StepOut we do not want to step into the deferred function
|
||||
// when it's called by runtime.deferreturn so we do not populate
|
||||
// DeferReturns.
|
||||
bp.DeferReturns = []uint64{}
|
||||
}
|
||||
}
|
||||
|
||||
if topframe.Ret != 0 {
|
||||
if err := dbp.setInternalBreakpoints(topframe.Current.PC, []uint64{topframe.Ret}, NextBreakpoint, cond); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return dbp.Continue()
|
||||
}
|
||||
|
||||
// SwitchThread changes from current thread to the thread specified by `tid`.
|
||||
func (dbp *Process) SwitchThread(tid int) error {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
if th, ok := dbp.threads[tid]; ok {
|
||||
dbp.currentThread = th
|
||||
dbp.selectedGoroutine, _ = dbp.currentThread.GetG()
|
||||
return nil
|
||||
}
|
||||
return fmt.Errorf("thread %d does not exist", tid)
|
||||
}
|
||||
|
||||
// SwitchGoroutine changes from current thread to the thread
|
||||
// running the specified goroutine.
|
||||
func (dbp *Process) SwitchGoroutine(gid int) error {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
g, err := dbp.FindGoroutine(gid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if g == nil {
|
||||
// user specified -1 and selectedGoroutine is nil
|
||||
return nil
|
||||
}
|
||||
if g.thread != nil {
|
||||
return dbp.SwitchThread(g.thread.ID)
|
||||
}
|
||||
dbp.selectedGoroutine = g
|
||||
return nil
|
||||
}
|
||||
|
||||
// GoroutinesInfo returns an array of G structures representing the information
|
||||
// Delve cares about from the internal runtime G structure.
|
||||
func (dbp *Process) GoroutinesInfo() ([]*G, error) {
|
||||
if dbp.exited {
|
||||
return nil, &ProcessExitedError{}
|
||||
}
|
||||
if dbp.allGCache != nil {
|
||||
return dbp.allGCache, nil
|
||||
}
|
||||
|
||||
var (
|
||||
threadg = map[int]*Thread{}
|
||||
allg []*G
|
||||
rdr = dbp.DwarfReader()
|
||||
)
|
||||
|
||||
for i := range dbp.threads {
|
||||
if dbp.threads[i].blocked() {
|
||||
continue
|
||||
}
|
||||
g, _ := dbp.threads[i].GetG()
|
||||
if g != nil {
|
||||
threadg[g.ID] = dbp.threads[i]
|
||||
}
|
||||
}
|
||||
|
||||
addr, err := rdr.AddrFor("runtime.allglen")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allglenBytes, err := dbp.currentThread.readMemory(uintptr(addr), 8)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
allglen := binary.LittleEndian.Uint64(allglenBytes)
|
||||
|
||||
rdr.Seek(0)
|
||||
allgentryaddr, err := rdr.AddrFor("runtime.allgs")
|
||||
if err != nil {
|
||||
// try old name (pre Go 1.6)
|
||||
allgentryaddr, err = rdr.AddrFor("runtime.allg")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
faddr, err := dbp.currentThread.readMemory(uintptr(allgentryaddr), dbp.arch.PtrSize())
|
||||
allgptr := binary.LittleEndian.Uint64(faddr)
|
||||
|
||||
for i := uint64(0); i < allglen; i++ {
|
||||
gvar, err := dbp.currentThread.newGVariable(uintptr(allgptr+(i*uint64(dbp.arch.PtrSize()))), true)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g, err := gvar.parseG()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if thread, allocated := threadg[g.ID]; allocated {
|
||||
loc, err := thread.Location()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
g.thread = thread
|
||||
// Prefer actual thread location information.
|
||||
g.CurrentLoc = *loc
|
||||
}
|
||||
if g.Status != Gdead {
|
||||
allg = append(allg, g)
|
||||
}
|
||||
}
|
||||
dbp.allGCache = allg
|
||||
return allg, nil
|
||||
}
|
||||
|
||||
func (g *G) Thread() *Thread {
|
||||
return g.thread
|
||||
}
|
||||
|
||||
// Halt stops all threads.
|
||||
func (dbp *Process) Halt() (err error) {
|
||||
if dbp.exited {
|
||||
return &ProcessExitedError{}
|
||||
}
|
||||
for _, th := range dbp.threads {
|
||||
if err := th.Halt(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Registers obtains register values from the
|
||||
// "current" thread of the traced process.
|
||||
func (dbp *Process) Registers() (Registers, error) {
|
||||
return dbp.currentThread.Registers(false)
|
||||
}
|
||||
|
||||
// PC returns the PC of the current thread.
|
||||
func (dbp *Process) PC() (uint64, error) {
|
||||
return dbp.currentThread.PC()
|
||||
}
|
||||
|
||||
// CurrentBreakpoint returns the breakpoint the current thread
|
||||
// is stopped at.
|
||||
func (dbp *Process) CurrentBreakpoint() *Breakpoint {
|
||||
return dbp.currentThread.CurrentBreakpoint
|
||||
}
|
||||
|
||||
// DwarfReader returns a reader for the dwarf data
|
||||
func (dbp *Process) DwarfReader() *reader.Reader {
|
||||
return reader.New(dbp.dwarf)
|
||||
}
|
||||
|
||||
// Sources returns list of source files that comprise the debugged binary.
|
||||
func (dbp *Process) Sources() map[string]*gosym.Obj {
|
||||
return dbp.goSymTable.Files
|
||||
}
|
||||
|
||||
// Funcs returns list of functions present in the debugged program.
|
||||
func (dbp *Process) Funcs() []gosym.Func {
|
||||
return dbp.goSymTable.Funcs
|
||||
}
|
||||
|
||||
// Types returns list of types present in the debugged program.
|
||||
func (dbp *Process) Types() ([]string, error) {
|
||||
types := make([]string, 0, len(dbp.types))
|
||||
for k := range dbp.types {
|
||||
types = append(types, k)
|
||||
}
|
||||
return types, nil
|
||||
}
|
||||
|
||||
// PCToLine converts an instruction address to a file/line/function.
|
||||
func (dbp *Process) PCToLine(pc uint64) (string, int, *gosym.Func) {
|
||||
return dbp.goSymTable.PCToLine(pc)
|
||||
}
|
||||
|
||||
// FindBreakpointByID finds the breakpoint for the given ID.
|
||||
func (dbp *Process) FindBreakpointByID(id int) (*Breakpoint, bool) {
|
||||
for _, bp := range dbp.breakpoints {
|
||||
if bp.ID == id {
|
||||
return bp, true
|
||||
}
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// FindBreakpoint finds the breakpoint for the given pc.
|
||||
func (dbp *Process) FindBreakpoint(pc uint64) (*Breakpoint, bool) {
|
||||
// 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 {
|
||||
return bp, true
|
||||
}
|
||||
// Directly use addr to lookup breakpoint.
|
||||
if bp, ok := dbp.breakpoints[pc]; ok {
|
||||
return bp, true
|
||||
}
|
||||
return nil, false
|
||||
}
|
||||
|
||||
// Returns a new Process struct.
|
||||
func initializeDebugProcess(dbp *Process, path string, attach bool) (*Process, error) {
|
||||
if attach {
|
||||
var err error
|
||||
dbp.execPtraceFunc(func() { err = PtraceAttach(dbp.pid) })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
_, _, err = dbp.wait(dbp.pid, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
proc, err := os.FindProcess(dbp.pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.Process = proc
|
||||
err = dbp.LoadInformation(path)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := dbp.updateThreadList(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ver, isextld, err := dbp.getGoInformation()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.arch.SetGStructOffset(ver, isextld)
|
||||
// selectedGoroutine can not be set correctly by the call to updateThreadList
|
||||
// because without calling SetGStructOffset we can not read the G struct of currentThread
|
||||
// but without calling updateThreadList we can not examine memory to determine
|
||||
// the offset of g struct inside TLS
|
||||
dbp.selectedGoroutine, _ = dbp.currentThread.GetG()
|
||||
|
||||
panicpc, err := dbp.FindFunctionLocation("runtime.startpanic", true, 0)
|
||||
if err == nil {
|
||||
bp, err := dbp.SetBreakpoint(panicpc, UserBreakpoint, nil)
|
||||
if err == nil {
|
||||
bp.Name = "unrecovered-panic"
|
||||
bp.ID = -1
|
||||
dbp.breakpointIDCounter--
|
||||
}
|
||||
}
|
||||
|
||||
return dbp, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) ClearInternalBreakpoints() error {
|
||||
for _, bp := range dbp.breakpoints {
|
||||
if !bp.Internal() {
|
||||
continue
|
||||
}
|
||||
if _, err := dbp.ClearBreakpoint(bp.Addr); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
for i := range dbp.threads {
|
||||
if dbp.threads[i].CurrentBreakpoint != nil && dbp.threads[i].CurrentBreakpoint.Internal() {
|
||||
dbp.threads[i].CurrentBreakpoint = nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) handlePtraceFuncs() {
|
||||
// We must ensure here that we are running on the same thread during
|
||||
// while invoking the ptrace(2) syscall. This is due to the fact that ptrace(2) expects
|
||||
// all commands after PTRACE_ATTACH to come from the same thread.
|
||||
runtime.LockOSThread()
|
||||
|
||||
for fn := range dbp.ptraceChan {
|
||||
fn()
|
||||
dbp.ptraceDoneChan <- nil
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) execPtraceFunc(fn func()) {
|
||||
dbp.ptraceChan <- fn
|
||||
<-dbp.ptraceDoneChan
|
||||
}
|
||||
|
||||
func (dbp *Process) getGoInformation() (ver GoVersion, isextld bool, err error) {
|
||||
vv, err := dbp.EvalPackageVariable("runtime.buildVersion", LoadConfig{true, 0, 64, 0, 0})
|
||||
if err != nil {
|
||||
err = fmt.Errorf("Could not determine version number: %v\n", err)
|
||||
return
|
||||
}
|
||||
if vv.Unreadable != nil {
|
||||
err = fmt.Errorf("Unreadable version number: %v\n", vv.Unreadable)
|
||||
return
|
||||
}
|
||||
|
||||
ver, ok := ParseVersionString(constant.StringVal(vv.Value))
|
||||
if !ok {
|
||||
err = fmt.Errorf("Could not parse version number: %v\n", vv.Value)
|
||||
return
|
||||
}
|
||||
|
||||
rdr := dbp.DwarfReader()
|
||||
rdr.Seek(0)
|
||||
for entry, err := rdr.NextCompileUnit(); entry != nil; entry, err = rdr.NextCompileUnit() {
|
||||
if err != nil {
|
||||
return ver, isextld, err
|
||||
}
|
||||
if prod, ok := entry.Val(dwarf.AttrProducer).(string); ok && (strings.HasPrefix(prod, "GNU AS")) {
|
||||
isextld = true
|
||||
break
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// FindGoroutine returns a G struct representing the goroutine
|
||||
// specified by `gid`.
|
||||
func (dbp *Process) FindGoroutine(gid int) (*G, error) {
|
||||
if gid == -1 {
|
||||
return dbp.selectedGoroutine, nil
|
||||
}
|
||||
|
||||
gs, err := dbp.GoroutinesInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for i := range gs {
|
||||
if gs[i].ID == gid {
|
||||
return gs[i], nil
|
||||
}
|
||||
}
|
||||
return nil, fmt.Errorf("Unknown goroutine %d", gid)
|
||||
}
|
||||
|
||||
// ConvertEvalScope returns a new EvalScope in the context of the
|
||||
// specified goroutine ID and stack frame.
|
||||
func (dbp *Process) ConvertEvalScope(gid, frame int) (*EvalScope, error) {
|
||||
if dbp.exited {
|
||||
return nil, &ProcessExitedError{}
|
||||
}
|
||||
g, err := dbp.FindGoroutine(gid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if g == nil {
|
||||
return dbp.currentThread.Scope()
|
||||
}
|
||||
|
||||
var out EvalScope
|
||||
|
||||
if g.thread == nil {
|
||||
out.Thread = dbp.currentThread
|
||||
} else {
|
||||
out.Thread = g.thread
|
||||
}
|
||||
|
||||
locs, err := g.Stacktrace(frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if frame >= len(locs) {
|
||||
return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
|
||||
}
|
||||
|
||||
out.PC, out.CFA = locs[frame].Current.PC, locs[frame].CFA
|
||||
|
||||
return &out, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) postExit() {
|
||||
dbp.exited = true
|
||||
close(dbp.ptraceChan)
|
||||
close(dbp.ptraceDoneChan)
|
||||
}
|
231
vendor/github.com/derekparker/delve/pkg/proc/proc_darwin.c
generated
vendored
Normal file
231
vendor/github.com/derekparker/delve/pkg/proc/proc_darwin.c
generated
vendored
Normal file
@ -0,0 +1,231 @@
|
||||
#include "proc_darwin.h"
|
||||
|
||||
static const unsigned char info_plist[]
|
||||
__attribute__ ((section ("__TEXT,__info_plist"),used)) =
|
||||
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
|
||||
"<!DOCTYPE plist PUBLIC \"-//Apple Computer//DTD PLIST 1.0//EN\""
|
||||
" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n"
|
||||
"<plist version=\"1.0\">\n"
|
||||
"<dict>\n"
|
||||
" <key>CFBundleIdentifier</key>\n"
|
||||
" <string>org.dlv</string>\n"
|
||||
" <key>CFBundleName</key>\n"
|
||||
" <string>delve</string>\n"
|
||||
" <key>CFBundleVersion</key>\n"
|
||||
" <string>1.0</string>\n"
|
||||
" <key>SecTaskAccess</key>\n"
|
||||
" <array>\n"
|
||||
" <string>allowed</string>\n"
|
||||
" <string>debug</string>\n"
|
||||
" </array>\n"
|
||||
"</dict>\n"
|
||||
"</plist>\n";
|
||||
|
||||
kern_return_t
|
||||
acquire_mach_task(int tid,
|
||||
task_t *task,
|
||||
mach_port_t *port_set,
|
||||
mach_port_t *exception_port,
|
||||
mach_port_t *notification_port)
|
||||
{
|
||||
kern_return_t kret;
|
||||
mach_port_t prev_not;
|
||||
mach_port_t self = mach_task_self();
|
||||
|
||||
kret = task_for_pid(self, tid, task);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Allocate exception port.
|
||||
kret = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, exception_port);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = mach_port_insert_right(self, *exception_port, *exception_port, MACH_MSG_TYPE_MAKE_SEND);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = task_set_exception_ports(*task, EXC_MASK_BREAKPOINT|EXC_MASK_SOFTWARE, *exception_port,
|
||||
EXCEPTION_DEFAULT, THREAD_STATE_NONE);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Allocate notification port to alert of when the process dies.
|
||||
kret = mach_port_allocate(self, MACH_PORT_RIGHT_RECEIVE, notification_port);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = mach_port_insert_right(self, *notification_port, *notification_port, MACH_MSG_TYPE_MAKE_SEND);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = mach_port_request_notification(self, *task, MACH_NOTIFY_DEAD_NAME, 0, *notification_port,
|
||||
MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev_not);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Create port set.
|
||||
kret = mach_port_allocate(self, MACH_PORT_RIGHT_PORT_SET, port_set);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Move exception and notification ports to port set.
|
||||
kret = mach_port_move_member(self, *exception_port, *port_set);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
return mach_port_move_member(self, *notification_port, *port_set);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
reset_exception_ports(task_t task, mach_port_t *exception_port, mach_port_t *notification_port) {
|
||||
kern_return_t kret;
|
||||
mach_port_t prev_not;
|
||||
mach_port_t self = mach_task_self();
|
||||
|
||||
kret = task_set_exception_ports(task, EXC_MASK_BREAKPOINT|EXC_MASK_SOFTWARE, *exception_port,
|
||||
EXCEPTION_DEFAULT, THREAD_STATE_NONE);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
kret = mach_port_request_notification(self, task, MACH_NOTIFY_DEAD_NAME, 0, *notification_port,
|
||||
MACH_MSG_TYPE_MAKE_SEND_ONCE, &prev_not);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
char *
|
||||
find_executable(int pid) {
|
||||
static char pathbuf[PATH_MAX];
|
||||
proc_pidpath(pid, pathbuf, PATH_MAX);
|
||||
return pathbuf;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
get_threads(task_t task, void *slice, int limit) {
|
||||
kern_return_t kret;
|
||||
thread_act_array_t list;
|
||||
mach_msg_type_number_t count;
|
||||
|
||||
kret = task_threads(task, &list, &count);
|
||||
if (kret != KERN_SUCCESS) {
|
||||
return kret;
|
||||
}
|
||||
|
||||
if (count > limit) {
|
||||
vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
|
||||
return -2;
|
||||
}
|
||||
|
||||
memcpy(slice, (void*)list, count*sizeof(list[0]));
|
||||
|
||||
kret = vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
return (kern_return_t)0;
|
||||
}
|
||||
|
||||
int
|
||||
thread_count(task_t task) {
|
||||
kern_return_t kret;
|
||||
thread_act_array_t list;
|
||||
mach_msg_type_number_t count;
|
||||
|
||||
kret = task_threads(task, &list, &count);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
kret = vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
mach_port_t
|
||||
mach_port_wait(mach_port_t port_set, task_t *task, int nonblocking) {
|
||||
kern_return_t kret;
|
||||
thread_act_t thread;
|
||||
NDR_record_t *ndr;
|
||||
integer_t *data;
|
||||
union
|
||||
{
|
||||
mach_msg_header_t hdr;
|
||||
char data[256];
|
||||
} msg;
|
||||
mach_msg_option_t opts = MACH_RCV_MSG|MACH_RCV_INTERRUPT;
|
||||
if (nonblocking) {
|
||||
opts |= MACH_RCV_TIMEOUT;
|
||||
}
|
||||
|
||||
// Wait for mach msg.
|
||||
kret = mach_msg(&msg.hdr, opts,
|
||||
0, sizeof(msg.data), port_set, 10, MACH_PORT_NULL);
|
||||
if (kret == MACH_RCV_INTERRUPTED) return kret;
|
||||
if (kret != MACH_MSG_SUCCESS) return 0;
|
||||
|
||||
|
||||
switch (msg.hdr.msgh_id) {
|
||||
case 2401: { // Exception
|
||||
// 2401 is the exception_raise event, defined in:
|
||||
// http://opensource.apple.com/source/xnu/xnu-2422.1.72/osfmk/mach/exc.defs?txt
|
||||
// compile this file with mig to get the C version of the description
|
||||
|
||||
mach_msg_body_t *bod = (mach_msg_body_t*)(&msg.hdr + 1);
|
||||
mach_msg_port_descriptor_t *desc = (mach_msg_port_descriptor_t *)(bod + 1);
|
||||
thread = desc[0].name;
|
||||
*task = desc[1].name;
|
||||
ndr = (NDR_record_t *)(desc + 2);
|
||||
data = (integer_t *)(ndr + 1);
|
||||
|
||||
if (thread_suspend(thread) != KERN_SUCCESS) return 0;
|
||||
// Send our reply back so the kernel knows this exception has been handled.
|
||||
kret = mach_send_reply(msg.hdr);
|
||||
if (kret != MACH_MSG_SUCCESS) return 0;
|
||||
if (data[2] == EXC_SOFT_SIGNAL) {
|
||||
if (data[3] != SIGTRAP) {
|
||||
if (thread_resume(thread) != KERN_SUCCESS) return 0;
|
||||
return mach_port_wait(port_set, task, nonblocking);
|
||||
}
|
||||
}
|
||||
return thread;
|
||||
}
|
||||
|
||||
case 72: { // Death
|
||||
// 72 is mach_notify_dead_name, defined in:
|
||||
// https://opensource.apple.com/source/xnu/xnu-1228.7.58/osfmk/mach/notify.defs?txt
|
||||
// compile this file with mig to get the C version of the description
|
||||
ndr = (NDR_record_t *)(&msg.hdr + 1);
|
||||
*task = *((mach_port_name_t *)(ndr + 1));
|
||||
return msg.hdr.msgh_local_port;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
mach_send_reply(mach_msg_header_t hdr) {
|
||||
mig_reply_error_t reply;
|
||||
mach_msg_header_t *rh = &reply.Head;
|
||||
rh->msgh_bits = MACH_MSGH_BITS(MACH_MSGH_BITS_REMOTE(hdr.msgh_bits), 0);
|
||||
rh->msgh_remote_port = hdr.msgh_remote_port;
|
||||
rh->msgh_size = (mach_msg_size_t) sizeof(mig_reply_error_t);
|
||||
rh->msgh_local_port = MACH_PORT_NULL;
|
||||
rh->msgh_id = hdr.msgh_id + 100;
|
||||
|
||||
reply.NDR = NDR_record;
|
||||
reply.RetCode = KERN_SUCCESS;
|
||||
|
||||
return mach_msg(&reply.Head, MACH_SEND_MSG|MACH_SEND_INTERRUPT, rh->msgh_size, 0,
|
||||
MACH_PORT_NULL, MACH_MSG_TIMEOUT_NONE, MACH_PORT_NULL);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
raise_exception(mach_port_t task, mach_port_t thread, mach_port_t exception_port, exception_type_t exception) {
|
||||
return exception_raise(exception_port, thread, task, exception, 0, 0);
|
||||
}
|
||||
|
||||
task_t
|
||||
get_task_for_pid(int pid) {
|
||||
task_t task = 0;
|
||||
mach_port_t self = mach_task_self();
|
||||
|
||||
task_for_pid(self, pid, &task);
|
||||
return task;
|
||||
}
|
||||
|
||||
int
|
||||
task_is_valid(task_t task) {
|
||||
struct task_basic_info info;
|
||||
mach_msg_type_number_t count = TASK_BASIC_INFO_COUNT;
|
||||
return task_info(task, TASK_BASIC_INFO, (task_info_t)&info, &count) == KERN_SUCCESS;
|
||||
}
|
511
vendor/github.com/derekparker/delve/pkg/proc/proc_darwin.go
generated
vendored
Normal file
511
vendor/github.com/derekparker/delve/pkg/proc/proc_darwin.go
generated
vendored
Normal file
@ -0,0 +1,511 @@
|
||||
package proc
|
||||
|
||||
// #include "proc_darwin.h"
|
||||
// #include "threads_darwin.h"
|
||||
// #include "exec_darwin.h"
|
||||
// #include <stdlib.h>
|
||||
import "C"
|
||||
import (
|
||||
"debug/gosym"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"golang.org/x/debug/macho"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// OSProcessDetails holds Darwin specific information.
|
||||
type OSProcessDetails struct {
|
||||
task C.task_t // mach task for the debugged process.
|
||||
exceptionPort C.mach_port_t // mach port for receiving mach exceptions.
|
||||
notificationPort C.mach_port_t // mach port for dead name notification (process exit).
|
||||
initialized bool
|
||||
|
||||
// the main port we use, will return messages from both the
|
||||
// exception and notification ports.
|
||||
portSet C.mach_port_t
|
||||
}
|
||||
|
||||
// Launch creates and begins debugging a new process. Uses a
|
||||
// custom fork/exec process in order to take advantage of
|
||||
// PT_SIGEXC on Darwin which will turn Unix signals into
|
||||
// Mach exceptions.
|
||||
func Launch(cmd []string, wd string) (*Process, error) {
|
||||
// check that the argument to Launch is an executable file
|
||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
||||
return nil, NotExecutableErr
|
||||
}
|
||||
argv0Go, err := filepath.Abs(cmd[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// Make sure the binary exists.
|
||||
if filepath.Base(cmd[0]) == cmd[0] {
|
||||
if _, err := exec.LookPath(cmd[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
if _, err := os.Stat(argv0Go); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
argv0 := C.CString(argv0Go)
|
||||
argvSlice := make([]*C.char, 0, len(cmd)+1)
|
||||
for _, arg := range cmd {
|
||||
argvSlice = append(argvSlice, C.CString(arg))
|
||||
}
|
||||
// argv array must be null terminated.
|
||||
argvSlice = append(argvSlice, nil)
|
||||
|
||||
dbp := New(0)
|
||||
var pid int
|
||||
dbp.execPtraceFunc(func() {
|
||||
ret := C.fork_exec(argv0, &argvSlice[0], C.int(len(argvSlice)),
|
||||
C.CString(wd),
|
||||
&dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort,
|
||||
&dbp.os.notificationPort)
|
||||
pid = int(ret)
|
||||
})
|
||||
if pid <= 0 {
|
||||
return nil, fmt.Errorf("could not fork/exec")
|
||||
}
|
||||
dbp.pid = pid
|
||||
for i := range argvSlice {
|
||||
C.free(unsafe.Pointer(argvSlice[i]))
|
||||
}
|
||||
|
||||
// Initialize enough of the Process state so that we can use resume and
|
||||
// trapWait to wait until the child process calls execve.
|
||||
|
||||
for {
|
||||
err = dbp.updateThreadListForTask(C.get_task_for_pid(C.int(dbp.pid)))
|
||||
if err == nil {
|
||||
break
|
||||
}
|
||||
if err != couldNotGetThreadCount && err != couldNotGetThreadList {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
if err := dbp.resume(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.allGCache = nil
|
||||
for _, th := range dbp.threads {
|
||||
th.clearBreakpointState()
|
||||
}
|
||||
|
||||
trapthread, err := dbp.trapWait(-1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := dbp.Halt(); err != nil {
|
||||
return nil, dbp.exitGuard(err)
|
||||
}
|
||||
|
||||
_, err = dbp.waitForStop()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
dbp.os.initialized = true
|
||||
dbp, err = initializeDebugProcess(dbp, argv0Go, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if err := dbp.SwitchThread(trapthread.ID); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return dbp, err
|
||||
}
|
||||
|
||||
// Attach to an existing process with the given PID.
|
||||
func Attach(pid int) (*Process, error) {
|
||||
dbp := New(pid)
|
||||
|
||||
kret := C.acquire_mach_task(C.int(pid),
|
||||
&dbp.os.task, &dbp.os.portSet, &dbp.os.exceptionPort,
|
||||
&dbp.os.notificationPort)
|
||||
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not attach to %d", pid)
|
||||
}
|
||||
|
||||
dbp.os.initialized = true
|
||||
|
||||
return initializeDebugProcess(dbp, "", true)
|
||||
}
|
||||
|
||||
// Kill kills the process.
|
||||
func (dbp *Process) Kill() (err error) {
|
||||
if dbp.exited {
|
||||
return nil
|
||||
}
|
||||
err = sys.Kill(-dbp.pid, sys.SIGKILL)
|
||||
if err != nil {
|
||||
return errors.New("could not deliver signal: " + err.Error())
|
||||
}
|
||||
for port := range dbp.threads {
|
||||
if C.thread_resume(C.thread_act_t(port)) != C.KERN_SUCCESS {
|
||||
return errors.New("could not resume task")
|
||||
}
|
||||
}
|
||||
for {
|
||||
var task C.task_t
|
||||
port := C.mach_port_wait(dbp.os.portSet, &task, C.int(0))
|
||||
if port == dbp.os.notificationPort {
|
||||
break
|
||||
}
|
||||
}
|
||||
dbp.postExit()
|
||||
return
|
||||
}
|
||||
|
||||
func (dbp *Process) requestManualStop() (err error) {
|
||||
var (
|
||||
task = C.mach_port_t(dbp.os.task)
|
||||
thread = C.mach_port_t(dbp.currentThread.os.threadAct)
|
||||
exceptionPort = C.mach_port_t(dbp.os.exceptionPort)
|
||||
)
|
||||
kret := C.raise_exception(task, thread, exceptionPort, C.EXC_BREAKPOINT)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("could not raise mach exception")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var couldNotGetThreadCount = errors.New("could not get thread count")
|
||||
var couldNotGetThreadList = errors.New("could not get thread list")
|
||||
|
||||
func (dbp *Process) updateThreadList() error {
|
||||
return dbp.updateThreadListForTask(dbp.os.task)
|
||||
}
|
||||
|
||||
func (dbp *Process) updateThreadListForTask(task C.task_t) error {
|
||||
var (
|
||||
err error
|
||||
kret C.kern_return_t
|
||||
count C.int
|
||||
list []uint32
|
||||
)
|
||||
|
||||
for {
|
||||
count = C.thread_count(task)
|
||||
if count == -1 {
|
||||
return couldNotGetThreadCount
|
||||
}
|
||||
list = make([]uint32, count)
|
||||
|
||||
// TODO(dp) might be better to malloc mem in C and then free it here
|
||||
// instead of getting count above and passing in a slice
|
||||
kret = C.get_threads(task, unsafe.Pointer(&list[0]), count)
|
||||
if kret != -2 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return couldNotGetThreadList
|
||||
}
|
||||
|
||||
for _, thread := range dbp.threads {
|
||||
thread.os.exists = false
|
||||
}
|
||||
|
||||
for _, port := range list {
|
||||
thread, ok := dbp.threads[int(port)]
|
||||
if !ok {
|
||||
thread, err = dbp.addThread(int(port), false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
thread.os.exists = true
|
||||
}
|
||||
|
||||
for threadID, thread := range dbp.threads {
|
||||
if !thread.os.exists {
|
||||
delete(dbp.threads, threadID)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) addThread(port int, attach bool) (*Thread, error) {
|
||||
if thread, ok := dbp.threads[port]; ok {
|
||||
return thread, nil
|
||||
}
|
||||
thread := &Thread{
|
||||
ID: port,
|
||||
dbp: dbp,
|
||||
os: new(OSSpecificDetails),
|
||||
}
|
||||
dbp.threads[port] = thread
|
||||
thread.os.threadAct = C.thread_act_t(port)
|
||||
if dbp.currentThread == nil {
|
||||
dbp.SwitchThread(thread.ID)
|
||||
}
|
||||
return thread, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugFrame(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameSec := exe.Section("__debug_frame")
|
||||
debugInfoSec := exe.Section("__debug_info")
|
||||
|
||||
if debugFrameSec != nil && debugInfoSec != nil {
|
||||
debugFrame, err := exe.Section("__debug_frame").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get __debug_frame section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dat, err := debugInfoSec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_info section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
||||
} else {
|
||||
fmt.Println("could not find __debug_frame section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) obtainGoSymbols(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var (
|
||||
symdat []byte
|
||||
pclndat []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if sec := exe.Section("__gosymtab"); sec != nil {
|
||||
symdat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gosymtab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if sec := exe.Section("__gopclntab"); sec != nil {
|
||||
pclndat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gopclntab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, exe.Section("__text").Addr)
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
fmt.Println("could not get initialize line table", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dbp.goSymTable = tab
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugLineInfo(exe *macho.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if sec := exe.Section("__debug_line"); sec != nil {
|
||||
debugLine, err := exe.Section("__debug_line").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get __debug_line section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
fmt.Println("could not find __debug_line section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var UnsupportedArchErr = errors.New("unsupported architecture - only darwin/amd64 is supported")
|
||||
|
||||
func (dbp *Process) findExecutable(path string) (*macho.File, string, error) {
|
||||
if path == "" {
|
||||
path = C.GoString(C.find_executable(C.int(dbp.pid)))
|
||||
}
|
||||
exe, err := macho.Open(path)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
if exe.Cpu != macho.CpuAmd64 {
|
||||
return nil, path, UnsupportedArchErr
|
||||
}
|
||||
dbp.dwarf, err = exe.DWARF()
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
return exe, path, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
for {
|
||||
task := dbp.os.task
|
||||
port := C.mach_port_wait(dbp.os.portSet, &task, C.int(0))
|
||||
|
||||
switch port {
|
||||
case dbp.os.notificationPort:
|
||||
// on macOS >= 10.12.1 the task_t changes after an execve, we could
|
||||
// receive the notification for the death of the pre-execve task_t,
|
||||
// this could also happen *before* we are notified that our task_t has
|
||||
// changed.
|
||||
if dbp.os.task != task {
|
||||
continue
|
||||
}
|
||||
if !dbp.os.initialized {
|
||||
if pidtask := C.get_task_for_pid(C.int(dbp.pid)); pidtask != 0 && dbp.os.task != pidtask {
|
||||
continue
|
||||
}
|
||||
}
|
||||
_, status, err := dbp.wait(dbp.pid, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbp.postExit()
|
||||
return nil, ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
|
||||
|
||||
case C.MACH_RCV_INTERRUPTED:
|
||||
if !dbp.halt {
|
||||
// Call trapWait again, it seems
|
||||
// MACH_RCV_INTERRUPTED is emitted before
|
||||
// process natural death _sometimes_.
|
||||
continue
|
||||
}
|
||||
return nil, nil
|
||||
|
||||
case 0:
|
||||
return nil, fmt.Errorf("error while waiting for task")
|
||||
}
|
||||
|
||||
// In macOS 10.12.1 if we received a notification for a task other than
|
||||
// the inferior's task and the inferior's task is no longer valid, this
|
||||
// means inferior called execve and its task_t changed.
|
||||
if dbp.os.task != task && C.task_is_valid(dbp.os.task) == 0 {
|
||||
dbp.os.task = task
|
||||
kret := C.reset_exception_ports(dbp.os.task, &dbp.os.exceptionPort, &dbp.os.notificationPort)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not follow task across exec: %d\n", kret)
|
||||
}
|
||||
}
|
||||
|
||||
// Since we cannot be notified of new threads on OS X
|
||||
// this is as good a time as any to check for them.
|
||||
dbp.updateThreadList()
|
||||
th, ok := dbp.threads[int(port)]
|
||||
if !ok {
|
||||
if dbp.halt {
|
||||
dbp.halt = false
|
||||
return th, nil
|
||||
}
|
||||
if dbp.firstStart || th.singleStepping {
|
||||
dbp.firstStart = false
|
||||
return th, nil
|
||||
}
|
||||
if err := th.Continue(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
continue
|
||||
}
|
||||
return th, nil
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) waitForStop() ([]int, error) {
|
||||
ports := make([]int, 0, len(dbp.threads))
|
||||
count := 0
|
||||
for {
|
||||
var task C.task_t
|
||||
port := C.mach_port_wait(dbp.os.portSet, &task, C.int(1))
|
||||
if port != 0 && port != dbp.os.notificationPort && port != C.MACH_RCV_INTERRUPTED {
|
||||
count = 0
|
||||
ports = append(ports, int(port))
|
||||
} else {
|
||||
n := C.num_running_threads(dbp.os.task)
|
||||
if n == 0 {
|
||||
return ports, nil
|
||||
} else if n < 0 {
|
||||
return nil, fmt.Errorf("error waiting for thread stop %d", n)
|
||||
} else if count > 16 {
|
||||
return nil, fmt.Errorf("could not stop process %d", n)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
|
||||
ports, err := dbp.waitForStop()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
trapthread.SetCurrentBreakpoint()
|
||||
for _, port := range ports {
|
||||
if th, ok := dbp.threads[port]; ok {
|
||||
err := th.SetCurrentBreakpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||
var status sys.WaitStatus
|
||||
wpid, err := sys.Wait4(pid, &status, options, nil)
|
||||
return wpid, &status, err
|
||||
}
|
||||
|
||||
func killProcess(pid int) error {
|
||||
return sys.Kill(pid, sys.SIGINT)
|
||||
}
|
||||
|
||||
func (dbp *Process) exitGuard(err error) error {
|
||||
if err != ErrContinueThread {
|
||||
return err
|
||||
}
|
||||
_, status, werr := dbp.wait(dbp.pid, sys.WNOHANG)
|
||||
if werr == nil && status.Exited() {
|
||||
dbp.postExit()
|
||||
return ProcessExitedError{Pid: dbp.pid, Status: status.ExitStatus()}
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
func (dbp *Process) resume() error {
|
||||
// all threads stopped over a breakpoint are made to step over it
|
||||
for _, thread := range dbp.threads {
|
||||
if thread.CurrentBreakpoint != nil {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.CurrentBreakpoint = nil
|
||||
}
|
||||
}
|
||||
// everything is resumed
|
||||
for _, thread := range dbp.threads {
|
||||
if err := thread.resume(); err != nil {
|
||||
return dbp.exitGuard(err)
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
54
vendor/github.com/derekparker/delve/pkg/proc/proc_darwin.h
generated
vendored
Normal file
54
vendor/github.com/derekparker/delve/pkg/proc/proc_darwin.h
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
#include <sys/types.h>
|
||||
#include <libproc.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include "mach_exc.h"
|
||||
#include "exc.h"
|
||||
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
boolean_t exc_server(
|
||||
mach_msg_header_t *InHeadP,
|
||||
mach_msg_header_t *OutHeadP);
|
||||
|
||||
#ifdef mig_external
|
||||
mig_external
|
||||
#else
|
||||
extern
|
||||
#endif /* mig_external */
|
||||
boolean_t mach_exc_server(
|
||||
mach_msg_header_t *InHeadP,
|
||||
mach_msg_header_t *OutHeadP);
|
||||
|
||||
kern_return_t
|
||||
acquire_mach_task(int, task_t*, mach_port_t*, mach_port_t*, mach_port_t*);
|
||||
|
||||
char *
|
||||
find_executable(int pid);
|
||||
|
||||
kern_return_t
|
||||
get_threads(task_t task, void *data,int limit);
|
||||
|
||||
int
|
||||
thread_count(task_t task);
|
||||
|
||||
mach_port_t
|
||||
mach_port_wait(mach_port_t, task_t*, int);
|
||||
|
||||
kern_return_t
|
||||
mach_send_reply(mach_msg_header_t);
|
||||
|
||||
kern_return_t
|
||||
raise_exception(mach_port_t, mach_port_t, mach_port_t, exception_type_t);
|
||||
|
||||
kern_return_t
|
||||
reset_exception_ports(task_t task, mach_port_t *exception_port, mach_port_t *notification_port);
|
||||
|
||||
task_t
|
||||
get_task_for_pid(int pid);
|
||||
|
||||
int
|
||||
task_is_valid(task_t task);
|
489
vendor/github.com/derekparker/delve/pkg/proc/proc_linux.go
generated
vendored
Normal file
489
vendor/github.com/derekparker/delve/pkg/proc/proc_linux.go
generated
vendored
Normal file
@ -0,0 +1,489 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/gosym"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"syscall"
|
||||
"time"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
||||
"golang.org/x/debug/elf"
|
||||
)
|
||||
|
||||
// Process statuses
|
||||
const (
|
||||
StatusSleeping = 'S'
|
||||
StatusRunning = 'R'
|
||||
StatusTraceStop = 't'
|
||||
StatusZombie = 'Z'
|
||||
|
||||
// Kernel 2.6 has TraceStop as T
|
||||
// TODO(derekparker) Since this means something different based on the
|
||||
// version of the kernel ('T' is job control stop on modern 3.x+ kernels) we
|
||||
// may want to differentiate at some point.
|
||||
StatusTraceStopT = 'T'
|
||||
)
|
||||
|
||||
// OSProcessDetails contains Linux specific
|
||||
// process details.
|
||||
type OSProcessDetails struct {
|
||||
comm string
|
||||
}
|
||||
|
||||
// Launch creates and begins debugging a new process. First entry in
|
||||
// `cmd` is the program to run, and then rest are the arguments
|
||||
// to be supplied to that process. `wd` is working directory of the program.
|
||||
func Launch(cmd []string, wd string) (*Process, error) {
|
||||
var (
|
||||
proc *exec.Cmd
|
||||
err error
|
||||
)
|
||||
// check that the argument to Launch is an executable file
|
||||
if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
|
||||
return nil, NotExecutableErr
|
||||
}
|
||||
dbp := New(0)
|
||||
dbp.execPtraceFunc(func() {
|
||||
proc = exec.Command(cmd[0])
|
||||
proc.Args = cmd
|
||||
proc.Stdout = os.Stdout
|
||||
proc.Stderr = os.Stderr
|
||||
proc.SysProcAttr = &syscall.SysProcAttr{Ptrace: true, Setpgid: true}
|
||||
if wd != "" {
|
||||
proc.Dir = wd
|
||||
}
|
||||
err = proc.Start()
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
dbp.pid = proc.Process.Pid
|
||||
_, _, err = dbp.wait(proc.Process.Pid, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("waiting for target execve failed: %s", err)
|
||||
}
|
||||
return initializeDebugProcess(dbp, proc.Path, false)
|
||||
}
|
||||
|
||||
// Attach to an existing process with the given PID.
|
||||
func Attach(pid int) (*Process, error) {
|
||||
return initializeDebugProcess(New(pid), "", true)
|
||||
}
|
||||
|
||||
// Kill kills the target process.
|
||||
func (dbp *Process) Kill() (err error) {
|
||||
if dbp.exited {
|
||||
return nil
|
||||
}
|
||||
if !dbp.threads[dbp.pid].Stopped() {
|
||||
return errors.New("process must be stopped in order to kill it")
|
||||
}
|
||||
if err = sys.Kill(-dbp.pid, sys.SIGKILL); err != nil {
|
||||
return errors.New("could not deliver signal " + err.Error())
|
||||
}
|
||||
if _, _, err = dbp.wait(dbp.pid, 0); err != nil {
|
||||
return
|
||||
}
|
||||
dbp.postExit()
|
||||
return
|
||||
}
|
||||
|
||||
func (dbp *Process) requestManualStop() (err error) {
|
||||
return sys.Kill(dbp.pid, sys.SIGTRAP)
|
||||
}
|
||||
|
||||
// Attach to a newly created thread, and store that thread in our list of
|
||||
// known threads.
|
||||
func (dbp *Process) addThread(tid int, attach bool) (*Thread, error) {
|
||||
if thread, ok := dbp.threads[tid]; ok {
|
||||
return thread, nil
|
||||
}
|
||||
|
||||
var err error
|
||||
if attach {
|
||||
dbp.execPtraceFunc(func() { err = sys.PtraceAttach(tid) })
|
||||
if err != nil && err != sys.EPERM {
|
||||
// Do not return err if err == EPERM,
|
||||
// we may already be tracing this thread due to
|
||||
// PTRACE_O_TRACECLONE. We will surely blow up later
|
||||
// if we truly don't have permissions.
|
||||
return nil, fmt.Errorf("could not attach to new thread %d %s", tid, err)
|
||||
}
|
||||
pid, status, err := dbp.wait(tid, 0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if status.Exited() {
|
||||
return nil, fmt.Errorf("thread already exited %d", pid)
|
||||
}
|
||||
}
|
||||
|
||||
dbp.execPtraceFunc(func() { err = syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) })
|
||||
if err == syscall.ESRCH {
|
||||
if _, _, err = dbp.wait(tid, 0); err != nil {
|
||||
return nil, fmt.Errorf("error while waiting after adding thread: %d %s", tid, err)
|
||||
}
|
||||
dbp.execPtraceFunc(func() { err = syscall.PtraceSetOptions(tid, syscall.PTRACE_O_TRACECLONE) })
|
||||
if err == syscall.ESRCH {
|
||||
return nil, err
|
||||
}
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not set options for new traced thread %d %s", tid, err)
|
||||
}
|
||||
}
|
||||
|
||||
dbp.threads[tid] = &Thread{
|
||||
ID: tid,
|
||||
dbp: dbp,
|
||||
os: new(OSSpecificDetails),
|
||||
}
|
||||
if dbp.currentThread == nil {
|
||||
dbp.SwitchThread(tid)
|
||||
}
|
||||
return dbp.threads[tid], nil
|
||||
}
|
||||
|
||||
func (dbp *Process) updateThreadList() error {
|
||||
tids, _ := filepath.Glob(fmt.Sprintf("/proc/%d/task/*", dbp.pid))
|
||||
for _, tidpath := range tids {
|
||||
tidstr := filepath.Base(tidpath)
|
||||
tid, err := strconv.Atoi(tidstr)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if _, err := dbp.addThread(tid, tid != dbp.pid); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
var UnsupportedArchErr = errors.New("unsupported architecture - only linux/amd64 is supported")
|
||||
|
||||
func (dbp *Process) findExecutable(path string) (*elf.File, string, error) {
|
||||
if path == "" {
|
||||
path = fmt.Sprintf("/proc/%d/exe", dbp.pid)
|
||||
}
|
||||
f, err := os.OpenFile(path, 0, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
elfFile, err := elf.NewFile(f)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
if elfFile.Machine != elf.EM_X86_64 {
|
||||
return nil, path, UnsupportedArchErr
|
||||
}
|
||||
dbp.dwarf, err = elfFile.DWARF()
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
return elfFile, path, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugFrame(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameSec := exe.Section(".debug_frame")
|
||||
debugInfoSec := exe.Section(".debug_info")
|
||||
|
||||
if debugFrameSec != nil && debugInfoSec != nil {
|
||||
debugFrame, err := exe.Section(".debug_frame").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_frame section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dat, err := debugInfoSec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_info section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
||||
} else {
|
||||
fmt.Println("could not find .debug_frame section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) obtainGoSymbols(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
var (
|
||||
symdat []byte
|
||||
pclndat []byte
|
||||
err error
|
||||
)
|
||||
|
||||
if sec := exe.Section(".gosymtab"); sec != nil {
|
||||
symdat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gosymtab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
if sec := exe.Section(".gopclntab"); sec != nil {
|
||||
pclndat, err = sec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .gopclntab section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, exe.Section(".text").Addr)
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
fmt.Println("could not get initialize line table", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dbp.goSymTable = tab
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugLineInfo(exe *elf.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if sec := exe.Section(".debug_line"); sec != nil {
|
||||
debugLine, err := exe.Section(".debug_line").Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_line section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
fmt.Println("could not find .debug_line section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
for {
|
||||
wpid, status, err := dbp.wait(pid, 0)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("wait err %s %d", err, pid)
|
||||
}
|
||||
if wpid == 0 {
|
||||
continue
|
||||
}
|
||||
th, ok := dbp.threads[wpid]
|
||||
if ok {
|
||||
th.Status = (*WaitStatus)(status)
|
||||
}
|
||||
if status.Exited() {
|
||||
if wpid == dbp.pid {
|
||||
dbp.postExit()
|
||||
return nil, ProcessExitedError{Pid: wpid, Status: status.ExitStatus()}
|
||||
}
|
||||
delete(dbp.threads, wpid)
|
||||
continue
|
||||
}
|
||||
if status.StopSignal() == sys.SIGTRAP && status.TrapCause() == sys.PTRACE_EVENT_CLONE {
|
||||
// A traced thread has cloned a new thread, grab the pid and
|
||||
// add it to our list of traced threads.
|
||||
var cloned uint
|
||||
dbp.execPtraceFunc(func() { cloned, err = sys.PtraceGetEventMsg(wpid) })
|
||||
if err != nil {
|
||||
if err == sys.ESRCH {
|
||||
// thread died while we were adding it
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("could not get event message: %s", err)
|
||||
}
|
||||
th, err = dbp.addThread(int(cloned), false)
|
||||
if err != nil {
|
||||
if err == sys.ESRCH {
|
||||
// thread died while we were adding it
|
||||
continue
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
if err = th.Continue(); err != nil {
|
||||
if err == sys.ESRCH {
|
||||
// thread died while we were adding it
|
||||
delete(dbp.threads, th.ID)
|
||||
continue
|
||||
}
|
||||
return nil, fmt.Errorf("could not continue new thread %d %s", cloned, err)
|
||||
}
|
||||
if err = dbp.threads[int(wpid)].Continue(); err != nil {
|
||||
if err != sys.ESRCH {
|
||||
return nil, fmt.Errorf("could not continue existing thread %d %s", wpid, err)
|
||||
}
|
||||
}
|
||||
continue
|
||||
}
|
||||
if th == nil {
|
||||
// Sometimes we get an unknown thread, ignore it?
|
||||
continue
|
||||
}
|
||||
if status.StopSignal() == sys.SIGTRAP && dbp.halt {
|
||||
th.running = false
|
||||
dbp.halt = false
|
||||
return th, nil
|
||||
}
|
||||
if status.StopSignal() == sys.SIGTRAP {
|
||||
th.running = false
|
||||
return th, nil
|
||||
}
|
||||
if th != nil {
|
||||
// TODO(dp) alert user about unexpected signals here.
|
||||
if err := th.resumeWithSig(int(status.StopSignal())); err != nil {
|
||||
if err == sys.ESRCH {
|
||||
return nil, ProcessExitedError{Pid: dbp.pid}
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
comm, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", dbp.pid))
|
||||
if err == nil {
|
||||
// removes newline character
|
||||
comm = bytes.TrimSuffix(comm, []byte("\n"))
|
||||
}
|
||||
|
||||
if comm == nil || len(comm) <= 0 {
|
||||
stat, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", dbp.pid))
|
||||
if err != nil {
|
||||
fmt.Printf("Could not read proc stat: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
expr := fmt.Sprintf("%d\\s*\\((.*)\\)", dbp.pid)
|
||||
rexp, err := regexp.Compile(expr)
|
||||
if err != nil {
|
||||
fmt.Printf("Regexp compile error: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
match := rexp.FindSubmatch(stat)
|
||||
if match == nil {
|
||||
fmt.Printf("No match found using regexp '%s' in /proc/%d/stat\n", expr, dbp.pid)
|
||||
os.Exit(1)
|
||||
}
|
||||
comm = match[1]
|
||||
}
|
||||
dbp.os.comm = strings.Replace(string(comm), "%", "%%", -1)
|
||||
}
|
||||
|
||||
func status(pid int, comm string) rune {
|
||||
f, err := os.Open(fmt.Sprintf("/proc/%d/stat", pid))
|
||||
if err != nil {
|
||||
return '\000'
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var (
|
||||
p int
|
||||
state rune
|
||||
)
|
||||
|
||||
// The second field of /proc/pid/stat is the name of the task in parenthesis.
|
||||
// The name of the task is the base name of the executable for this process limited to TASK_COMM_LEN characters
|
||||
// Since both parenthesis and spaces can appear inside the name of the task and no escaping happens we need to read the name of the executable first
|
||||
// See: include/linux/sched.c:315 and include/linux/sched.c:1510
|
||||
fmt.Fscanf(f, "%d ("+comm+") %c", &p, &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) {
|
||||
var s sys.WaitStatus
|
||||
if (pid != dbp.pid) || (options != 0) {
|
||||
wpid, err := sys.Wait4(pid, &s, sys.WALL|options, nil)
|
||||
return wpid, &s, err
|
||||
}
|
||||
// If we call wait4/waitpid on a thread that is the leader of its group,
|
||||
// with options == 0, while ptracing and the thread leader has exited leaving
|
||||
// zombies of its own then waitpid hangs forever this is apparently intended
|
||||
// behaviour in the linux kernel because it's just so convenient.
|
||||
// Therefore we call wait4 in a loop with WNOHANG, sleeping a while between
|
||||
// calls and exiting when either wait4 succeeds or we find out that the thread
|
||||
// has become a zombie.
|
||||
// References:
|
||||
// https://sourceware.org/bugzilla/show_bug.cgi?id=12702
|
||||
// https://sourceware.org/bugzilla/show_bug.cgi?id=10095
|
||||
// https://sourceware.org/bugzilla/attachment.cgi?id=5685
|
||||
for {
|
||||
wpid, err := sys.Wait4(pid, &s, sys.WNOHANG|sys.WALL|options, nil)
|
||||
if err != nil {
|
||||
return 0, nil, err
|
||||
}
|
||||
if wpid != 0 {
|
||||
return wpid, &s, err
|
||||
}
|
||||
if status(pid, dbp.os.comm) == StatusZombie {
|
||||
return pid, nil, nil
|
||||
}
|
||||
time.Sleep(200 * time.Millisecond)
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
|
||||
for _, th := range dbp.threads {
|
||||
if th.CurrentBreakpoint == nil {
|
||||
err := th.SetCurrentBreakpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) exitGuard(err error) error {
|
||||
if err != sys.ESRCH {
|
||||
return err
|
||||
}
|
||||
if status(dbp.pid, dbp.os.comm) == StatusZombie {
|
||||
_, err := dbp.trapWait(-1)
|
||||
return err
|
||||
}
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func (dbp *Process) resume() error {
|
||||
// all threads stopped over a breakpoint are made to step over it
|
||||
for _, thread := range dbp.threads {
|
||||
if thread.CurrentBreakpoint != nil {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.CurrentBreakpoint = nil
|
||||
}
|
||||
}
|
||||
// everything is resumed
|
||||
for _, thread := range dbp.threads {
|
||||
if err := thread.resume(); err != nil && err != sys.ESRCH {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func killProcess(pid int) error {
|
||||
return sys.Kill(pid, sys.SIGINT)
|
||||
}
|
672
vendor/github.com/derekparker/delve/pkg/proc/proc_windows.go
generated
vendored
Normal file
672
vendor/github.com/derekparker/delve/pkg/proc/proc_windows.go
generated
vendored
Normal file
@ -0,0 +1,672 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"debug/pe"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
"sync"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/windows"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/frame"
|
||||
"github.com/derekparker/delve/pkg/dwarf/line"
|
||||
"golang.org/x/debug/dwarf"
|
||||
)
|
||||
|
||||
// OSProcessDetails holds Windows specific information.
|
||||
type OSProcessDetails struct {
|
||||
hProcess syscall.Handle
|
||||
breakThread int
|
||||
}
|
||||
|
||||
// Launch creates and begins debugging a new process.
|
||||
func Launch(cmd []string, wd string) (*Process, error) {
|
||||
argv0Go, err := filepath.Abs(cmd[0])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// Make sure the binary exists and is an executable file
|
||||
if filepath.Base(cmd[0]) == cmd[0] {
|
||||
if _, err := exec.LookPath(cmd[0]); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
peFile, err := openExecutablePath(argv0Go)
|
||||
if err != nil {
|
||||
return nil, NotExecutableErr
|
||||
}
|
||||
peFile.Close()
|
||||
|
||||
// Duplicate the stdin/stdout/stderr handles
|
||||
files := []uintptr{uintptr(syscall.Stdin), uintptr(syscall.Stdout), uintptr(syscall.Stderr)}
|
||||
p, _ := syscall.GetCurrentProcess()
|
||||
fd := make([]syscall.Handle, len(files))
|
||||
for i := range files {
|
||||
err := syscall.DuplicateHandle(p, syscall.Handle(files[i]), p, &fd[i], 0, true, syscall.DUPLICATE_SAME_ACCESS)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
defer syscall.CloseHandle(syscall.Handle(fd[i]))
|
||||
}
|
||||
|
||||
argv0, err := syscall.UTF16PtrFromString(argv0Go)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
// create suitable command line for CreateProcess
|
||||
// see https://github.com/golang/go/blob/master/src/syscall/exec_windows.go#L326
|
||||
// adapted from standard library makeCmdLine
|
||||
// see https://github.com/golang/go/blob/master/src/syscall/exec_windows.go#L86
|
||||
var cmdLineGo string
|
||||
if len(cmd) >= 1 {
|
||||
for _, v := range cmd {
|
||||
if cmdLineGo != "" {
|
||||
cmdLineGo += " "
|
||||
}
|
||||
cmdLineGo += syscall.EscapeArg(v)
|
||||
}
|
||||
}
|
||||
|
||||
var cmdLine *uint16
|
||||
if cmdLineGo != "" {
|
||||
if cmdLine, err = syscall.UTF16PtrFromString(cmdLineGo); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
var workingDir *uint16
|
||||
if wd != "" {
|
||||
if workingDir, err = syscall.UTF16PtrFromString(wd); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize the startup info and create process
|
||||
si := new(sys.StartupInfo)
|
||||
si.Cb = uint32(unsafe.Sizeof(*si))
|
||||
si.Flags = syscall.STARTF_USESTDHANDLES
|
||||
si.StdInput = sys.Handle(fd[0])
|
||||
si.StdOutput = sys.Handle(fd[1])
|
||||
si.StdErr = sys.Handle(fd[2])
|
||||
pi := new(sys.ProcessInformation)
|
||||
|
||||
dbp := New(0)
|
||||
dbp.execPtraceFunc(func() {
|
||||
if wd == "" {
|
||||
err = sys.CreateProcess(argv0, cmdLine, nil, nil, true, _DEBUG_ONLY_THIS_PROCESS, nil, nil, si, pi)
|
||||
} else {
|
||||
err = sys.CreateProcess(argv0, cmdLine, nil, nil, true, _DEBUG_ONLY_THIS_PROCESS, nil, workingDir, si, pi)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
sys.CloseHandle(sys.Handle(pi.Process))
|
||||
sys.CloseHandle(sys.Handle(pi.Thread))
|
||||
|
||||
dbp.pid = int(pi.ProcessId)
|
||||
|
||||
return newDebugProcess(dbp, argv0Go)
|
||||
}
|
||||
|
||||
// newDebugProcess prepares process pid for debugging.
|
||||
func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
|
||||
// It should not actually be possible for the
|
||||
// call to waitForDebugEvent to fail, since Windows
|
||||
// will always fire a CREATE_PROCESS_DEBUG_EVENT event
|
||||
// immediately after launching under DEBUG_ONLY_THIS_PROCESS.
|
||||
// Attaching with DebugActiveProcess has similar effect.
|
||||
var err error
|
||||
var tid, exitCode int
|
||||
dbp.execPtraceFunc(func() {
|
||||
tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tid == 0 {
|
||||
dbp.postExit()
|
||||
return nil, ProcessExitedError{Pid: dbp.pid, Status: exitCode}
|
||||
}
|
||||
// Suspend all threads so that the call to _ContinueDebugEvent will
|
||||
// not resume the target.
|
||||
for _, thread := range dbp.threads {
|
||||
_, err := _SuspendThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
|
||||
dbp.execPtraceFunc(func() {
|
||||
err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return initializeDebugProcess(dbp, exepath, false)
|
||||
}
|
||||
|
||||
// findExePath searches for process pid, and returns its executable path.
|
||||
func findExePath(pid int) (string, error) {
|
||||
// Original code suggested different approach (see below).
|
||||
// Maybe it could be useful in the future.
|
||||
//
|
||||
// Find executable path from PID/handle on Windows:
|
||||
// https://msdn.microsoft.com/en-us/library/aa366789(VS.85).aspx
|
||||
|
||||
p, err := syscall.OpenProcess(syscall.PROCESS_QUERY_INFORMATION, false, uint32(pid))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
defer syscall.CloseHandle(p)
|
||||
|
||||
n := uint32(128)
|
||||
for {
|
||||
buf := make([]uint16, int(n))
|
||||
err = _QueryFullProcessImageName(p, 0, &buf[0], &n)
|
||||
switch err {
|
||||
case syscall.ERROR_INSUFFICIENT_BUFFER:
|
||||
// try bigger buffer
|
||||
n *= 2
|
||||
// but stop if it gets too big
|
||||
if n > 10000 {
|
||||
return "", err
|
||||
}
|
||||
case nil:
|
||||
return syscall.UTF16ToString(buf[:n]), nil
|
||||
default:
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Attach to an existing process with the given PID.
|
||||
func Attach(pid int) (*Process, error) {
|
||||
// TODO: Probably should have SeDebugPrivilege before starting here.
|
||||
err := _DebugActiveProcess(uint32(pid))
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
exepath, err := findExePath(pid)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newDebugProcess(New(pid), exepath)
|
||||
}
|
||||
|
||||
// Kill kills the process.
|
||||
func (dbp *Process) Kill() error {
|
||||
if dbp.exited {
|
||||
return nil
|
||||
}
|
||||
if !dbp.threads[dbp.pid].Stopped() {
|
||||
return errors.New("process must be stopped in order to kill it")
|
||||
}
|
||||
// TODO: Should not have to ignore failures here,
|
||||
// but some tests appear to Kill twice causing
|
||||
// this to fail on second attempt.
|
||||
_ = syscall.TerminateProcess(dbp.os.hProcess, 1)
|
||||
dbp.exited = true
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) requestManualStop() error {
|
||||
return _DebugBreakProcess(dbp.os.hProcess)
|
||||
}
|
||||
|
||||
func (dbp *Process) updateThreadList() error {
|
||||
// We ignore this request since threads are being
|
||||
// tracked as they are created/killed in waitForDebugEvent.
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) addThread(hThread syscall.Handle, threadID int, attach, suspendNewThreads bool) (*Thread, error) {
|
||||
if thread, ok := dbp.threads[threadID]; ok {
|
||||
return thread, nil
|
||||
}
|
||||
thread := &Thread{
|
||||
ID: threadID,
|
||||
dbp: dbp,
|
||||
os: new(OSSpecificDetails),
|
||||
}
|
||||
thread.os.hThread = hThread
|
||||
dbp.threads[threadID] = thread
|
||||
if dbp.currentThread == nil {
|
||||
dbp.SwitchThread(thread.ID)
|
||||
}
|
||||
if suspendNewThreads {
|
||||
_, err := _SuspendThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return thread, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugFrame(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
debugFrameSec := exe.Section(".debug_frame")
|
||||
debugInfoSec := exe.Section(".debug_info")
|
||||
|
||||
if debugFrameSec != nil && debugInfoSec != nil {
|
||||
debugFrame, err := debugFrameSec.Data()
|
||||
if err != nil && uint32(len(debugFrame)) < debugFrameSec.Size {
|
||||
fmt.Println("could not get .debug_frame section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if 0 < debugFrameSec.VirtualSize && debugFrameSec.VirtualSize < debugFrameSec.Size {
|
||||
debugFrame = debugFrame[:debugFrameSec.VirtualSize]
|
||||
}
|
||||
dat, err := debugInfoSec.Data()
|
||||
if err != nil {
|
||||
fmt.Println("could not get .debug_info section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
dbp.frameEntries = frame.Parse(debugFrame, frame.DwarfEndian(dat))
|
||||
} else {
|
||||
fmt.Println("could not find .debug_frame section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func findPESymbol(f *pe.File, name string) (*pe.Symbol, error) {
|
||||
for _, s := range f.Symbols {
|
||||
if s.Name != name {
|
||||
continue
|
||||
}
|
||||
if s.SectionNumber <= 0 {
|
||||
return nil, fmt.Errorf("symbol %s: invalid section number %d", name, s.SectionNumber)
|
||||
}
|
||||
if len(f.Sections) < int(s.SectionNumber) {
|
||||
return nil, fmt.Errorf("symbol %s: section number %d is larger than max %d", name, s.SectionNumber, len(f.Sections))
|
||||
}
|
||||
return s, nil
|
||||
}
|
||||
return nil, fmt.Errorf("no %s symbol found", name)
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func loadPETable(f *pe.File, sname, ename string) ([]byte, error) {
|
||||
ssym, err := findPESymbol(f, sname)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
esym, err := findPESymbol(f, ename)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if ssym.SectionNumber != esym.SectionNumber {
|
||||
return nil, fmt.Errorf("%s and %s symbols must be in the same section", sname, ename)
|
||||
}
|
||||
sect := f.Sections[ssym.SectionNumber-1]
|
||||
data, err := sect.Data()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return data[ssym.Value:esym.Value], nil
|
||||
}
|
||||
|
||||
// Borrowed from https://golang.org/src/cmd/internal/objfile/pe.go
|
||||
func pcln(exe *pe.File) (textStart uint64, symtab, pclntab []byte, err error) {
|
||||
var imageBase uint64
|
||||
switch oh := exe.OptionalHeader.(type) {
|
||||
case *pe.OptionalHeader32:
|
||||
imageBase = uint64(oh.ImageBase)
|
||||
case *pe.OptionalHeader64:
|
||||
imageBase = oh.ImageBase
|
||||
default:
|
||||
return 0, nil, nil, fmt.Errorf("pe file format not recognized")
|
||||
}
|
||||
if sect := exe.Section(".text"); sect != nil {
|
||||
textStart = imageBase + uint64(sect.VirtualAddress)
|
||||
}
|
||||
if pclntab, err = loadPETable(exe, "runtime.pclntab", "runtime.epclntab"); err != nil {
|
||||
// We didn't find the symbols, so look for the names used in 1.3 and earlier.
|
||||
// TODO: Remove code looking for the old symbols when we no longer care about 1.3.
|
||||
var err2 error
|
||||
if pclntab, err2 = loadPETable(exe, "pclntab", "epclntab"); err2 != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
}
|
||||
if symtab, err = loadPETable(exe, "runtime.symtab", "runtime.esymtab"); err != nil {
|
||||
// Same as above.
|
||||
var err2 error
|
||||
if symtab, err2 = loadPETable(exe, "symtab", "esymtab"); err2 != nil {
|
||||
return 0, nil, nil, err
|
||||
}
|
||||
}
|
||||
return textStart, symtab, pclntab, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) obtainGoSymbols(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
_, symdat, pclndat, err := pcln(exe)
|
||||
if err != nil {
|
||||
fmt.Println("could not get Go symbols", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
pcln := gosym.NewLineTable(pclndat, uint64(exe.Section(".text").Offset))
|
||||
tab, err := gosym.NewTable(symdat, pcln)
|
||||
if err != nil {
|
||||
fmt.Println("could not get initialize line table", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
dbp.goSymTable = tab
|
||||
}
|
||||
|
||||
func (dbp *Process) parseDebugLineInfo(exe *pe.File, wg *sync.WaitGroup) {
|
||||
defer wg.Done()
|
||||
|
||||
if sec := exe.Section(".debug_line"); sec != nil {
|
||||
debugLine, err := sec.Data()
|
||||
if err != nil && uint32(len(debugLine)) < sec.Size {
|
||||
fmt.Println("could not get .debug_line section", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
if 0 < sec.VirtualSize && sec.VirtualSize < sec.Size {
|
||||
debugLine = debugLine[:sec.VirtualSize]
|
||||
}
|
||||
dbp.lineInfo = line.Parse(debugLine)
|
||||
} else {
|
||||
fmt.Println("could not find .debug_line section in binary")
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
var UnsupportedArchErr = errors.New("unsupported architecture of windows/386 - only windows/amd64 is supported")
|
||||
|
||||
func (dbp *Process) findExecutable(path string) (*pe.File, string, error) {
|
||||
peFile, err := openExecutablePath(path)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
if peFile.Machine != pe.IMAGE_FILE_MACHINE_AMD64 {
|
||||
return nil, path, UnsupportedArchErr
|
||||
}
|
||||
dbp.dwarf, err = dwarfFromPE(peFile)
|
||||
if err != nil {
|
||||
return nil, path, err
|
||||
}
|
||||
return peFile, path, nil
|
||||
}
|
||||
|
||||
func openExecutablePath(path string) (*pe.File, error) {
|
||||
f, err := os.OpenFile(path, 0, os.ModePerm)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return pe.NewFile(f)
|
||||
}
|
||||
|
||||
// Adapted from src/debug/pe/file.go: pe.(*File).DWARF()
|
||||
func dwarfFromPE(f *pe.File) (*dwarf.Data, error) {
|
||||
// There are many other DWARF sections, but these
|
||||
// are the ones the debug/dwarf package uses.
|
||||
// Don't bother loading others.
|
||||
var names = [...]string{"abbrev", "info", "line", "str"}
|
||||
var dat [len(names)][]byte
|
||||
for i, name := range names {
|
||||
name = ".debug_" + name
|
||||
s := f.Section(name)
|
||||
if s == nil {
|
||||
continue
|
||||
}
|
||||
b, err := s.Data()
|
||||
if err != nil && uint32(len(b)) < s.Size {
|
||||
return nil, err
|
||||
}
|
||||
if 0 < s.VirtualSize && s.VirtualSize < s.Size {
|
||||
b = b[:s.VirtualSize]
|
||||
}
|
||||
dat[i] = b
|
||||
}
|
||||
|
||||
abbrev, info, line, str := dat[0], dat[1], dat[2], dat[3]
|
||||
return dwarf.New(abbrev, nil, nil, info, line, nil, nil, str)
|
||||
}
|
||||
|
||||
type waitForDebugEventFlags int
|
||||
|
||||
const (
|
||||
waitBlocking waitForDebugEventFlags = 1 << iota
|
||||
waitSuspendNewThreads
|
||||
)
|
||||
|
||||
func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, exitCode int, err error) {
|
||||
var debugEvent _DEBUG_EVENT
|
||||
shouldExit := false
|
||||
for {
|
||||
continueStatus := uint32(_DBG_CONTINUE)
|
||||
var milliseconds uint32 = 0
|
||||
if flags&waitBlocking != 0 {
|
||||
milliseconds = syscall.INFINITE
|
||||
}
|
||||
// Wait for a debug event...
|
||||
err := _WaitForDebugEvent(&debugEvent, milliseconds)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
// ... handle each event kind ...
|
||||
unionPtr := unsafe.Pointer(&debugEvent.U[0])
|
||||
switch debugEvent.DebugEventCode {
|
||||
case _CREATE_PROCESS_DEBUG_EVENT:
|
||||
debugInfo := (*_CREATE_PROCESS_DEBUG_INFO)(unionPtr)
|
||||
hFile := debugInfo.File
|
||||
if hFile != 0 && hFile != syscall.InvalidHandle {
|
||||
err = syscall.CloseHandle(hFile)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
dbp.os.hProcess = debugInfo.Process
|
||||
_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, flags&waitSuspendNewThreads != 0)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
break
|
||||
case _CREATE_THREAD_DEBUG_EVENT:
|
||||
debugInfo := (*_CREATE_THREAD_DEBUG_INFO)(unionPtr)
|
||||
_, err = dbp.addThread(debugInfo.Thread, int(debugEvent.ThreadId), false, flags&waitSuspendNewThreads != 0)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
break
|
||||
case _EXIT_THREAD_DEBUG_EVENT:
|
||||
delete(dbp.threads, int(debugEvent.ThreadId))
|
||||
break
|
||||
case _OUTPUT_DEBUG_STRING_EVENT:
|
||||
//TODO: Handle debug output strings
|
||||
break
|
||||
case _LOAD_DLL_DEBUG_EVENT:
|
||||
debugInfo := (*_LOAD_DLL_DEBUG_INFO)(unionPtr)
|
||||
hFile := debugInfo.File
|
||||
if hFile != 0 && hFile != syscall.InvalidHandle {
|
||||
err = syscall.CloseHandle(hFile)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
}
|
||||
break
|
||||
case _UNLOAD_DLL_DEBUG_EVENT:
|
||||
break
|
||||
case _RIP_EVENT:
|
||||
break
|
||||
case _EXCEPTION_DEBUG_EVENT:
|
||||
exception := (*_EXCEPTION_DEBUG_INFO)(unionPtr)
|
||||
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
|
||||
return tid, 0, nil
|
||||
} else {
|
||||
continueStatus = _DBG_CONTINUE
|
||||
}
|
||||
case _EXCEPTION_SINGLE_STEP:
|
||||
dbp.os.breakThread = tid
|
||||
return tid, 0, nil
|
||||
default:
|
||||
continueStatus = _DBG_EXCEPTION_NOT_HANDLED
|
||||
}
|
||||
case _EXIT_PROCESS_DEBUG_EVENT:
|
||||
debugInfo := (*_EXIT_PROCESS_DEBUG_INFO)(unionPtr)
|
||||
exitCode = int(debugInfo.ExitCode)
|
||||
shouldExit = true
|
||||
default:
|
||||
return 0, 0, fmt.Errorf("unknown debug event code: %d", debugEvent.DebugEventCode)
|
||||
}
|
||||
|
||||
// .. and then continue unless we received an event that indicated we should break into debugger.
|
||||
err = _ContinueDebugEvent(debugEvent.ProcessId, debugEvent.ThreadId, continueStatus)
|
||||
if err != nil {
|
||||
return 0, 0, err
|
||||
}
|
||||
|
||||
if shouldExit {
|
||||
return 0, exitCode, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (dbp *Process) trapWait(pid int) (*Thread, error) {
|
||||
var err error
|
||||
var tid, exitCode int
|
||||
dbp.execPtraceFunc(func() {
|
||||
tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
|
||||
})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if tid == 0 {
|
||||
dbp.postExit()
|
||||
return nil, ProcessExitedError{Pid: dbp.pid, Status: exitCode}
|
||||
}
|
||||
th := dbp.threads[tid]
|
||||
return th, nil
|
||||
}
|
||||
|
||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
|
||||
wg.Done()
|
||||
}
|
||||
|
||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
|
||||
return 0, nil, fmt.Errorf("not implemented: wait")
|
||||
}
|
||||
|
||||
func (dbp *Process) setCurrentBreakpoints(trapthread *Thread) error {
|
||||
// While the debug event that stopped the target was being propagated
|
||||
// other target threads could generate other debug events.
|
||||
// After this function we need to know about all the threads
|
||||
// stopped on a breakpoint. To do that we first suspend all target
|
||||
// threads and then repeatedly call _ContinueDebugEvent followed by
|
||||
// waitForDebugEvent in non-blocking mode.
|
||||
// We need to explicitly call SuspendThread because otherwise the
|
||||
// call to _ContinueDebugEvent will resume execution of some of the
|
||||
// target threads.
|
||||
|
||||
err := trapthread.SetCurrentBreakpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for _, thread := range dbp.threads {
|
||||
thread.running = false
|
||||
_, err := _SuspendThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
var err error
|
||||
var tid int
|
||||
dbp.execPtraceFunc(func() {
|
||||
err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
|
||||
if err == nil {
|
||||
tid, _, _ = dbp.waitForDebugEvent(waitSuspendNewThreads)
|
||||
}
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tid == 0 {
|
||||
break
|
||||
}
|
||||
err = dbp.threads[tid].SetCurrentBreakpoint()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func (dbp *Process) exitGuard(err error) error {
|
||||
return err
|
||||
}
|
||||
|
||||
func (dbp *Process) resume() error {
|
||||
for _, thread := range dbp.threads {
|
||||
if thread.CurrentBreakpoint != nil {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.CurrentBreakpoint = nil
|
||||
}
|
||||
}
|
||||
|
||||
for _, thread := range dbp.threads {
|
||||
thread.running = true
|
||||
_, err := _ResumeThread(thread.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func killProcess(pid int) error {
|
||||
p, err := os.FindProcess(pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return p.Kill()
|
||||
}
|
28
vendor/github.com/derekparker/delve/pkg/proc/ptrace_darwin.go
generated
vendored
Normal file
28
vendor/github.com/derekparker/delve/pkg/proc/ptrace_darwin.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package proc
|
||||
|
||||
import sys "golang.org/x/sys/unix"
|
||||
|
||||
// PtraceAttach executes the sys.PtraceAttach call.
|
||||
func PtraceAttach(pid int) error {
|
||||
return sys.PtraceAttach(pid)
|
||||
}
|
||||
|
||||
// PtraceDetach executes the PT_DETACH ptrace call.
|
||||
func PtraceDetach(tid, sig int) error {
|
||||
return ptrace(sys.PT_DETACH, tid, 1, uintptr(sig))
|
||||
}
|
||||
|
||||
// PtraceCont executes the PTRACE_CONT ptrace call.
|
||||
func PtraceCont(tid, sig int) error {
|
||||
return ptrace(sys.PTRACE_CONT, tid, 1, 0)
|
||||
}
|
||||
|
||||
// PtraceSingleStep returns PT_STEP ptrace call.
|
||||
func PtraceSingleStep(tid int) error {
|
||||
return ptrace(sys.PT_STEP, tid, 1, 0)
|
||||
}
|
||||
|
||||
func ptrace(request, pid int, addr uintptr, data uintptr) (err error) {
|
||||
_, _, err = sys.Syscall6(sys.SYS_PTRACE, uintptr(request), uintptr(pid), uintptr(addr), uintptr(data), 0, 0)
|
||||
return
|
||||
}
|
96
vendor/github.com/derekparker/delve/pkg/proc/ptrace_linux.go
generated
vendored
Normal file
96
vendor/github.com/derekparker/delve/pkg/proc/ptrace_linux.go
generated
vendored
Normal file
@ -0,0 +1,96 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// PtraceAttach executes the sys.PtraceAttach call.
|
||||
func PtraceAttach(pid int) error {
|
||||
return sys.PtraceAttach(pid)
|
||||
}
|
||||
|
||||
// PtraceDetach calls ptrace(PTRACE_DETACH).
|
||||
func PtraceDetach(tid, sig int) error {
|
||||
_, _, err := sys.Syscall6(sys.SYS_PTRACE, sys.PTRACE_DETACH, uintptr(tid), 1, uintptr(sig), 0, 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PtraceCont executes ptrace PTRACE_CONT
|
||||
func PtraceCont(tid, sig int) error {
|
||||
return sys.PtraceCont(tid, sig)
|
||||
}
|
||||
|
||||
// PtraceSingleStep executes ptrace PTRACE_SINGLE_STEP.
|
||||
func PtraceSingleStep(tid int) error {
|
||||
return sys.PtraceSingleStep(tid)
|
||||
}
|
||||
|
||||
// PtracePokeUser execute ptrace PTRACE_POKE_USER.
|
||||
func PtracePokeUser(tid int, off, addr uintptr) error {
|
||||
_, _, err := sys.Syscall6(sys.SYS_PTRACE, sys.PTRACE_POKEUSR, uintptr(tid), uintptr(off), uintptr(addr), 0, 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// PtracePeekUser execute ptrace PTRACE_PEEK_USER.
|
||||
func PtracePeekUser(tid int, off uintptr) (uintptr, error) {
|
||||
var val uintptr
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_PTRACE, syscall.PTRACE_PEEKUSR, uintptr(tid), uintptr(off), uintptr(unsafe.Pointer(&val)), 0, 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return 0, err
|
||||
}
|
||||
return val, nil
|
||||
}
|
||||
|
||||
// PtraceGetRegset returns floating point registers of the specified thread
|
||||
// using PTRACE.
|
||||
// See amd64_linux_fetch_inferior_registers in gdb/amd64-linux-nat.c.html
|
||||
// and amd64_supply_xsave in gdb/amd64-tdep.c.html
|
||||
// and Section 13.1 (and following) of Intel® 64 and IA-32 Architectures Software Developer’s Manual, Volume 1: Basic Architecture
|
||||
func PtraceGetRegset(tid int) (regset PtraceXsave, err error) {
|
||||
_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETFPREGS, uintptr(tid), uintptr(0), uintptr(unsafe.Pointer(®set.PtraceFpRegs)), 0, 0)
|
||||
if err == syscall.Errno(0) {
|
||||
err = nil
|
||||
}
|
||||
|
||||
var xstateargs [_X86_XSTATE_MAX_SIZE]byte
|
||||
iov := sys.Iovec{Base: &xstateargs[0], Len: _X86_XSTATE_MAX_SIZE}
|
||||
_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETREGSET, uintptr(tid), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0)
|
||||
if err != syscall.Errno(0) {
|
||||
return
|
||||
} else {
|
||||
err = nil
|
||||
}
|
||||
|
||||
if _XSAVE_HEADER_START+_XSAVE_HEADER_LEN >= iov.Len {
|
||||
return
|
||||
}
|
||||
xsaveheader := xstateargs[_XSAVE_HEADER_START : _XSAVE_HEADER_START+_XSAVE_HEADER_LEN]
|
||||
xstate_bv := binary.LittleEndian.Uint64(xsaveheader[0:8])
|
||||
xcomp_bv := binary.LittleEndian.Uint64(xsaveheader[8:16])
|
||||
|
||||
if xcomp_bv&(1<<63) != 0 {
|
||||
// compact format not supported
|
||||
return
|
||||
}
|
||||
|
||||
if xstate_bv&(1<<2) == 0 {
|
||||
// AVX state not present
|
||||
return
|
||||
}
|
||||
|
||||
avxstate := xstateargs[_XSAVE_EXTENDED_REGION_START:iov.Len]
|
||||
regset.AvxState = true
|
||||
copy(regset.YmmSpace[:], avxstate[:len(regset.YmmSpace)])
|
||||
|
||||
return
|
||||
}
|
13
vendor/github.com/derekparker/delve/pkg/proc/ptrace_windows.go
generated
vendored
Normal file
13
vendor/github.com/derekparker/delve/pkg/proc/ptrace_windows.go
generated
vendored
Normal file
@ -0,0 +1,13 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func PtraceAttach(pid int) error {
|
||||
return fmt.Errorf("not implemented: PtraceAttach")
|
||||
}
|
||||
|
||||
func PtraceDetach(tid, sig int) error {
|
||||
return _DebugActiveProcessStop(uint32(tid))
|
||||
}
|
231
vendor/github.com/derekparker/delve/pkg/proc/registers.go
generated
vendored
Normal file
231
vendor/github.com/derekparker/delve/pkg/proc/registers.go
generated
vendored
Normal file
@ -0,0 +1,231 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"math"
|
||||
"os"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// Registers is an interface for a generic register type. The
|
||||
// interface encapsulates the generic values / actions
|
||||
// we need independent of arch. The concrete register types
|
||||
// will be different depending on OS/Arch.
|
||||
type Registers interface {
|
||||
PC() uint64
|
||||
SP() uint64
|
||||
BP() uint64
|
||||
CX() uint64
|
||||
TLS() uint64
|
||||
Get(int) (uint64, error)
|
||||
SetPC(*Thread, uint64) error
|
||||
Slice() []Register
|
||||
}
|
||||
|
||||
type Register struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
func appendWordReg(regs []Register, name string, value uint16) []Register {
|
||||
return append(regs, Register{name, fmt.Sprintf("%#04x", value)})
|
||||
}
|
||||
|
||||
func appendDwordReg(regs []Register, name string, value uint32) []Register {
|
||||
return append(regs, Register{name, fmt.Sprintf("%#08x", value)})
|
||||
}
|
||||
|
||||
func appendQwordReg(regs []Register, name string, value uint64) []Register {
|
||||
return append(regs, Register{name, fmt.Sprintf("%#016x", value)})
|
||||
}
|
||||
|
||||
func appendFlagReg(regs []Register, name string, value uint64, descr flagRegisterDescr, size int) []Register {
|
||||
return append(regs, Register{name, descr.Describe(value, size)})
|
||||
}
|
||||
|
||||
func appendX87Reg(regs []Register, index int, exponent uint16, mantissa uint64) []Register {
|
||||
var f float64
|
||||
fset := false
|
||||
|
||||
const (
|
||||
_SIGNBIT = 1 << 15
|
||||
_EXP_BIAS = (1 << 14) - 1 // 2^(n-1) - 1 = 16383
|
||||
_SPECIALEXP = (1 << 15) - 1 // all bits set
|
||||
_HIGHBIT = 1 << 63
|
||||
_QUIETBIT = 1 << 62
|
||||
)
|
||||
|
||||
sign := 1.0
|
||||
if exponent&_SIGNBIT != 0 {
|
||||
sign = -1.0
|
||||
}
|
||||
exponent &= ^uint16(_SIGNBIT)
|
||||
|
||||
NaN := math.NaN()
|
||||
Inf := math.Inf(+1)
|
||||
|
||||
switch exponent {
|
||||
case 0:
|
||||
switch {
|
||||
case mantissa == 0:
|
||||
f = sign * 0.0
|
||||
fset = true
|
||||
case mantissa&_HIGHBIT != 0:
|
||||
f = NaN
|
||||
fset = true
|
||||
}
|
||||
case _SPECIALEXP:
|
||||
switch {
|
||||
case mantissa&_HIGHBIT == 0:
|
||||
f = sign * Inf
|
||||
fset = true
|
||||
default:
|
||||
f = NaN // signaling NaN
|
||||
fset = true
|
||||
}
|
||||
default:
|
||||
if mantissa&_HIGHBIT == 0 {
|
||||
f = NaN
|
||||
fset = true
|
||||
}
|
||||
}
|
||||
|
||||
if !fset {
|
||||
significand := float64(mantissa) / (1 << 63)
|
||||
f = sign * math.Ldexp(significand, int(exponent-_EXP_BIAS))
|
||||
}
|
||||
|
||||
return append(regs, Register{fmt.Sprintf("ST(%d)", index), fmt.Sprintf("%#04x%016x\t%g", exponent, mantissa, f)})
|
||||
}
|
||||
|
||||
func appendSSEReg(regs []Register, name string, xmm []byte) []Register {
|
||||
buf := bytes.NewReader(xmm)
|
||||
|
||||
var out bytes.Buffer
|
||||
var vi [16]uint8
|
||||
for i := range vi {
|
||||
binary.Read(buf, binary.LittleEndian, &vi[i])
|
||||
}
|
||||
|
||||
fmt.Fprintf(&out, "0x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x%02x", vi[15], vi[14], vi[13], vi[12], vi[11], vi[10], vi[9], vi[8], vi[7], vi[6], vi[5], vi[4], vi[3], vi[2], vi[1], vi[0])
|
||||
|
||||
fmt.Fprintf(&out, "\tv2_int={ %02x%02x%02x%02x%02x%02x%02x%02x %02x%02x%02x%02x%02x%02x%02x%02x }", vi[7], vi[6], vi[5], vi[4], vi[3], vi[2], vi[1], vi[0], vi[15], vi[14], vi[13], vi[12], vi[11], vi[10], vi[9], vi[8])
|
||||
|
||||
fmt.Fprintf(&out, "\tv4_int={ %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x %02x%02x%02x%02x }", vi[3], vi[2], vi[1], vi[0], vi[7], vi[6], vi[5], vi[4], vi[11], vi[10], vi[9], vi[8], vi[15], vi[14], vi[13], vi[12])
|
||||
|
||||
fmt.Fprintf(&out, "\tv8_int={ %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x %02x%02x }", vi[1], vi[0], vi[3], vi[2], vi[5], vi[4], vi[7], vi[6], vi[9], vi[8], vi[11], vi[10], vi[13], vi[12], vi[15], vi[14])
|
||||
|
||||
fmt.Fprintf(&out, "\tv16_int={ %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x %02x }", vi[0], vi[1], vi[2], vi[3], vi[4], vi[5], vi[6], vi[7], vi[8], vi[9], vi[10], vi[11], vi[12], vi[13], vi[14], vi[15])
|
||||
|
||||
buf.Seek(0, os.SEEK_SET)
|
||||
var v2 [2]float64
|
||||
for i := range v2 {
|
||||
binary.Read(buf, binary.LittleEndian, &v2[i])
|
||||
}
|
||||
fmt.Fprintf(&out, "\tv2_float={ %g %g }", v2[0], v2[1])
|
||||
|
||||
buf.Seek(0, os.SEEK_SET)
|
||||
var v4 [4]float32
|
||||
for i := range v4 {
|
||||
binary.Read(buf, binary.LittleEndian, &v4[i])
|
||||
}
|
||||
fmt.Fprintf(&out, "\tv4_float={ %g %g %g %g }", v4[0], v4[1], v4[2], v4[3])
|
||||
|
||||
return append(regs, Register{name, out.String()})
|
||||
}
|
||||
|
||||
var UnknownRegisterError = errors.New("unknown register")
|
||||
|
||||
// Registers obtains register values from the debugged process.
|
||||
func (t *Thread) Registers(floatingPoint bool) (Registers, error) {
|
||||
return registers(t, floatingPoint)
|
||||
}
|
||||
|
||||
// PC returns the current PC for this thread.
|
||||
func (t *Thread) PC() (uint64, error) {
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return regs.PC(), nil
|
||||
}
|
||||
|
||||
type flagRegisterDescr []flagDescr
|
||||
type flagDescr struct {
|
||||
name string
|
||||
mask uint64
|
||||
}
|
||||
|
||||
var mxcsrDescription flagRegisterDescr = []flagDescr{
|
||||
{"FZ", 1 << 15},
|
||||
{"RZ/RN", 1<<14 | 1<<13},
|
||||
{"PM", 1 << 12},
|
||||
{"UM", 1 << 11},
|
||||
{"OM", 1 << 10},
|
||||
{"ZM", 1 << 9},
|
||||
{"DM", 1 << 8},
|
||||
{"IM", 1 << 7},
|
||||
{"DAZ", 1 << 6},
|
||||
{"PE", 1 << 5},
|
||||
{"UE", 1 << 4},
|
||||
{"OE", 1 << 3},
|
||||
{"ZE", 1 << 2},
|
||||
{"DE", 1 << 1},
|
||||
{"IE", 1 << 0},
|
||||
}
|
||||
|
||||
var eflagsDescription flagRegisterDescr = []flagDescr{
|
||||
{"CF", 1 << 0},
|
||||
{"", 1 << 1},
|
||||
{"PF", 1 << 2},
|
||||
{"AF", 1 << 4},
|
||||
{"ZF", 1 << 6},
|
||||
{"SF", 1 << 7},
|
||||
{"TF", 1 << 8},
|
||||
{"IF", 1 << 9},
|
||||
{"DF", 1 << 10},
|
||||
{"OF", 1 << 11},
|
||||
{"IOPL", 1<<12 | 1<<13},
|
||||
{"NT", 1 << 14},
|
||||
{"RF", 1 << 16},
|
||||
{"VM", 1 << 17},
|
||||
{"AC", 1 << 18},
|
||||
{"VIF", 1 << 19},
|
||||
{"VIP", 1 << 20},
|
||||
{"ID", 1 << 21},
|
||||
}
|
||||
|
||||
func (descr flagRegisterDescr) Mask() uint64 {
|
||||
var r uint64
|
||||
for _, f := range descr {
|
||||
r = r | f.mask
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (descr flagRegisterDescr) Describe(reg uint64, bitsize int) string {
|
||||
var r []string
|
||||
for _, f := range descr {
|
||||
if f.name == "" {
|
||||
continue
|
||||
}
|
||||
// rbm is f.mask with only the right-most bit set:
|
||||
// 0001 1100 -> 0000 0100
|
||||
rbm := f.mask & -f.mask
|
||||
if rbm == f.mask {
|
||||
if reg&f.mask != 0 {
|
||||
r = append(r, f.name)
|
||||
}
|
||||
} else {
|
||||
x := (reg & f.mask) >> uint64(math.Log2(float64(rbm)))
|
||||
r = append(r, fmt.Sprintf("%s=%x", f.name, x))
|
||||
}
|
||||
}
|
||||
if reg & ^descr.Mask() != 0 {
|
||||
r = append(r, fmt.Sprintf("unknown_flags=%x", reg&^descr.Mask()))
|
||||
}
|
||||
return fmt.Sprintf("%#0*x\t[%s]", bitsize/4, reg, strings.Join(r, " "))
|
||||
}
|
367
vendor/github.com/derekparker/delve/pkg/proc/registers_darwin_amd64.go
generated
vendored
Normal file
367
vendor/github.com/derekparker/delve/pkg/proc/registers_darwin_amd64.go
generated
vendored
Normal file
@ -0,0 +1,367 @@
|
||||
package proc
|
||||
|
||||
// #include "threads_darwin.h"
|
||||
import "C"
|
||||
import (
|
||||
"encoding/binary"
|
||||
"fmt"
|
||||
"rsc.io/x86/x86asm"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Regs represents CPU registers on an AMD64 processor.
|
||||
type Regs struct {
|
||||
rax uint64
|
||||
rbx uint64
|
||||
rcx uint64
|
||||
rdx uint64
|
||||
rdi uint64
|
||||
rsi uint64
|
||||
rbp uint64
|
||||
rsp uint64
|
||||
r8 uint64
|
||||
r9 uint64
|
||||
r10 uint64
|
||||
r11 uint64
|
||||
r12 uint64
|
||||
r13 uint64
|
||||
r14 uint64
|
||||
r15 uint64
|
||||
rip uint64
|
||||
rflags uint64
|
||||
cs uint64
|
||||
fs uint64
|
||||
gs uint64
|
||||
gsBase uint64
|
||||
fpregs []Register
|
||||
}
|
||||
|
||||
func (r *Regs) Slice() []Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
}{
|
||||
{"Rip", r.rip},
|
||||
{"Rsp", r.rsp},
|
||||
{"Rax", r.rax},
|
||||
{"Rbx", r.rbx},
|
||||
{"Rcx", r.rcx},
|
||||
{"Rdx", r.rdx},
|
||||
{"Rdi", r.rdi},
|
||||
{"Rsi", r.rsi},
|
||||
{"Rbp", r.rbp},
|
||||
{"R8", r.r8},
|
||||
{"R9", r.r9},
|
||||
{"R10", r.r10},
|
||||
{"R11", r.r11},
|
||||
{"R12", r.r12},
|
||||
{"R13", r.r13},
|
||||
{"R14", r.r14},
|
||||
{"R15", r.r15},
|
||||
{"Rflags", r.rflags},
|
||||
{"Cs", r.cs},
|
||||
{"Fs", r.fs},
|
||||
{"Gs", r.gs},
|
||||
{"Gs_base", r.gsBase},
|
||||
}
|
||||
out := make([]Register, 0, len(regs)+len(r.fpregs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Rflags" {
|
||||
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
|
||||
} else {
|
||||
out = appendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
out = append(out, r.fpregs...)
|
||||
return out
|
||||
}
|
||||
|
||||
// PC returns the current program counter
|
||||
// i.e. the RIP CPU register.
|
||||
func (r *Regs) PC() uint64 {
|
||||
return r.rip
|
||||
}
|
||||
|
||||
// SP returns the stack pointer location,
|
||||
// i.e. the RSP register.
|
||||
func (r *Regs) SP() uint64 {
|
||||
return r.rsp
|
||||
}
|
||||
|
||||
func (r *Regs) BP() uint64 {
|
||||
return r.rbp
|
||||
}
|
||||
|
||||
// CX returns the value of the RCX register.
|
||||
func (r *Regs) CX() uint64 {
|
||||
return r.rcx
|
||||
}
|
||||
|
||||
// TLS returns the value of the register
|
||||
// that contains the location of the thread
|
||||
// local storage segment.
|
||||
func (r *Regs) TLS() uint64 {
|
||||
return r.gsBase
|
||||
}
|
||||
|
||||
// SetPC sets the RIP register to the value specified by `pc`.
|
||||
func (r *Regs) SetPC(thread *Thread, pc uint64) error {
|
||||
kret := C.set_pc(thread.os.threadAct, C.uint64_t(pc))
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("could not set pc")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *Regs) Get(n int) (uint64, error) {
|
||||
reg := x86asm.Reg(n)
|
||||
const (
|
||||
mask8 = 0x000f
|
||||
mask16 = 0x00ff
|
||||
mask32 = 0xffff
|
||||
)
|
||||
|
||||
switch reg {
|
||||
// 8-bit
|
||||
case x86asm.AL:
|
||||
return r.rax & mask8, nil
|
||||
case x86asm.CL:
|
||||
return r.rcx & mask8, nil
|
||||
case x86asm.DL:
|
||||
return r.rdx & mask8, nil
|
||||
case x86asm.BL:
|
||||
return r.rbx & mask8, nil
|
||||
case x86asm.AH:
|
||||
return (r.rax >> 8) & mask8, nil
|
||||
case x86asm.CH:
|
||||
return (r.rcx >> 8) & mask8, nil
|
||||
case x86asm.DH:
|
||||
return (r.rdx >> 8) & mask8, nil
|
||||
case x86asm.BH:
|
||||
return (r.rbx >> 8) & mask8, nil
|
||||
case x86asm.SPB:
|
||||
return r.rsp & mask8, nil
|
||||
case x86asm.BPB:
|
||||
return r.rbp & mask8, nil
|
||||
case x86asm.SIB:
|
||||
return r.rsi & mask8, nil
|
||||
case x86asm.DIB:
|
||||
return r.rdi & mask8, nil
|
||||
case x86asm.R8B:
|
||||
return r.r8 & mask8, nil
|
||||
case x86asm.R9B:
|
||||
return r.r9 & mask8, nil
|
||||
case x86asm.R10B:
|
||||
return r.r10 & mask8, nil
|
||||
case x86asm.R11B:
|
||||
return r.r11 & mask8, nil
|
||||
case x86asm.R12B:
|
||||
return r.r12 & mask8, nil
|
||||
case x86asm.R13B:
|
||||
return r.r13 & mask8, nil
|
||||
case x86asm.R14B:
|
||||
return r.r14 & mask8, nil
|
||||
case x86asm.R15B:
|
||||
return r.r15 & mask8, nil
|
||||
|
||||
// 16-bit
|
||||
case x86asm.AX:
|
||||
return r.rax & mask16, nil
|
||||
case x86asm.CX:
|
||||
return r.rcx & mask16, nil
|
||||
case x86asm.DX:
|
||||
return r.rdx & mask16, nil
|
||||
case x86asm.BX:
|
||||
return r.rbx & mask16, nil
|
||||
case x86asm.SP:
|
||||
return r.rsp & mask16, nil
|
||||
case x86asm.BP:
|
||||
return r.rbp & mask16, nil
|
||||
case x86asm.SI:
|
||||
return r.rsi & mask16, nil
|
||||
case x86asm.DI:
|
||||
return r.rdi & mask16, nil
|
||||
case x86asm.R8W:
|
||||
return r.r8 & mask16, nil
|
||||
case x86asm.R9W:
|
||||
return r.r9 & mask16, nil
|
||||
case x86asm.R10W:
|
||||
return r.r10 & mask16, nil
|
||||
case x86asm.R11W:
|
||||
return r.r11 & mask16, nil
|
||||
case x86asm.R12W:
|
||||
return r.r12 & mask16, nil
|
||||
case x86asm.R13W:
|
||||
return r.r13 & mask16, nil
|
||||
case x86asm.R14W:
|
||||
return r.r14 & mask16, nil
|
||||
case x86asm.R15W:
|
||||
return r.r15 & mask16, nil
|
||||
|
||||
// 32-bit
|
||||
case x86asm.EAX:
|
||||
return r.rax & mask32, nil
|
||||
case x86asm.ECX:
|
||||
return r.rcx & mask32, nil
|
||||
case x86asm.EDX:
|
||||
return r.rdx & mask32, nil
|
||||
case x86asm.EBX:
|
||||
return r.rbx & mask32, nil
|
||||
case x86asm.ESP:
|
||||
return r.rsp & mask32, nil
|
||||
case x86asm.EBP:
|
||||
return r.rbp & mask32, nil
|
||||
case x86asm.ESI:
|
||||
return r.rsi & mask32, nil
|
||||
case x86asm.EDI:
|
||||
return r.rdi & mask32, nil
|
||||
case x86asm.R8L:
|
||||
return r.r8 & mask32, nil
|
||||
case x86asm.R9L:
|
||||
return r.r9 & mask32, nil
|
||||
case x86asm.R10L:
|
||||
return r.r10 & mask32, nil
|
||||
case x86asm.R11L:
|
||||
return r.r11 & mask32, nil
|
||||
case x86asm.R12L:
|
||||
return r.r12 & mask32, nil
|
||||
case x86asm.R13L:
|
||||
return r.r13 & mask32, nil
|
||||
case x86asm.R14L:
|
||||
return r.r14 & mask32, nil
|
||||
case x86asm.R15L:
|
||||
return r.r15 & mask32, nil
|
||||
|
||||
// 64-bit
|
||||
case x86asm.RAX:
|
||||
return r.rax, nil
|
||||
case x86asm.RCX:
|
||||
return r.rcx, nil
|
||||
case x86asm.RDX:
|
||||
return r.rdx, nil
|
||||
case x86asm.RBX:
|
||||
return r.rbx, nil
|
||||
case x86asm.RSP:
|
||||
return r.rsp, nil
|
||||
case x86asm.RBP:
|
||||
return r.rbp, nil
|
||||
case x86asm.RSI:
|
||||
return r.rsi, nil
|
||||
case x86asm.RDI:
|
||||
return r.rdi, nil
|
||||
case x86asm.R8:
|
||||
return r.r8, nil
|
||||
case x86asm.R9:
|
||||
return r.r9, nil
|
||||
case x86asm.R10:
|
||||
return r.r10, nil
|
||||
case x86asm.R11:
|
||||
return r.r11, nil
|
||||
case x86asm.R12:
|
||||
return r.r12, nil
|
||||
case x86asm.R13:
|
||||
return r.r13, nil
|
||||
case x86asm.R14:
|
||||
return r.r14, nil
|
||||
case x86asm.R15:
|
||||
return r.r15, nil
|
||||
}
|
||||
|
||||
return 0, UnknownRegisterError
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
|
||||
var state C.x86_thread_state64_t
|
||||
var identity C.thread_identifier_info_data_t
|
||||
kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &state)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not get registers")
|
||||
}
|
||||
kret = C.get_identity(C.mach_port_name_t(thread.os.threadAct), &identity)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not get thread identity informations")
|
||||
}
|
||||
/*
|
||||
thread_identifier_info::thread_handle contains the base of the
|
||||
thread-specific data area, which on x86 and x86_64 is the thread’s base
|
||||
address of the %gs segment. 10.9.2 xnu-2422.90.20/osfmk/kern/thread.c
|
||||
thread_info_internal() gets the value from
|
||||
machine_thread::cthread_self, which is the same value used to set the
|
||||
%gs base in xnu-2422.90.20/osfmk/i386/pcb_native.c
|
||||
act_machine_switch_pcb().
|
||||
--
|
||||
comment copied from chromium's crashpad
|
||||
https://chromium.googlesource.com/crashpad/crashpad/+/master/snapshot/mac/process_reader.cc
|
||||
*/
|
||||
regs := &Regs{
|
||||
rax: uint64(state.__rax),
|
||||
rbx: uint64(state.__rbx),
|
||||
rcx: uint64(state.__rcx),
|
||||
rdx: uint64(state.__rdx),
|
||||
rdi: uint64(state.__rdi),
|
||||
rsi: uint64(state.__rsi),
|
||||
rbp: uint64(state.__rbp),
|
||||
rsp: uint64(state.__rsp),
|
||||
r8: uint64(state.__r8),
|
||||
r9: uint64(state.__r9),
|
||||
r10: uint64(state.__r10),
|
||||
r11: uint64(state.__r11),
|
||||
r12: uint64(state.__r12),
|
||||
r13: uint64(state.__r13),
|
||||
r14: uint64(state.__r14),
|
||||
r15: uint64(state.__r15),
|
||||
rip: uint64(state.__rip),
|
||||
rflags: uint64(state.__rflags),
|
||||
cs: uint64(state.__cs),
|
||||
fs: uint64(state.__fs),
|
||||
gs: uint64(state.__gs),
|
||||
gsBase: uint64(identity.thread_handle),
|
||||
}
|
||||
|
||||
if floatingPoint {
|
||||
// https://opensource.apple.com/source/xnu/xnu-792.13.8/osfmk/mach/i386/thread_status.h?txt
|
||||
var fpstate C.x86_float_state64_t
|
||||
kret = C.get_fpu_registers(C.mach_port_name_t(thread.os.threadAct), &fpstate)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not get floating point registers")
|
||||
}
|
||||
|
||||
regs.fpregs = appendWordReg(regs.fpregs, "CW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fcw))))
|
||||
regs.fpregs = appendWordReg(regs.fpregs, "SW", *((*uint16)(unsafe.Pointer(&fpstate.__fpu_fsw))))
|
||||
regs.fpregs = appendWordReg(regs.fpregs, "TW", uint16(fpstate.__fpu_ftw))
|
||||
regs.fpregs = appendWordReg(regs.fpregs, "FOP", uint16(fpstate.__fpu_fop))
|
||||
regs.fpregs = appendQwordReg(regs.fpregs, "FIP", uint64(fpstate.__fpu_cs)<<32|uint64(fpstate.__fpu_ip))
|
||||
regs.fpregs = appendQwordReg(regs.fpregs, "FDP", uint64(fpstate.__fpu_ds)<<32|uint64(fpstate.__fpu_dp))
|
||||
|
||||
for i, st := range []*C.char{&fpstate.__fpu_stmm0.__mmst_reg[0], &fpstate.__fpu_stmm1.__mmst_reg[0], &fpstate.__fpu_stmm2.__mmst_reg[0], &fpstate.__fpu_stmm3.__mmst_reg[0], &fpstate.__fpu_stmm4.__mmst_reg[0], &fpstate.__fpu_stmm5.__mmst_reg[0], &fpstate.__fpu_stmm6.__mmst_reg[0], &fpstate.__fpu_stmm7.__mmst_reg[0]} {
|
||||
stb := C.GoBytes(unsafe.Pointer(st), 10)
|
||||
mantissa := binary.LittleEndian.Uint64(stb[:8])
|
||||
exponent := binary.LittleEndian.Uint16(stb[8:])
|
||||
regs.fpregs = appendX87Reg(regs.fpregs, i, exponent, mantissa)
|
||||
}
|
||||
|
||||
regs.fpregs = appendFlagReg(regs.fpregs, "MXCSR", uint64(fpstate.__fpu_mxcsr), mxcsrDescription, 32)
|
||||
regs.fpregs = appendDwordReg(regs.fpregs, "MXCSR_MASK", uint32(fpstate.__fpu_mxcsrmask))
|
||||
|
||||
for i, xmm := range []*C.char{&fpstate.__fpu_xmm0.__xmm_reg[0], &fpstate.__fpu_xmm1.__xmm_reg[0], &fpstate.__fpu_xmm2.__xmm_reg[0], &fpstate.__fpu_xmm3.__xmm_reg[0], &fpstate.__fpu_xmm4.__xmm_reg[0], &fpstate.__fpu_xmm5.__xmm_reg[0], &fpstate.__fpu_xmm6.__xmm_reg[0], &fpstate.__fpu_xmm7.__xmm_reg[0], &fpstate.__fpu_xmm8.__xmm_reg[0], &fpstate.__fpu_xmm9.__xmm_reg[0], &fpstate.__fpu_xmm10.__xmm_reg[0], &fpstate.__fpu_xmm11.__xmm_reg[0], &fpstate.__fpu_xmm12.__xmm_reg[0], &fpstate.__fpu_xmm13.__xmm_reg[0], &fpstate.__fpu_xmm14.__xmm_reg[0], &fpstate.__fpu_xmm15.__xmm_reg[0]} {
|
||||
regs.fpregs = appendSSEReg(regs.fpregs, fmt.Sprintf("XMM%d", i), C.GoBytes(unsafe.Pointer(xmm), 16))
|
||||
}
|
||||
}
|
||||
return regs, nil
|
||||
}
|
||||
|
||||
func (thread *Thread) saveRegisters() (Registers, error) {
|
||||
kret := C.get_registers(C.mach_port_name_t(thread.os.threadAct), &thread.os.registers)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return nil, fmt.Errorf("could not save register contents")
|
||||
}
|
||||
return &Regs{rip: uint64(thread.os.registers.__rip), rsp: uint64(thread.os.registers.__rsp)}, nil
|
||||
}
|
||||
|
||||
func (thread *Thread) restoreRegisters() error {
|
||||
kret := C.set_registers(C.mach_port_name_t(thread.os.threadAct), &thread.os.registers)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("could not save register contents")
|
||||
}
|
||||
return nil
|
||||
}
|
329
vendor/github.com/derekparker/delve/pkg/proc/registers_linux_amd64.go
generated
vendored
Normal file
329
vendor/github.com/derekparker/delve/pkg/proc/registers_linux_amd64.go
generated
vendored
Normal file
@ -0,0 +1,329 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"rsc.io/x86/x86asm"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// Regs is a wrapper for sys.PtraceRegs.
|
||||
type Regs struct {
|
||||
regs *sys.PtraceRegs
|
||||
fpregs []Register
|
||||
}
|
||||
|
||||
func (r *Regs) Slice() []Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
}{
|
||||
{"Rip", r.regs.Rip},
|
||||
{"Rsp", r.regs.Rsp},
|
||||
{"Rax", r.regs.Rax},
|
||||
{"Rbx", r.regs.Rbx},
|
||||
{"Rcx", r.regs.Rcx},
|
||||
{"Rdx", r.regs.Rdx},
|
||||
{"Rdi", r.regs.Rdi},
|
||||
{"Rsi", r.regs.Rsi},
|
||||
{"Rbp", r.regs.Rbp},
|
||||
{"R8", r.regs.R8},
|
||||
{"R9", r.regs.R9},
|
||||
{"R10", r.regs.R10},
|
||||
{"R11", r.regs.R11},
|
||||
{"R12", r.regs.R12},
|
||||
{"R13", r.regs.R13},
|
||||
{"R14", r.regs.R14},
|
||||
{"R15", r.regs.R15},
|
||||
{"Orig_rax", r.regs.Orig_rax},
|
||||
{"Cs", r.regs.Cs},
|
||||
{"Eflags", r.regs.Eflags},
|
||||
{"Ss", r.regs.Ss},
|
||||
{"Fs_base", r.regs.Fs_base},
|
||||
{"Gs_base", r.regs.Gs_base},
|
||||
{"Ds", r.regs.Ds},
|
||||
{"Es", r.regs.Es},
|
||||
{"Fs", r.regs.Fs},
|
||||
{"Gs", r.regs.Gs},
|
||||
}
|
||||
out := make([]Register, 0, len(regs)+len(r.fpregs))
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Eflags" {
|
||||
out = appendFlagReg(out, reg.k, reg.v, eflagsDescription, 64)
|
||||
} else {
|
||||
out = appendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
out = append(out, r.fpregs...)
|
||||
return out
|
||||
}
|
||||
|
||||
// PC returns the value of RIP register.
|
||||
func (r *Regs) PC() uint64 {
|
||||
return r.regs.PC()
|
||||
}
|
||||
|
||||
// SP returns the value of RSP register.
|
||||
func (r *Regs) SP() uint64 {
|
||||
return r.regs.Rsp
|
||||
}
|
||||
|
||||
func (r *Regs) BP() uint64 {
|
||||
return r.regs.Rbp
|
||||
}
|
||||
|
||||
// CX returns the value of RCX register.
|
||||
func (r *Regs) CX() uint64 {
|
||||
return r.regs.Rcx
|
||||
}
|
||||
|
||||
// TLS returns the address of the thread
|
||||
// local storage memory segment.
|
||||
func (r *Regs) TLS() uint64 {
|
||||
return r.regs.Fs_base
|
||||
}
|
||||
|
||||
// SetPC sets RIP to the value specified by 'pc'.
|
||||
func (r *Regs) SetPC(thread *Thread, pc uint64) (err error) {
|
||||
r.regs.SetPC(pc)
|
||||
thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) })
|
||||
return
|
||||
}
|
||||
|
||||
func (r *Regs) Get(n int) (uint64, error) {
|
||||
reg := x86asm.Reg(n)
|
||||
const (
|
||||
mask8 = 0x000f
|
||||
mask16 = 0x00ff
|
||||
mask32 = 0xffff
|
||||
)
|
||||
|
||||
switch reg {
|
||||
// 8-bit
|
||||
case x86asm.AL:
|
||||
return r.regs.Rax & mask8, nil
|
||||
case x86asm.CL:
|
||||
return r.regs.Rcx & mask8, nil
|
||||
case x86asm.DL:
|
||||
return r.regs.Rdx & mask8, nil
|
||||
case x86asm.BL:
|
||||
return r.regs.Rbx & mask8, nil
|
||||
case x86asm.AH:
|
||||
return (r.regs.Rax >> 8) & mask8, nil
|
||||
case x86asm.CH:
|
||||
return (r.regs.Rcx >> 8) & mask8, nil
|
||||
case x86asm.DH:
|
||||
return (r.regs.Rdx >> 8) & mask8, nil
|
||||
case x86asm.BH:
|
||||
return (r.regs.Rbx >> 8) & mask8, nil
|
||||
case x86asm.SPB:
|
||||
return r.regs.Rsp & mask8, nil
|
||||
case x86asm.BPB:
|
||||
return r.regs.Rbp & mask8, nil
|
||||
case x86asm.SIB:
|
||||
return r.regs.Rsi & mask8, nil
|
||||
case x86asm.DIB:
|
||||
return r.regs.Rdi & mask8, nil
|
||||
case x86asm.R8B:
|
||||
return r.regs.R8 & mask8, nil
|
||||
case x86asm.R9B:
|
||||
return r.regs.R9 & mask8, nil
|
||||
case x86asm.R10B:
|
||||
return r.regs.R10 & mask8, nil
|
||||
case x86asm.R11B:
|
||||
return r.regs.R11 & mask8, nil
|
||||
case x86asm.R12B:
|
||||
return r.regs.R12 & mask8, nil
|
||||
case x86asm.R13B:
|
||||
return r.regs.R13 & mask8, nil
|
||||
case x86asm.R14B:
|
||||
return r.regs.R14 & mask8, nil
|
||||
case x86asm.R15B:
|
||||
return r.regs.R15 & mask8, nil
|
||||
|
||||
// 16-bit
|
||||
case x86asm.AX:
|
||||
return r.regs.Rax & mask16, nil
|
||||
case x86asm.CX:
|
||||
return r.regs.Rcx & mask16, nil
|
||||
case x86asm.DX:
|
||||
return r.regs.Rdx & mask16, nil
|
||||
case x86asm.BX:
|
||||
return r.regs.Rbx & mask16, nil
|
||||
case x86asm.SP:
|
||||
return r.regs.Rsp & mask16, nil
|
||||
case x86asm.BP:
|
||||
return r.regs.Rbp & mask16, nil
|
||||
case x86asm.SI:
|
||||
return r.regs.Rsi & mask16, nil
|
||||
case x86asm.DI:
|
||||
return r.regs.Rdi & mask16, nil
|
||||
case x86asm.R8W:
|
||||
return r.regs.R8 & mask16, nil
|
||||
case x86asm.R9W:
|
||||
return r.regs.R9 & mask16, nil
|
||||
case x86asm.R10W:
|
||||
return r.regs.R10 & mask16, nil
|
||||
case x86asm.R11W:
|
||||
return r.regs.R11 & mask16, nil
|
||||
case x86asm.R12W:
|
||||
return r.regs.R12 & mask16, nil
|
||||
case x86asm.R13W:
|
||||
return r.regs.R13 & mask16, nil
|
||||
case x86asm.R14W:
|
||||
return r.regs.R14 & mask16, nil
|
||||
case x86asm.R15W:
|
||||
return r.regs.R15 & mask16, nil
|
||||
|
||||
// 32-bit
|
||||
case x86asm.EAX:
|
||||
return r.regs.Rax & mask32, nil
|
||||
case x86asm.ECX:
|
||||
return r.regs.Rcx & mask32, nil
|
||||
case x86asm.EDX:
|
||||
return r.regs.Rdx & mask32, nil
|
||||
case x86asm.EBX:
|
||||
return r.regs.Rbx & mask32, nil
|
||||
case x86asm.ESP:
|
||||
return r.regs.Rsp & mask32, nil
|
||||
case x86asm.EBP:
|
||||
return r.regs.Rbp & mask32, nil
|
||||
case x86asm.ESI:
|
||||
return r.regs.Rsi & mask32, nil
|
||||
case x86asm.EDI:
|
||||
return r.regs.Rdi & mask32, nil
|
||||
case x86asm.R8L:
|
||||
return r.regs.R8 & mask32, nil
|
||||
case x86asm.R9L:
|
||||
return r.regs.R9 & mask32, nil
|
||||
case x86asm.R10L:
|
||||
return r.regs.R10 & mask32, nil
|
||||
case x86asm.R11L:
|
||||
return r.regs.R11 & mask32, nil
|
||||
case x86asm.R12L:
|
||||
return r.regs.R12 & mask32, nil
|
||||
case x86asm.R13L:
|
||||
return r.regs.R13 & mask32, nil
|
||||
case x86asm.R14L:
|
||||
return r.regs.R14 & mask32, nil
|
||||
case x86asm.R15L:
|
||||
return r.regs.R15 & mask32, nil
|
||||
|
||||
// 64-bit
|
||||
case x86asm.RAX:
|
||||
return r.regs.Rax, nil
|
||||
case x86asm.RCX:
|
||||
return r.regs.Rcx, nil
|
||||
case x86asm.RDX:
|
||||
return r.regs.Rdx, nil
|
||||
case x86asm.RBX:
|
||||
return r.regs.Rbx, nil
|
||||
case x86asm.RSP:
|
||||
return r.regs.Rsp, nil
|
||||
case x86asm.RBP:
|
||||
return r.regs.Rbp, nil
|
||||
case x86asm.RSI:
|
||||
return r.regs.Rsi, nil
|
||||
case x86asm.RDI:
|
||||
return r.regs.Rdi, nil
|
||||
case x86asm.R8:
|
||||
return r.regs.R8, nil
|
||||
case x86asm.R9:
|
||||
return r.regs.R9, nil
|
||||
case x86asm.R10:
|
||||
return r.regs.R10, nil
|
||||
case x86asm.R11:
|
||||
return r.regs.R11, nil
|
||||
case x86asm.R12:
|
||||
return r.regs.R12, nil
|
||||
case x86asm.R13:
|
||||
return r.regs.R13, nil
|
||||
case x86asm.R14:
|
||||
return r.regs.R14, nil
|
||||
case x86asm.R15:
|
||||
return r.regs.R15, nil
|
||||
}
|
||||
|
||||
return 0, UnknownRegisterError
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
|
||||
var (
|
||||
regs sys.PtraceRegs
|
||||
err error
|
||||
)
|
||||
thread.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(thread.ID, ®s) })
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := &Regs{®s, nil}
|
||||
if floatingPoint {
|
||||
r.fpregs, err = thread.fpRegisters()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h
|
||||
type PtraceFpRegs struct {
|
||||
Cwd uint16
|
||||
Swd uint16
|
||||
Ftw uint16
|
||||
Fop uint16
|
||||
Rip uint64
|
||||
Rdp uint64
|
||||
Mxcsr uint32
|
||||
MxcrMask uint32
|
||||
StSpace [32]uint32
|
||||
XmmSpace [256]byte
|
||||
padding [24]uint32
|
||||
}
|
||||
|
||||
type PtraceXsave struct {
|
||||
PtraceFpRegs
|
||||
AvxState bool // contains AVX state
|
||||
YmmSpace [256]byte
|
||||
}
|
||||
|
||||
const (
|
||||
_X86_XSTATE_MAX_SIZE = 2688
|
||||
_NT_X86_XSTATE = 0x202
|
||||
|
||||
_XSAVE_HEADER_START = 512
|
||||
_XSAVE_HEADER_LEN = 64
|
||||
_XSAVE_EXTENDED_REGION_START = 576
|
||||
_XSAVE_SSE_REGION_LEN = 416
|
||||
)
|
||||
|
||||
func (thread *Thread) fpRegisters() (regs []Register, err error) {
|
||||
var fpregs PtraceXsave
|
||||
thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) })
|
||||
|
||||
// x87 registers
|
||||
regs = appendWordReg(regs, "CW", fpregs.Cwd)
|
||||
regs = appendWordReg(regs, "SW", fpregs.Swd)
|
||||
regs = appendWordReg(regs, "TW", fpregs.Ftw)
|
||||
regs = appendWordReg(regs, "FOP", fpregs.Fop)
|
||||
regs = appendQwordReg(regs, "FIP", fpregs.Rip)
|
||||
regs = appendQwordReg(regs, "FDP", fpregs.Rdp)
|
||||
|
||||
for i := 0; i < len(fpregs.StSpace); i += 4 {
|
||||
regs = appendX87Reg(regs, i/4, uint16(fpregs.StSpace[i+2]), uint64(fpregs.StSpace[i+1])<<32|uint64(fpregs.StSpace[i]))
|
||||
}
|
||||
|
||||
// SSE registers
|
||||
regs = appendFlagReg(regs, "MXCSR", uint64(fpregs.Mxcsr), mxcsrDescription, 32)
|
||||
regs = appendDwordReg(regs, "MXCSR_MASK", fpregs.MxcrMask)
|
||||
|
||||
for i := 0; i < len(fpregs.XmmSpace); i += 16 {
|
||||
regs = appendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), fpregs.XmmSpace[i:i+16])
|
||||
if fpregs.AvxState {
|
||||
regs = appendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), fpregs.YmmSpace[i:i+16])
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
351
vendor/github.com/derekparker/delve/pkg/proc/registers_windows_amd64.go
generated
vendored
Normal file
351
vendor/github.com/derekparker/delve/pkg/proc/registers_windows_amd64.go
generated
vendored
Normal file
@ -0,0 +1,351 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"rsc.io/x86/x86asm"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
// Regs represents CPU registers on an AMD64 processor.
|
||||
type Regs struct {
|
||||
rax uint64
|
||||
rbx uint64
|
||||
rcx uint64
|
||||
rdx uint64
|
||||
rdi uint64
|
||||
rsi uint64
|
||||
rbp uint64
|
||||
rsp uint64
|
||||
r8 uint64
|
||||
r9 uint64
|
||||
r10 uint64
|
||||
r11 uint64
|
||||
r12 uint64
|
||||
r13 uint64
|
||||
r14 uint64
|
||||
r15 uint64
|
||||
rip uint64
|
||||
eflags uint64
|
||||
cs uint64
|
||||
fs uint64
|
||||
gs uint64
|
||||
tls uint64
|
||||
fltSave *_XMM_SAVE_AREA32
|
||||
}
|
||||
|
||||
func (r *Regs) Slice() []Register {
|
||||
var regs = []struct {
|
||||
k string
|
||||
v uint64
|
||||
}{
|
||||
{"Rip", r.rip},
|
||||
{"Rsp", r.rsp},
|
||||
{"Rax", r.rax},
|
||||
{"Rbx", r.rbx},
|
||||
{"Rcx", r.rcx},
|
||||
{"Rdx", r.rdx},
|
||||
{"Rdi", r.rdi},
|
||||
{"Rsi", r.rsi},
|
||||
{"Rbp", r.rbp},
|
||||
{"R8", r.r8},
|
||||
{"R9", r.r9},
|
||||
{"R10", r.r10},
|
||||
{"R11", r.r11},
|
||||
{"R12", r.r12},
|
||||
{"R13", r.r13},
|
||||
{"R14", r.r14},
|
||||
{"R15", r.r15},
|
||||
{"Eflags", r.eflags},
|
||||
{"Cs", r.cs},
|
||||
{"Fs", r.fs},
|
||||
{"Gs", r.gs},
|
||||
{"TLS", r.tls},
|
||||
}
|
||||
outlen := len(regs)
|
||||
if r.fltSave != nil {
|
||||
outlen += 6 + 8 + 2 + 16
|
||||
}
|
||||
out := make([]Register, 0, outlen)
|
||||
for _, reg := range regs {
|
||||
if reg.k == "Eflags" {
|
||||
out = append(out, Register{reg.k, eflagsDescription.Describe(reg.v, 64)})
|
||||
} else {
|
||||
out = appendQwordReg(out, reg.k, reg.v)
|
||||
}
|
||||
}
|
||||
if r.fltSave != nil {
|
||||
out = appendWordReg(out, "CW", r.fltSave.ControlWord)
|
||||
out = appendWordReg(out, "SW", r.fltSave.StatusWord)
|
||||
out = appendWordReg(out, "TW", uint16(r.fltSave.TagWord))
|
||||
out = appendWordReg(out, "FOP", r.fltSave.ErrorOpcode)
|
||||
out = appendQwordReg(out, "FIP", uint64(r.fltSave.ErrorSelector)<<32|uint64(r.fltSave.ErrorOffset))
|
||||
out = appendQwordReg(out, "FDP", uint64(r.fltSave.DataSelector)<<32|uint64(r.fltSave.DataOffset))
|
||||
|
||||
for i := range r.fltSave.FloatRegisters {
|
||||
out = appendX87Reg(out, i, uint16(r.fltSave.FloatRegisters[i].High), r.fltSave.FloatRegisters[i].Low)
|
||||
}
|
||||
|
||||
out = appendFlagReg(out, "MXCSR", uint64(r.fltSave.MxCsr), mxcsrDescription, 32)
|
||||
out = appendDwordReg(out, "MXCSR_MASK", r.fltSave.MxCsr_Mask)
|
||||
|
||||
for i := 0; i < len(r.fltSave.XmmRegisters); i += 16 {
|
||||
out = appendSSEReg(out, fmt.Sprintf("XMM%d", i/16), r.fltSave.XmmRegisters[i:i+16])
|
||||
}
|
||||
}
|
||||
return out
|
||||
}
|
||||
|
||||
// PC returns the current program counter
|
||||
// i.e. the RIP CPU register.
|
||||
func (r *Regs) PC() uint64 {
|
||||
return r.rip
|
||||
}
|
||||
|
||||
// SP returns the stack pointer location,
|
||||
// i.e. the RSP register.
|
||||
func (r *Regs) SP() uint64 {
|
||||
return r.rsp
|
||||
}
|
||||
|
||||
func (r *Regs) BP() uint64 {
|
||||
return r.rbp
|
||||
}
|
||||
|
||||
// CX returns the value of the RCX register.
|
||||
func (r *Regs) CX() uint64 {
|
||||
return r.rcx
|
||||
}
|
||||
|
||||
// TLS returns the value of the register
|
||||
// that contains the location of the thread
|
||||
// local storage segment.
|
||||
func (r *Regs) TLS() uint64 {
|
||||
return r.tls
|
||||
}
|
||||
|
||||
// SetPC sets the RIP register to the value specified by `pc`.
|
||||
func (r *Regs) SetPC(thread *Thread, pc uint64) error {
|
||||
context := newCONTEXT()
|
||||
context.ContextFlags = _CONTEXT_ALL
|
||||
|
||||
err := _GetThreadContext(thread.os.hThread, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context.Rip = pc
|
||||
|
||||
return _SetThreadContext(thread.os.hThread, context)
|
||||
}
|
||||
|
||||
func (r *Regs) Get(n int) (uint64, error) {
|
||||
reg := x86asm.Reg(n)
|
||||
const (
|
||||
mask8 = 0x000f
|
||||
mask16 = 0x00ff
|
||||
mask32 = 0xffff
|
||||
)
|
||||
|
||||
switch reg {
|
||||
// 8-bit
|
||||
case x86asm.AL:
|
||||
return r.rax & mask8, nil
|
||||
case x86asm.CL:
|
||||
return r.rcx & mask8, nil
|
||||
case x86asm.DL:
|
||||
return r.rdx & mask8, nil
|
||||
case x86asm.BL:
|
||||
return r.rbx & mask8, nil
|
||||
case x86asm.AH:
|
||||
return (r.rax >> 8) & mask8, nil
|
||||
case x86asm.CH:
|
||||
return (r.rcx >> 8) & mask8, nil
|
||||
case x86asm.DH:
|
||||
return (r.rdx >> 8) & mask8, nil
|
||||
case x86asm.BH:
|
||||
return (r.rbx >> 8) & mask8, nil
|
||||
case x86asm.SPB:
|
||||
return r.rsp & mask8, nil
|
||||
case x86asm.BPB:
|
||||
return r.rbp & mask8, nil
|
||||
case x86asm.SIB:
|
||||
return r.rsi & mask8, nil
|
||||
case x86asm.DIB:
|
||||
return r.rdi & mask8, nil
|
||||
case x86asm.R8B:
|
||||
return r.r8 & mask8, nil
|
||||
case x86asm.R9B:
|
||||
return r.r9 & mask8, nil
|
||||
case x86asm.R10B:
|
||||
return r.r10 & mask8, nil
|
||||
case x86asm.R11B:
|
||||
return r.r11 & mask8, nil
|
||||
case x86asm.R12B:
|
||||
return r.r12 & mask8, nil
|
||||
case x86asm.R13B:
|
||||
return r.r13 & mask8, nil
|
||||
case x86asm.R14B:
|
||||
return r.r14 & mask8, nil
|
||||
case x86asm.R15B:
|
||||
return r.r15 & mask8, nil
|
||||
|
||||
// 16-bit
|
||||
case x86asm.AX:
|
||||
return r.rax & mask16, nil
|
||||
case x86asm.CX:
|
||||
return r.rcx & mask16, nil
|
||||
case x86asm.DX:
|
||||
return r.rdx & mask16, nil
|
||||
case x86asm.BX:
|
||||
return r.rbx & mask16, nil
|
||||
case x86asm.SP:
|
||||
return r.rsp & mask16, nil
|
||||
case x86asm.BP:
|
||||
return r.rbp & mask16, nil
|
||||
case x86asm.SI:
|
||||
return r.rsi & mask16, nil
|
||||
case x86asm.DI:
|
||||
return r.rdi & mask16, nil
|
||||
case x86asm.R8W:
|
||||
return r.r8 & mask16, nil
|
||||
case x86asm.R9W:
|
||||
return r.r9 & mask16, nil
|
||||
case x86asm.R10W:
|
||||
return r.r10 & mask16, nil
|
||||
case x86asm.R11W:
|
||||
return r.r11 & mask16, nil
|
||||
case x86asm.R12W:
|
||||
return r.r12 & mask16, nil
|
||||
case x86asm.R13W:
|
||||
return r.r13 & mask16, nil
|
||||
case x86asm.R14W:
|
||||
return r.r14 & mask16, nil
|
||||
case x86asm.R15W:
|
||||
return r.r15 & mask16, nil
|
||||
|
||||
// 32-bit
|
||||
case x86asm.EAX:
|
||||
return r.rax & mask32, nil
|
||||
case x86asm.ECX:
|
||||
return r.rcx & mask32, nil
|
||||
case x86asm.EDX:
|
||||
return r.rdx & mask32, nil
|
||||
case x86asm.EBX:
|
||||
return r.rbx & mask32, nil
|
||||
case x86asm.ESP:
|
||||
return r.rsp & mask32, nil
|
||||
case x86asm.EBP:
|
||||
return r.rbp & mask32, nil
|
||||
case x86asm.ESI:
|
||||
return r.rsi & mask32, nil
|
||||
case x86asm.EDI:
|
||||
return r.rdi & mask32, nil
|
||||
case x86asm.R8L:
|
||||
return r.r8 & mask32, nil
|
||||
case x86asm.R9L:
|
||||
return r.r9 & mask32, nil
|
||||
case x86asm.R10L:
|
||||
return r.r10 & mask32, nil
|
||||
case x86asm.R11L:
|
||||
return r.r11 & mask32, nil
|
||||
case x86asm.R12L:
|
||||
return r.r12 & mask32, nil
|
||||
case x86asm.R13L:
|
||||
return r.r13 & mask32, nil
|
||||
case x86asm.R14L:
|
||||
return r.r14 & mask32, nil
|
||||
case x86asm.R15L:
|
||||
return r.r15 & mask32, nil
|
||||
|
||||
// 64-bit
|
||||
case x86asm.RAX:
|
||||
return r.rax, nil
|
||||
case x86asm.RCX:
|
||||
return r.rcx, nil
|
||||
case x86asm.RDX:
|
||||
return r.rdx, nil
|
||||
case x86asm.RBX:
|
||||
return r.rbx, nil
|
||||
case x86asm.RSP:
|
||||
return r.rsp, nil
|
||||
case x86asm.RBP:
|
||||
return r.rbp, nil
|
||||
case x86asm.RSI:
|
||||
return r.rsi, nil
|
||||
case x86asm.RDI:
|
||||
return r.rdi, nil
|
||||
case x86asm.R8:
|
||||
return r.r8, nil
|
||||
case x86asm.R9:
|
||||
return r.r9, nil
|
||||
case x86asm.R10:
|
||||
return r.r10, nil
|
||||
case x86asm.R11:
|
||||
return r.r11, nil
|
||||
case x86asm.R12:
|
||||
return r.r12, nil
|
||||
case x86asm.R13:
|
||||
return r.r13, nil
|
||||
case x86asm.R14:
|
||||
return r.r14, nil
|
||||
case x86asm.R15:
|
||||
return r.r15, nil
|
||||
}
|
||||
|
||||
return 0, UnknownRegisterError
|
||||
}
|
||||
|
||||
func registers(thread *Thread, floatingPoint bool) (Registers, error) {
|
||||
context := newCONTEXT()
|
||||
|
||||
context.ContextFlags = _CONTEXT_ALL
|
||||
err := _GetThreadContext(thread.os.hThread, context)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
var threadInfo _THREAD_BASIC_INFORMATION
|
||||
status := _NtQueryInformationThread(thread.os.hThread, _ThreadBasicInformation, uintptr(unsafe.Pointer(&threadInfo)), uint32(unsafe.Sizeof(threadInfo)), nil)
|
||||
if !_NT_SUCCESS(status) {
|
||||
return nil, fmt.Errorf("NtQueryInformationThread failed: it returns 0x%x", status)
|
||||
}
|
||||
|
||||
regs := &Regs{
|
||||
rax: uint64(context.Rax),
|
||||
rbx: uint64(context.Rbx),
|
||||
rcx: uint64(context.Rcx),
|
||||
rdx: uint64(context.Rdx),
|
||||
rdi: uint64(context.Rdi),
|
||||
rsi: uint64(context.Rsi),
|
||||
rbp: uint64(context.Rbp),
|
||||
rsp: uint64(context.Rsp),
|
||||
r8: uint64(context.R8),
|
||||
r9: uint64(context.R9),
|
||||
r10: uint64(context.R10),
|
||||
r11: uint64(context.R11),
|
||||
r12: uint64(context.R12),
|
||||
r13: uint64(context.R13),
|
||||
r14: uint64(context.R14),
|
||||
r15: uint64(context.R15),
|
||||
rip: uint64(context.Rip),
|
||||
eflags: uint64(context.EFlags),
|
||||
cs: uint64(context.SegCs),
|
||||
fs: uint64(context.SegFs),
|
||||
gs: uint64(context.SegGs),
|
||||
tls: uint64(threadInfo.TebBaseAddress),
|
||||
}
|
||||
|
||||
if floatingPoint {
|
||||
regs.fltSave = &context.FltSave
|
||||
}
|
||||
|
||||
return regs, nil
|
||||
}
|
||||
|
||||
func (thread *Thread) saveRegisters() (Registers, error) {
|
||||
return nil, fmt.Errorf("not implemented: saveRegisters")
|
||||
}
|
||||
|
||||
func (thread *Thread) restoreRegisters() error {
|
||||
return fmt.Errorf("not implemented: restoreRegisters")
|
||||
}
|
261
vendor/github.com/derekparker/delve/pkg/proc/stack.go
generated
vendored
Normal file
261
vendor/github.com/derekparker/delve/pkg/proc/stack.go
generated
vendored
Normal file
@ -0,0 +1,261 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"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
|
||||
// could not be found during stack trace.
|
||||
type NoReturnAddr struct {
|
||||
fn string
|
||||
}
|
||||
|
||||
func (nra NoReturnAddr) Error() string {
|
||||
return fmt.Sprintf("could not find return address for %s", nra.fn)
|
||||
}
|
||||
|
||||
// Stackframe represents a frame in a system stack.
|
||||
type Stackframe struct {
|
||||
// Address the function above this one on the call stack will return to.
|
||||
Current Location
|
||||
// Address of the call instruction for the function above on the call stack.
|
||||
Call Location
|
||||
// Start address of the stack frame.
|
||||
CFA int64
|
||||
// Description of the stack frame.
|
||||
FDE *frame.FrameDescriptionEntry
|
||||
// Return address for this stack frame (as read from the stack frame itself).
|
||||
Ret uint64
|
||||
// Address to the memory location containing the return address
|
||||
addrret uint64
|
||||
}
|
||||
|
||||
// Scope returns a new EvalScope using this frame.
|
||||
func (frame *Stackframe) Scope(thread *Thread) *EvalScope {
|
||||
return &EvalScope{Thread: thread, PC: frame.Current.PC, CFA: frame.CFA}
|
||||
}
|
||||
|
||||
// ReturnAddress returns the return address of the function
|
||||
// this thread is executing.
|
||||
func (t *Thread) ReturnAddress() (uint64, error) {
|
||||
locations, err := t.Stacktrace(2)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if len(locations) < 2 {
|
||||
return 0, NoReturnAddr{locations[0].Current.Fn.BaseName()}
|
||||
}
|
||||
return locations[1].Current.PC, nil
|
||||
}
|
||||
|
||||
func (t *Thread) stackIterator(stkbar []savedLR, stkbarPos int) (*stackIterator, error) {
|
||||
regs, err := t.Registers(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return newStackIterator(t.dbp, regs.PC(), regs.SP(), regs.BP(), stkbar, stkbarPos), nil
|
||||
}
|
||||
|
||||
// Stacktrace returns the stack trace for thread.
|
||||
// Note the locations in the array are return addresses not call addresses.
|
||||
func (t *Thread) Stacktrace(depth int) ([]Stackframe, error) {
|
||||
it, err := t.stackIterator(nil, -1)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return it.stacktrace(depth)
|
||||
}
|
||||
|
||||
func (g *G) stackIterator() (*stackIterator, error) {
|
||||
stkbar, err := g.stkbar()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if g.thread != nil {
|
||||
return g.thread.stackIterator(stkbar, g.stkbarPos)
|
||||
}
|
||||
return newStackIterator(g.dbp, g.PC, g.SP, 0, stkbar, g.stkbarPos), nil
|
||||
}
|
||||
|
||||
// Stacktrace returns the stack trace for a goroutine.
|
||||
// Note the locations in the array are return addresses not call addresses.
|
||||
func (g *G) Stacktrace(depth int) ([]Stackframe, error) {
|
||||
it, err := g.stackIterator()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return it.stacktrace(depth)
|
||||
}
|
||||
|
||||
// GoroutineLocation returns the location of the given
|
||||
// goroutine.
|
||||
func (dbp *Process) GoroutineLocation(g *G) *Location {
|
||||
f, l, fn := dbp.PCToLine(g.PC)
|
||||
return &Location{PC: g.PC, File: f, Line: l, Fn: fn}
|
||||
}
|
||||
|
||||
// NullAddrError is an error for a null address.
|
||||
type NullAddrError struct{}
|
||||
|
||||
func (n NullAddrError) Error() string {
|
||||
return "NULL address"
|
||||
}
|
||||
|
||||
// stackIterator holds information
|
||||
// required to iterate and walk the program
|
||||
// stack.
|
||||
type stackIterator struct {
|
||||
pc, sp, bp uint64
|
||||
top bool
|
||||
atend bool
|
||||
frame Stackframe
|
||||
dbp *Process
|
||||
err error
|
||||
|
||||
stackBarrierPC uint64
|
||||
stkbar []savedLR
|
||||
}
|
||||
|
||||
type savedLR struct {
|
||||
ptr uint64
|
||||
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.
|
||||
func (it *stackIterator) Next() bool {
|
||||
if it.err != nil || it.atend {
|
||||
return false
|
||||
}
|
||||
it.frame, it.err = it.dbp.frameInfo(it.pc, it.sp, it.bp, it.top)
|
||||
if it.err != nil {
|
||||
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.atend = true
|
||||
it.err = nil
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
if it.frame.Ret <= 0 {
|
||||
it.atend = 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.
|
||||
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") {
|
||||
it.atend = true
|
||||
return true
|
||||
}
|
||||
|
||||
it.top = false
|
||||
it.pc = it.frame.Ret
|
||||
it.sp = uint64(it.frame.CFA)
|
||||
it.bp, _ = readUintRaw(it.dbp.currentThread, uintptr(it.bp), int64(it.dbp.arch.PtrSize()))
|
||||
return true
|
||||
}
|
||||
|
||||
// Frame returns the frame the iterator is pointing at.
|
||||
func (it *stackIterator) Frame() Stackframe {
|
||||
if it.err != nil {
|
||||
panic(it.err)
|
||||
}
|
||||
return it.frame
|
||||
}
|
||||
|
||||
// Err returns the error encountered during stack iteration.
|
||||
func (it *stackIterator) Err() error {
|
||||
return it.err
|
||||
}
|
||||
|
||||
func (dbp *Process) frameInfo(pc, sp, bp uint64, top bool) (Stackframe, error) {
|
||||
fde, err := dbp.frameEntries.FDEForPC(pc)
|
||||
if _, nofde := err.(*frame.NoFDEForPCError); nofde {
|
||||
if bp == 0 {
|
||||
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)
|
||||
cfa := int64(sp) + spoffset
|
||||
|
||||
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 {
|
||||
return Stackframe{}, NullAddrError{}
|
||||
}
|
||||
f, l, fn := dbp.PCToLine(pc)
|
||||
ret, err := readUintRaw(dbp.currentThread, retaddr, int64(dbp.arch.PtrSize()))
|
||||
if err != nil {
|
||||
return Stackframe{}, err
|
||||
}
|
||||
r := Stackframe{Current: Location{PC: pc, File: f, Line: l, Fn: fn}, CFA: cfa, FDE: fde, Ret: ret, addrret: uint64(retaddr)}
|
||||
if !top {
|
||||
r.Call.File, r.Call.Line, r.Call.Fn = dbp.PCToLine(pc - 1)
|
||||
r.Call.PC = r.Current.PC
|
||||
} else {
|
||||
r.Call = r.Current
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (it *stackIterator) stacktrace(depth int) ([]Stackframe, error) {
|
||||
if depth < 0 {
|
||||
return nil, errors.New("negative maximum stack depth")
|
||||
}
|
||||
frames := make([]Stackframe, 0, depth+1)
|
||||
for it.Next() {
|
||||
frames = append(frames, it.Frame())
|
||||
if len(frames) >= depth+1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
if err := it.Err(); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return frames, nil
|
||||
}
|
112
vendor/github.com/derekparker/delve/pkg/proc/syscall_windows.go
generated
vendored
Normal file
112
vendor/github.com/derekparker/delve/pkg/proc/syscall_windows.go
generated
vendored
Normal file
@ -0,0 +1,112 @@
|
||||
//go:generate go run $GOROOT/src/syscall/mksyscall_windows.go -output zsyscall_windows.go syscall_windows.go
|
||||
|
||||
package proc
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
)
|
||||
|
||||
type _NTSTATUS int32
|
||||
|
||||
type _CLIENT_ID struct {
|
||||
UniqueProcess syscall.Handle
|
||||
UniqueThread syscall.Handle
|
||||
}
|
||||
|
||||
type _THREAD_BASIC_INFORMATION struct {
|
||||
ExitStatus _NTSTATUS
|
||||
TebBaseAddress uintptr
|
||||
ClientId _CLIENT_ID
|
||||
AffinityMask uintptr
|
||||
Priority int32
|
||||
BasePriority int32
|
||||
}
|
||||
|
||||
type _CREATE_PROCESS_DEBUG_INFO struct {
|
||||
File syscall.Handle
|
||||
Process syscall.Handle
|
||||
Thread syscall.Handle
|
||||
BaseOfImage uintptr
|
||||
DebugInfoFileOffset uint32
|
||||
DebugInfoSize uint32
|
||||
ThreadLocalBase uintptr
|
||||
StartAddress uintptr
|
||||
ImageName uintptr
|
||||
Unicode uint16
|
||||
}
|
||||
|
||||
type _CREATE_THREAD_DEBUG_INFO struct {
|
||||
Thread syscall.Handle
|
||||
ThreadLocalBase uintptr
|
||||
StartAddress uintptr
|
||||
}
|
||||
|
||||
type _EXIT_PROCESS_DEBUG_INFO struct {
|
||||
ExitCode uint32
|
||||
}
|
||||
|
||||
type _LOAD_DLL_DEBUG_INFO struct {
|
||||
File syscall.Handle
|
||||
BaseOfDll uintptr
|
||||
DebugInfoFileOffset uint32
|
||||
DebugInfoSize uint32
|
||||
ImageName uintptr
|
||||
Unicode uint16
|
||||
}
|
||||
|
||||
type _EXCEPTION_DEBUG_INFO struct {
|
||||
ExceptionRecord _EXCEPTION_RECORD
|
||||
FirstChance uint32
|
||||
}
|
||||
|
||||
type _EXCEPTION_RECORD struct {
|
||||
ExceptionCode uint32
|
||||
ExceptionFlags uint32
|
||||
ExceptionRecord *_EXCEPTION_RECORD
|
||||
ExceptionAddress uintptr
|
||||
NumberParameters uint32
|
||||
ExceptionInformation [_EXCEPTION_MAXIMUM_PARAMETERS]uintptr
|
||||
}
|
||||
|
||||
const (
|
||||
_ThreadBasicInformation = 0
|
||||
|
||||
_DBG_CONTINUE = 0x00010002
|
||||
_DBG_EXCEPTION_NOT_HANDLED = 0x80010001
|
||||
|
||||
_EXCEPTION_DEBUG_EVENT = 1
|
||||
_CREATE_THREAD_DEBUG_EVENT = 2
|
||||
_CREATE_PROCESS_DEBUG_EVENT = 3
|
||||
_EXIT_THREAD_DEBUG_EVENT = 4
|
||||
_EXIT_PROCESS_DEBUG_EVENT = 5
|
||||
_LOAD_DLL_DEBUG_EVENT = 6
|
||||
_UNLOAD_DLL_DEBUG_EVENT = 7
|
||||
_OUTPUT_DEBUG_STRING_EVENT = 8
|
||||
_RIP_EVENT = 9
|
||||
|
||||
// DEBUG_ONLY_THIS_PROCESS tracks https://msdn.microsoft.com/en-us/library/windows/desktop/ms684863(v=vs.85).aspx
|
||||
_DEBUG_ONLY_THIS_PROCESS = 0x00000002
|
||||
|
||||
_EXCEPTION_BREAKPOINT = 0x80000003
|
||||
_EXCEPTION_SINGLE_STEP = 0x80000004
|
||||
|
||||
_EXCEPTION_MAXIMUM_PARAMETERS = 15
|
||||
)
|
||||
|
||||
func _NT_SUCCESS(x _NTSTATUS) bool {
|
||||
return x >= 0
|
||||
}
|
||||
|
||||
//sys _NtQueryInformationThread(threadHandle syscall.Handle, infoclass int32, info uintptr, infolen uint32, retlen *uint32) (status _NTSTATUS) = ntdll.NtQueryInformationThread
|
||||
//sys _GetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) = kernel32.GetThreadContext
|
||||
//sys _SetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) = kernel32.SetThreadContext
|
||||
//sys _SuspendThread(threadid syscall.Handle) (prevsuspcount uint32, err error) [failretval==0xffffffff] = kernel32.SuspendThread
|
||||
//sys _ResumeThread(threadid syscall.Handle) (prevsuspcount uint32, err error) [failretval==0xffffffff] = kernel32.ResumeThread
|
||||
//sys _ContinueDebugEvent(processid uint32, threadid uint32, continuestatus uint32) (err error) = kernel32.ContinueDebugEvent
|
||||
//sys _WriteProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, byteswritten *uintptr) (err error) = kernel32.WriteProcessMemory
|
||||
//sys _ReadProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, bytesread *uintptr) (err error) = kernel32.ReadProcessMemory
|
||||
//sys _DebugBreakProcess(process syscall.Handle) (err error) = kernel32.DebugBreakProcess
|
||||
//sys _WaitForDebugEvent(debugevent *_DEBUG_EVENT, milliseconds uint32) (err error) = kernel32.WaitForDebugEvent
|
||||
//sys _DebugActiveProcess(processid uint32) (err error) = kernel32.DebugActiveProcess
|
||||
//sys _DebugActiveProcessStop(processid uint32) (err error) = kernel32.DebugActiveProcessStop
|
||||
//sys _QueryFullProcessImageName(process syscall.Handle, flags uint32, exename *uint16, size *uint32) (err error) = kernel32.QueryFullProcessImageNameW
|
114
vendor/github.com/derekparker/delve/pkg/proc/syscall_windows_amd64.go
generated
vendored
Normal file
114
vendor/github.com/derekparker/delve/pkg/proc/syscall_windows_amd64.go
generated
vendored
Normal file
@ -0,0 +1,114 @@
|
||||
package proc
|
||||
|
||||
import "unsafe"
|
||||
|
||||
const (
|
||||
_CONTEXT_AMD64 = 0x100000
|
||||
_CONTEXT_CONTROL = (_CONTEXT_AMD64 | 0x1)
|
||||
_CONTEXT_INTEGER = (_CONTEXT_AMD64 | 0x2)
|
||||
_CONTEXT_SEGMENTS = (_CONTEXT_AMD64 | 0x4)
|
||||
_CONTEXT_FLOATING_POINT = (_CONTEXT_AMD64 | 0x8)
|
||||
_CONTEXT_DEBUG_REGISTERS = (_CONTEXT_AMD64 | 0x10)
|
||||
_CONTEXT_FULL = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_FLOATING_POINT)
|
||||
_CONTEXT_ALL = (_CONTEXT_CONTROL | _CONTEXT_INTEGER | _CONTEXT_SEGMENTS | _CONTEXT_FLOATING_POINT | _CONTEXT_DEBUG_REGISTERS)
|
||||
_CONTEXT_EXCEPTION_ACTIVE = 0x8000000
|
||||
_CONTEXT_SERVICE_ACTIVE = 0x10000000
|
||||
_CONTEXT_EXCEPTION_REQUEST = 0x40000000
|
||||
_CONTEXT_EXCEPTION_REPORTING = 0x80000000
|
||||
)
|
||||
|
||||
type _M128A struct {
|
||||
Low uint64
|
||||
High int64
|
||||
}
|
||||
|
||||
type _XMM_SAVE_AREA32 struct {
|
||||
ControlWord uint16
|
||||
StatusWord uint16
|
||||
TagWord byte
|
||||
Reserved1 byte
|
||||
ErrorOpcode uint16
|
||||
ErrorOffset uint32
|
||||
ErrorSelector uint16
|
||||
Reserved2 uint16
|
||||
DataOffset uint32
|
||||
DataSelector uint16
|
||||
Reserved3 uint16
|
||||
MxCsr uint32
|
||||
MxCsr_Mask uint32
|
||||
FloatRegisters [8]_M128A
|
||||
XmmRegisters [256]byte
|
||||
Reserved4 [96]byte
|
||||
}
|
||||
|
||||
type _CONTEXT struct {
|
||||
P1Home uint64
|
||||
P2Home uint64
|
||||
P3Home uint64
|
||||
P4Home uint64
|
||||
P5Home uint64
|
||||
P6Home uint64
|
||||
|
||||
ContextFlags uint32
|
||||
MxCsr uint32
|
||||
|
||||
SegCs uint16
|
||||
SegDs uint16
|
||||
SegEs uint16
|
||||
SegFs uint16
|
||||
SegGs uint16
|
||||
SegSs uint16
|
||||
EFlags uint32
|
||||
|
||||
Dr0 uint64
|
||||
Dr1 uint64
|
||||
Dr2 uint64
|
||||
Dr3 uint64
|
||||
Dr6 uint64
|
||||
Dr7 uint64
|
||||
|
||||
Rax uint64
|
||||
Rcx uint64
|
||||
Rdx uint64
|
||||
Rbx uint64
|
||||
Rsp uint64
|
||||
Rbp uint64
|
||||
Rsi uint64
|
||||
Rdi uint64
|
||||
R8 uint64
|
||||
R9 uint64
|
||||
R10 uint64
|
||||
R11 uint64
|
||||
R12 uint64
|
||||
R13 uint64
|
||||
R14 uint64
|
||||
R15 uint64
|
||||
|
||||
Rip uint64
|
||||
|
||||
FltSave _XMM_SAVE_AREA32
|
||||
|
||||
VectorRegister [26]_M128A
|
||||
VectorControl uint64
|
||||
|
||||
DebugControl uint64
|
||||
LastBranchToRip uint64
|
||||
LastBranchFromRip uint64
|
||||
LastExceptionToRip uint64
|
||||
LastExceptionFromRip uint64
|
||||
}
|
||||
|
||||
// newCONTEXT allocates Windows CONTEXT structure aligned to 16 bytes.
|
||||
func newCONTEXT() *_CONTEXT {
|
||||
var c *_CONTEXT
|
||||
buf := make([]byte, unsafe.Sizeof(*c)+15)
|
||||
return (*_CONTEXT)(unsafe.Pointer((uintptr(unsafe.Pointer(&buf[15]))) &^ 15))
|
||||
}
|
||||
|
||||
type _DEBUG_EVENT struct {
|
||||
DebugEventCode uint32
|
||||
ProcessId uint32
|
||||
ThreadId uint32
|
||||
_ uint32 // to align Union properly
|
||||
U [160]byte
|
||||
}
|
510
vendor/github.com/derekparker/delve/pkg/proc/threads.go
generated
vendored
Normal file
510
vendor/github.com/derekparker/delve/pkg/proc/threads.go
generated
vendored
Normal file
@ -0,0 +1,510 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"encoding/binary"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"golang.org/x/debug/dwarf"
|
||||
)
|
||||
|
||||
// Thread represents a single thread in the traced process
|
||||
// ID represents the thread id or port, Process holds a reference to the
|
||||
// Process struct that contains info on the process as
|
||||
// a whole, and Status represents the last result of a `wait` call
|
||||
// on this thread.
|
||||
type Thread struct {
|
||||
ID int // Thread ID or mach port
|
||||
Status *WaitStatus // Status returned from last wait call
|
||||
CurrentBreakpoint *Breakpoint // Breakpoint thread is currently stopped at
|
||||
BreakpointConditionMet bool // Output of evaluating the breakpoint's condition
|
||||
BreakpointConditionError error // Error evaluating the breakpoint's condition
|
||||
|
||||
dbp *Process
|
||||
singleStepping bool
|
||||
running bool
|
||||
os *OSSpecificDetails
|
||||
}
|
||||
|
||||
// Location represents the location of a thread.
|
||||
// Holds information on the current instruction
|
||||
// address, the source file:line, and the function.
|
||||
type Location struct {
|
||||
PC uint64
|
||||
File string
|
||||
Line int
|
||||
Fn *gosym.Func
|
||||
}
|
||||
|
||||
// Continue the execution of this thread.
|
||||
//
|
||||
// If we are currently at a breakpoint, we'll clear it
|
||||
// first and then resume execution. Thread will continue until
|
||||
// it hits a breakpoint or is signaled.
|
||||
func (thread *Thread) Continue() error {
|
||||
pc, err := thread.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
// Check whether we are stopped at a breakpoint, and
|
||||
// if so, single step over it before continuing.
|
||||
if _, ok := thread.dbp.FindBreakpoint(pc); ok {
|
||||
if err := thread.StepInstruction(); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
return thread.resume()
|
||||
}
|
||||
|
||||
// StepInstruction steps a single instruction.
|
||||
//
|
||||
// Executes exactly one instruction and then returns.
|
||||
// If the thread is at a breakpoint, we first clear it,
|
||||
// execute the instruction, and then replace the breakpoint.
|
||||
// Otherwise we simply execute the next instruction.
|
||||
func (thread *Thread) StepInstruction() (err error) {
|
||||
thread.running = true
|
||||
thread.singleStepping = true
|
||||
defer func() {
|
||||
thread.singleStepping = false
|
||||
thread.running = false
|
||||
}()
|
||||
pc, err := thread.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
bp, ok := thread.dbp.FindBreakpoint(pc)
|
||||
if ok {
|
||||
// Clear the breakpoint so that we can continue execution.
|
||||
_, err = bp.Clear(thread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Restore breakpoint now that we have passed it.
|
||||
defer func() {
|
||||
err = thread.dbp.writeSoftwareBreakpoint(thread, bp.Addr)
|
||||
}()
|
||||
}
|
||||
|
||||
err = thread.singleStep()
|
||||
if err != nil {
|
||||
if _, exited := err.(ProcessExitedError); exited {
|
||||
return err
|
||||
}
|
||||
return fmt.Errorf("step failed: %s", err.Error())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Location returns the threads location, including the file:line
|
||||
// of the corresponding source code, the function we're in
|
||||
// and the current instruction address.
|
||||
func (thread *Thread) Location() (*Location, error) {
|
||||
pc, err := thread.PC()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
f, l, fn := thread.dbp.PCToLine(pc)
|
||||
return &Location{PC: pc, File: f, Line: l, Fn: fn}, nil
|
||||
}
|
||||
|
||||
// ThreadBlockedError is returned when the thread
|
||||
// is blocked in the scheduler.
|
||||
type ThreadBlockedError struct{}
|
||||
|
||||
func (tbe ThreadBlockedError) Error() string {
|
||||
return ""
|
||||
}
|
||||
|
||||
// returns topmost frame of g or thread if g is nil
|
||||
func topframe(g *G, thread *Thread) (Stackframe, error) {
|
||||
var frames []Stackframe
|
||||
var err error
|
||||
|
||||
if g == nil {
|
||||
if thread.blocked() {
|
||||
return Stackframe{}, ThreadBlockedError{}
|
||||
}
|
||||
frames, err = thread.Stacktrace(0)
|
||||
} else {
|
||||
frames, err = g.Stacktrace(0)
|
||||
}
|
||||
if err != nil {
|
||||
return Stackframe{}, err
|
||||
}
|
||||
if len(frames) < 1 {
|
||||
return Stackframe{}, errors.New("empty stack trace")
|
||||
}
|
||||
return frames[0], nil
|
||||
}
|
||||
|
||||
// Set breakpoints at every line, and the return address. Also look for
|
||||
// a deferred function and set a breakpoint there too.
|
||||
// If stepInto is true it will also set breakpoints inside all
|
||||
// functions called on the current source line, for non-absolute CALLs
|
||||
// a breakpoint of kind StepBreakpoint is set on the CALL instruction,
|
||||
// Continue will take care of setting a breakpoint to the destination
|
||||
// once the CALL is reached.
|
||||
func (dbp *Process) next(stepInto bool) error {
|
||||
topframe, err := topframe(dbp.selectedGoroutine, dbp.currentThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
success := false
|
||||
defer func() {
|
||||
if !success {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
}
|
||||
}()
|
||||
|
||||
csource := filepath.Ext(topframe.Current.File) != ".go"
|
||||
thread := dbp.currentThread
|
||||
currentGoroutine := false
|
||||
if dbp.selectedGoroutine != nil && dbp.selectedGoroutine.thread != nil {
|
||||
thread = dbp.selectedGoroutine.thread
|
||||
currentGoroutine = true
|
||||
}
|
||||
|
||||
text, err := thread.Disassemble(topframe.FDE.Begin(), topframe.FDE.End(), currentGoroutine)
|
||||
if err != nil && stepInto {
|
||||
return err
|
||||
}
|
||||
|
||||
cond := sameGoroutineCondition(dbp.selectedGoroutine)
|
||||
|
||||
if stepInto {
|
||||
for _, instr := range text {
|
||||
if instr.Loc.File != topframe.Current.File || instr.Loc.Line != topframe.Current.Line || !instr.IsCall() {
|
||||
continue
|
||||
}
|
||||
|
||||
if instr.DestLoc != nil && instr.DestLoc.Fn != nil {
|
||||
if err := dbp.setStepIntoBreakpoint([]AsmInstruction{instr}, cond); err != nil {
|
||||
return err
|
||||
}
|
||||
} else {
|
||||
// Non-absolute call instruction, set a StepBreakpoint here
|
||||
if _, err := dbp.SetBreakpoint(instr.Loc.PC, StepBreakpoint, cond); err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if !csource {
|
||||
deferreturns := []uint64{}
|
||||
|
||||
// Find all runtime.deferreturn locations in the function
|
||||
// See documentation of Breakpoint.DeferCond for why this is necessary
|
||||
for _, instr := range text {
|
||||
if instr.IsCall() && instr.DestLoc != nil && instr.DestLoc.Fn != nil && instr.DestLoc.Fn.Name == "runtime.deferreturn" {
|
||||
deferreturns = append(deferreturns, instr.Loc.PC)
|
||||
}
|
||||
}
|
||||
|
||||
// Set breakpoint on the most recently deferred function (if any)
|
||||
var deferpc uint64 = 0
|
||||
if dbp.selectedGoroutine != nil {
|
||||
deferPCEntry := dbp.selectedGoroutine.DeferPC()
|
||||
if deferPCEntry != 0 {
|
||||
_, _, deferfn := dbp.goSymTable.PCToLine(deferPCEntry)
|
||||
var err error
|
||||
deferpc, err = dbp.FirstPCAfterPrologue(deferfn, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
if deferpc != 0 && deferpc != topframe.Current.PC {
|
||||
bp, err := dbp.SetBreakpoint(deferpc, NextDeferBreakpoint, cond)
|
||||
if err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if bp != nil {
|
||||
bp.DeferReturns = deferreturns
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add breakpoints on all the lines in the current function
|
||||
pcs, err := dbp.lineInfo.AllPCsBetween(topframe.FDE.Begin(), topframe.FDE.End()-1, topframe.Current.File)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !csource {
|
||||
var covered bool
|
||||
for i := range pcs {
|
||||
if topframe.FDE.Cover(pcs[i]) {
|
||||
covered = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !covered {
|
||||
fn := dbp.goSymTable.PCToFunc(topframe.Ret)
|
||||
if dbp.selectedGoroutine != nil && fn != nil && fn.Name == "runtime.goexit" {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Add a breakpoint on the return address for the current frame
|
||||
pcs = append(pcs, topframe.Ret)
|
||||
success = true
|
||||
return dbp.setInternalBreakpoints(topframe.Current.PC, pcs, NextBreakpoint, cond)
|
||||
}
|
||||
|
||||
func (dbp *Process) setStepIntoBreakpoint(text []AsmInstruction, cond ast.Expr) error {
|
||||
if len(text) <= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
instr := text[0]
|
||||
|
||||
if instr.DestLoc == nil || instr.DestLoc.Fn == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
fn := instr.DestLoc.Fn
|
||||
|
||||
// Ensure PC and Entry match, otherwise StepInto is likely to set
|
||||
// its breakpoint before DestLoc.PC and hence run too far ahead.
|
||||
// Calls to runtime.duffzero and duffcopy have this problem.
|
||||
if fn.Entry != instr.DestLoc.PC {
|
||||
return nil
|
||||
}
|
||||
|
||||
// Skip unexported runtime functions
|
||||
if strings.HasPrefix(fn.Name, "runtime.") && !isExportedRuntime(fn.Name) {
|
||||
return nil
|
||||
}
|
||||
|
||||
//TODO(aarzilli): if we want to let users hide functions
|
||||
// or entire packages from being stepped into with 'step'
|
||||
// those extra checks should be done here.
|
||||
|
||||
// Set a breakpoint after the function's prologue
|
||||
pc, _ := dbp.FirstPCAfterPrologue(fn, false)
|
||||
if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, cond); err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// setInternalBreakpoints sets a breakpoint to all addresses specified in pcs
|
||||
// skipping over curpc and curpc-1
|
||||
func (dbp *Process) setInternalBreakpoints(curpc uint64, pcs []uint64, kind BreakpointKind, cond ast.Expr) error {
|
||||
for i := range pcs {
|
||||
if pcs[i] == curpc || pcs[i] == curpc-1 {
|
||||
continue
|
||||
}
|
||||
if _, err := dbp.SetBreakpoint(pcs[i], kind, cond); err != nil {
|
||||
if _, ok := err.(BreakpointExistsError); !ok {
|
||||
dbp.ClearInternalBreakpoints()
|
||||
return err
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// SetPC sets the PC for this thread.
|
||||
func (thread *Thread) SetPC(pc uint64) error {
|
||||
regs, err := thread.Registers(false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return regs.SetPC(thread, pc)
|
||||
}
|
||||
|
||||
func (thread *Thread) getGVariable() (*Variable, error) {
|
||||
regs, err := thread.Registers(false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if thread.dbp.arch.GStructOffset() == 0 {
|
||||
// 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)
|
||||
return nil, fmt.Errorf("g struct offset not initialized")
|
||||
}
|
||||
|
||||
gaddrbs, err := thread.readMemory(uintptr(regs.TLS()+thread.dbp.arch.GStructOffset()), thread.dbp.arch.PtrSize())
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
gaddr := uintptr(binary.LittleEndian.Uint64(gaddrbs))
|
||||
|
||||
// On Windows, the value at TLS()+GStructOffset() is a
|
||||
// pointer to the G struct.
|
||||
needsDeref := runtime.GOOS == "windows"
|
||||
|
||||
return thread.newGVariable(gaddr, needsDeref)
|
||||
}
|
||||
|
||||
func (thread *Thread) newGVariable(gaddr uintptr, deref bool) (*Variable, error) {
|
||||
typ, err := thread.dbp.findType("runtime.g")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
name := ""
|
||||
|
||||
if deref {
|
||||
typ = &dwarf.PtrType{dwarf.CommonType{int64(thread.dbp.arch.PtrSize()), "", reflect.Ptr, 0}, typ}
|
||||
} else {
|
||||
name = "runtime.curg"
|
||||
}
|
||||
|
||||
return thread.newVariable(name, gaddr, typ), nil
|
||||
}
|
||||
|
||||
// GetG returns information on the G (goroutine) that is executing on this thread.
|
||||
//
|
||||
// The G structure for a thread is stored in thread local storage. Here we simply
|
||||
// calculate the address and read and parse the G struct.
|
||||
//
|
||||
// We cannot simply use the allg linked list in order to find the M that represents
|
||||
// the given OS thread and follow its G pointer because on Darwin mach ports are not
|
||||
// universal, so our port for this thread would not map to the `id` attribute of the M
|
||||
// structure. Also, when linked against libc, Go prefers the libc version of clone as
|
||||
// opposed to the runtime version. This has the consequence of not setting M.id for
|
||||
// any thread, regardless of OS.
|
||||
//
|
||||
// In order to get around all this craziness, we read the address of the G structure for
|
||||
// the current thread from the thread local storage area.
|
||||
func (thread *Thread) GetG() (g *G, err error) {
|
||||
gaddr, err := thread.getGVariable()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
g, err = gaddr.parseG()
|
||||
if err == nil {
|
||||
g.thread = thread
|
||||
if loc, err := thread.Location(); err == nil {
|
||||
g.CurrentLoc = *loc
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Stopped returns whether the thread is stopped at
|
||||
// the operating system level. Actual implementation
|
||||
// is OS dependant, look in OS thread file.
|
||||
func (thread *Thread) Stopped() bool {
|
||||
return thread.stopped()
|
||||
}
|
||||
|
||||
// Halt stops this thread from executing. Actual
|
||||
// implementation is OS dependant. Look in OS
|
||||
// thread file.
|
||||
func (thread *Thread) Halt() (err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
thread.running = false
|
||||
}
|
||||
}()
|
||||
if thread.Stopped() {
|
||||
return
|
||||
}
|
||||
err = thread.halt()
|
||||
return
|
||||
}
|
||||
|
||||
// Scope returns the current EvalScope for this thread.
|
||||
func (thread *Thread) Scope() (*EvalScope, error) {
|
||||
locations, err := thread.Stacktrace(0)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(locations) < 1 {
|
||||
return nil, errors.New("could not decode first frame")
|
||||
}
|
||||
return locations[0].Scope(thread), nil
|
||||
}
|
||||
|
||||
// SetCurrentBreakpoint sets the current breakpoint that this
|
||||
// thread is stopped at as CurrentBreakpoint on the thread struct.
|
||||
func (thread *Thread) SetCurrentBreakpoint() error {
|
||||
thread.CurrentBreakpoint = nil
|
||||
pc, err := thread.PC()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if bp, ok := thread.dbp.FindBreakpoint(pc); ok {
|
||||
thread.CurrentBreakpoint = bp
|
||||
if err = thread.SetPC(bp.Addr); err != nil {
|
||||
return err
|
||||
}
|
||||
thread.BreakpointConditionMet, thread.BreakpointConditionError = bp.checkCondition(thread)
|
||||
if thread.onTriggeredBreakpoint() {
|
||||
if g, err := thread.GetG(); err == nil {
|
||||
thread.CurrentBreakpoint.HitCount[g.ID]++
|
||||
}
|
||||
thread.CurrentBreakpoint.TotalHitCount++
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (thread *Thread) clearBreakpointState() {
|
||||
thread.CurrentBreakpoint = nil
|
||||
thread.BreakpointConditionMet = false
|
||||
thread.BreakpointConditionError = nil
|
||||
}
|
||||
|
||||
func (thread *Thread) onTriggeredBreakpoint() bool {
|
||||
return (thread.CurrentBreakpoint != nil) && thread.BreakpointConditionMet
|
||||
}
|
||||
|
||||
func (thread *Thread) onTriggeredInternalBreakpoint() bool {
|
||||
return thread.onTriggeredBreakpoint() && thread.CurrentBreakpoint.Internal()
|
||||
}
|
||||
|
||||
func (thread *Thread) onRuntimeBreakpoint() bool {
|
||||
loc, err := thread.Location()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
return loc.Fn != nil && loc.Fn.Name == "runtime.breakpoint"
|
||||
}
|
||||
|
||||
// onNextGorutine returns true if this thread is on the goroutine requested by the current 'next' command
|
||||
func (thread *Thread) onNextGoroutine() (bool, error) {
|
||||
var bp *Breakpoint
|
||||
for i := range thread.dbp.breakpoints {
|
||||
if thread.dbp.breakpoints[i].Internal() {
|
||||
bp = thread.dbp.breakpoints[i]
|
||||
break
|
||||
}
|
||||
}
|
||||
if bp == nil {
|
||||
return false, nil
|
||||
}
|
||||
if bp.Kind == NextDeferBreakpoint {
|
||||
// we just want to check the condition on the goroutine id here
|
||||
bp.Kind = NextBreakpoint
|
||||
defer func() {
|
||||
bp.Kind = NextDeferBreakpoint
|
||||
}()
|
||||
}
|
||||
return bp.checkCondition(thread)
|
||||
}
|
177
vendor/github.com/derekparker/delve/pkg/proc/threads_darwin.c
generated
vendored
Normal file
177
vendor/github.com/derekparker/delve/pkg/proc/threads_darwin.c
generated
vendored
Normal file
@ -0,0 +1,177 @@
|
||||
#include "threads_darwin.h"
|
||||
|
||||
int
|
||||
write_memory(task_t task, mach_vm_address_t addr, void *d, mach_msg_type_number_t len) {
|
||||
kern_return_t kret;
|
||||
vm_region_submap_short_info_data_64_t info;
|
||||
mach_msg_type_number_t count = VM_REGION_SUBMAP_SHORT_INFO_COUNT_64;
|
||||
mach_vm_size_t l = len;
|
||||
mach_port_t objname;
|
||||
|
||||
if (len == 1) l = 2;
|
||||
kret = mach_vm_region((vm_map_t)task, &(mach_vm_address_t){addr}, (mach_vm_size_t*)&l, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&info, &count, &objname);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
// Set permissions to enable writting to this memory location
|
||||
kret = mach_vm_protect(task, addr, len, FALSE, VM_PROT_WRITE|VM_PROT_COPY|VM_PROT_READ);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
kret = mach_vm_write((vm_map_t)task, addr, (vm_offset_t)d, len);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
// Restore virtual memory permissions
|
||||
kret = mach_vm_protect(task, addr, len, FALSE, info.protection);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
int
|
||||
read_memory(task_t task, mach_vm_address_t addr, void *d, mach_msg_type_number_t len) {
|
||||
kern_return_t kret;
|
||||
pointer_t data;
|
||||
mach_msg_type_number_t count;
|
||||
|
||||
kret = mach_vm_read((vm_map_t)task, addr, len, &data, &count);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
memcpy(d, (void *)data, len);
|
||||
|
||||
kret = vm_deallocate(task, data, len);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
return count;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
get_registers(mach_port_name_t task, x86_thread_state64_t *state) {
|
||||
kern_return_t kret;
|
||||
mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;
|
||||
// TODO(dp) - possible memory leak - vm_deallocate state
|
||||
return thread_get_state(task, x86_THREAD_STATE64, (thread_state_t)state, &stateCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
get_fpu_registers(mach_port_name_t task, x86_float_state64_t *state) {
|
||||
kern_return_t kret;
|
||||
mach_msg_type_number_t stateCount = x86_FLOAT_STATE64_COUNT;
|
||||
return thread_get_state(task, x86_FLOAT_STATE64, (thread_state_t)state, &stateCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
get_identity(mach_port_name_t task, thread_identifier_info_data_t *idinfo) {
|
||||
mach_msg_type_number_t idinfoCount = THREAD_IDENTIFIER_INFO_COUNT;
|
||||
return thread_info(task, THREAD_IDENTIFIER_INFO, (thread_info_t)idinfo, &idinfoCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
set_registers(mach_port_name_t task, x86_thread_state64_t *state) {
|
||||
mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;
|
||||
return thread_set_state(task, x86_THREAD_STATE64, (thread_state_t)state, stateCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
set_pc(thread_act_t task, uint64_t pc) {
|
||||
kern_return_t kret;
|
||||
x86_thread_state64_t state;
|
||||
mach_msg_type_number_t stateCount = x86_THREAD_STATE64_COUNT;
|
||||
|
||||
kret = thread_get_state(task, x86_THREAD_STATE64, (thread_state_t)&state, &stateCount);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
state.__rip = pc;
|
||||
|
||||
return thread_set_state(task, x86_THREAD_STATE64, (thread_state_t)&state, stateCount);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
single_step(thread_act_t thread) {
|
||||
kern_return_t kret;
|
||||
x86_thread_state64_t regs;
|
||||
mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
|
||||
|
||||
kret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)®s, &count);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Set trap bit in rflags
|
||||
regs.__rflags |= 0x100UL;
|
||||
|
||||
kret = thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t)®s, count);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
return resume_thread(thread);
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
resume_thread(thread_act_t thread) {
|
||||
kern_return_t kret;
|
||||
struct thread_basic_info info;
|
||||
unsigned int info_count = THREAD_BASIC_INFO_COUNT;
|
||||
|
||||
kret = thread_info((thread_t)thread, THREAD_BASIC_INFO, (thread_info_t)&info, &info_count);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
for (int i = 0; i < info.suspend_count; i++) {
|
||||
kret = thread_resume(thread);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
}
|
||||
return KERN_SUCCESS;
|
||||
}
|
||||
|
||||
kern_return_t
|
||||
clear_trap_flag(thread_act_t thread) {
|
||||
kern_return_t kret;
|
||||
x86_thread_state64_t regs;
|
||||
mach_msg_type_number_t count = x86_THREAD_STATE64_COUNT;
|
||||
|
||||
kret = thread_get_state(thread, x86_THREAD_STATE64, (thread_state_t)®s, &count);
|
||||
if (kret != KERN_SUCCESS) return kret;
|
||||
|
||||
// Clear trap bit in rflags
|
||||
regs.__rflags ^= 0x100UL;
|
||||
|
||||
return thread_set_state(thread, x86_THREAD_STATE64, (thread_state_t)®s, count);
|
||||
}
|
||||
|
||||
int
|
||||
thread_blocked(thread_act_t thread) {
|
||||
kern_return_t kret;
|
||||
struct thread_basic_info info;
|
||||
unsigned int info_count = THREAD_BASIC_INFO_COUNT;
|
||||
|
||||
kret = thread_info((thread_t)thread, THREAD_BASIC_INFO, (thread_info_t)&info, &info_count);
|
||||
if (kret != KERN_SUCCESS) return -1;
|
||||
|
||||
return info.suspend_count;
|
||||
}
|
||||
|
||||
int
|
||||
num_running_threads(task_t task) {
|
||||
kern_return_t kret;
|
||||
thread_act_array_t list;
|
||||
mach_msg_type_number_t count;
|
||||
int i, n = 0;
|
||||
|
||||
kret = task_threads(task, &list, &count);
|
||||
if (kret != KERN_SUCCESS) {
|
||||
return -kret;
|
||||
}
|
||||
|
||||
for (i = 0; i < count; ++i) {
|
||||
thread_act_t thread = list[i];
|
||||
struct thread_basic_info info;
|
||||
unsigned int info_count = THREAD_BASIC_INFO_COUNT;
|
||||
|
||||
kret = thread_info((thread_t)thread, THREAD_BASIC_INFO, (thread_info_t)&info, &info_count);
|
||||
|
||||
if (kret == KERN_SUCCESS) {
|
||||
if (info.suspend_count == 0) {
|
||||
++n;
|
||||
} else {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
kret = vm_deallocate(mach_task_self(), (vm_address_t) list, count * sizeof(list[0]));
|
||||
if (kret != KERN_SUCCESS) return -kret;
|
||||
|
||||
return n;
|
||||
}
|
137
vendor/github.com/derekparker/delve/pkg/proc/threads_darwin.go
generated
vendored
Normal file
137
vendor/github.com/derekparker/delve/pkg/proc/threads_darwin.go
generated
vendored
Normal file
@ -0,0 +1,137 @@
|
||||
package proc
|
||||
|
||||
// #include "threads_darwin.h"
|
||||
// #include "proc_darwin.h"
|
||||
import "C"
|
||||
import (
|
||||
"fmt"
|
||||
"unsafe"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// WaitStatus is a synonym for the platform-specific WaitStatus
|
||||
type WaitStatus sys.WaitStatus
|
||||
|
||||
// OSSpecificDetails holds information specific to the OSX/Darwin
|
||||
// operating system / kernel.
|
||||
type OSSpecificDetails struct {
|
||||
threadAct C.thread_act_t
|
||||
registers C.x86_thread_state64_t
|
||||
exists bool
|
||||
}
|
||||
|
||||
// ErrContinueThread is the error returned when a thread could not
|
||||
// be continued.
|
||||
var ErrContinueThread = fmt.Errorf("could not continue thread")
|
||||
|
||||
func (t *Thread) halt() (err error) {
|
||||
kret := C.thread_suspend(t.os.threadAct)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
errStr := C.GoString(C.mach_error_string(C.mach_error_t(kret)))
|
||||
// check that the thread still exists before complaining
|
||||
err2 := t.dbp.updateThreadList()
|
||||
if err2 != nil {
|
||||
err = fmt.Errorf("could not suspend thread %d %s (additionally could not update thread list: %v)", t.ID, errStr, err2)
|
||||
return
|
||||
}
|
||||
|
||||
if _, ok := t.dbp.threads[t.ID]; ok {
|
||||
err = fmt.Errorf("could not suspend thread %d %s", t.ID, errStr)
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) singleStep() error {
|
||||
kret := C.single_step(t.os.threadAct)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("could not single step")
|
||||
}
|
||||
for {
|
||||
twthread, err := t.dbp.trapWait(t.dbp.pid)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if twthread.ID == t.ID {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
kret = C.clear_trap_flag(t.os.threadAct)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return fmt.Errorf("could not clear CPU trap flag")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Thread) resume() error {
|
||||
t.running = true
|
||||
// TODO(dp) set flag for ptrace stops
|
||||
var err error
|
||||
t.dbp.execPtraceFunc(func() { err = PtraceCont(t.dbp.pid, 0) })
|
||||
if err == nil {
|
||||
return nil
|
||||
}
|
||||
kret := C.resume_thread(t.os.threadAct)
|
||||
if kret != C.KERN_SUCCESS {
|
||||
return ErrContinueThread
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (t *Thread) blocked() bool {
|
||||
// TODO(dp) cache the func pc to remove this lookup
|
||||
pc, err := t.PC()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
fn := t.dbp.goSymTable.PCToFunc(pc)
|
||||
if fn == nil {
|
||||
return false
|
||||
}
|
||||
switch fn.Name {
|
||||
case "runtime.kevent", "runtime.mach_semaphore_wait", "runtime.usleep":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Thread) stopped() bool {
|
||||
return C.thread_blocked(t.os.threadAct) > C.int(0)
|
||||
}
|
||||
|
||||
func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
|
||||
if len(data) == 0 {
|
||||
return 0, nil
|
||||
}
|
||||
var (
|
||||
vmData = unsafe.Pointer(&data[0])
|
||||
vmAddr = C.mach_vm_address_t(addr)
|
||||
length = C.mach_msg_type_number_t(len(data))
|
||||
)
|
||||
if ret := C.write_memory(t.dbp.os.task, vmAddr, vmData, length); ret < 0 {
|
||||
return 0, fmt.Errorf("could not write memory")
|
||||
}
|
||||
return len(data), nil
|
||||
}
|
||||
|
||||
func (t *Thread) readMemory(addr uintptr, size int) ([]byte, error) {
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var (
|
||||
buf = make([]byte, size)
|
||||
vmData = unsafe.Pointer(&buf[0])
|
||||
vmAddr = C.mach_vm_address_t(addr)
|
||||
length = C.mach_msg_type_number_t(size)
|
||||
)
|
||||
|
||||
ret := C.read_memory(t.dbp.os.task, vmAddr, vmData, length)
|
||||
if ret < 0 {
|
||||
return nil, fmt.Errorf("could not read memory")
|
||||
}
|
||||
return buf, nil
|
||||
}
|
41
vendor/github.com/derekparker/delve/pkg/proc/threads_darwin.h
generated
vendored
Normal file
41
vendor/github.com/derekparker/delve/pkg/proc/threads_darwin.h
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <mach/mach.h>
|
||||
#include <mach/mach_vm.h>
|
||||
#include <mach/thread_info.h>
|
||||
|
||||
int
|
||||
write_memory(task_t, mach_vm_address_t, void *, mach_msg_type_number_t);
|
||||
|
||||
int
|
||||
read_memory(task_t, mach_vm_address_t, void *, mach_msg_type_number_t);
|
||||
|
||||
kern_return_t
|
||||
get_registers(mach_port_name_t, x86_thread_state64_t*);
|
||||
|
||||
kern_return_t
|
||||
get_fpu_registers(mach_port_name_t, x86_float_state64_t *);
|
||||
|
||||
kern_return_t
|
||||
set_pc(thread_act_t, uint64_t);
|
||||
|
||||
kern_return_t
|
||||
single_step(thread_act_t);
|
||||
|
||||
kern_return_t
|
||||
clear_trap_flag(thread_act_t);
|
||||
|
||||
kern_return_t
|
||||
resume_thread(thread_act_t);
|
||||
|
||||
kern_return_t
|
||||
set_registers(mach_port_name_t, x86_thread_state64_t*);
|
||||
|
||||
kern_return_t
|
||||
get_identity(mach_port_name_t, thread_identifier_info_data_t *);
|
||||
|
||||
int
|
||||
thread_blocked(thread_act_t thread);
|
||||
|
||||
int
|
||||
num_running_threads(task_t task);
|
108
vendor/github.com/derekparker/delve/pkg/proc/threads_linux.go
generated
vendored
Normal file
108
vendor/github.com/derekparker/delve/pkg/proc/threads_linux.go
generated
vendored
Normal file
@ -0,0 +1,108 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
type WaitStatus sys.WaitStatus
|
||||
|
||||
// OSSpecificDetails hold Linux specific
|
||||
// process details.
|
||||
type OSSpecificDetails struct {
|
||||
registers sys.PtraceRegs
|
||||
}
|
||||
|
||||
func (t *Thread) halt() (err error) {
|
||||
err = sys.Tgkill(t.dbp.pid, t.ID, sys.SIGSTOP)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("halt err %s on thread %d", err, t.ID)
|
||||
return
|
||||
}
|
||||
_, _, err = t.dbp.wait(t.ID, 0)
|
||||
if err != nil {
|
||||
err = fmt.Errorf("wait err %s on thread %d", err, t.ID)
|
||||
return
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) stopped() bool {
|
||||
state := status(t.ID, t.dbp.os.comm)
|
||||
return state == StatusTraceStop || state == StatusTraceStopT
|
||||
}
|
||||
|
||||
func (t *Thread) resume() error {
|
||||
return t.resumeWithSig(0)
|
||||
}
|
||||
|
||||
func (t *Thread) resumeWithSig(sig int) (err error) {
|
||||
t.running = true
|
||||
t.dbp.execPtraceFunc(func() { err = PtraceCont(t.ID, sig) })
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) singleStep() (err error) {
|
||||
for {
|
||||
t.dbp.execPtraceFunc(func() { err = sys.PtraceSingleStep(t.ID) })
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
wpid, status, err := t.dbp.waitFast(t.ID)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if (status == nil || status.Exited()) && wpid == t.dbp.pid {
|
||||
t.dbp.postExit()
|
||||
rs := 0
|
||||
if status != nil {
|
||||
rs = status.ExitStatus()
|
||||
}
|
||||
return ProcessExitedError{Pid: t.dbp.pid, Status: rs}
|
||||
}
|
||||
if wpid == t.ID && status.StopSignal() == sys.SIGTRAP {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Thread) blocked() bool {
|
||||
pc, _ := t.PC()
|
||||
fn := t.dbp.goSymTable.PCToFunc(pc)
|
||||
if fn != nil && ((fn.Name == "runtime.futex") || (fn.Name == "runtime.usleep") || (fn.Name == "runtime.clone")) {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (t *Thread) saveRegisters() (Registers, error) {
|
||||
var err error
|
||||
t.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(t.ID, &t.os.registers) })
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not save register contents")
|
||||
}
|
||||
return &Regs{&t.os.registers, nil}, nil
|
||||
}
|
||||
|
||||
func (t *Thread) restoreRegisters() (err error) {
|
||||
t.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(t.ID, &t.os.registers) })
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) writeMemory(addr uintptr, data []byte) (written int, err error) {
|
||||
if len(data) == 0 {
|
||||
return
|
||||
}
|
||||
t.dbp.execPtraceFunc(func() { written, err = sys.PtracePokeData(t.ID, addr, data) })
|
||||
return
|
||||
}
|
||||
|
||||
func (t *Thread) readMemory(addr uintptr, size int) (data []byte, err error) {
|
||||
if size == 0 {
|
||||
return
|
||||
}
|
||||
data = make([]byte, size)
|
||||
t.dbp.execPtraceFunc(func() { _, err = sys.PtracePeekData(t.ID, addr, data) })
|
||||
return
|
||||
}
|
151
vendor/github.com/derekparker/delve/pkg/proc/threads_windows.go
generated
vendored
Normal file
151
vendor/github.com/derekparker/delve/pkg/proc/threads_windows.go
generated
vendored
Normal file
@ -0,0 +1,151 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
|
||||
sys "golang.org/x/sys/windows"
|
||||
)
|
||||
|
||||
// WaitStatus is a synonym for the platform-specific WaitStatus
|
||||
type WaitStatus sys.WaitStatus
|
||||
|
||||
// OSSpecificDetails holds information specific to the Windows
|
||||
// operating system / kernel.
|
||||
type OSSpecificDetails struct {
|
||||
hThread syscall.Handle
|
||||
}
|
||||
|
||||
func (t *Thread) halt() (err error) {
|
||||
// Ignore the request to halt. On Windows, all threads are halted
|
||||
// on return from WaitForDebugEvent.
|
||||
return nil
|
||||
|
||||
// TODO - This may not be correct in all usages of dbp.Halt. There
|
||||
// are some callers who use dbp.Halt() to stop the process when it is not
|
||||
// already broken on a debug event.
|
||||
}
|
||||
|
||||
func (t *Thread) singleStep() error {
|
||||
context := newCONTEXT()
|
||||
context.ContextFlags = _CONTEXT_ALL
|
||||
|
||||
// Set the processor TRAP flag
|
||||
err := _GetThreadContext(t.os.hThread, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context.EFlags |= 0x100
|
||||
|
||||
err = _SetThreadContext(t.os.hThread, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
_, err = _ResumeThread(t.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
for {
|
||||
var tid, exitCode int
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
tid, exitCode, err = t.dbp.waitForDebugEvent(waitBlocking | waitSuspendNewThreads)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if tid == 0 {
|
||||
t.dbp.postExit()
|
||||
return ProcessExitedError{Pid: t.dbp.pid, Status: exitCode}
|
||||
}
|
||||
|
||||
if t.dbp.os.breakThread == t.ID {
|
||||
break
|
||||
}
|
||||
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.dbp.os.breakThread), _DBG_CONTINUE)
|
||||
})
|
||||
}
|
||||
|
||||
_, err = _SuspendThread(t.os.hThread)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
|
||||
})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
// Unset the processor TRAP flag
|
||||
err = _GetThreadContext(t.os.hThread, context)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
context.EFlags &= ^uint32(0x100)
|
||||
|
||||
return _SetThreadContext(t.os.hThread, context)
|
||||
}
|
||||
|
||||
func (t *Thread) resume() error {
|
||||
t.running = true
|
||||
var err error
|
||||
t.dbp.execPtraceFunc(func() {
|
||||
//TODO: Note that we are ignoring the thread we were asked to continue and are continuing the
|
||||
//thread that we last broke on.
|
||||
err = _ContinueDebugEvent(uint32(t.dbp.pid), uint32(t.ID), _DBG_CONTINUE)
|
||||
})
|
||||
return err
|
||||
}
|
||||
|
||||
func (t *Thread) blocked() bool {
|
||||
// TODO: Probably incorrect - what are the runtime functions that
|
||||
// indicate blocking on Windows?
|
||||
pc, err := t.PC()
|
||||
if err != nil {
|
||||
return false
|
||||
}
|
||||
fn := t.dbp.goSymTable.PCToFunc(pc)
|
||||
if fn == nil {
|
||||
return false
|
||||
}
|
||||
switch fn.Name {
|
||||
case "runtime.kevent", "runtime.usleep":
|
||||
return true
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (t *Thread) stopped() bool {
|
||||
// TODO: We are assuming that threads are always stopped
|
||||
// during command execution.
|
||||
return true
|
||||
}
|
||||
|
||||
func (t *Thread) writeMemory(addr uintptr, data []byte) (int, error) {
|
||||
var count uintptr
|
||||
err := _WriteProcessMemory(t.dbp.os.hProcess, addr, &data[0], uintptr(len(data)), &count)
|
||||
if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
return int(count), nil
|
||||
}
|
||||
|
||||
func (t *Thread) readMemory(addr uintptr, size int) ([]byte, error) {
|
||||
if size == 0 {
|
||||
return nil, nil
|
||||
}
|
||||
var count uintptr
|
||||
buf := make([]byte, size)
|
||||
err := _ReadProcessMemory(t.dbp.os.hProcess, addr, &buf[0], uintptr(size), &count)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return buf[:count], nil
|
||||
}
|
755
vendor/github.com/derekparker/delve/pkg/proc/types.go
generated
vendored
Normal file
755
vendor/github.com/derekparker/delve/pkg/proc/types.go
generated
vendored
Normal file
@ -0,0 +1,755 @@
|
||||
package proc
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/ast"
|
||||
"go/constant"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"sort"
|
||||
"strconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"unsafe"
|
||||
|
||||
"github.com/derekparker/delve/pkg/dwarf/reader"
|
||||
|
||||
"golang.org/x/debug/dwarf"
|
||||
)
|
||||
|
||||
// The kind field in runtime._type is a reflect.Kind value plus
|
||||
// some extra flags defined here.
|
||||
// See equivalent declaration in $GOROOT/src/reflect/type.go
|
||||
const (
|
||||
kindDirectIface = 1 << 5
|
||||
kindGCProg = 1 << 6 // Type.gc points to GC program
|
||||
kindNoPointers = 1 << 7
|
||||
kindMask = (1 << 5) - 1
|
||||
)
|
||||
|
||||
// Value of tflag field in runtime._type.
|
||||
// See $GOROOT/reflect/type.go for a description of these flags.
|
||||
const (
|
||||
tflagUncommon = 1 << 0
|
||||
tflagExtraStar = 1 << 1
|
||||
tflagNamed = 1 << 2
|
||||
)
|
||||
|
||||
// Do not call this function directly it isn't able to deal correctly with package paths
|
||||
func (dbp *Process) findType(name string) (dwarf.Type, error) {
|
||||
off, found := dbp.types[name]
|
||||
if !found {
|
||||
return nil, reader.TypeNotFoundErr
|
||||
}
|
||||
return dbp.dwarf.Type(off)
|
||||
}
|
||||
|
||||
func (dbp *Process) pointerTo(typ dwarf.Type) dwarf.Type {
|
||||
return &dwarf.PtrType{dwarf.CommonType{int64(dbp.arch.PtrSize()), "*" + typ.Common().Name, reflect.Ptr, 0}, typ}
|
||||
}
|
||||
|
||||
func (dbp *Process) findTypeExpr(expr ast.Expr) (dwarf.Type, error) {
|
||||
dbp.loadPackageMap()
|
||||
if lit, islit := expr.(*ast.BasicLit); islit && lit.Kind == token.STRING {
|
||||
// Allow users to specify type names verbatim as quoted
|
||||
// string. Useful as a catch-all workaround for cases where we don't
|
||||
// parse/serialize types correctly or can not resolve package paths.
|
||||
typn, _ := strconv.Unquote(lit.Value)
|
||||
return dbp.findType(typn)
|
||||
}
|
||||
dbp.expandPackagesInType(expr)
|
||||
if snode, ok := expr.(*ast.StarExpr); ok {
|
||||
// Pointer types only appear in the dwarf informations when
|
||||
// a pointer to the type is used in the target program, here
|
||||
// we create a pointer type on the fly so that the user can
|
||||
// specify a pointer to any variable used in the target program
|
||||
ptyp, err := dbp.findTypeExpr(snode.X)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return dbp.pointerTo(ptyp), nil
|
||||
}
|
||||
return dbp.findType(exprToString(expr))
|
||||
}
|
||||
|
||||
func complexType(typename string) bool {
|
||||
for _, ch := range typename {
|
||||
switch ch {
|
||||
case '*', '[', '<', '{', '(', ' ':
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func (dbp *Process) loadPackageMap() error {
|
||||
if dbp.packageMap != nil {
|
||||
return nil
|
||||
}
|
||||
dbp.packageMap = map[string]string{}
|
||||
reader := dbp.DwarfReader()
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if entry.Tag != dwarf.TagTypedef && entry.Tag != dwarf.TagBaseType && entry.Tag != dwarf.TagClassType && entry.Tag != dwarf.TagStructType {
|
||||
continue
|
||||
}
|
||||
|
||||
typename, ok := entry.Val(dwarf.AttrName).(string)
|
||||
if !ok || complexType(typename) {
|
||||
continue
|
||||
}
|
||||
|
||||
dot := strings.LastIndex(typename, ".")
|
||||
if dot < 0 {
|
||||
continue
|
||||
}
|
||||
path := typename[:dot]
|
||||
slash := strings.LastIndex(path, "/")
|
||||
if slash < 0 || slash+1 >= len(path) {
|
||||
continue
|
||||
}
|
||||
name := path[slash+1:]
|
||||
dbp.packageMap[name] = path
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type sortFunctionsDebugInfoByLowpc []functionDebugInfo
|
||||
|
||||
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()
|
||||
dbp.types = make(map[string]dwarf.Offset)
|
||||
dbp.functions = []functionDebugInfo{}
|
||||
reader := dbp.DwarfReader()
|
||||
for entry, err := reader.Next(); entry != nil; entry, err = reader.Next() {
|
||||
if err != nil {
|
||||
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)
|
||||
if !ok {
|
||||
continue
|
||||
}
|
||||
if _, exists := dbp.types[name]; !exists {
|
||||
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) {
|
||||
switch e := expr.(type) {
|
||||
case *ast.ArrayType:
|
||||
dbp.expandPackagesInType(e.Elt)
|
||||
case *ast.ChanType:
|
||||
dbp.expandPackagesInType(e.Value)
|
||||
case *ast.FuncType:
|
||||
for i := range e.Params.List {
|
||||
dbp.expandPackagesInType(e.Params.List[i].Type)
|
||||
}
|
||||
if e.Results != nil {
|
||||
for i := range e.Results.List {
|
||||
dbp.expandPackagesInType(e.Results.List[i].Type)
|
||||
}
|
||||
}
|
||||
case *ast.MapType:
|
||||
dbp.expandPackagesInType(e.Key)
|
||||
dbp.expandPackagesInType(e.Value)
|
||||
case *ast.ParenExpr:
|
||||
dbp.expandPackagesInType(e.X)
|
||||
case *ast.SelectorExpr:
|
||||
switch x := e.X.(type) {
|
||||
case *ast.Ident:
|
||||
if path, ok := dbp.packageMap[x.Name]; ok {
|
||||
x.Name = path
|
||||
}
|
||||
default:
|
||||
dbp.expandPackagesInType(e.X)
|
||||
}
|
||||
case *ast.StarExpr:
|
||||
dbp.expandPackagesInType(e.X)
|
||||
default:
|
||||
// nothing to do
|
||||
}
|
||||
}
|
||||
|
||||
type nameOfRuntimeTypeEntry struct {
|
||||
typename string
|
||||
kind int64
|
||||
}
|
||||
|
||||
// Returns the type name of the type described in _type.
|
||||
// _type is a non-loaded Variable pointing to runtime._type struct in the target.
|
||||
// The returned string is in the format that's used in DWARF data
|
||||
func nameOfRuntimeType(_type *Variable) (typename string, kind int64, err error) {
|
||||
if e, ok := _type.dbp.nameOfRuntimeType[_type.Addr]; ok {
|
||||
return e.typename, e.kind, nil
|
||||
}
|
||||
|
||||
var tflag int64
|
||||
|
||||
if tflagField := _type.loadFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
|
||||
tflag, _ = constant.Int64Val(tflagField.Value)
|
||||
}
|
||||
if kindField := _type.loadFieldNamed("kind"); kindField != nil && kindField.Value != nil {
|
||||
kind, _ = constant.Int64Val(kindField.Value)
|
||||
}
|
||||
|
||||
// Named types are defined by a 'type' expression, everything else
|
||||
// (for example pointers to named types) are not considered named.
|
||||
if tflag&tflagNamed != 0 {
|
||||
typename, err = nameOfNamedRuntimeType(_type, kind, tflag)
|
||||
return typename, kind, err
|
||||
} else {
|
||||
typename, err = nameOfUnnamedRuntimeType(_type, kind, tflag)
|
||||
return typename, kind, err
|
||||
}
|
||||
|
||||
_type.dbp.nameOfRuntimeType[_type.Addr] = nameOfRuntimeTypeEntry{typename, kind}
|
||||
|
||||
return typename, kind, nil
|
||||
}
|
||||
|
||||
// The layout of a runtime._type struct is as follows:
|
||||
//
|
||||
// <runtime._type><kind specific struct fields><runtime.uncommontype>
|
||||
//
|
||||
// with the 'uncommon type struct' being optional
|
||||
//
|
||||
// For named types first we extract the type name from the 'str'
|
||||
// field in the runtime._type struct.
|
||||
// Then we prepend the package path from the runtime.uncommontype
|
||||
// struct, when it exists.
|
||||
//
|
||||
// To find out the memory address of the runtime.uncommontype struct
|
||||
// we first cast the Variable pointing to the runtime._type struct
|
||||
// to a struct specific to the type's kind (for example, if the type
|
||||
// being described is a slice type the variable will be specialized
|
||||
// to a runtime.slicetype).
|
||||
func nameOfNamedRuntimeType(_type *Variable, kind, tflag int64) (typename string, err error) {
|
||||
var strOff int64
|
||||
if strField := _type.loadFieldNamed("str"); strField != nil && strField.Value != nil {
|
||||
strOff, _ = constant.Int64Val(strField.Value)
|
||||
} else {
|
||||
return "", errors.New("could not find str field")
|
||||
}
|
||||
|
||||
// The following code is adapted from reflect.(*rtype).Name.
|
||||
// For a description of how memory is organized for type names read
|
||||
// the comment to 'type name struct' in $GOROOT/src/reflect/type.go
|
||||
|
||||
typename, _, _, err = _type.dbp.resolveNameOff(_type.Addr, uintptr(strOff))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if tflag&tflagExtraStar != 0 {
|
||||
typename = typename[1:]
|
||||
}
|
||||
|
||||
if i := strings.Index(typename, "."); i >= 0 {
|
||||
typename = typename[i+1:]
|
||||
} else {
|
||||
return typename, nil
|
||||
}
|
||||
|
||||
// The following code is adapted from reflect.(*rtype).PkgPath in
|
||||
// $GOROOT/src/reflect/type.go
|
||||
|
||||
_type, err = specificRuntimeType(_type, kind)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
if ut := uncommon(_type, tflag); ut != nil {
|
||||
if pkgPathField := ut.loadFieldNamed("pkgpath"); pkgPathField != nil && pkgPathField.Value != nil {
|
||||
pkgPathOff, _ := constant.Int64Val(pkgPathField.Value)
|
||||
pkgPath, _, _, err := _type.dbp.resolveNameOff(_type.Addr, uintptr(pkgPathOff))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
typename = pkgPath + "." + typename
|
||||
}
|
||||
}
|
||||
|
||||
return typename, nil
|
||||
}
|
||||
|
||||
func nameOfUnnamedRuntimeType(_type *Variable, kind, tflag int64) (string, error) {
|
||||
_type, err := specificRuntimeType(_type, kind)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
// The types referred to here are defined in $GOROOT/src/runtime/type.go
|
||||
switch reflect.Kind(kind & kindMask) {
|
||||
case reflect.Array:
|
||||
var len int64
|
||||
if lenField := _type.loadFieldNamed("len"); lenField != nil && lenField.Value != nil {
|
||||
len, _ = constant.Int64Val(lenField.Value)
|
||||
}
|
||||
elemname, err := fieldToType(_type, "elem")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return fmt.Sprintf("[%d]%s", len, elemname), nil
|
||||
case reflect.Chan:
|
||||
elemname, err := fieldToType(_type, "elem")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "chan " + elemname, nil
|
||||
case reflect.Func:
|
||||
return nameOfFuncRuntimeType(_type, tflag, true)
|
||||
case reflect.Interface:
|
||||
return nameOfInterfaceRuntimeType(_type, kind, tflag)
|
||||
case reflect.Map:
|
||||
keyname, err := fieldToType(_type, "key")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
elemname, err := fieldToType(_type, "elem")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "map[" + keyname + "]" + elemname, nil
|
||||
case reflect.Ptr:
|
||||
elemname, err := fieldToType(_type, "elem")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "*" + elemname, nil
|
||||
case reflect.Slice:
|
||||
elemname, err := fieldToType(_type, "elem")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return "[]" + elemname, nil
|
||||
case reflect.Struct:
|
||||
return nameOfStructRuntimeType(_type, kind, tflag)
|
||||
default:
|
||||
return nameOfNamedRuntimeType(_type, kind, tflag)
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the expression describing an anonymous function type.
|
||||
// A runtime.functype is followed by a runtime.uncommontype
|
||||
// (optional) and then by an array of pointers to runtime._type,
|
||||
// one for each input and output argument.
|
||||
func nameOfFuncRuntimeType(_type *Variable, tflag int64, anonymous bool) (string, error) {
|
||||
rtyp, err := _type.dbp.findType("runtime._type")
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
prtyp := _type.dbp.pointerTo(rtyp)
|
||||
|
||||
uadd := _type.RealType.Common().ByteSize
|
||||
if ut := uncommon(_type, tflag); ut != nil {
|
||||
uadd += ut.RealType.Common().ByteSize
|
||||
}
|
||||
|
||||
var inCount, outCount int64
|
||||
if inCountField := _type.loadFieldNamed("inCount"); inCountField != nil && inCountField.Value != nil {
|
||||
inCount, _ = constant.Int64Val(inCountField.Value)
|
||||
}
|
||||
if outCountField := _type.loadFieldNamed("outCount"); outCountField != nil && outCountField.Value != nil {
|
||||
outCount, _ = constant.Int64Val(outCountField.Value)
|
||||
// only the lowest 15 bits of outCount are used, rest are flags
|
||||
outCount = outCount & (1<<15 - 1)
|
||||
}
|
||||
|
||||
cursortyp := _type.newVariable("", _type.Addr+uintptr(uadd), prtyp)
|
||||
var buf bytes.Buffer
|
||||
if anonymous {
|
||||
buf.WriteString("func(")
|
||||
} else {
|
||||
buf.WriteString("(")
|
||||
}
|
||||
|
||||
for i := int64(0); i < inCount; i++ {
|
||||
argtype := cursortyp.maybeDereference()
|
||||
cursortyp.Addr += uintptr(_type.dbp.arch.PtrSize())
|
||||
argtypename, _, err := nameOfRuntimeType(argtype)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf.WriteString(argtypename)
|
||||
if i != inCount-1 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
}
|
||||
buf.WriteString(")")
|
||||
|
||||
switch outCount {
|
||||
case 0:
|
||||
// nothing to do
|
||||
case 1:
|
||||
buf.WriteString(" ")
|
||||
argtype := cursortyp.maybeDereference()
|
||||
argtypename, _, err := nameOfRuntimeType(argtype)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf.WriteString(argtypename)
|
||||
default:
|
||||
buf.WriteString(" (")
|
||||
for i := int64(0); i < outCount; i++ {
|
||||
argtype := cursortyp.maybeDereference()
|
||||
cursortyp.Addr += uintptr(_type.dbp.arch.PtrSize())
|
||||
argtypename, _, err := nameOfRuntimeType(argtype)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
buf.WriteString(argtypename)
|
||||
if i != inCount-1 {
|
||||
buf.WriteString(", ")
|
||||
}
|
||||
}
|
||||
buf.WriteString(")")
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("interface {")
|
||||
|
||||
methods, _ := _type.structMember("methods")
|
||||
methods.loadArrayValues(0, LoadConfig{false, 1, 0, 4096, -1})
|
||||
if methods.Unreadable != nil {
|
||||
return "", nil
|
||||
}
|
||||
|
||||
if len(methods.Children) == 0 {
|
||||
buf.WriteString("}")
|
||||
return buf.String(), nil
|
||||
} else {
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
|
||||
for i, im := range methods.Children {
|
||||
var methodname, methodtype string
|
||||
for i := range im.Children {
|
||||
switch im.Children[i].Name {
|
||||
case "name":
|
||||
nameoff, _ := constant.Int64Val(im.Children[i].Value)
|
||||
var err error
|
||||
methodname, _, _, err = _type.dbp.resolveNameOff(_type.Addr, uintptr(nameoff))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
case "typ":
|
||||
typeoff, _ := constant.Int64Val(im.Children[i].Value)
|
||||
typ, err := _type.dbp.resolveTypeOff(_type.Addr, uintptr(typeoff))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
typ, err = specificRuntimeType(typ, int64(reflect.Func))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
var tflag int64
|
||||
if tflagField := typ.loadFieldNamed("tflag"); tflagField != nil && tflagField.Value != nil {
|
||||
tflag, _ = constant.Int64Val(tflagField.Value)
|
||||
}
|
||||
methodtype, err = nameOfFuncRuntimeType(typ, tflag, false)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
buf.WriteString(methodname)
|
||||
buf.WriteString(methodtype)
|
||||
|
||||
if i != len(methods.Children)-1 {
|
||||
buf.WriteString("; ")
|
||||
} else {
|
||||
buf.WriteString(" }")
|
||||
}
|
||||
}
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error) {
|
||||
var buf bytes.Buffer
|
||||
buf.WriteString("struct {")
|
||||
|
||||
fields, _ := _type.structMember("fields")
|
||||
fields.loadArrayValues(0, LoadConfig{false, 1, 0, 4096, -1})
|
||||
if fields.Unreadable != nil {
|
||||
return "", fields.Unreadable
|
||||
}
|
||||
|
||||
if len(fields.Children) == 0 {
|
||||
buf.WriteString("}")
|
||||
return buf.String(), nil
|
||||
} else {
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
|
||||
for i, field := range fields.Children {
|
||||
var fieldname, fieldtypename string
|
||||
var typeField *Variable
|
||||
for i := range field.Children {
|
||||
switch field.Children[i].Name {
|
||||
case "name":
|
||||
nameoff, _ := constant.Int64Val(field.Children[i].Value)
|
||||
var err error
|
||||
fieldname, _, _, err = _type.dbp.loadName(uintptr(nameoff))
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
case "typ":
|
||||
typeField = field.Children[i].maybeDereference()
|
||||
var err error
|
||||
fieldtypename, _, err = nameOfRuntimeType(typeField)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// fieldname will be the empty string for anonymous fields
|
||||
if fieldname != "" {
|
||||
buf.WriteString(fieldname)
|
||||
buf.WriteString(" ")
|
||||
}
|
||||
buf.WriteString(fieldtypename)
|
||||
if i != len(fields.Children)-1 {
|
||||
buf.WriteString("; ")
|
||||
} else {
|
||||
buf.WriteString(" }")
|
||||
}
|
||||
}
|
||||
|
||||
return buf.String(), nil
|
||||
}
|
||||
|
||||
func fieldToType(_type *Variable, fieldName string) (string, error) {
|
||||
typeField, err := _type.structMember(fieldName)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
typeField = typeField.maybeDereference()
|
||||
typename, _, err := nameOfRuntimeType(typeField)
|
||||
return typename, err
|
||||
}
|
||||
|
||||
func specificRuntimeType(_type *Variable, kind int64) (*Variable, error) {
|
||||
rtyp, err := _type.dbp.findType("runtime._type")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
prtyp := _type.dbp.pointerTo(rtyp)
|
||||
|
||||
uintptrtyp, err := _type.dbp.findType("uintptr")
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
uint32typ := &dwarf.UintType{dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 4, Name: "uint32"}}}
|
||||
uint16typ := &dwarf.UintType{dwarf.BasicType{CommonType: dwarf.CommonType{ByteSize: 2, Name: "uint16"}}}
|
||||
|
||||
newStructType := func(name string, sz uintptr) *dwarf.StructType {
|
||||
return &dwarf.StructType{dwarf.CommonType{Name: name, ByteSize: int64(sz)}, name, "struct", nil, false}
|
||||
}
|
||||
|
||||
appendField := func(typ *dwarf.StructType, name string, fieldtype dwarf.Type, off uintptr) {
|
||||
typ.Field = append(typ.Field, &dwarf.StructField{Name: name, ByteOffset: int64(off), Type: fieldtype})
|
||||
}
|
||||
|
||||
newSliceType := func(elemtype dwarf.Type) *dwarf.SliceType {
|
||||
r := newStructType("[]"+elemtype.Common().Name, uintptr(3*uintptrtyp.Size()))
|
||||
appendField(r, "array", _type.dbp.pointerTo(elemtype), 0)
|
||||
appendField(r, "len", uintptrtyp, uintptr(uintptrtyp.Size()))
|
||||
appendField(r, "cap", uintptrtyp, uintptr(2*uintptrtyp.Size()))
|
||||
return &dwarf.SliceType{StructType: *r, ElemType: elemtype}
|
||||
}
|
||||
|
||||
var typ *dwarf.StructType
|
||||
|
||||
type rtype struct {
|
||||
size uintptr
|
||||
ptrdata uintptr
|
||||
hash uint32 // hash of type; avoids computation in hash tables
|
||||
tflag uint8 // extra type information flags
|
||||
align uint8 // alignment of variable with this type
|
||||
fieldAlign uint8 // alignment of struct field with this type
|
||||
kind uint8 // enumeration for C
|
||||
alg *byte // algorithm table
|
||||
gcdata *byte // garbage collection data
|
||||
str int32 // string form
|
||||
ptrToThis int32 // type for pointer to this type, may be zero
|
||||
}
|
||||
|
||||
switch reflect.Kind(kind & kindMask) {
|
||||
case reflect.Array:
|
||||
// runtime.arraytype
|
||||
var a struct {
|
||||
rtype
|
||||
elem *rtype // array element type
|
||||
slice *rtype // slice type
|
||||
len uintptr
|
||||
}
|
||||
typ = newStructType("runtime.arraytype", unsafe.Sizeof(a))
|
||||
appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
|
||||
appendField(typ, "len", uintptrtyp, unsafe.Offsetof(a.len))
|
||||
case reflect.Chan:
|
||||
// runtime.chantype
|
||||
var a struct {
|
||||
rtype
|
||||
elem *rtype // channel element type
|
||||
dir uintptr // channel direction (ChanDir)
|
||||
}
|
||||
typ = newStructType("runtime.chantype", unsafe.Sizeof(a))
|
||||
appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
|
||||
case reflect.Func:
|
||||
// runtime.functype
|
||||
var a struct {
|
||||
rtype `reflect:"func"`
|
||||
inCount uint16
|
||||
outCount uint16 // top bit is set if last input parameter is ...
|
||||
}
|
||||
typ = newStructType("runtime.functype", unsafe.Sizeof(a))
|
||||
appendField(typ, "inCount", uint16typ, unsafe.Offsetof(a.inCount))
|
||||
appendField(typ, "outCount", uint16typ, unsafe.Offsetof(a.outCount))
|
||||
case reflect.Interface:
|
||||
// runtime.imethod
|
||||
type imethod struct {
|
||||
name uint32 // name of method
|
||||
typ uint32 // .(*FuncType) underneath
|
||||
}
|
||||
|
||||
var im imethod
|
||||
|
||||
// runtime.interfacetype
|
||||
var a struct {
|
||||
rtype `reflect:"interface"`
|
||||
pkgPath *byte // import path
|
||||
methods []imethod // sorted by hash
|
||||
}
|
||||
|
||||
imethodtype := newStructType("runtime.imethod", unsafe.Sizeof(im))
|
||||
appendField(imethodtype, "name", uint32typ, unsafe.Offsetof(im.name))
|
||||
appendField(imethodtype, "typ", uint32typ, unsafe.Offsetof(im.typ))
|
||||
typ = newStructType("runtime.interfacetype", unsafe.Sizeof(a))
|
||||
appendField(typ, "methods", newSliceType(imethodtype), unsafe.Offsetof(a.methods))
|
||||
case reflect.Map:
|
||||
// runtime.maptype
|
||||
var a struct {
|
||||
rtype `reflect:"map"`
|
||||
key *rtype // map key type
|
||||
elem *rtype // map element (value) type
|
||||
bucket *rtype // internal bucket structure
|
||||
hmap *rtype // internal map header
|
||||
keysize uint8 // size of key slot
|
||||
indirectkey uint8 // store ptr to key instead of key itself
|
||||
valuesize uint8 // size of value slot
|
||||
indirectvalue uint8 // store ptr to value instead of value itself
|
||||
bucketsize uint16 // size of bucket
|
||||
reflexivekey bool // true if k==k for all keys
|
||||
needkeyupdate bool // true if we need to update key on an overwrite
|
||||
}
|
||||
typ = newStructType("runtime.maptype", unsafe.Sizeof(a))
|
||||
appendField(typ, "key", prtyp, unsafe.Offsetof(a.key))
|
||||
appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
|
||||
case reflect.Ptr:
|
||||
// runtime.ptrtype
|
||||
var a struct {
|
||||
rtype `reflect:"ptr"`
|
||||
elem *rtype // pointer element (pointed at) type
|
||||
}
|
||||
typ = newStructType("runtime.ptrtype", unsafe.Sizeof(a))
|
||||
appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
|
||||
case reflect.Slice:
|
||||
// runtime.slicetype
|
||||
var a struct {
|
||||
rtype `reflect:"slice"`
|
||||
elem *rtype // slice element type
|
||||
}
|
||||
|
||||
typ = newStructType("runtime.slicetype", unsafe.Sizeof(a))
|
||||
appendField(typ, "elem", prtyp, unsafe.Offsetof(a.elem))
|
||||
case reflect.Struct:
|
||||
// runtime.structtype
|
||||
type structField struct {
|
||||
name *byte // name is empty for embedded fields
|
||||
typ *rtype // type of field
|
||||
offset uintptr // byte offset of field within struct
|
||||
}
|
||||
|
||||
var sf structField
|
||||
|
||||
var a struct {
|
||||
rtype `reflect:"struct"`
|
||||
pkgPath *byte
|
||||
fields []structField // sorted by offset
|
||||
}
|
||||
|
||||
fieldtype := newStructType("runtime.structtype", unsafe.Sizeof(sf))
|
||||
appendField(fieldtype, "name", uintptrtyp, unsafe.Offsetof(sf.name))
|
||||
appendField(fieldtype, "typ", prtyp, unsafe.Offsetof(sf.typ))
|
||||
typ = newStructType("runtime.structtype", unsafe.Sizeof(a))
|
||||
appendField(typ, "fields", newSliceType(fieldtype), unsafe.Offsetof(a.fields))
|
||||
default:
|
||||
return _type, nil
|
||||
}
|
||||
|
||||
return _type.newVariable(_type.Name, _type.Addr, typ), nil
|
||||
}
|
||||
|
||||
// See reflect.(*rtype).uncommon in $GOROOT/src/reflect/type.go
|
||||
func uncommon(_type *Variable, tflag int64) *Variable {
|
||||
if tflag&tflagUncommon == 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
typ, err := _type.dbp.findType("runtime.uncommontype")
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return _type.newVariable(_type.Name, _type.Addr+uintptr(_type.RealType.Size()), typ)
|
||||
}
|
1723
vendor/github.com/derekparker/delve/pkg/proc/variables.go
generated
vendored
Normal file
1723
vendor/github.com/derekparker/delve/pkg/proc/variables.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
181
vendor/github.com/derekparker/delve/pkg/proc/zsyscall_windows.go
generated
vendored
Normal file
181
vendor/github.com/derekparker/delve/pkg/proc/zsyscall_windows.go
generated
vendored
Normal file
@ -0,0 +1,181 @@
|
||||
// MACHINE GENERATED BY 'go generate' COMMAND; DO NOT EDIT
|
||||
|
||||
package proc
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var _ unsafe.Pointer
|
||||
|
||||
var (
|
||||
modntdll = syscall.NewLazyDLL("ntdll.dll")
|
||||
modkernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
|
||||
procNtQueryInformationThread = modntdll.NewProc("NtQueryInformationThread")
|
||||
procGetThreadContext = modkernel32.NewProc("GetThreadContext")
|
||||
procSetThreadContext = modkernel32.NewProc("SetThreadContext")
|
||||
procSuspendThread = modkernel32.NewProc("SuspendThread")
|
||||
procResumeThread = modkernel32.NewProc("ResumeThread")
|
||||
procContinueDebugEvent = modkernel32.NewProc("ContinueDebugEvent")
|
||||
procWriteProcessMemory = modkernel32.NewProc("WriteProcessMemory")
|
||||
procReadProcessMemory = modkernel32.NewProc("ReadProcessMemory")
|
||||
procDebugBreakProcess = modkernel32.NewProc("DebugBreakProcess")
|
||||
procWaitForDebugEvent = modkernel32.NewProc("WaitForDebugEvent")
|
||||
procDebugActiveProcess = modkernel32.NewProc("DebugActiveProcess")
|
||||
procDebugActiveProcessStop = modkernel32.NewProc("DebugActiveProcessStop")
|
||||
procQueryFullProcessImageNameW = modkernel32.NewProc("QueryFullProcessImageNameW")
|
||||
)
|
||||
|
||||
func _NtQueryInformationThread(threadHandle syscall.Handle, infoclass int32, info uintptr, infolen uint32, retlen *uint32) (status _NTSTATUS) {
|
||||
r0, _, _ := syscall.Syscall6(procNtQueryInformationThread.Addr(), 5, uintptr(threadHandle), uintptr(infoclass), uintptr(info), uintptr(infolen), uintptr(unsafe.Pointer(retlen)), 0)
|
||||
status = _NTSTATUS(r0)
|
||||
return
|
||||
}
|
||||
|
||||
func _GetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procGetThreadContext.Addr(), 2, uintptr(thread), uintptr(unsafe.Pointer(context)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _SetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procSetThreadContext.Addr(), 2, uintptr(thread), uintptr(unsafe.Pointer(context)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _SuspendThread(threadid syscall.Handle) (prevsuspcount uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procSuspendThread.Addr(), 1, uintptr(threadid), 0, 0)
|
||||
prevsuspcount = uint32(r0)
|
||||
if prevsuspcount == 0xffffffff {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _ResumeThread(threadid syscall.Handle) (prevsuspcount uint32, err error) {
|
||||
r0, _, e1 := syscall.Syscall(procResumeThread.Addr(), 1, uintptr(threadid), 0, 0)
|
||||
prevsuspcount = uint32(r0)
|
||||
if prevsuspcount == 0xffffffff {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _ContinueDebugEvent(processid uint32, threadid uint32, continuestatus uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procContinueDebugEvent.Addr(), 3, uintptr(processid), uintptr(threadid), uintptr(continuestatus))
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _WriteProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, byteswritten *uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procWriteProcessMemory.Addr(), 5, uintptr(process), uintptr(baseaddr), uintptr(unsafe.Pointer(buffer)), uintptr(size), uintptr(unsafe.Pointer(byteswritten)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _ReadProcessMemory(process syscall.Handle, baseaddr uintptr, buffer *byte, size uintptr, bytesread *uintptr) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procReadProcessMemory.Addr(), 5, uintptr(process), uintptr(baseaddr), uintptr(unsafe.Pointer(buffer)), uintptr(size), uintptr(unsafe.Pointer(bytesread)), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _DebugBreakProcess(process syscall.Handle) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDebugBreakProcess.Addr(), 1, uintptr(process), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _WaitForDebugEvent(debugevent *_DEBUG_EVENT, milliseconds uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procWaitForDebugEvent.Addr(), 2, uintptr(unsafe.Pointer(debugevent)), uintptr(milliseconds), 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _DebugActiveProcess(processid uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDebugActiveProcess.Addr(), 1, uintptr(processid), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _DebugActiveProcessStop(processid uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall(procDebugActiveProcessStop.Addr(), 1, uintptr(processid), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func _QueryFullProcessImageName(process syscall.Handle, flags uint32, exename *uint16, size *uint32) (err error) {
|
||||
r1, _, e1 := syscall.Syscall6(procQueryFullProcessImageNameW.Addr(), 4, uintptr(process), uintptr(flags), uintptr(unsafe.Pointer(exename)), uintptr(unsafe.Pointer(size)), 0, 0)
|
||||
if r1 == 0 {
|
||||
if e1 != 0 {
|
||||
err = error(e1)
|
||||
} else {
|
||||
err = syscall.EINVAL
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
84
vendor/github.com/derekparker/delve/pkg/target/target.go
generated
vendored
Normal file
84
vendor/github.com/derekparker/delve/pkg/target/target.go
generated
vendored
Normal file
@ -0,0 +1,84 @@
|
||||
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)
|
||||
}
|
1426
vendor/github.com/derekparker/delve/pkg/terminal/command.go
generated
vendored
Normal file
1426
vendor/github.com/derekparker/delve/pkg/terminal/command.go
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
31
vendor/github.com/derekparker/delve/pkg/terminal/disasmprint.go
generated
vendored
Normal file
31
vendor/github.com/derekparker/delve/pkg/terminal/disasmprint.go
generated
vendored
Normal file
@ -0,0 +1,31 @@
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"fmt"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
"io"
|
||||
"path/filepath"
|
||||
"text/tabwriter"
|
||||
)
|
||||
|
||||
func DisasmPrint(dv api.AsmInstructions, out io.Writer) {
|
||||
bw := bufio.NewWriter(out)
|
||||
defer bw.Flush()
|
||||
if len(dv) > 0 && dv[0].Loc.Function != nil {
|
||||
fmt.Fprintf(bw, "TEXT %s(SB) %s\n", dv[0].Loc.Function.Name, dv[0].Loc.File)
|
||||
}
|
||||
tw := tabwriter.NewWriter(bw, 1, 8, 1, '\t', 0)
|
||||
defer tw.Flush()
|
||||
for _, inst := range dv {
|
||||
atbp := ""
|
||||
if inst.Breakpoint {
|
||||
atbp = "*"
|
||||
}
|
||||
atpc := ""
|
||||
if inst.AtPC {
|
||||
atpc = "=>"
|
||||
}
|
||||
fmt.Fprintf(tw, "%s\t%s:%d\t%#x%s\t%x\t%s\n", atpc, filepath.Base(inst.Loc.File), inst.Loc.Line, inst.Loc.PC, atbp, inst.Bytes, inst.Text)
|
||||
}
|
||||
}
|
54
vendor/github.com/derekparker/delve/pkg/terminal/docgen.go
generated
vendored
Normal file
54
vendor/github.com/derekparker/delve/pkg/terminal/docgen.go
generated
vendored
Normal file
@ -0,0 +1,54 @@
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func replaceDocPath(s string) string {
|
||||
const docpath = "$GOPATH/src/github.com/derekparker/delve/"
|
||||
|
||||
for {
|
||||
start := strings.Index(s, docpath)
|
||||
if start < 0 {
|
||||
return s
|
||||
}
|
||||
var end int
|
||||
for end = start + len(docpath); end < len(s); end++ {
|
||||
if s[end] == ' ' {
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
text := s[start+len(docpath) : end]
|
||||
s = s[:start] + fmt.Sprintf("[%s](//github.com/derekparker/delve/tree/master/%s)", text, text) + s[end:]
|
||||
}
|
||||
}
|
||||
|
||||
func (commands *Commands) WriteMarkdown(w io.Writer) {
|
||||
fmt.Fprintf(w, "# Commands\n\n")
|
||||
|
||||
fmt.Fprintf(w, "Command | Description\n")
|
||||
fmt.Fprintf(w, "--------|------------\n")
|
||||
for _, cmd := range commands.cmds {
|
||||
h := cmd.helpMsg
|
||||
if idx := strings.Index(h, "\n"); idx >= 0 {
|
||||
h = h[:idx]
|
||||
}
|
||||
fmt.Fprintf(w, "[%s](#%s) | %s\n", cmd.aliases[0], cmd.aliases[0], h)
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
|
||||
for _, cmd := range commands.cmds {
|
||||
fmt.Fprintf(w, "## %s\n%s\n\n", cmd.aliases[0], replaceDocPath(cmd.helpMsg))
|
||||
if len(cmd.aliases) > 1 {
|
||||
fmt.Fprintf(w, "Aliases:")
|
||||
for _, alias := range cmd.aliases[1:] {
|
||||
fmt.Fprintf(w, " %s", alias)
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
fmt.Fprintf(w, "\n")
|
||||
}
|
||||
}
|
251
vendor/github.com/derekparker/delve/pkg/terminal/terminal.go
generated
vendored
Normal file
251
vendor/github.com/derekparker/delve/pkg/terminal/terminal.go
generated
vendored
Normal file
@ -0,0 +1,251 @@
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"os/signal"
|
||||
"runtime"
|
||||
"strings"
|
||||
|
||||
"syscall"
|
||||
|
||||
"github.com/peterh/liner"
|
||||
|
||||
"github.com/derekparker/delve/pkg/config"
|
||||
"github.com/derekparker/delve/service"
|
||||
)
|
||||
|
||||
const (
|
||||
historyFile string = ".dbg_history"
|
||||
terminalBlueEscapeCode string = "\033[34m"
|
||||
terminalResetEscapeCode string = "\033[0m"
|
||||
)
|
||||
|
||||
// Term represents the terminal running dlv.
|
||||
type Term struct {
|
||||
client service.Client
|
||||
conf *config.Config
|
||||
prompt string
|
||||
line *liner.State
|
||||
cmds *Commands
|
||||
dumb bool
|
||||
stdout io.Writer
|
||||
InitFile string
|
||||
}
|
||||
|
||||
// New returns a new Term.
|
||||
func New(client service.Client, conf *config.Config) *Term {
|
||||
cmds := DebugCommands(client)
|
||||
if conf != nil && conf.Aliases != nil {
|
||||
cmds.Merge(conf.Aliases)
|
||||
}
|
||||
|
||||
var w io.Writer
|
||||
|
||||
dumb := strings.ToLower(os.Getenv("TERM")) == "dumb"
|
||||
if dumb {
|
||||
w = os.Stdout
|
||||
} else {
|
||||
w = getColorableWriter()
|
||||
}
|
||||
|
||||
return &Term{
|
||||
client: client,
|
||||
conf: conf,
|
||||
prompt: "(dlv) ",
|
||||
line: liner.NewLiner(),
|
||||
cmds: cmds,
|
||||
dumb: dumb,
|
||||
stdout: w,
|
||||
}
|
||||
}
|
||||
|
||||
// Close returns the terminal to its previous mode.
|
||||
func (t *Term) Close() {
|
||||
t.line.Close()
|
||||
}
|
||||
|
||||
// Run begins running dlv in the terminal.
|
||||
func (t *Term) Run() (int, error) {
|
||||
defer t.Close()
|
||||
|
||||
// Send the debugger a halt command on SIGINT
|
||||
ch := make(chan os.Signal)
|
||||
signal.Notify(ch, syscall.SIGINT)
|
||||
go func() {
|
||||
for range ch {
|
||||
fmt.Printf("received SIGINT, stopping process (will not forward signal)")
|
||||
_, err := t.client.Halt()
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "%v", err)
|
||||
}
|
||||
}
|
||||
}()
|
||||
|
||||
t.line.SetCompleter(func(line string) (c []string) {
|
||||
for _, cmd := range t.cmds.cmds {
|
||||
for _, alias := range cmd.aliases {
|
||||
if strings.HasPrefix(alias, strings.ToLower(line)) {
|
||||
c = append(c, alias)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
|
||||
fullHistoryFile, err := config.GetConfigFilePath(historyFile)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to load history file: %v.", err)
|
||||
}
|
||||
|
||||
f, err := os.Open(fullHistoryFile)
|
||||
if err != nil {
|
||||
f, err = os.Create(fullHistoryFile)
|
||||
if err != nil {
|
||||
fmt.Printf("Unable to open history file: %v. History will not be saved for this session.", err)
|
||||
}
|
||||
}
|
||||
|
||||
t.line.ReadHistory(f)
|
||||
f.Close()
|
||||
fmt.Println("Type 'help' for list of commands.")
|
||||
|
||||
if t.InitFile != "" {
|
||||
err := t.cmds.executeFile(t, t.InitFile)
|
||||
if err != nil {
|
||||
fmt.Fprintf(os.Stderr, "Error executing init file: %s\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
for {
|
||||
cmdstr, err := t.promptForInput()
|
||||
if err != nil {
|
||||
if err == io.EOF {
|
||||
fmt.Println("exit")
|
||||
return t.handleExit()
|
||||
}
|
||||
return 1, fmt.Errorf("Prompt for input failed.\n")
|
||||
}
|
||||
|
||||
cmdstr, args := parseCommand(cmdstr)
|
||||
if err := t.cmds.Call(cmdstr, args, t); err != nil {
|
||||
if _, ok := err.(ExitRequestError); ok {
|
||||
return t.handleExit()
|
||||
}
|
||||
// The type information gets lost in serialization / de-serialization,
|
||||
// so we do a string compare on the error message to see if the process
|
||||
// has exited, or if the command actually failed.
|
||||
if strings.Contains(err.Error(), "exited") {
|
||||
fmt.Fprintln(os.Stderr, err.Error())
|
||||
} else {
|
||||
fmt.Fprintf(os.Stderr, "Command failed: %s\n", err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Println prints a line to the terminal.
|
||||
func (t *Term) Println(prefix, str string) {
|
||||
if !t.dumb {
|
||||
prefix = fmt.Sprintf("%s%s%s", terminalBlueEscapeCode, prefix, terminalResetEscapeCode)
|
||||
}
|
||||
fmt.Fprintf(t.stdout, "%s%s\n", prefix, str)
|
||||
}
|
||||
|
||||
// Substitues directory to source file.
|
||||
//
|
||||
// Ensures that only directory is substitued, for example:
|
||||
// substitute from `/dir/subdir`, substitute to `/new`
|
||||
// for file path `/dir/subdir/file` will return file path `/new/file`.
|
||||
// for file path `/dir/subdir-2/file` substitution will not be applied.
|
||||
//
|
||||
// If more than one substitution rule is defined, the rules are applied
|
||||
// in the order they are defined, first rule that matches is used for
|
||||
// substitution.
|
||||
func (t *Term) substitutePath(path string) string {
|
||||
path = crossPlatformPath(path)
|
||||
if t.conf == nil {
|
||||
return path
|
||||
}
|
||||
separator := string(os.PathSeparator)
|
||||
for _, r := range t.conf.SubstitutePath {
|
||||
from := crossPlatformPath(r.From)
|
||||
to := r.To
|
||||
|
||||
if !strings.HasSuffix(from, separator) {
|
||||
from = from + separator
|
||||
}
|
||||
if !strings.HasSuffix(to, separator) {
|
||||
to = to + separator
|
||||
}
|
||||
if strings.HasPrefix(path, from) {
|
||||
return strings.Replace(path, from, to, 1)
|
||||
}
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func crossPlatformPath(path string) string {
|
||||
if runtime.GOOS == "darwin" || runtime.GOOS == "windows" {
|
||||
return strings.ToLower(path)
|
||||
}
|
||||
return path
|
||||
}
|
||||
|
||||
func (t *Term) promptForInput() (string, error) {
|
||||
l, err := t.line.Prompt(t.prompt)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
l = strings.TrimSuffix(l, "\n")
|
||||
if l != "" {
|
||||
t.line.AppendHistory(l)
|
||||
}
|
||||
|
||||
return l, nil
|
||||
}
|
||||
|
||||
func (t *Term) handleExit() (int, error) {
|
||||
fullHistoryFile, err := config.GetConfigFilePath(historyFile)
|
||||
if err != nil {
|
||||
fmt.Println("Error saving history file:", err)
|
||||
} else {
|
||||
if f, err := os.OpenFile(fullHistoryFile, os.O_RDWR, 0666); err == nil {
|
||||
_, err = t.line.WriteHistory(f)
|
||||
if err != nil {
|
||||
fmt.Println("readline history error:", err)
|
||||
}
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
|
||||
s, err := t.client.GetState()
|
||||
if err != nil {
|
||||
return 1, err
|
||||
}
|
||||
if !s.Exited {
|
||||
kill := true
|
||||
if t.client.AttachedToExistingProcess() {
|
||||
answer, err := t.line.Prompt("Would you like to kill the process? [Y/n] ")
|
||||
if err != nil {
|
||||
return 2, io.EOF
|
||||
}
|
||||
answer = strings.ToLower(strings.TrimSpace(answer))
|
||||
kill = (answer != "n" && answer != "no")
|
||||
}
|
||||
if err := t.client.Detach(kill); err != nil {
|
||||
return 1, err
|
||||
}
|
||||
}
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func parseCommand(cmdstr string) (string, string) {
|
||||
vals := strings.SplitN(cmdstr, " ", 2)
|
||||
if len(vals) == 1 {
|
||||
return vals[0], ""
|
||||
}
|
||||
return vals[0], strings.TrimSpace(vals[1])
|
||||
}
|
14
vendor/github.com/derekparker/delve/pkg/terminal/terminal_other.go
generated
vendored
Normal file
14
vendor/github.com/derekparker/delve/pkg/terminal/terminal_other.go
generated
vendored
Normal file
@ -0,0 +1,14 @@
|
||||
// +build !windows
|
||||
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
// getColorableWriter simply returns stdout on
|
||||
// *nix machines.
|
||||
func getColorableWriter() io.Writer {
|
||||
return os.Stdout
|
||||
}
|
35
vendor/github.com/derekparker/delve/pkg/terminal/terminal_windows.go
generated
vendored
Normal file
35
vendor/github.com/derekparker/delve/pkg/terminal/terminal_windows.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
package terminal
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
"syscall"
|
||||
|
||||
"github.com/mattn/go-colorable"
|
||||
)
|
||||
|
||||
// getColorableWriter will return a writer that is capable
|
||||
// of interpreting ANSI escape codes for terminal colors.
|
||||
func getColorableWriter() io.Writer {
|
||||
if strings.ToLower(os.Getenv("ConEmuANSI")) == "on" {
|
||||
// The ConEmu terminal is installed. Use it.
|
||||
return os.Stdout
|
||||
}
|
||||
|
||||
const ENABLE_VIRTUAL_TERMINAL_PROCESSING = 0x0004
|
||||
|
||||
h, err := syscall.GetStdHandle(syscall.STD_OUTPUT_HANDLE)
|
||||
if err != nil {
|
||||
return os.Stdout
|
||||
}
|
||||
var m uint32
|
||||
err = syscall.GetConsoleMode(h, &m)
|
||||
if err != nil {
|
||||
return os.Stdout
|
||||
}
|
||||
if m&ENABLE_VIRTUAL_TERMINAL_PROCESSING != 0 {
|
||||
return os.Stdout
|
||||
}
|
||||
return colorable.NewColorableStdout()
|
||||
}
|
25
vendor/github.com/derekparker/delve/pkg/version/version.go
generated
vendored
Normal file
25
vendor/github.com/derekparker/delve/pkg/version/version.go
generated
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
package version
|
||||
|
||||
import "fmt"
|
||||
|
||||
// Version represents the current version of Delve.
|
||||
type Version struct {
|
||||
Major string
|
||||
Minor string
|
||||
Patch string
|
||||
Metadata string
|
||||
Build string
|
||||
}
|
||||
|
||||
var (
|
||||
// DelveVersion is the current version of Delve.
|
||||
DelveVersion = Version{Major: "0", Minor: "12", Patch: "1", Metadata: ""}
|
||||
)
|
||||
|
||||
func (v Version) String() string {
|
||||
ver := fmt.Sprintf("Version: %s.%s.%s", v.Major, v.Minor, v.Patch)
|
||||
if v.Metadata != "" {
|
||||
ver += "-" + v.Metadata
|
||||
}
|
||||
return fmt.Sprintf("%s\nBuild: %s", ver, v.Build)
|
||||
}
|
276
vendor/github.com/derekparker/delve/service/api/conversions.go
generated
vendored
Normal file
276
vendor/github.com/derekparker/delve/service/api/conversions.go
generated
vendored
Normal file
@ -0,0 +1,276 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"debug/gosym"
|
||||
"go/constant"
|
||||
"go/printer"
|
||||
"go/token"
|
||||
"reflect"
|
||||
"strconv"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
|
||||
"golang.org/x/debug/dwarf"
|
||||
)
|
||||
|
||||
// ConvertBreakpoint converts from a proc.Breakpoint to
|
||||
// an api.Breakpoint.
|
||||
func ConvertBreakpoint(bp *proc.Breakpoint) *Breakpoint {
|
||||
b := &Breakpoint{
|
||||
Name: bp.Name,
|
||||
ID: bp.ID,
|
||||
FunctionName: bp.FunctionName,
|
||||
File: bp.File,
|
||||
Line: bp.Line,
|
||||
Addr: bp.Addr,
|
||||
Tracepoint: bp.Tracepoint,
|
||||
Stacktrace: bp.Stacktrace,
|
||||
Goroutine: bp.Goroutine,
|
||||
Variables: bp.Variables,
|
||||
LoadArgs: LoadConfigFromProc(bp.LoadArgs),
|
||||
LoadLocals: LoadConfigFromProc(bp.LoadLocals),
|
||||
TotalHitCount: bp.TotalHitCount,
|
||||
}
|
||||
|
||||
b.HitCount = map[string]uint64{}
|
||||
for idx := range bp.HitCount {
|
||||
b.HitCount[strconv.Itoa(idx)] = bp.HitCount[idx]
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
printer.Fprint(&buf, token.NewFileSet(), bp.Cond)
|
||||
b.Cond = buf.String()
|
||||
|
||||
return b
|
||||
}
|
||||
|
||||
// ConvertThread converts a proc.Thread into an
|
||||
// api thread.
|
||||
func ConvertThread(th *proc.Thread) *Thread {
|
||||
var (
|
||||
function *Function
|
||||
file string
|
||||
line int
|
||||
pc uint64
|
||||
gid int
|
||||
)
|
||||
|
||||
loc, err := th.Location()
|
||||
if err == nil {
|
||||
pc = loc.PC
|
||||
file = loc.File
|
||||
line = loc.Line
|
||||
function = ConvertFunction(loc.Fn)
|
||||
}
|
||||
|
||||
var bp *Breakpoint
|
||||
|
||||
if th.CurrentBreakpoint != nil && th.BreakpointConditionMet {
|
||||
bp = ConvertBreakpoint(th.CurrentBreakpoint)
|
||||
}
|
||||
|
||||
if g, _ := th.GetG(); g != nil {
|
||||
gid = g.ID
|
||||
}
|
||||
|
||||
return &Thread{
|
||||
ID: th.ID,
|
||||
PC: pc,
|
||||
File: file,
|
||||
Line: line,
|
||||
Function: function,
|
||||
GoroutineID: gid,
|
||||
Breakpoint: bp,
|
||||
}
|
||||
}
|
||||
|
||||
func prettyTypeName(typ dwarf.Type) string {
|
||||
if typ == nil {
|
||||
return ""
|
||||
}
|
||||
if typ.Common().Name != "" {
|
||||
return typ.Common().Name
|
||||
}
|
||||
r := typ.String()
|
||||
if r == "*void" {
|
||||
return "unsafe.Pointer"
|
||||
}
|
||||
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.
|
||||
func ConvertVar(v *proc.Variable) *Variable {
|
||||
r := Variable{
|
||||
Addr: v.Addr,
|
||||
OnlyAddr: v.OnlyAddr,
|
||||
Name: v.Name,
|
||||
Kind: v.Kind,
|
||||
Len: v.Len,
|
||||
Cap: v.Cap,
|
||||
}
|
||||
|
||||
r.Type = prettyTypeName(v.DwarfType)
|
||||
r.RealType = prettyTypeName(v.RealType)
|
||||
|
||||
if v.Unreadable != nil {
|
||||
r.Unreadable = v.Unreadable.Error()
|
||||
}
|
||||
|
||||
if v.Value != nil {
|
||||
switch v.Kind {
|
||||
case reflect.Float32:
|
||||
r.Value = convertFloatValue(v, 32)
|
||||
case reflect.Float64:
|
||||
r.Value = convertFloatValue(v, 64)
|
||||
case reflect.String, reflect.Func:
|
||||
r.Value = constant.StringVal(v.Value)
|
||||
default:
|
||||
r.Value = v.Value.String()
|
||||
}
|
||||
}
|
||||
|
||||
switch v.Kind {
|
||||
case reflect.Complex64:
|
||||
r.Children = make([]Variable, 2)
|
||||
r.Len = 2
|
||||
|
||||
real, _ := constant.Float64Val(constant.Real(v.Value))
|
||||
imag, _ := constant.Float64Val(constant.Imag(v.Value))
|
||||
|
||||
r.Children[0].Name = "real"
|
||||
r.Children[0].Kind = reflect.Float32
|
||||
r.Children[0].Value = strconv.FormatFloat(real, 'f', -1, 32)
|
||||
|
||||
r.Children[1].Name = "imaginary"
|
||||
r.Children[1].Kind = reflect.Float32
|
||||
r.Children[1].Value = strconv.FormatFloat(imag, 'f', -1, 32)
|
||||
case reflect.Complex128:
|
||||
r.Children = make([]Variable, 2)
|
||||
r.Len = 2
|
||||
|
||||
real, _ := constant.Float64Val(constant.Real(v.Value))
|
||||
imag, _ := constant.Float64Val(constant.Imag(v.Value))
|
||||
|
||||
r.Children[0].Name = "real"
|
||||
r.Children[0].Kind = reflect.Float64
|
||||
r.Children[0].Value = strconv.FormatFloat(real, 'f', -1, 64)
|
||||
|
||||
r.Children[1].Name = "imaginary"
|
||||
r.Children[1].Kind = reflect.Float64
|
||||
r.Children[1].Value = strconv.FormatFloat(imag, 'f', -1, 64)
|
||||
|
||||
default:
|
||||
r.Children = make([]Variable, len(v.Children))
|
||||
|
||||
for i := range v.Children {
|
||||
r.Children[i] = *ConvertVar(&v.Children[i])
|
||||
}
|
||||
}
|
||||
|
||||
return &r
|
||||
}
|
||||
|
||||
// ConvertFunction converts from gosym.Func to
|
||||
// api.Function.
|
||||
func ConvertFunction(fn *gosym.Func) *Function {
|
||||
if fn == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &Function{
|
||||
Name: fn.Name,
|
||||
Type: fn.Type,
|
||||
Value: fn.Value,
|
||||
GoType: fn.GoType,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertGoroutine converts from proc.G to api.Goroutine.
|
||||
func ConvertGoroutine(g *proc.G) *Goroutine {
|
||||
th := g.Thread()
|
||||
tid := 0
|
||||
if th != nil {
|
||||
tid = th.ID
|
||||
}
|
||||
return &Goroutine{
|
||||
ID: g.ID,
|
||||
CurrentLoc: ConvertLocation(g.CurrentLoc),
|
||||
UserCurrentLoc: ConvertLocation(g.UserCurrent()),
|
||||
GoStatementLoc: ConvertLocation(g.Go()),
|
||||
ThreadID: tid,
|
||||
}
|
||||
}
|
||||
|
||||
// ConvertLocation converts from proc.Location to api.Location.
|
||||
func ConvertLocation(loc proc.Location) Location {
|
||||
return Location{
|
||||
PC: loc.PC,
|
||||
File: loc.File,
|
||||
Line: loc.Line,
|
||||
Function: ConvertFunction(loc.Fn),
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertAsmInstruction(inst proc.AsmInstruction, text string) AsmInstruction {
|
||||
var destloc *Location
|
||||
if inst.DestLoc != nil {
|
||||
r := ConvertLocation(*inst.DestLoc)
|
||||
destloc = &r
|
||||
}
|
||||
return AsmInstruction{
|
||||
Loc: ConvertLocation(inst.Loc),
|
||||
DestLoc: destloc,
|
||||
Text: text,
|
||||
Bytes: inst.Bytes,
|
||||
Breakpoint: inst.Breakpoint,
|
||||
AtPC: inst.AtPC,
|
||||
}
|
||||
}
|
||||
|
||||
func LoadConfigToProc(cfg *LoadConfig) *proc.LoadConfig {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
return &proc.LoadConfig{
|
||||
cfg.FollowPointers,
|
||||
cfg.MaxVariableRecurse,
|
||||
cfg.MaxStringLen,
|
||||
cfg.MaxArrayValues,
|
||||
cfg.MaxStructFields,
|
||||
}
|
||||
}
|
||||
|
||||
func LoadConfigFromProc(cfg *proc.LoadConfig) *LoadConfig {
|
||||
if cfg == nil {
|
||||
return nil
|
||||
}
|
||||
return &LoadConfig{
|
||||
cfg.FollowPointers,
|
||||
cfg.MaxVariableRecurse,
|
||||
cfg.MaxStringLen,
|
||||
cfg.MaxArrayValues,
|
||||
cfg.MaxStructFields,
|
||||
}
|
||||
}
|
||||
|
||||
func ConvertRegisters(in []proc.Register) (out []Register) {
|
||||
out = make([]Register, len(in))
|
||||
for i := range in {
|
||||
out[i] = Register{in[i].Name, in[i].Value}
|
||||
}
|
||||
return
|
||||
}
|
323
vendor/github.com/derekparker/delve/service/api/prettyprint.go
generated
vendored
Normal file
323
vendor/github.com/derekparker/delve/service/api/prettyprint.go
generated
vendored
Normal file
@ -0,0 +1,323 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
const (
|
||||
// strings longer than this will cause slices, arrays and structs to be printed on multiple lines when newlines is enabled
|
||||
maxShortStringLen = 7
|
||||
// string used for one indentation level (when printing on multiple lines)
|
||||
indentString = "\t"
|
||||
)
|
||||
|
||||
// SinglelineString returns a representation of v on a single line.
|
||||
func (v *Variable) SinglelineString() string {
|
||||
var buf bytes.Buffer
|
||||
v.writeTo(&buf, true, false, true, "")
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
// MultilineString returns a representation of v on multiple lines.
|
||||
func (v *Variable) MultilineString(indent string) string {
|
||||
var buf bytes.Buffer
|
||||
v.writeTo(&buf, true, true, true, indent)
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
func (v *Variable) writeTo(buf io.Writer, top, newlines, includeType bool, indent string) {
|
||||
if v.Unreadable != "" {
|
||||
fmt.Fprintf(buf, "(unreadable %s)", v.Unreadable)
|
||||
return
|
||||
}
|
||||
|
||||
if !top && v.Addr == 0 {
|
||||
if includeType && v.Type != "void" {
|
||||
fmt.Fprintf(buf, "%s nil", v.Type)
|
||||
} else {
|
||||
fmt.Fprintf(buf, "nil")
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
switch v.Kind {
|
||||
case reflect.Slice:
|
||||
v.writeSliceTo(buf, newlines, includeType, indent)
|
||||
case reflect.Array:
|
||||
v.writeArrayTo(buf, newlines, includeType, indent)
|
||||
case reflect.Ptr:
|
||||
if v.Type == "" {
|
||||
fmt.Fprintf(buf, "nil")
|
||||
} else if v.Children[0].OnlyAddr && v.Children[0].Addr != 0 {
|
||||
fmt.Fprintf(buf, "(%s)(0x%x)", v.Type, v.Children[0].Addr)
|
||||
} else {
|
||||
fmt.Fprintf(buf, "*")
|
||||
v.Children[0].writeTo(buf, false, newlines, includeType, indent)
|
||||
}
|
||||
case reflect.UnsafePointer:
|
||||
fmt.Fprintf(buf, "unsafe.Pointer(0x%x)", v.Children[0].Addr)
|
||||
case reflect.String:
|
||||
v.writeStringTo(buf)
|
||||
case reflect.Chan:
|
||||
if newlines {
|
||||
v.writeStructTo(buf, newlines, includeType, indent)
|
||||
} else {
|
||||
if len(v.Children) == 0 {
|
||||
fmt.Fprintf(buf, "%s nil", v.Type)
|
||||
} else {
|
||||
fmt.Fprintf(buf, "%s %s/%s", v.Type, v.Children[0].Value, v.Children[1].Value)
|
||||
}
|
||||
}
|
||||
case reflect.Struct:
|
||||
v.writeStructTo(buf, newlines, includeType, indent)
|
||||
case reflect.Interface:
|
||||
if includeType {
|
||||
if v.Children[0].Kind == reflect.Invalid {
|
||||
fmt.Fprintf(buf, "%s ", v.Type)
|
||||
if v.Children[0].Addr == 0 {
|
||||
fmt.Fprintf(buf, "nil")
|
||||
return
|
||||
}
|
||||
} else {
|
||||
fmt.Fprintf(buf, "%s(%s) ", v.Type, v.Children[0].Type)
|
||||
}
|
||||
}
|
||||
data := v.Children[0]
|
||||
if data.Kind == reflect.Ptr {
|
||||
if len(data.Children) == 0 {
|
||||
fmt.Fprintf(buf, "...")
|
||||
} else if data.Children[0].Addr == 0 {
|
||||
fmt.Fprintf(buf, "nil")
|
||||
} else if data.Children[0].OnlyAddr {
|
||||
fmt.Fprintf(buf, "0x%x", v.Children[0].Addr)
|
||||
} else {
|
||||
v.Children[0].writeTo(buf, false, newlines, !includeType, indent)
|
||||
}
|
||||
} else {
|
||||
v.Children[0].writeTo(buf, false, newlines, !includeType, indent)
|
||||
}
|
||||
case reflect.Map:
|
||||
v.writeMapTo(buf, newlines, includeType, indent)
|
||||
case reflect.Func:
|
||||
if v.Value == "" {
|
||||
fmt.Fprintf(buf, "nil")
|
||||
} else {
|
||||
fmt.Fprintf(buf, "%s", v.Value)
|
||||
}
|
||||
case reflect.Complex64, reflect.Complex128:
|
||||
fmt.Fprintf(buf, "(%s + %si)", v.Children[0].Value, v.Children[1].Value)
|
||||
default:
|
||||
if v.Value != "" {
|
||||
buf.Write([]byte(v.Value))
|
||||
} else {
|
||||
fmt.Fprintf(buf, "(unknown %s)", v.Kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Variable) writeStringTo(buf io.Writer) {
|
||||
s := v.Value
|
||||
if len(s) != int(v.Len) {
|
||||
s = fmt.Sprintf("%s...+%d more", s, int(v.Len)-len(s))
|
||||
}
|
||||
fmt.Fprintf(buf, "%q", s)
|
||||
}
|
||||
|
||||
func (v *Variable) writeSliceTo(buf io.Writer, newlines, includeType bool, indent string) {
|
||||
if includeType {
|
||||
fmt.Fprintf(buf, "%s len: %d, cap: %d, ", v.Type, v.Len, v.Cap)
|
||||
}
|
||||
v.writeSliceOrArrayTo(buf, newlines, indent)
|
||||
}
|
||||
|
||||
func (v *Variable) writeArrayTo(buf io.Writer, newlines, includeType bool, indent string) {
|
||||
if includeType {
|
||||
fmt.Fprintf(buf, "%s ", v.Type)
|
||||
}
|
||||
v.writeSliceOrArrayTo(buf, newlines, indent)
|
||||
}
|
||||
|
||||
func (v *Variable) writeStructTo(buf io.Writer, newlines, includeType bool, indent string) {
|
||||
if int(v.Len) != len(v.Children) && len(v.Children) == 0 {
|
||||
fmt.Fprintf(buf, "(*%s)(0x%x)", v.Type, v.Addr)
|
||||
return
|
||||
}
|
||||
|
||||
if includeType {
|
||||
fmt.Fprintf(buf, "%s ", v.Type)
|
||||
}
|
||||
|
||||
nl := v.shouldNewlineStruct(newlines)
|
||||
|
||||
fmt.Fprintf(buf, "{")
|
||||
|
||||
for i := range v.Children {
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
}
|
||||
fmt.Fprintf(buf, "%s: ", v.Children[i].Name)
|
||||
v.Children[i].writeTo(buf, false, nl, true, indent+indentString)
|
||||
if i != len(v.Children)-1 || nl {
|
||||
fmt.Fprintf(buf, ",")
|
||||
if !nl {
|
||||
fmt.Fprintf(buf, " ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(v.Children) != int(v.Len) {
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
} else {
|
||||
fmt.Fprintf(buf, ",")
|
||||
}
|
||||
fmt.Fprintf(buf, "...+%d more", int(v.Len)-len(v.Children))
|
||||
}
|
||||
|
||||
fmt.Fprintf(buf, "}")
|
||||
}
|
||||
|
||||
func (v *Variable) writeMapTo(buf io.Writer, newlines, includeType bool, indent string) {
|
||||
if includeType {
|
||||
fmt.Fprintf(buf, "%s ", v.Type)
|
||||
}
|
||||
|
||||
nl := newlines && (len(v.Children) > 0)
|
||||
|
||||
fmt.Fprintf(buf, "[")
|
||||
|
||||
for i := 0; i < len(v.Children); i += 2 {
|
||||
key := &v.Children[i]
|
||||
value := &v.Children[i+1]
|
||||
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
}
|
||||
|
||||
key.writeTo(buf, false, false, false, indent+indentString)
|
||||
fmt.Fprintf(buf, ": ")
|
||||
value.writeTo(buf, false, nl, false, indent+indentString)
|
||||
if i != len(v.Children)-1 || nl {
|
||||
fmt.Fprintf(buf, ", ")
|
||||
}
|
||||
}
|
||||
|
||||
if len(v.Children)/2 != int(v.Len) {
|
||||
if len(v.Children) != 0 {
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
} else {
|
||||
fmt.Fprintf(buf, ",")
|
||||
}
|
||||
fmt.Fprintf(buf, "...+%d more", int(v.Len)-(len(v.Children)/2))
|
||||
} else {
|
||||
fmt.Fprintf(buf, "...")
|
||||
}
|
||||
}
|
||||
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s", indent)
|
||||
}
|
||||
fmt.Fprintf(buf, "]")
|
||||
}
|
||||
|
||||
func (v *Variable) shouldNewlineArray(newlines bool) bool {
|
||||
if !newlines || len(v.Children) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
kind, hasptr := (&v.Children[0]).recursiveKind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map, reflect.Interface:
|
||||
return true
|
||||
case reflect.String:
|
||||
if hasptr {
|
||||
return true
|
||||
}
|
||||
for i := range v.Children {
|
||||
if len(v.Children[i].Value) > maxShortStringLen {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
default:
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func (v *Variable) recursiveKind() (reflect.Kind, bool) {
|
||||
hasptr := false
|
||||
var kind reflect.Kind
|
||||
for {
|
||||
kind = v.Kind
|
||||
if kind == reflect.Ptr {
|
||||
hasptr = true
|
||||
v = &(v.Children[0])
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
return kind, hasptr
|
||||
}
|
||||
|
||||
func (v *Variable) shouldNewlineStruct(newlines bool) bool {
|
||||
if !newlines || len(v.Children) == 0 {
|
||||
return false
|
||||
}
|
||||
|
||||
for i := range v.Children {
|
||||
kind, hasptr := (&v.Children[i]).recursiveKind()
|
||||
|
||||
switch kind {
|
||||
case reflect.Slice, reflect.Array, reflect.Struct, reflect.Map, reflect.Interface:
|
||||
return true
|
||||
case reflect.String:
|
||||
if hasptr {
|
||||
return true
|
||||
}
|
||||
if len(v.Children[i].Value) > maxShortStringLen {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return false
|
||||
}
|
||||
|
||||
func (v *Variable) writeSliceOrArrayTo(buf io.Writer, newlines bool, indent string) {
|
||||
nl := v.shouldNewlineArray(newlines)
|
||||
fmt.Fprintf(buf, "[")
|
||||
|
||||
for i := range v.Children {
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
}
|
||||
v.Children[i].writeTo(buf, false, nl, false, indent+indentString)
|
||||
if i != len(v.Children)-1 || nl {
|
||||
fmt.Fprintf(buf, ",")
|
||||
}
|
||||
}
|
||||
|
||||
if len(v.Children) != int(v.Len) {
|
||||
if len(v.Children) != 0 {
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s%s", indent, indentString)
|
||||
} else {
|
||||
fmt.Fprintf(buf, ",")
|
||||
}
|
||||
fmt.Fprintf(buf, "...+%d more", int(v.Len)-len(v.Children))
|
||||
} else {
|
||||
fmt.Fprintf(buf, "...")
|
||||
}
|
||||
}
|
||||
|
||||
if nl {
|
||||
fmt.Fprintf(buf, "\n%s", indent)
|
||||
}
|
||||
|
||||
fmt.Fprintf(buf, "]")
|
||||
}
|
320
vendor/github.com/derekparker/delve/service/api/types.go
generated
vendored
Normal file
320
vendor/github.com/derekparker/delve/service/api/types.go
generated
vendored
Normal file
@ -0,0 +1,320 @@
|
||||
package api
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"reflect"
|
||||
"strconv"
|
||||
"unicode"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
)
|
||||
|
||||
var NotExecutableErr = proc.NotExecutableErr
|
||||
|
||||
// DebuggerState represents the current context of the debugger.
|
||||
type DebuggerState struct {
|
||||
// CurrentThread is the currently selected debugger thread.
|
||||
CurrentThread *Thread `json:"currentThread,omitempty"`
|
||||
// SelectedGoroutine is the currently selected goroutine
|
||||
SelectedGoroutine *Goroutine `json:"currentGoroutine,omitempty"`
|
||||
// List of all the process threads
|
||||
Threads []*Thread
|
||||
// NextInProgress indicates that a next or step operation was interrupted by another breakpoint
|
||||
// or a manual stop and is waiting to complete.
|
||||
// While NextInProgress is set further requests for next or step may be rejected.
|
||||
// Either execute continue until NextInProgress is false or call CancelNext
|
||||
NextInProgress bool
|
||||
// Exited indicates whether the debugged process has exited.
|
||||
Exited bool `json:"exited"`
|
||||
ExitStatus int `json:"exitStatus"`
|
||||
// Filled by RPCClient.Continue, indicates an error
|
||||
Err error `json:"-"`
|
||||
}
|
||||
|
||||
// Breakpoint addresses a location at which process execution may be
|
||||
// suspended.
|
||||
type Breakpoint struct {
|
||||
// ID is a unique identifier for the breakpoint.
|
||||
ID int `json:"id"`
|
||||
// User defined name of the breakpoint
|
||||
Name string `json:"name"`
|
||||
// Addr is the address of the breakpoint.
|
||||
Addr uint64 `json:"addr"`
|
||||
// File is the source file for the breakpoint.
|
||||
File string `json:"file"`
|
||||
// Line is a line in File for the breakpoint.
|
||||
Line int `json:"line"`
|
||||
// FunctionName is the name of the function at the current breakpoint, and
|
||||
// may not always be available.
|
||||
FunctionName string `json:"functionName,omitempty"`
|
||||
|
||||
// Breakpoint condition
|
||||
Cond string
|
||||
|
||||
// tracepoint flag
|
||||
Tracepoint bool `json:"continue"`
|
||||
// retrieve goroutine information
|
||||
Goroutine bool `json:"goroutine"`
|
||||
// number of stack frames to retrieve
|
||||
Stacktrace int `json:"stacktrace"`
|
||||
// expressions to evaluate
|
||||
Variables []string `json:"variables,omitempty"`
|
||||
// LoadArgs requests loading function arguments when the breakpoint is hit
|
||||
LoadArgs *LoadConfig
|
||||
// LoadLocals requests loading function locals when the breakpoint is hit
|
||||
LoadLocals *LoadConfig
|
||||
// number of times a breakpoint has been reached in a certain goroutine
|
||||
HitCount map[string]uint64 `json:"hitCount"`
|
||||
// number of times a breakpoint has been reached
|
||||
TotalHitCount uint64 `json:"totalHitCount"`
|
||||
}
|
||||
|
||||
func ValidBreakpointName(name string) error {
|
||||
if _, err := strconv.Atoi(name); err == nil {
|
||||
return errors.New("breakpoint name can not be a number")
|
||||
}
|
||||
|
||||
for _, ch := range name {
|
||||
if !(unicode.IsLetter(ch) || unicode.IsDigit(ch)) {
|
||||
return fmt.Errorf("invalid character in breakpoint name '%c'", ch)
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Thread is a thread within the debugged process.
|
||||
type Thread struct {
|
||||
// ID is a unique identifier for the thread.
|
||||
ID int `json:"id"`
|
||||
// PC is the current program counter for the thread.
|
||||
PC uint64 `json:"pc"`
|
||||
// File is the file for the program counter.
|
||||
File string `json:"file"`
|
||||
// Line is the line number for the program counter.
|
||||
Line int `json:"line"`
|
||||
// Function is function information at the program counter. May be nil.
|
||||
Function *Function `json:"function,omitempty"`
|
||||
|
||||
// ID of the goroutine running on this thread
|
||||
GoroutineID int `json:"goroutineID"`
|
||||
|
||||
// Breakpoint this thread is stopped at
|
||||
Breakpoint *Breakpoint `json:"breakPoint,omitempty"`
|
||||
// Informations requested by the current breakpoint
|
||||
BreakpointInfo *BreakpointInfo `json:"breakPointInfo,omitrempty"`
|
||||
}
|
||||
|
||||
type Location struct {
|
||||
PC uint64 `json:"pc"`
|
||||
File string `json:"file"`
|
||||
Line int `json:"line"`
|
||||
Function *Function `json:"function,omitempty"`
|
||||
}
|
||||
|
||||
type Stackframe struct {
|
||||
Location
|
||||
Locals []Variable
|
||||
Arguments []Variable
|
||||
}
|
||||
|
||||
func (frame *Stackframe) Var(name string) *Variable {
|
||||
for i := range frame.Locals {
|
||||
if frame.Locals[i].Name == name {
|
||||
return &frame.Locals[i]
|
||||
}
|
||||
}
|
||||
for i := range frame.Arguments {
|
||||
if frame.Arguments[i].Name == name {
|
||||
return &frame.Arguments[i]
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Function represents thread-scoped function information.
|
||||
type Function struct {
|
||||
// Name is the function name.
|
||||
Name string `json:"name"`
|
||||
Value uint64 `json:"value"`
|
||||
Type byte `json:"type"`
|
||||
GoType uint64 `json:"goType"`
|
||||
}
|
||||
|
||||
// Variable describes a variable.
|
||||
type Variable struct {
|
||||
// Name of the variable or struct member
|
||||
Name string `json:"name"`
|
||||
// Address of the variable or struct member
|
||||
Addr uintptr `json:"addr"`
|
||||
// Only the address field is filled (result of evaluating expressions like &<expr>)
|
||||
OnlyAddr bool `json:"onlyAddr"`
|
||||
// Go type of the variable
|
||||
Type string `json:"type"`
|
||||
// Type of the variable after resolving any typedefs
|
||||
RealType string `json:"realType"`
|
||||
|
||||
Kind reflect.Kind `json:"kind"`
|
||||
|
||||
//Strings have their length capped at proc.maxArrayValues, use Len for the real length of a string
|
||||
//Function variables will store the name of the function in this field
|
||||
Value string `json:"value"`
|
||||
|
||||
// Number of elements in an array or a slice, number of keys for a map, number of struct members for a struct, length of strings
|
||||
Len int64 `json:"len"`
|
||||
// Cap value for slices
|
||||
Cap int64 `json:"cap"`
|
||||
|
||||
// Array and slice elements, member fields of structs, key/value pairs of maps, value of complex numbers
|
||||
// The Name field in this slice will always be the empty string except for structs (when it will be the field name) and for complex numbers (when it will be "real" and "imaginary")
|
||||
// For maps each map entry will have to items in this slice, even numbered items will represent map keys and odd numbered items will represent their values
|
||||
// This field's length is capped at proc.maxArrayValues for slices and arrays and 2*proc.maxArrayValues for maps, in the circumnstances where the cap takes effect len(Children) != Len
|
||||
// The other length cap applied to this field is related to maximum recursion depth, when the maximum recursion depth is reached this field is left empty, contrary to the previous one this cap also applies to structs (otherwise structs will always have all their member fields returned)
|
||||
Children []Variable `json:"children"`
|
||||
|
||||
// Unreadable addresses will have this field set
|
||||
Unreadable string `json:"unreadable"`
|
||||
}
|
||||
|
||||
// LoadConfig describes how to load values from target's memory
|
||||
type LoadConfig struct {
|
||||
// FollowPointers requests pointers to be automatically dereferenced.
|
||||
FollowPointers bool
|
||||
// MaxVariableRecurse is how far to recurse when evaluating nested types.
|
||||
MaxVariableRecurse int
|
||||
// MaxStringLen is the maximum number of bytes read from a string
|
||||
MaxStringLen int
|
||||
// MaxArrayValues is the maximum number of elements read from an array, a slice or a map.
|
||||
MaxArrayValues int
|
||||
// MaxStructFields is the maximum number of fields read from a struct, -1 will read all fields.
|
||||
MaxStructFields int
|
||||
}
|
||||
|
||||
// Goroutine represents the information relevant to Delve from the runtime's
|
||||
// internal G structure.
|
||||
type Goroutine struct {
|
||||
// ID is a unique identifier for the goroutine.
|
||||
ID int `json:"id"`
|
||||
// Current location of the goroutine
|
||||
CurrentLoc Location `json:"currentLoc"`
|
||||
// Current location of the goroutine, excluding calls inside runtime
|
||||
UserCurrentLoc Location `json:"userCurrentLoc"`
|
||||
// Location of the go instruction that started this goroutine
|
||||
GoStatementLoc Location `json:"goStatementLoc"`
|
||||
// ID of the associated thread for running goroutines
|
||||
ThreadID int `json:"threadID"`
|
||||
}
|
||||
|
||||
// DebuggerCommand is a command which changes the debugger's execution state.
|
||||
type DebuggerCommand struct {
|
||||
// Name is the command to run.
|
||||
Name string `json:"name"`
|
||||
// ThreadID is used to specify which thread to use with the SwitchThread
|
||||
// command.
|
||||
ThreadID int `json:"threadID,omitempty"`
|
||||
// GoroutineID is used to specify which thread to use with the SwitchGoroutine
|
||||
// command.
|
||||
GoroutineID int `json:"goroutineID,omitempty"`
|
||||
}
|
||||
|
||||
// Informations about the current breakpoint
|
||||
type BreakpointInfo struct {
|
||||
Stacktrace []Stackframe `json:"stacktrace,omitempty"`
|
||||
Goroutine *Goroutine `json:"goroutine,omitempty"`
|
||||
Variables []Variable `json:"variables,omitempty"`
|
||||
Arguments []Variable `json:"arguments,omitempty"`
|
||||
Locals []Variable `json:"locals,omitempty"`
|
||||
}
|
||||
|
||||
type EvalScope struct {
|
||||
GoroutineID int
|
||||
Frame int
|
||||
}
|
||||
|
||||
const (
|
||||
// Continue resumes process execution.
|
||||
Continue = "continue"
|
||||
// Step continues to next source line, entering function calls.
|
||||
Step = "step"
|
||||
// StepOut continues to the return address of the current function
|
||||
StepOut = "stepOut"
|
||||
// SingleStep continues for exactly 1 cpu instruction.
|
||||
StepInstruction = "stepInstruction"
|
||||
// Next continues to the next source line, not entering function calls.
|
||||
Next = "next"
|
||||
// SwitchThread switches the debugger's current thread context.
|
||||
SwitchThread = "switchThread"
|
||||
// SwitchGoroutine switches the debugger's current thread context to the thread running the specified goroutine
|
||||
SwitchGoroutine = "switchGoroutine"
|
||||
// Halt suspends the process.
|
||||
Halt = "halt"
|
||||
)
|
||||
|
||||
type AssemblyFlavour int
|
||||
|
||||
const (
|
||||
GNUFlavour = AssemblyFlavour(proc.GNUFlavour)
|
||||
IntelFlavour = AssemblyFlavour(proc.IntelFlavour)
|
||||
)
|
||||
|
||||
// AsmInstruction represents one assembly instruction at some address
|
||||
type AsmInstruction struct {
|
||||
// Loc is the location of this instruction
|
||||
Loc Location
|
||||
// Destination of CALL instructions
|
||||
DestLoc *Location
|
||||
// Text is the formatted representation of the instruction
|
||||
Text string
|
||||
// Bytes is the instruction as read from memory
|
||||
Bytes []byte
|
||||
// If Breakpoint is true a breakpoint is set at this instruction
|
||||
Breakpoint bool
|
||||
// In AtPC is true this is the instruction the current thread is stopped at
|
||||
AtPC bool
|
||||
}
|
||||
|
||||
type AsmInstructions []AsmInstruction
|
||||
|
||||
type GetVersionIn struct {
|
||||
}
|
||||
|
||||
type GetVersionOut struct {
|
||||
DelveVersion string
|
||||
APIVersion int
|
||||
}
|
||||
|
||||
type SetAPIVersionIn struct {
|
||||
APIVersion int
|
||||
}
|
||||
|
||||
type SetAPIVersionOut struct {
|
||||
}
|
||||
|
||||
type Register struct {
|
||||
Name string
|
||||
Value string
|
||||
}
|
||||
|
||||
type Registers []Register
|
||||
|
||||
func (regs Registers) String() string {
|
||||
maxlen := 0
|
||||
for _, reg := range regs {
|
||||
if n := len(reg.Name); n > maxlen {
|
||||
maxlen = n
|
||||
}
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
for _, reg := range regs {
|
||||
fmt.Fprintf(&buf, "%*s = %s\n", maxlen, reg.Name, reg.Value)
|
||||
}
|
||||
return buf.String()
|
||||
}
|
||||
|
||||
type DiscardedBreakpoint struct {
|
||||
Breakpoint *Breakpoint
|
||||
Reason string
|
||||
}
|
115
vendor/github.com/derekparker/delve/service/client.go
generated
vendored
Normal file
115
vendor/github.com/derekparker/delve/service/client.go
generated
vendored
Normal file
@ -0,0 +1,115 @@
|
||||
package service
|
||||
|
||||
import (
|
||||
"time"
|
||||
|
||||
"github.com/derekparker/delve/service/api"
|
||||
)
|
||||
|
||||
// Client represents a debugger service client. All client methods are
|
||||
// synchronous.
|
||||
type Client interface {
|
||||
// Returns the pid of the process we are debugging.
|
||||
ProcessPid() int
|
||||
|
||||
// LastModified returns the time that the process' executable was modified.
|
||||
LastModified() time.Time
|
||||
|
||||
// Detach detaches the debugger, optionally killing the process.
|
||||
Detach(killProcess bool) error
|
||||
|
||||
// Restarts program.
|
||||
Restart() ([]api.DiscardedBreakpoint, error)
|
||||
|
||||
// GetState returns the current debugger state.
|
||||
GetState() (*api.DebuggerState, error)
|
||||
|
||||
// Continue resumes process execution.
|
||||
Continue() <-chan *api.DebuggerState
|
||||
// Next continues to the next source line, not entering function calls.
|
||||
Next() (*api.DebuggerState, error)
|
||||
// Step continues to the next source line, entering function calls.
|
||||
Step() (*api.DebuggerState, error)
|
||||
// StepOut continues to the return address of the current function
|
||||
StepOut() (*api.DebuggerState, error)
|
||||
|
||||
// SingleStep will step a single cpu instruction.
|
||||
StepInstruction() (*api.DebuggerState, error)
|
||||
// SwitchThread switches the current thread context.
|
||||
SwitchThread(threadID int) (*api.DebuggerState, error)
|
||||
// SwitchGoroutine switches the current goroutine (and the current thread as well)
|
||||
SwitchGoroutine(goroutineID int) (*api.DebuggerState, error)
|
||||
// Halt suspends the process.
|
||||
Halt() (*api.DebuggerState, error)
|
||||
|
||||
// GetBreakpoint gets a breakpoint by ID.
|
||||
GetBreakpoint(id int) (*api.Breakpoint, error)
|
||||
// GetBreakpointByName gets a breakpoint by name.
|
||||
GetBreakpointByName(name string) (*api.Breakpoint, error)
|
||||
// CreateBreakpoint creates a new breakpoint.
|
||||
CreateBreakpoint(*api.Breakpoint) (*api.Breakpoint, error)
|
||||
// ListBreakpoints gets all breakpoints.
|
||||
ListBreakpoints() ([]*api.Breakpoint, error)
|
||||
// ClearBreakpoint deletes a breakpoint by ID.
|
||||
ClearBreakpoint(id int) (*api.Breakpoint, error)
|
||||
// ClearBreakpointByName deletes a breakpoint by name
|
||||
ClearBreakpointByName(name string) (*api.Breakpoint, error)
|
||||
// Allows user to update an existing breakpoint for example to change the information
|
||||
// retrieved when the breakpoint is hit or to change, add or remove the break condition
|
||||
AmendBreakpoint(*api.Breakpoint) error
|
||||
// Cancels a Next or Step call that was interrupted by a manual stop or by another breakpoint
|
||||
CancelNext() error
|
||||
|
||||
// ListThreads lists all threads.
|
||||
ListThreads() ([]*api.Thread, error)
|
||||
// GetThread gets a thread by its ID.
|
||||
GetThread(id int) (*api.Thread, error)
|
||||
|
||||
// ListPackageVariables lists all package variables in the context of the current thread.
|
||||
ListPackageVariables(filter string, cfg api.LoadConfig) ([]api.Variable, error)
|
||||
// EvalVariable returns a variable in the context of the current thread.
|
||||
EvalVariable(scope api.EvalScope, symbol string, cfg api.LoadConfig) (*api.Variable, error)
|
||||
|
||||
// SetVariable sets the value of a variable
|
||||
SetVariable(scope api.EvalScope, symbol, value string) error
|
||||
|
||||
// ListSources lists all source files in the process matching filter.
|
||||
ListSources(filter string) ([]string, error)
|
||||
// ListFunctions lists all functions in the process matching filter.
|
||||
ListFunctions(filter string) ([]string, error)
|
||||
// ListTypes lists all types in the process matching filter.
|
||||
ListTypes(filter string) ([]string, error)
|
||||
// ListLocals lists all local variables in scope.
|
||||
ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error)
|
||||
// ListFunctionArgs lists all arguments to the current function.
|
||||
ListFunctionArgs(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error)
|
||||
// ListRegisters lists registers and their values.
|
||||
ListRegisters(threadID int, includeFp bool) (api.Registers, error)
|
||||
|
||||
// ListGoroutines lists all goroutines.
|
||||
ListGoroutines() ([]*api.Goroutine, error)
|
||||
|
||||
// Returns stacktrace
|
||||
Stacktrace(int, int, *api.LoadConfig) ([]api.Stackframe, error)
|
||||
|
||||
// Returns whether we attached to a running process or not
|
||||
AttachedToExistingProcess() bool
|
||||
|
||||
// Returns concrete location information described by a location expression
|
||||
// loc ::= <filename>:<line> | <function>[:<line>] | /<regex>/ | (+|-)<offset> | <line> | *<address>
|
||||
// * <filename> can be the full path of a file or just a suffix
|
||||
// * <function> ::= <package>.<receiver type>.<name> | <package>.(*<receiver type>).<name> | <receiver type>.<name> | <package>.<name> | (*<receiver type>).<name> | <name>
|
||||
// * <function> must be unambiguous
|
||||
// * /<regex>/ will return a location for each function matched by regex
|
||||
// * +<offset> returns a location for the line that is <offset> lines after the current line
|
||||
// * -<offset> returns a location for the line that is <offset> lines before the current line
|
||||
// * <line> returns a location for a line in the current file
|
||||
// * *<address> returns the location corresponding to the specified address
|
||||
// NOTE: this function does not actually set breakpoints.
|
||||
FindLocation(scope api.EvalScope, loc string) ([]api.Location, error)
|
||||
|
||||
// Disassemble code between startPC and endPC
|
||||
DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error)
|
||||
// Disassemble code of the function containing PC
|
||||
DisassemblePC(scope api.EvalScope, pc uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error)
|
||||
}
|
28
vendor/github.com/derekparker/delve/service/config.go
generated
vendored
Normal file
28
vendor/github.com/derekparker/delve/service/config.go
generated
vendored
Normal file
@ -0,0 +1,28 @@
|
||||
package service
|
||||
|
||||
import "net"
|
||||
|
||||
// Config provides the configuration to start a Debugger and expose it with a
|
||||
// service.
|
||||
//
|
||||
// Only one of ProcessArgs or AttachPid should be specified. If ProcessArgs is
|
||||
// provided, a new process will be launched. Otherwise, the debugger will try
|
||||
// to attach to an existing process with AttachPid.
|
||||
type Config struct {
|
||||
// Listener is used to serve requests.
|
||||
Listener net.Listener
|
||||
// ProcessArgs are the arguments to launch a new process.
|
||||
ProcessArgs []string
|
||||
// WorkingDir is working directory of the new process. This field is used
|
||||
// only when launching a new process.
|
||||
WorkingDir string
|
||||
|
||||
// AttachPid is the PID of an existing process to which the debugger should
|
||||
// attach.
|
||||
AttachPid int
|
||||
// AcceptMulti configures the server to accept multiple connection.
|
||||
// Note that the server API is not reentrant and clients will have to coordinate.
|
||||
AcceptMulti bool
|
||||
// APIVersion selects which version of the API to serve (default: 1).
|
||||
APIVersion int
|
||||
}
|
835
vendor/github.com/derekparker/delve/service/debugger/debugger.go
generated
vendored
Normal file
835
vendor/github.com/derekparker/delve/service/debugger/debugger.go
generated
vendored
Normal file
@ -0,0 +1,835 @@
|
||||
package debugger
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"errors"
|
||||
"fmt"
|
||||
"go/parser"
|
||||
"log"
|
||||
"path/filepath"
|
||||
"regexp"
|
||||
"runtime"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
"github.com/derekparker/delve/pkg/target"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
)
|
||||
|
||||
// Debugger service.
|
||||
//
|
||||
// Debugger provides a higher level of
|
||||
// abstraction over proc.Process.
|
||||
// It handles converting from internal types to
|
||||
// the types expected by clients. It also handles
|
||||
// functionality needed by clients, but not needed in
|
||||
// lower lever packages such as proc.
|
||||
type Debugger struct {
|
||||
config *Config
|
||||
// TODO(DO NOT MERGE WITHOUT) rename to targetMutex
|
||||
processMutex sync.Mutex
|
||||
target target.Interface
|
||||
}
|
||||
|
||||
// Config provides the configuration to start a Debugger.
|
||||
//
|
||||
// Only one of ProcessArgs or AttachPid should be specified. If ProcessArgs is
|
||||
// provided, a new process will be launched. Otherwise, the debugger will try
|
||||
// to attach to an existing process with AttachPid.
|
||||
type Config struct {
|
||||
// ProcessArgs are the arguments to launch a new process.
|
||||
ProcessArgs []string
|
||||
// WorkingDir is working directory of the new process. This field is used
|
||||
// only when launching a new process.
|
||||
WorkingDir string
|
||||
|
||||
// AttachPid is the PID of an existing process to which the debugger should
|
||||
// attach.
|
||||
AttachPid int
|
||||
}
|
||||
|
||||
// New creates a new Debugger.
|
||||
func New(config *Config) (*Debugger, error) {
|
||||
d := &Debugger{
|
||||
config: config,
|
||||
}
|
||||
|
||||
// Create the process by either attaching or launching.
|
||||
if d.config.AttachPid > 0 {
|
||||
log.Printf("attaching to pid %d", d.config.AttachPid)
|
||||
p, err := proc.Attach(d.config.AttachPid)
|
||||
if err != nil {
|
||||
return nil, attachErrorMessage(d.config.AttachPid, err)
|
||||
}
|
||||
d.target = p
|
||||
} else {
|
||||
log.Printf("launching process with args: %v", d.config.ProcessArgs)
|
||||
p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir)
|
||||
if err != nil {
|
||||
if err != proc.NotExecutableErr && err != proc.UnsupportedArchErr {
|
||||
err = fmt.Errorf("could not launch process: %s", err)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
d.target = p
|
||||
}
|
||||
return d, nil
|
||||
}
|
||||
|
||||
// ProcessPid returns the PID of the process
|
||||
// the debugger is debugging.
|
||||
func (d *Debugger) ProcessPid() int {
|
||||
return d.target.Pid()
|
||||
}
|
||||
|
||||
// LastModified returns the time that the process' executable was last
|
||||
// modified.
|
||||
func (d *Debugger) LastModified() time.Time {
|
||||
return d.target.LastModified()
|
||||
}
|
||||
|
||||
// Detach detaches from the target process.
|
||||
// If `kill` is true we will kill the process after
|
||||
// detaching.
|
||||
func (d *Debugger) Detach(kill bool) error {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
return d.detach(kill)
|
||||
}
|
||||
|
||||
func (d *Debugger) detach(kill bool) error {
|
||||
if d.config.AttachPid != 0 {
|
||||
return d.target.Detach(kill)
|
||||
}
|
||||
return d.target.Kill()
|
||||
}
|
||||
|
||||
// Restart will restart the target process, first killing
|
||||
// and then exec'ing it again.
|
||||
func (d *Debugger) Restart() ([]api.DiscardedBreakpoint, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
if !d.target.Exited() {
|
||||
if d.target.Running() {
|
||||
d.target.Halt()
|
||||
}
|
||||
// Ensure the process is in a PTRACE_STOP.
|
||||
if err := stopProcess(d.ProcessPid()); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := d.detach(true); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
p, err := proc.Launch(d.config.ProcessArgs, d.config.WorkingDir)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not launch process: %s", err)
|
||||
}
|
||||
discarded := []api.DiscardedBreakpoint{}
|
||||
for _, oldBp := range d.breakpoints() {
|
||||
if oldBp.ID < 0 {
|
||||
continue
|
||||
}
|
||||
if len(oldBp.File) > 0 {
|
||||
oldBp.Addr, err = p.FindFileLocation(oldBp.File, oldBp.Line)
|
||||
if err != nil {
|
||||
discarded = append(discarded, api.DiscardedBreakpoint{oldBp, err.Error()})
|
||||
continue
|
||||
}
|
||||
}
|
||||
newBp, err := p.SetBreakpoint(oldBp.Addr, proc.UserBreakpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := copyBreakpointInfo(newBp, oldBp); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
}
|
||||
d.target = p
|
||||
return discarded, nil
|
||||
}
|
||||
|
||||
// State returns the current state of the debugger.
|
||||
func (d *Debugger) State() (*api.DebuggerState, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
return d.state()
|
||||
}
|
||||
|
||||
func (d *Debugger) state() (*api.DebuggerState, error) {
|
||||
if d.target.Exited() {
|
||||
return nil, proc.ProcessExitedError{Pid: d.ProcessPid()}
|
||||
}
|
||||
|
||||
var (
|
||||
state *api.DebuggerState
|
||||
goroutine *api.Goroutine
|
||||
)
|
||||
|
||||
if d.target.SelectedGoroutine() != nil {
|
||||
goroutine = api.ConvertGoroutine(d.target.SelectedGoroutine())
|
||||
}
|
||||
|
||||
state = &api.DebuggerState{
|
||||
SelectedGoroutine: goroutine,
|
||||
Exited: d.target.Exited(),
|
||||
}
|
||||
|
||||
for i := range d.target.Threads() {
|
||||
th := api.ConvertThread(d.target.Threads()[i])
|
||||
state.Threads = append(state.Threads, th)
|
||||
if i == d.target.CurrentThread().ID {
|
||||
state.CurrentThread = th
|
||||
}
|
||||
}
|
||||
|
||||
for _, bp := range d.target.Breakpoints() {
|
||||
if bp.Internal() {
|
||||
state.NextInProgress = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
return state, nil
|
||||
}
|
||||
|
||||
// CreateBreakpoint creates a breakpoint.
|
||||
func (d *Debugger) CreateBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
var (
|
||||
createdBp *api.Breakpoint
|
||||
addr uint64
|
||||
err error
|
||||
)
|
||||
|
||||
if requestedBp.Name != "" {
|
||||
if err = api.ValidBreakpointName(requestedBp.Name); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if d.findBreakpointByName(requestedBp.Name) != nil {
|
||||
return nil, errors.New("breakpoint name already exists")
|
||||
}
|
||||
}
|
||||
|
||||
switch {
|
||||
case len(requestedBp.File) > 0:
|
||||
fileName := requestedBp.File
|
||||
if runtime.GOOS == "windows" {
|
||||
// Accept fileName which is case-insensitive and slash-insensitive match
|
||||
fileNameNormalized := strings.ToLower(filepath.ToSlash(fileName))
|
||||
for symFile := range d.target.Sources() {
|
||||
if fileNameNormalized == strings.ToLower(filepath.ToSlash(symFile)) {
|
||||
fileName = symFile
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
addr, err = d.target.FindFileLocation(fileName, requestedBp.Line)
|
||||
case len(requestedBp.FunctionName) > 0:
|
||||
if requestedBp.Line >= 0 {
|
||||
addr, err = d.target.FindFunctionLocation(requestedBp.FunctionName, false, requestedBp.Line)
|
||||
} else {
|
||||
addr, err = d.target.FindFunctionLocation(requestedBp.FunctionName, true, 0)
|
||||
}
|
||||
default:
|
||||
addr = requestedBp.Addr
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
bp, err := d.target.SetBreakpoint(addr, proc.UserBreakpoint, nil)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if err := copyBreakpointInfo(bp, requestedBp); err != nil {
|
||||
if _, err1 := d.target.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)
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
createdBp = api.ConvertBreakpoint(bp)
|
||||
log.Printf("created breakpoint: %#v", createdBp)
|
||||
return createdBp, nil
|
||||
}
|
||||
|
||||
func (d *Debugger) AmendBreakpoint(amend *api.Breakpoint) error {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
original := d.findBreakpoint(amend.ID)
|
||||
if original == nil {
|
||||
return fmt.Errorf("no breakpoint with ID %d", amend.ID)
|
||||
}
|
||||
if err := api.ValidBreakpointName(amend.Name); err != nil {
|
||||
return err
|
||||
}
|
||||
return copyBreakpointInfo(original, amend)
|
||||
}
|
||||
|
||||
func (d *Debugger) CancelNext() error {
|
||||
return d.target.ClearInternalBreakpoints()
|
||||
}
|
||||
|
||||
func copyBreakpointInfo(bp *proc.Breakpoint, requested *api.Breakpoint) (err error) {
|
||||
bp.Name = requested.Name
|
||||
bp.Tracepoint = requested.Tracepoint
|
||||
bp.Goroutine = requested.Goroutine
|
||||
bp.Stacktrace = requested.Stacktrace
|
||||
bp.Variables = requested.Variables
|
||||
bp.LoadArgs = api.LoadConfigToProc(requested.LoadArgs)
|
||||
bp.LoadLocals = api.LoadConfigToProc(requested.LoadLocals)
|
||||
bp.Cond = nil
|
||||
if requested.Cond != "" {
|
||||
bp.Cond, err = parser.ParseExpr(requested.Cond)
|
||||
}
|
||||
return err
|
||||
}
|
||||
|
||||
// ClearBreakpoint clears a breakpoint.
|
||||
func (d *Debugger) ClearBreakpoint(requestedBp *api.Breakpoint) (*api.Breakpoint, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
var clearedBp *api.Breakpoint
|
||||
bp, err := d.target.ClearBreakpoint(requestedBp.Addr)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("Can't clear breakpoint @%x: %s", requestedBp.Addr, err)
|
||||
}
|
||||
clearedBp = api.ConvertBreakpoint(bp)
|
||||
log.Printf("cleared breakpoint: %#v", clearedBp)
|
||||
return clearedBp, err
|
||||
}
|
||||
|
||||
// Breakpoints returns the list of current breakpoints.
|
||||
func (d *Debugger) Breakpoints() []*api.Breakpoint {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
return d.breakpoints()
|
||||
}
|
||||
|
||||
func (d *Debugger) breakpoints() []*api.Breakpoint {
|
||||
bps := []*api.Breakpoint{}
|
||||
for _, bp := range d.target.Breakpoints() {
|
||||
if bp.Internal() {
|
||||
continue
|
||||
}
|
||||
bps = append(bps, api.ConvertBreakpoint(bp))
|
||||
}
|
||||
return bps
|
||||
}
|
||||
|
||||
// FindBreakpoint returns the breakpoint specified by 'id'.
|
||||
func (d *Debugger) FindBreakpoint(id int) *api.Breakpoint {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
bp := d.findBreakpoint(id)
|
||||
if bp == nil {
|
||||
return nil
|
||||
}
|
||||
return api.ConvertBreakpoint(bp)
|
||||
}
|
||||
|
||||
func (d *Debugger) findBreakpoint(id int) *proc.Breakpoint {
|
||||
for _, bp := range d.target.Breakpoints() {
|
||||
if bp.ID == id {
|
||||
return bp
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// FindBreakpointByName returns the breakpoint specified by 'name'
|
||||
func (d *Debugger) FindBreakpointByName(name string) *api.Breakpoint {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
return d.findBreakpointByName(name)
|
||||
}
|
||||
|
||||
func (d *Debugger) findBreakpointByName(name string) *api.Breakpoint {
|
||||
for _, bp := range d.breakpoints() {
|
||||
if bp.Name == name {
|
||||
return bp
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Threads returns the threads of the target process.
|
||||
func (d *Debugger) Threads() ([]*api.Thread, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
if d.target.Exited() {
|
||||
return nil, &proc.ProcessExitedError{}
|
||||
}
|
||||
threads := []*api.Thread{}
|
||||
for _, th := range d.target.Threads() {
|
||||
threads = append(threads, api.ConvertThread(th))
|
||||
}
|
||||
return threads, nil
|
||||
}
|
||||
|
||||
// FindThread returns the thread for the given 'id'.
|
||||
func (d *Debugger) FindThread(id int) (*api.Thread, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
if d.target.Exited() {
|
||||
return nil, &proc.ProcessExitedError{}
|
||||
}
|
||||
|
||||
for _, th := range d.target.Threads() {
|
||||
if th.ID == id {
|
||||
return api.ConvertThread(th), nil
|
||||
}
|
||||
}
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// Command handles commands which control the debugger lifecycle
|
||||
func (d *Debugger) Command(command *api.DebuggerCommand) (*api.DebuggerState, error) {
|
||||
var err error
|
||||
|
||||
if command.Name == api.Halt {
|
||||
// RequestManualStop does not invoke any ptrace syscalls, so it's safe to
|
||||
// access the process directly.
|
||||
log.Print("halting")
|
||||
err = d.target.RequestManualStop()
|
||||
}
|
||||
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
switch command.Name {
|
||||
case api.Continue:
|
||||
log.Print("continuing")
|
||||
err = d.target.Continue()
|
||||
if err != nil {
|
||||
if exitedErr, exited := err.(proc.ProcessExitedError); exited {
|
||||
state := &api.DebuggerState{}
|
||||
state.Exited = true
|
||||
state.ExitStatus = exitedErr.Status
|
||||
state.Err = errors.New(exitedErr.Error())
|
||||
return state, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
state, stateErr := d.state()
|
||||
if stateErr != nil {
|
||||
return state, stateErr
|
||||
}
|
||||
err = d.collectBreakpointInformation(state)
|
||||
return state, err
|
||||
|
||||
case api.Next:
|
||||
log.Print("nexting")
|
||||
err = d.target.Next()
|
||||
case api.Step:
|
||||
log.Print("stepping")
|
||||
err = d.target.Step()
|
||||
case api.StepInstruction:
|
||||
log.Print("single stepping")
|
||||
err = d.target.StepInstruction()
|
||||
case api.StepOut:
|
||||
log.Print("step out")
|
||||
err = d.target.StepOut()
|
||||
case api.SwitchThread:
|
||||
log.Printf("switching to thread %d", command.ThreadID)
|
||||
err = d.target.SwitchThread(command.ThreadID)
|
||||
case api.SwitchGoroutine:
|
||||
log.Printf("switching to goroutine %d", command.GoroutineID)
|
||||
err = d.target.SwitchGoroutine(command.GoroutineID)
|
||||
case api.Halt:
|
||||
// RequestManualStop already called
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return d.state()
|
||||
}
|
||||
|
||||
func (d *Debugger) collectBreakpointInformation(state *api.DebuggerState) error {
|
||||
if state == nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
for i := range state.Threads {
|
||||
if state.Threads[i].Breakpoint == nil {
|
||||
continue
|
||||
}
|
||||
|
||||
bp := state.Threads[i].Breakpoint
|
||||
bpi := &api.BreakpointInfo{}
|
||||
state.Threads[i].BreakpointInfo = bpi
|
||||
|
||||
if bp.Goroutine {
|
||||
g, err := d.target.CurrentThread().GetG()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bpi.Goroutine = api.ConvertGoroutine(g)
|
||||
}
|
||||
|
||||
if bp.Stacktrace > 0 {
|
||||
rawlocs, err := d.target.CurrentThread().Stacktrace(bp.Stacktrace)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bpi.Stacktrace, err = d.convertStacktrace(rawlocs, nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
|
||||
s, err := d.target.Threads()[state.Threads[i].ID].Scope()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if len(bp.Variables) > 0 {
|
||||
bpi.Variables = make([]api.Variable, len(bp.Variables))
|
||||
}
|
||||
for i := range bp.Variables {
|
||||
v, err := s.EvalVariable(bp.Variables[i], proc.LoadConfig{true, 1, 64, 64, -1})
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
bpi.Variables[i] = *api.ConvertVar(v)
|
||||
}
|
||||
if bp.LoadArgs != nil {
|
||||
if vars, err := s.FunctionArguments(*api.LoadConfigToProc(bp.LoadArgs)); err == nil {
|
||||
bpi.Arguments = convertVars(vars)
|
||||
}
|
||||
}
|
||||
if bp.LoadLocals != nil {
|
||||
if locals, err := s.LocalVariables(*api.LoadConfigToProc(bp.LoadLocals)); err == nil {
|
||||
bpi.Locals = convertVars(locals)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
// Sources returns a list of the source files for target binary.
|
||||
func (d *Debugger) Sources(filter string) ([]string, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
regex, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
}
|
||||
|
||||
files := []string{}
|
||||
for f := range d.target.Sources() {
|
||||
if regex.Match([]byte(f)) {
|
||||
files = append(files, f)
|
||||
}
|
||||
}
|
||||
return files, nil
|
||||
}
|
||||
|
||||
// Functions returns a list of functions in the target process.
|
||||
func (d *Debugger) Functions(filter string) ([]string, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
return regexFilterFuncs(filter, d.target.Funcs())
|
||||
}
|
||||
|
||||
func (d *Debugger) Types(filter string) ([]string, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
regex, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
}
|
||||
|
||||
types, err := d.target.Types()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
r := make([]string, 0, len(types))
|
||||
for _, typ := range types {
|
||||
if regex.Match([]byte(typ)) {
|
||||
r = append(r, typ)
|
||||
}
|
||||
}
|
||||
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func regexFilterFuncs(filter string, allFuncs []gosym.Func) ([]string, error) {
|
||||
regex, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
}
|
||||
|
||||
funcs := []string{}
|
||||
for _, f := range allFuncs {
|
||||
if f.Sym != nil && regex.Match([]byte(f.Name)) {
|
||||
funcs = append(funcs, f.Name)
|
||||
}
|
||||
}
|
||||
return funcs, nil
|
||||
}
|
||||
|
||||
// PackageVariables returns a list of package variables for the thread,
|
||||
// optionally regexp filtered using regexp described in 'filter'.
|
||||
func (d *Debugger) PackageVariables(threadID int, filter string, cfg proc.LoadConfig) ([]api.Variable, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
regex, err := regexp.Compile(filter)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("invalid filter argument: %s", err.Error())
|
||||
}
|
||||
|
||||
vars := []api.Variable{}
|
||||
thread, found := d.target.Threads()[threadID]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
||||
}
|
||||
scope, err := thread.Scope()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pv, err := scope.PackageVariables(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, v := range pv {
|
||||
if regex.Match([]byte(v.Name)) {
|
||||
vars = append(vars, *api.ConvertVar(v))
|
||||
}
|
||||
}
|
||||
return vars, err
|
||||
}
|
||||
|
||||
// Registers returns string representation of the CPU registers.
|
||||
func (d *Debugger) Registers(threadID int, floatingPoint bool) (api.Registers, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
thread, found := d.target.Threads()[threadID]
|
||||
if !found {
|
||||
return nil, fmt.Errorf("couldn't find thread %d", threadID)
|
||||
}
|
||||
regs, err := thread.Registers(floatingPoint)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return api.ConvertRegisters(regs.Slice()), err
|
||||
}
|
||||
|
||||
func convertVars(pv []*proc.Variable) []api.Variable {
|
||||
vars := make([]api.Variable, 0, len(pv))
|
||||
for _, v := range pv {
|
||||
vars = append(vars, *api.ConvertVar(v))
|
||||
}
|
||||
return vars
|
||||
}
|
||||
|
||||
// LocalVariables returns a list of the local variables.
|
||||
func (d *Debugger) LocalVariables(scope api.EvalScope, cfg proc.LoadConfig) ([]api.Variable, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pv, err := s.LocalVariables(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertVars(pv), err
|
||||
}
|
||||
|
||||
// FunctionArguments returns the arguments to the current function.
|
||||
func (d *Debugger) FunctionArguments(scope api.EvalScope, cfg proc.LoadConfig) ([]api.Variable, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
pv, err := s.FunctionArguments(cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return convertVars(pv), nil
|
||||
}
|
||||
|
||||
// EvalVariableInScope will attempt to evaluate the variable represented by 'symbol'
|
||||
// in the scope provided.
|
||||
func (d *Debugger) EvalVariableInScope(scope api.EvalScope, symbol string, cfg proc.LoadConfig) (*api.Variable, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
v, err := s.EvalVariable(symbol, cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return api.ConvertVar(v), err
|
||||
}
|
||||
|
||||
// SetVariableInScope will set the value of the variable represented by
|
||||
// 'symbol' to the value given, in the given scope.
|
||||
func (d *Debugger) SetVariableInScope(scope api.EvalScope, symbol, value string) error {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return s.SetVariable(symbol, value)
|
||||
}
|
||||
|
||||
// Goroutines will return a list of goroutines in the target process.
|
||||
func (d *Debugger) Goroutines() ([]*api.Goroutine, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
goroutines := []*api.Goroutine{}
|
||||
gs, err := d.target.GoroutinesInfo()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
for _, g := range gs {
|
||||
goroutines = append(goroutines, api.ConvertGoroutine(g))
|
||||
}
|
||||
return goroutines, err
|
||||
}
|
||||
|
||||
// Stacktrace returns a list of Stackframes for the given goroutine. The
|
||||
// length of the returned list will be min(stack_len, depth).
|
||||
// If 'full' is true, then local vars, function args, etc will be returned as well.
|
||||
func (d *Debugger) Stacktrace(goroutineID, depth int, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
var rawlocs []proc.Stackframe
|
||||
|
||||
g, err := d.target.FindGoroutine(goroutineID)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
if g == nil {
|
||||
rawlocs, err = d.target.CurrentThread().Stacktrace(depth)
|
||||
} else {
|
||||
rawlocs, err = g.Stacktrace(depth)
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return d.convertStacktrace(rawlocs, cfg)
|
||||
}
|
||||
|
||||
func (d *Debugger) convertStacktrace(rawlocs []proc.Stackframe, cfg *proc.LoadConfig) ([]api.Stackframe, error) {
|
||||
locations := make([]api.Stackframe, 0, len(rawlocs))
|
||||
for i := range rawlocs {
|
||||
frame := api.Stackframe{Location: api.ConvertLocation(rawlocs[i].Call)}
|
||||
if cfg != nil && rawlocs[i].Current.Fn != nil {
|
||||
var err error
|
||||
scope := rawlocs[i].Scope(d.target.CurrentThread())
|
||||
locals, err := scope.LocalVariables(*cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
arguments, err := scope.FunctionArguments(*cfg)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
frame.Locals = convertVars(locals)
|
||||
frame.Arguments = convertVars(arguments)
|
||||
}
|
||||
locations = append(locations, frame)
|
||||
}
|
||||
|
||||
return locations, nil
|
||||
}
|
||||
|
||||
// FindLocation will find the location specified by 'locStr'.
|
||||
func (d *Debugger) FindLocation(scope api.EvalScope, locStr string) ([]api.Location, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
loc, err := parseLocationSpec(locStr)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
s, _ := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame)
|
||||
|
||||
locs, err := loc.Find(d, s, locStr)
|
||||
for i := range locs {
|
||||
file, line, fn := d.target.PCToLine(locs[i].PC)
|
||||
locs[i].File = file
|
||||
locs[i].Line = line
|
||||
locs[i].Function = api.ConvertFunction(fn)
|
||||
}
|
||||
return locs, err
|
||||
}
|
||||
|
||||
// Disassembles code between startPC and endPC
|
||||
// if endPC == 0 it will find the function containing startPC and disassemble the whole function
|
||||
func (d *Debugger) Disassemble(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
|
||||
d.processMutex.Lock()
|
||||
defer d.processMutex.Unlock()
|
||||
|
||||
if endPC == 0 {
|
||||
_, _, fn := d.target.PCToLine(startPC)
|
||||
if fn == nil {
|
||||
return nil, fmt.Errorf("Address 0x%x does not belong to any function", startPC)
|
||||
}
|
||||
startPC = fn.Entry
|
||||
endPC = fn.End
|
||||
}
|
||||
|
||||
currentGoroutine := true
|
||||
thread := d.target.CurrentThread()
|
||||
|
||||
if s, err := d.target.ConvertEvalScope(scope.GoroutineID, scope.Frame); err == nil {
|
||||
thread = s.Thread
|
||||
if scope.GoroutineID != -1 {
|
||||
g, _ := s.Thread.GetG()
|
||||
if g == nil || g.ID != scope.GoroutineID {
|
||||
currentGoroutine = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
insts, err := thread.Disassemble(startPC, endPC, currentGoroutine)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
disass := make(api.AsmInstructions, len(insts))
|
||||
|
||||
for i := range insts {
|
||||
disass[i] = api.ConvertAsmInstruction(insts[i], insts[i].Text(proc.AssemblyFlavour(flavour)))
|
||||
}
|
||||
|
||||
return disass, nil
|
||||
}
|
15
vendor/github.com/derekparker/delve/service/debugger/debugger_darwin.go
generated
vendored
Normal file
15
vendor/github.com/derekparker/delve/service/debugger/debugger_darwin.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
package debugger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
sys "golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
func attachErrorMessage(pid int, err error) error {
|
||||
//TODO: mention certificates?
|
||||
return fmt.Errorf("could not attach to pid %d: %s", pid, err)
|
||||
}
|
||||
|
||||
func stopProcess(pid int) error {
|
||||
return sys.Kill(pid, sys.SIGSTOP)
|
||||
}
|
35
vendor/github.com/derekparker/delve/service/debugger/debugger_linux.go
generated
vendored
Normal file
35
vendor/github.com/derekparker/delve/service/debugger/debugger_linux.go
generated
vendored
Normal file
@ -0,0 +1,35 @@
|
||||
package debugger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
sys "golang.org/x/sys/unix"
|
||||
"io/ioutil"
|
||||
"os"
|
||||
"syscall"
|
||||
)
|
||||
|
||||
func attachErrorMessage(pid int, err error) error {
|
||||
fallbackerr := fmt.Errorf("could not attach to pid %d: %s", pid, err)
|
||||
if serr, ok := err.(syscall.Errno); ok {
|
||||
switch serr {
|
||||
case syscall.EPERM:
|
||||
bs, err := ioutil.ReadFile("/proc/sys/kernel/yama/ptrace_scope")
|
||||
if err == nil && len(bs) >= 1 && bs[0] != '0' {
|
||||
// Yama documentation: https://www.kernel.org/doc/Documentation/security/Yama.txt
|
||||
return fmt.Errorf("Could not attach to pid %d: set /proc/sys/kernel/yama/ptrace_scope to 0", pid)
|
||||
}
|
||||
fi, err := os.Stat(fmt.Sprintf("/proc/%d", pid))
|
||||
if err != nil {
|
||||
return fallbackerr
|
||||
}
|
||||
if fi.Sys().(*syscall.Stat_t).Uid != uint32(os.Getuid()) {
|
||||
return fmt.Errorf("Could not attach to pid %d: current user does not own the process", pid)
|
||||
}
|
||||
}
|
||||
}
|
||||
return fallbackerr
|
||||
}
|
||||
|
||||
func stopProcess(pid int) error {
|
||||
return sys.Kill(pid, sys.SIGSTOP)
|
||||
}
|
16
vendor/github.com/derekparker/delve/service/debugger/debugger_windows.go
generated
vendored
Normal file
16
vendor/github.com/derekparker/delve/service/debugger/debugger_windows.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
package debugger
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func attachErrorMessage(pid int, err error) error {
|
||||
return fmt.Errorf("could not attach to pid %d: %s", pid, err)
|
||||
}
|
||||
|
||||
func stopProcess(pid int) error {
|
||||
// We cannot gracefully stop a process on Windows,
|
||||
// so just ignore this request and let `Detach` kill
|
||||
// the process.
|
||||
return nil
|
||||
}
|
411
vendor/github.com/derekparker/delve/service/debugger/locations.go
generated
vendored
Normal file
411
vendor/github.com/derekparker/delve/service/debugger/locations.go
generated
vendored
Normal file
@ -0,0 +1,411 @@
|
||||
package debugger
|
||||
|
||||
import (
|
||||
"debug/gosym"
|
||||
"fmt"
|
||||
"go/constant"
|
||||
"path/filepath"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"strconv"
|
||||
"strings"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
)
|
||||
|
||||
const maxFindLocationCandidates = 5
|
||||
|
||||
type LocationSpec interface {
|
||||
Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error)
|
||||
}
|
||||
|
||||
type NormalLocationSpec struct {
|
||||
Base string
|
||||
FuncBase *FuncLocationSpec
|
||||
LineOffset int
|
||||
}
|
||||
|
||||
type RegexLocationSpec struct {
|
||||
FuncRegex string
|
||||
}
|
||||
|
||||
type AddrLocationSpec struct {
|
||||
AddrExpr string
|
||||
}
|
||||
|
||||
type OffsetLocationSpec struct {
|
||||
Offset int
|
||||
}
|
||||
|
||||
type LineLocationSpec struct {
|
||||
Line int
|
||||
}
|
||||
|
||||
type FuncLocationSpec struct {
|
||||
PackageName string
|
||||
AbsolutePackage bool
|
||||
ReceiverName string
|
||||
PackageOrReceiverName string
|
||||
BaseName string
|
||||
}
|
||||
|
||||
func parseLocationSpec(locStr string) (LocationSpec, error) {
|
||||
rest := locStr
|
||||
|
||||
malformed := func(reason string) error {
|
||||
return fmt.Errorf("Malformed breakpoint location \"%s\" at %d: %s", locStr, len(locStr)-len(rest), reason)
|
||||
}
|
||||
|
||||
if len(rest) <= 0 {
|
||||
return nil, malformed("empty string")
|
||||
}
|
||||
|
||||
switch rest[0] {
|
||||
case '+', '-':
|
||||
offset, err := strconv.Atoi(rest)
|
||||
if err != nil {
|
||||
return nil, malformed(err.Error())
|
||||
}
|
||||
return &OffsetLocationSpec{offset}, nil
|
||||
|
||||
case '/':
|
||||
if rest[len(rest)-1] == '/' {
|
||||
rx, rest := readRegex(rest[1:])
|
||||
if len(rest) < 0 {
|
||||
return nil, malformed("non-terminated regular expression")
|
||||
}
|
||||
if len(rest) > 1 {
|
||||
return nil, malformed("no line offset can be specified for regular expression locations")
|
||||
}
|
||||
return &RegexLocationSpec{rx}, nil
|
||||
} else {
|
||||
return parseLocationSpecDefault(locStr, rest)
|
||||
}
|
||||
|
||||
case '*':
|
||||
return &AddrLocationSpec{rest[1:]}, nil
|
||||
|
||||
default:
|
||||
return parseLocationSpecDefault(locStr, rest)
|
||||
}
|
||||
}
|
||||
|
||||
func parseLocationSpecDefault(locStr, rest string) (LocationSpec, error) {
|
||||
malformed := func(reason string) error {
|
||||
return fmt.Errorf("Malformed breakpoint location \"%s\" at %d: %s", locStr, len(locStr)-len(rest), reason)
|
||||
}
|
||||
|
||||
v := strings.Split(rest, ":")
|
||||
if len(v) > 2 {
|
||||
// On Windows, path may contain ":", so split only on last ":"
|
||||
v = []string{strings.Join(v[0:len(v)-1], ":"), v[len(v)-1]}
|
||||
}
|
||||
|
||||
if len(v) == 1 {
|
||||
n, err := strconv.ParseInt(v[0], 0, 64)
|
||||
if err == nil {
|
||||
return &LineLocationSpec{int(n)}, nil
|
||||
}
|
||||
}
|
||||
|
||||
spec := &NormalLocationSpec{}
|
||||
|
||||
spec.Base = v[0]
|
||||
spec.FuncBase = parseFuncLocationSpec(spec.Base)
|
||||
|
||||
if len(v) < 2 {
|
||||
spec.LineOffset = -1
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
rest = v[1]
|
||||
|
||||
var err error
|
||||
spec.LineOffset, err = strconv.Atoi(rest)
|
||||
if err != nil || spec.LineOffset < 0 {
|
||||
return nil, malformed("line offset negative or not a number")
|
||||
}
|
||||
|
||||
return spec, nil
|
||||
}
|
||||
|
||||
func readRegex(in string) (rx string, rest string) {
|
||||
out := make([]rune, 0, len(in))
|
||||
escaped := false
|
||||
for i, ch := range in {
|
||||
if escaped {
|
||||
if ch == '/' {
|
||||
out = append(out, '/')
|
||||
} else {
|
||||
out = append(out, '\\')
|
||||
out = append(out, ch)
|
||||
}
|
||||
escaped = false
|
||||
} else {
|
||||
switch ch {
|
||||
case '\\':
|
||||
escaped = true
|
||||
case '/':
|
||||
return string(out), in[i:]
|
||||
default:
|
||||
out = append(out, ch)
|
||||
}
|
||||
}
|
||||
}
|
||||
return string(out), ""
|
||||
}
|
||||
|
||||
func parseFuncLocationSpec(in string) *FuncLocationSpec {
|
||||
var v []string
|
||||
pathend := strings.LastIndex(in, "/")
|
||||
if pathend < 0 {
|
||||
v = strings.Split(in, ".")
|
||||
} else {
|
||||
v = strings.Split(in[pathend:], ".")
|
||||
if len(v) > 0 {
|
||||
v[0] = in[:pathend] + v[0]
|
||||
}
|
||||
}
|
||||
|
||||
var spec FuncLocationSpec
|
||||
switch len(v) {
|
||||
case 1:
|
||||
spec.BaseName = v[0]
|
||||
|
||||
case 2:
|
||||
spec.BaseName = v[1]
|
||||
r := stripReceiverDecoration(v[0])
|
||||
if r != v[0] {
|
||||
spec.ReceiverName = r
|
||||
} else if strings.Index(r, "/") >= 0 {
|
||||
spec.PackageName = r
|
||||
} else {
|
||||
spec.PackageOrReceiverName = r
|
||||
}
|
||||
|
||||
case 3:
|
||||
spec.BaseName = v[2]
|
||||
spec.ReceiverName = stripReceiverDecoration(v[1])
|
||||
spec.PackageName = v[0]
|
||||
|
||||
default:
|
||||
return nil
|
||||
}
|
||||
|
||||
if strings.HasPrefix(spec.PackageName, "/") {
|
||||
spec.PackageName = spec.PackageName[1:]
|
||||
spec.AbsolutePackage = true
|
||||
}
|
||||
|
||||
if strings.Index(spec.BaseName, "/") >= 0 || strings.Index(spec.ReceiverName, "/") >= 0 {
|
||||
return nil
|
||||
}
|
||||
|
||||
return &spec
|
||||
}
|
||||
|
||||
func stripReceiverDecoration(in string) string {
|
||||
if len(in) < 3 {
|
||||
return in
|
||||
}
|
||||
if (in[0] != '(') || (in[1] != '*') || (in[len(in)-1] != ')') {
|
||||
return in
|
||||
}
|
||||
|
||||
return in[2 : len(in)-1]
|
||||
}
|
||||
|
||||
func (spec *FuncLocationSpec) Match(sym *gosym.Sym) bool {
|
||||
if spec.BaseName != sym.BaseName() {
|
||||
return false
|
||||
}
|
||||
|
||||
recv := stripReceiverDecoration(sym.ReceiverName())
|
||||
if spec.ReceiverName != "" && spec.ReceiverName != recv {
|
||||
return false
|
||||
}
|
||||
if spec.PackageName != "" {
|
||||
if spec.AbsolutePackage {
|
||||
if spec.PackageName != sym.PackageName() {
|
||||
return false
|
||||
}
|
||||
} else {
|
||||
if !partialPathMatch(spec.PackageName, sym.PackageName()) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
if spec.PackageOrReceiverName != "" && !partialPathMatch(spec.PackageOrReceiverName, sym.PackageName()) && spec.PackageOrReceiverName != recv {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func (loc *RegexLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
funcs := d.target.Funcs()
|
||||
matches, err := regexFilterFuncs(loc.FuncRegex, funcs)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
r := make([]api.Location, 0, len(matches))
|
||||
for i := range matches {
|
||||
addr, err := d.target.FindFunctionLocation(matches[i], true, 0)
|
||||
if err == nil {
|
||||
r = append(r, api.Location{PC: addr})
|
||||
}
|
||||
}
|
||||
return r, nil
|
||||
}
|
||||
|
||||
func (loc *AddrLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
if scope == nil {
|
||||
addr, err := strconv.ParseInt(loc.AddrExpr, 0, 64)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not determine current location (scope is nil)")
|
||||
}
|
||||
return []api.Location{{PC: uint64(addr)}}, nil
|
||||
} else {
|
||||
v, err := scope.EvalExpression(loc.AddrExpr, proc.LoadConfig{true, 0, 0, 0, 0})
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if v.Unreadable != nil {
|
||||
return nil, v.Unreadable
|
||||
}
|
||||
switch v.Kind {
|
||||
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uintptr:
|
||||
addr, _ := constant.Uint64Val(v.Value)
|
||||
return []api.Location{{PC: addr}}, nil
|
||||
case reflect.Func:
|
||||
_, _, fn := d.target.PCToLine(uint64(v.Base))
|
||||
pc, err := d.target.FirstPCAfterPrologue(fn, false)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []api.Location{{PC: uint64(pc)}}, nil
|
||||
default:
|
||||
return nil, fmt.Errorf("wrong expression kind: %v", v.Kind)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func (loc *NormalLocationSpec) FileMatch(path string) bool {
|
||||
return partialPathMatch(loc.Base, path)
|
||||
}
|
||||
|
||||
func partialPathMatch(expr, path string) bool {
|
||||
if runtime.GOOS == "windows" {
|
||||
// Accept `expr` which is case-insensitive and slash-insensitive match to `path`
|
||||
expr = strings.ToLower(filepath.ToSlash(expr))
|
||||
path = strings.ToLower(filepath.ToSlash(path))
|
||||
}
|
||||
if len(expr) < len(path)-1 {
|
||||
return strings.HasSuffix(path, expr) && (path[len(path)-len(expr)-1] == '/')
|
||||
} else {
|
||||
return expr == path
|
||||
}
|
||||
}
|
||||
|
||||
type AmbiguousLocationError struct {
|
||||
Location string
|
||||
CandidatesString []string
|
||||
CandidatesLocation []api.Location
|
||||
}
|
||||
|
||||
func (ale AmbiguousLocationError) Error() string {
|
||||
var candidates []string
|
||||
if ale.CandidatesLocation != nil {
|
||||
for i := range ale.CandidatesLocation {
|
||||
candidates = append(candidates, ale.CandidatesLocation[i].Function.Name)
|
||||
}
|
||||
|
||||
} else {
|
||||
candidates = ale.CandidatesString
|
||||
}
|
||||
return fmt.Sprintf("Location \"%s\" ambiguous: %s…", ale.Location, strings.Join(candidates, ", "))
|
||||
}
|
||||
|
||||
func (loc *NormalLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
funcs := d.target.Funcs()
|
||||
files := d.target.Sources()
|
||||
|
||||
candidates := []string{}
|
||||
for file := range files {
|
||||
if loc.FileMatch(file) {
|
||||
candidates = append(candidates, file)
|
||||
if len(candidates) >= maxFindLocationCandidates {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if loc.FuncBase != nil {
|
||||
for _, f := range funcs {
|
||||
if f.Sym == nil {
|
||||
continue
|
||||
}
|
||||
if loc.FuncBase.Match(f.Sym) {
|
||||
if loc.Base == f.Name {
|
||||
// if an exact match for the function name is found use it
|
||||
candidates = []string{f.Name}
|
||||
break
|
||||
}
|
||||
if len(candidates) < maxFindLocationCandidates {
|
||||
candidates = append(candidates, f.Name)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
switch len(candidates) {
|
||||
case 1:
|
||||
var addr uint64
|
||||
var err error
|
||||
if filepath.IsAbs(candidates[0]) {
|
||||
if loc.LineOffset < 0 {
|
||||
return nil, fmt.Errorf("Malformed breakpoint location, no line offset specified")
|
||||
}
|
||||
addr, err = d.target.FindFileLocation(candidates[0], loc.LineOffset)
|
||||
} else {
|
||||
if loc.LineOffset < 0 {
|
||||
addr, err = d.target.FindFunctionLocation(candidates[0], true, 0)
|
||||
} else {
|
||||
addr, err = d.target.FindFunctionLocation(candidates[0], false, loc.LineOffset)
|
||||
}
|
||||
}
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return []api.Location{{PC: addr}}, nil
|
||||
|
||||
case 0:
|
||||
return nil, fmt.Errorf("Location \"%s\" not found", locStr)
|
||||
default:
|
||||
return nil, AmbiguousLocationError{Location: locStr, CandidatesString: candidates}
|
||||
}
|
||||
}
|
||||
|
||||
func (loc *OffsetLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
if scope == nil {
|
||||
return nil, fmt.Errorf("could not determine current location (scope is nil)")
|
||||
}
|
||||
file, line, fn := d.target.PCToLine(scope.PC)
|
||||
if fn == nil {
|
||||
return nil, fmt.Errorf("could not determine current location")
|
||||
}
|
||||
addr, err := d.target.FindFileLocation(file, line+loc.Offset)
|
||||
return []api.Location{{PC: addr}}, err
|
||||
}
|
||||
|
||||
func (loc *LineLocationSpec) Find(d *Debugger, scope *proc.EvalScope, locStr string) ([]api.Location, error) {
|
||||
if scope == nil {
|
||||
return nil, fmt.Errorf("could not determine current location (scope is nil)")
|
||||
}
|
||||
file, _, fn := d.target.PCToLine(scope.PC)
|
||||
if fn == nil {
|
||||
return nil, fmt.Errorf("could not determine current location")
|
||||
}
|
||||
addr, err := d.target.FindFileLocation(file, loc.Line)
|
||||
return []api.Location{{PC: addr}}, err
|
||||
}
|
312
vendor/github.com/derekparker/delve/service/rpc1/client.go
generated
vendored
Normal file
312
vendor/github.com/derekparker/delve/service/rpc1/client.go
generated
vendored
Normal file
@ -0,0 +1,312 @@
|
||||
package rpc1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"log"
|
||||
"net/rpc"
|
||||
"net/rpc/jsonrpc"
|
||||
|
||||
"sync"
|
||||
|
||||
"github.com/derekparker/delve/service/api"
|
||||
)
|
||||
|
||||
// Client is a RPC service.Client.
|
||||
type RPCClient struct {
|
||||
addr string
|
||||
processPid int
|
||||
client *rpc.Client
|
||||
haltMu sync.Mutex
|
||||
haltReq bool
|
||||
}
|
||||
|
||||
var unsupportedApiError = errors.New("unsupported")
|
||||
|
||||
// NewClient creates a new RPCClient.
|
||||
func NewClient(addr string) *RPCClient {
|
||||
client, err := jsonrpc.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
log.Fatal("dialing:", err)
|
||||
}
|
||||
return &RPCClient{
|
||||
addr: addr,
|
||||
client: client,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *RPCClient) ProcessPid() int {
|
||||
var pid int
|
||||
c.call("ProcessPid", nil, &pid)
|
||||
return pid
|
||||
}
|
||||
|
||||
func (c *RPCClient) Detach(kill bool) error {
|
||||
return c.call("Detach", kill, nil)
|
||||
}
|
||||
|
||||
func (c *RPCClient) Restart() error {
|
||||
return c.call("Restart", nil, nil)
|
||||
}
|
||||
|
||||
func (c *RPCClient) GetState() (*api.DebuggerState, error) {
|
||||
state := new(api.DebuggerState)
|
||||
err := c.call("State", nil, state)
|
||||
return state, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) Continue() <-chan *api.DebuggerState {
|
||||
ch := make(chan *api.DebuggerState)
|
||||
c.haltMu.Lock()
|
||||
c.haltReq = false
|
||||
c.haltMu.Unlock()
|
||||
go func() {
|
||||
for {
|
||||
c.haltMu.Lock()
|
||||
if c.haltReq {
|
||||
c.haltMu.Unlock()
|
||||
close(ch)
|
||||
return
|
||||
}
|
||||
c.haltMu.Unlock()
|
||||
state := new(api.DebuggerState)
|
||||
err := c.call("Command", &api.DebuggerCommand{Name: api.Continue}, state)
|
||||
if err != nil {
|
||||
state.Err = err
|
||||
}
|
||||
if state.Exited {
|
||||
// Error types apparently cannot be marshalled by Go correctly. Must reset error here.
|
||||
state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus)
|
||||
}
|
||||
ch <- state
|
||||
if err != nil || state.Exited {
|
||||
close(ch)
|
||||
return
|
||||
}
|
||||
|
||||
isbreakpoint := false
|
||||
istracepoint := true
|
||||
for i := range state.Threads {
|
||||
if state.Threads[i].Breakpoint != nil {
|
||||
isbreakpoint = true
|
||||
istracepoint = istracepoint && state.Threads[i].Breakpoint.Tracepoint
|
||||
}
|
||||
}
|
||||
|
||||
if !isbreakpoint || !istracepoint {
|
||||
close(ch)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func (c *RPCClient) Next() (*api.DebuggerState, error) {
|
||||
state := new(api.DebuggerState)
|
||||
err := c.call("Command", &api.DebuggerCommand{Name: api.Next}, state)
|
||||
return state, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) Step() (*api.DebuggerState, error) {
|
||||
state := new(api.DebuggerState)
|
||||
err := c.call("Command", &api.DebuggerCommand{Name: api.Step}, state)
|
||||
return state, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) StepInstruction() (*api.DebuggerState, error) {
|
||||
state := new(api.DebuggerState)
|
||||
err := c.call("Command", &api.DebuggerCommand{Name: api.StepInstruction}, state)
|
||||
return state, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) {
|
||||
state := new(api.DebuggerState)
|
||||
cmd := &api.DebuggerCommand{
|
||||
Name: api.SwitchThread,
|
||||
ThreadID: threadID,
|
||||
}
|
||||
err := c.call("Command", cmd, state)
|
||||
return state, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) SwitchGoroutine(goroutineID int) (*api.DebuggerState, error) {
|
||||
state := new(api.DebuggerState)
|
||||
cmd := &api.DebuggerCommand{
|
||||
Name: api.SwitchGoroutine,
|
||||
GoroutineID: goroutineID,
|
||||
}
|
||||
err := c.call("Command", cmd, state)
|
||||
return state, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) Halt() (*api.DebuggerState, error) {
|
||||
state := new(api.DebuggerState)
|
||||
c.haltMu.Lock()
|
||||
c.haltReq = true
|
||||
c.haltMu.Unlock()
|
||||
err := c.call("Command", &api.DebuggerCommand{Name: api.Halt}, state)
|
||||
return state, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) GetBreakpoint(id int) (*api.Breakpoint, error) {
|
||||
breakpoint := new(api.Breakpoint)
|
||||
err := c.call("GetBreakpoint", id, breakpoint)
|
||||
return breakpoint, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) GetBreakpointByName(name string) (*api.Breakpoint, error) {
|
||||
breakpoint := new(api.Breakpoint)
|
||||
err := c.call("GetBreakpointByName", name, breakpoint)
|
||||
return breakpoint, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) CreateBreakpoint(breakPoint *api.Breakpoint) (*api.Breakpoint, error) {
|
||||
newBreakpoint := new(api.Breakpoint)
|
||||
err := c.call("CreateBreakpoint", breakPoint, &newBreakpoint)
|
||||
return newBreakpoint, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListBreakpoints() ([]*api.Breakpoint, error) {
|
||||
var breakpoints []*api.Breakpoint
|
||||
err := c.call("ListBreakpoints", nil, &breakpoints)
|
||||
return breakpoints, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ClearBreakpoint(id int) (*api.Breakpoint, error) {
|
||||
bp := new(api.Breakpoint)
|
||||
err := c.call("ClearBreakpoint", id, bp)
|
||||
return bp, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ClearBreakpointByName(name string) (*api.Breakpoint, error) {
|
||||
bp := new(api.Breakpoint)
|
||||
err := c.call("ClearBreakpointByName", name, bp)
|
||||
return bp, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) AmendBreakpoint(bp *api.Breakpoint) error {
|
||||
err := c.call("AmendBreakpoint", bp, nil)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *RPCClient) CancelNext() error {
|
||||
return unsupportedApiError
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListThreads() ([]*api.Thread, error) {
|
||||
var threads []*api.Thread
|
||||
err := c.call("ListThreads", nil, &threads)
|
||||
return threads, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) GetThread(id int) (*api.Thread, error) {
|
||||
thread := new(api.Thread)
|
||||
err := c.call("GetThread", id, &thread)
|
||||
return thread, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) EvalVariable(scope api.EvalScope, symbol string) (*api.Variable, error) {
|
||||
v := new(api.Variable)
|
||||
err := c.call("EvalSymbol", EvalSymbolArgs{scope, symbol}, v)
|
||||
return v, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error {
|
||||
var unused int
|
||||
return c.call("SetSymbol", SetSymbolArgs{scope, symbol, value}, &unused)
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListSources(filter string) ([]string, error) {
|
||||
var sources []string
|
||||
err := c.call("ListSources", filter, &sources)
|
||||
return sources, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListFunctions(filter string) ([]string, error) {
|
||||
var funcs []string
|
||||
err := c.call("ListFunctions", filter, &funcs)
|
||||
return funcs, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListTypes(filter string) ([]string, error) {
|
||||
var types []string
|
||||
err := c.call("ListTypes", filter, &types)
|
||||
return types, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListPackageVariables(filter string) ([]api.Variable, error) {
|
||||
var vars []api.Variable
|
||||
err := c.call("ListPackageVars", filter, &vars)
|
||||
return vars, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListPackageVariablesFor(threadID int, filter string) ([]api.Variable, error) {
|
||||
var vars []api.Variable
|
||||
err := c.call("ListThreadPackageVars", &ThreadListArgs{Id: threadID, Filter: filter}, &vars)
|
||||
return vars, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListLocalVariables(scope api.EvalScope) ([]api.Variable, error) {
|
||||
var vars []api.Variable
|
||||
err := c.call("ListLocalVars", scope, &vars)
|
||||
return vars, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListRegisters() (string, error) {
|
||||
var regs string
|
||||
err := c.call("ListRegisters", nil, ®s)
|
||||
return regs, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListFunctionArgs(scope api.EvalScope) ([]api.Variable, error) {
|
||||
var vars []api.Variable
|
||||
err := c.call("ListFunctionArgs", scope, &vars)
|
||||
return vars, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListGoroutines() ([]*api.Goroutine, error) {
|
||||
var goroutines []*api.Goroutine
|
||||
err := c.call("ListGoroutines", nil, &goroutines)
|
||||
return goroutines, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) Stacktrace(goroutineId, depth int, full bool) ([]api.Stackframe, error) {
|
||||
var locations []api.Stackframe
|
||||
err := c.call("StacktraceGoroutine", &StacktraceGoroutineArgs{Id: goroutineId, Depth: depth, Full: full}, &locations)
|
||||
return locations, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) AttachedToExistingProcess() bool {
|
||||
var answer bool
|
||||
c.call("AttachedToExistingProcess", nil, &answer)
|
||||
return answer
|
||||
}
|
||||
|
||||
func (c *RPCClient) FindLocation(scope api.EvalScope, loc string) ([]api.Location, error) {
|
||||
var answer []api.Location
|
||||
err := c.call("FindLocation", FindLocationArgs{scope, loc}, &answer)
|
||||
return answer, err
|
||||
}
|
||||
|
||||
// Disassemble code between startPC and endPC
|
||||
func (c *RPCClient) DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
|
||||
var r api.AsmInstructions
|
||||
err := c.call("Disassemble", DisassembleRequest{scope, startPC, endPC, flavour}, &r)
|
||||
return r, err
|
||||
}
|
||||
|
||||
// Disassemble function containing pc
|
||||
func (c *RPCClient) DisassemblePC(scope api.EvalScope, pc uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
|
||||
var r api.AsmInstructions
|
||||
err := c.call("Disassemble", DisassembleRequest{scope, pc, 0, flavour}, &r)
|
||||
return r, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) url(path string) string {
|
||||
return fmt.Sprintf("http://%s%s", c.addr, path)
|
||||
}
|
||||
|
||||
func (c *RPCClient) call(method string, args, reply interface{}) error {
|
||||
return c.client.Call("RPCServer."+method, args, reply)
|
||||
}
|
5
vendor/github.com/derekparker/delve/service/rpc1/readme.txtr
generated
vendored
Normal file
5
vendor/github.com/derekparker/delve/service/rpc1/readme.txtr
generated
vendored
Normal file
@ -0,0 +1,5 @@
|
||||
This package implements version 1 of Delve's API and is only
|
||||
kept here for backwards compatibility. Client.go is the old
|
||||
client code used by Delve's frontend (delve/cmd/dlv), it is
|
||||
only preserved here for the backwards compatibility tests in
|
||||
service/test/integration1_test.go.
|
318
vendor/github.com/derekparker/delve/service/rpc1/server.go
generated
vendored
Normal file
318
vendor/github.com/derekparker/delve/service/rpc1/server.go
generated
vendored
Normal file
@ -0,0 +1,318 @@
|
||||
package rpc1
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
|
||||
"github.com/derekparker/delve/pkg/proc"
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
"github.com/derekparker/delve/service/debugger"
|
||||
)
|
||||
|
||||
var defaultLoadConfig = proc.LoadConfig{true, 1, 64, 64, -1}
|
||||
|
||||
type RPCServer struct {
|
||||
// config is all the information necessary to start the debugger and server.
|
||||
config *service.Config
|
||||
// debugger is a debugger service.
|
||||
debugger *debugger.Debugger
|
||||
}
|
||||
|
||||
func NewServer(config *service.Config, debugger *debugger.Debugger) *RPCServer {
|
||||
return &RPCServer{config, debugger}
|
||||
}
|
||||
|
||||
func (s *RPCServer) ProcessPid(arg1 interface{}, pid *int) error {
|
||||
*pid = s.debugger.ProcessPid()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) Detach(kill bool, ret *int) error {
|
||||
return s.debugger.Detach(kill)
|
||||
}
|
||||
|
||||
func (s *RPCServer) Restart(arg1 interface{}, arg2 *int) error {
|
||||
if s.config.AttachPid != 0 {
|
||||
return errors.New("cannot restart process Delve did not create")
|
||||
}
|
||||
_, err := s.debugger.Restart()
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) State(arg interface{}, state *api.DebuggerState) error {
|
||||
st, err := s.debugger.State()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*state = *st
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) Command(command *api.DebuggerCommand, cb service.RPCCallback) {
|
||||
st, err := s.debugger.Command(command)
|
||||
cb.Return(st, err)
|
||||
}
|
||||
|
||||
func (s *RPCServer) GetBreakpoint(id int, breakpoint *api.Breakpoint) error {
|
||||
bp := s.debugger.FindBreakpoint(id)
|
||||
if bp == nil {
|
||||
return fmt.Errorf("no breakpoint with id %d", id)
|
||||
}
|
||||
*breakpoint = *bp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) GetBreakpointByName(name string, breakpoint *api.Breakpoint) error {
|
||||
bp := s.debugger.FindBreakpointByName(name)
|
||||
if bp == nil {
|
||||
return fmt.Errorf("no breakpoint with name %s", name)
|
||||
}
|
||||
*breakpoint = *bp
|
||||
return nil
|
||||
}
|
||||
|
||||
type StacktraceGoroutineArgs struct {
|
||||
Id int
|
||||
Depth int
|
||||
Full bool
|
||||
}
|
||||
|
||||
func (s *RPCServer) StacktraceGoroutine(args *StacktraceGoroutineArgs, locations *[]api.Stackframe) error {
|
||||
var loadcfg *proc.LoadConfig = nil
|
||||
if args.Full {
|
||||
loadcfg = &defaultLoadConfig
|
||||
}
|
||||
locs, err := s.debugger.Stacktrace(args.Id, args.Depth, loadcfg)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*locations = locs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListBreakpoints(arg interface{}, breakpoints *[]*api.Breakpoint) error {
|
||||
*breakpoints = s.debugger.Breakpoints()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) CreateBreakpoint(bp, newBreakpoint *api.Breakpoint) error {
|
||||
createdbp, err := s.debugger.CreateBreakpoint(bp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*newBreakpoint = *createdbp
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) ClearBreakpoint(id int, breakpoint *api.Breakpoint) error {
|
||||
bp := s.debugger.FindBreakpoint(id)
|
||||
if bp == nil {
|
||||
return fmt.Errorf("no breakpoint with id %d", id)
|
||||
}
|
||||
deleted, err := s.debugger.ClearBreakpoint(bp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*breakpoint = *deleted
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) ClearBreakpointByName(name string, breakpoint *api.Breakpoint) error {
|
||||
bp := s.debugger.FindBreakpointByName(name)
|
||||
if bp == nil {
|
||||
return fmt.Errorf("no breakpoint with name %s", name)
|
||||
}
|
||||
deleted, err := s.debugger.ClearBreakpoint(bp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*breakpoint = *deleted
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) AmendBreakpoint(amend *api.Breakpoint, unused *int) error {
|
||||
*unused = 0
|
||||
return s.debugger.AmendBreakpoint(amend)
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListThreads(arg interface{}, threads *[]*api.Thread) (err error) {
|
||||
*threads, err = s.debugger.Threads()
|
||||
return err
|
||||
}
|
||||
|
||||
func (s *RPCServer) GetThread(id int, thread *api.Thread) error {
|
||||
t, err := s.debugger.FindThread(id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t == nil {
|
||||
return fmt.Errorf("no thread with id %d", id)
|
||||
}
|
||||
*thread = *t
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListPackageVars(filter string, variables *[]api.Variable) error {
|
||||
state, err := s.debugger.State()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
current := state.CurrentThread
|
||||
if current == nil {
|
||||
return fmt.Errorf("no current thread")
|
||||
}
|
||||
|
||||
vars, err := s.debugger.PackageVariables(current.ID, filter, defaultLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*variables = vars
|
||||
return nil
|
||||
}
|
||||
|
||||
type ThreadListArgs struct {
|
||||
Id int
|
||||
Filter string
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListThreadPackageVars(args *ThreadListArgs, variables *[]api.Variable) error {
|
||||
thread, err := s.debugger.FindThread(args.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if thread == nil {
|
||||
return fmt.Errorf("no thread with id %d", args.Id)
|
||||
}
|
||||
|
||||
vars, err := s.debugger.PackageVariables(args.Id, args.Filter, defaultLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*variables = vars
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListRegisters(arg interface{}, registers *string) error {
|
||||
state, err := s.debugger.State()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
regs, err := s.debugger.Registers(state.CurrentThread.ID, false)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*registers = regs.String()
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListLocalVars(scope api.EvalScope, variables *[]api.Variable) error {
|
||||
vars, err := s.debugger.LocalVariables(scope, defaultLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*variables = vars
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListFunctionArgs(scope api.EvalScope, variables *[]api.Variable) error {
|
||||
vars, err := s.debugger.FunctionArguments(scope, defaultLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*variables = vars
|
||||
return nil
|
||||
}
|
||||
|
||||
type EvalSymbolArgs struct {
|
||||
Scope api.EvalScope
|
||||
Symbol string
|
||||
}
|
||||
|
||||
func (s *RPCServer) EvalSymbol(args EvalSymbolArgs, variable *api.Variable) error {
|
||||
v, err := s.debugger.EvalVariableInScope(args.Scope, args.Symbol, defaultLoadConfig)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*variable = *v
|
||||
return nil
|
||||
}
|
||||
|
||||
type SetSymbolArgs struct {
|
||||
Scope api.EvalScope
|
||||
Symbol string
|
||||
Value string
|
||||
}
|
||||
|
||||
func (s *RPCServer) SetSymbol(args SetSymbolArgs, unused *int) error {
|
||||
*unused = 0
|
||||
return s.debugger.SetVariableInScope(args.Scope, args.Symbol, args.Value)
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListSources(filter string, sources *[]string) error {
|
||||
ss, err := s.debugger.Sources(filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*sources = ss
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListFunctions(filter string, funcs *[]string) error {
|
||||
fns, err := s.debugger.Functions(filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*funcs = fns
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListTypes(filter string, types *[]string) error {
|
||||
tps, err := s.debugger.Types(filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*types = tps
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *RPCServer) ListGoroutines(arg interface{}, goroutines *[]*api.Goroutine) error {
|
||||
gs, err := s.debugger.Goroutines()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
*goroutines = gs
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *RPCServer) AttachedToExistingProcess(arg interface{}, answer *bool) error {
|
||||
if c.config.AttachPid != 0 {
|
||||
*answer = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type FindLocationArgs struct {
|
||||
Scope api.EvalScope
|
||||
Loc string
|
||||
}
|
||||
|
||||
func (c *RPCServer) FindLocation(args FindLocationArgs, answer *[]api.Location) error {
|
||||
var err error
|
||||
*answer, err = c.debugger.FindLocation(args.Scope, args.Loc)
|
||||
return err
|
||||
}
|
||||
|
||||
type DisassembleRequest struct {
|
||||
Scope api.EvalScope
|
||||
StartPC, EndPC uint64
|
||||
Flavour api.AssemblyFlavour
|
||||
}
|
||||
|
||||
func (c *RPCServer) Disassemble(args DisassembleRequest, answer *api.AsmInstructions) error {
|
||||
var err error
|
||||
*answer, err = c.debugger.Disassemble(args.Scope, args.StartPC, args.EndPC, args.Flavour)
|
||||
return err
|
||||
}
|
308
vendor/github.com/derekparker/delve/service/rpc2/client.go
generated
vendored
Normal file
308
vendor/github.com/derekparker/delve/service/rpc2/client.go
generated
vendored
Normal file
@ -0,0 +1,308 @@
|
||||
package rpc2
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"net/rpc"
|
||||
"net/rpc/jsonrpc"
|
||||
"time"
|
||||
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
)
|
||||
|
||||
// Client is a RPC service.Client.
|
||||
type RPCClient struct {
|
||||
addr string
|
||||
processPid int
|
||||
client *rpc.Client
|
||||
}
|
||||
|
||||
// Ensure the implementation satisfies the interface.
|
||||
var _ service.Client = &RPCClient{}
|
||||
|
||||
// NewClient creates a new RPCClient.
|
||||
func NewClient(addr string) *RPCClient {
|
||||
client, err := jsonrpc.Dial("tcp", addr)
|
||||
if err != nil {
|
||||
log.Fatal("dialing:", err)
|
||||
}
|
||||
c := &RPCClient{addr: addr, client: client}
|
||||
c.call("SetApiVersion", api.SetAPIVersionIn{2}, &api.SetAPIVersionOut{})
|
||||
return c
|
||||
}
|
||||
|
||||
func (c *RPCClient) ProcessPid() int {
|
||||
out := new(ProcessPidOut)
|
||||
c.call("ProcessPid", ProcessPidIn{}, out)
|
||||
return out.Pid
|
||||
}
|
||||
|
||||
func (c *RPCClient) LastModified() time.Time {
|
||||
out := new(LastModifiedOut)
|
||||
c.call("LastModified", LastModifiedIn{}, out)
|
||||
return out.Time
|
||||
}
|
||||
|
||||
func (c *RPCClient) Detach(kill bool) error {
|
||||
out := new(DetachOut)
|
||||
return c.call("Detach", DetachIn{kill}, out)
|
||||
}
|
||||
|
||||
func (c *RPCClient) Restart() ([]api.DiscardedBreakpoint, error) {
|
||||
out := new(RestartOut)
|
||||
err := c.call("Restart", RestartIn{}, out)
|
||||
return out.DiscardedBreakpoints, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) GetState() (*api.DebuggerState, error) {
|
||||
var out StateOut
|
||||
err := c.call("State", StateIn{}, &out)
|
||||
return out.State, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) Continue() <-chan *api.DebuggerState {
|
||||
ch := make(chan *api.DebuggerState)
|
||||
go func() {
|
||||
for {
|
||||
out := new(CommandOut)
|
||||
err := c.call("Command", &api.DebuggerCommand{Name: api.Continue}, &out)
|
||||
state := out.State
|
||||
if err != nil {
|
||||
state.Err = err
|
||||
}
|
||||
if state.Exited {
|
||||
// Error types apparantly cannot be marshalled by Go correctly. Must reset error here.
|
||||
state.Err = fmt.Errorf("Process %d has exited with status %d", c.ProcessPid(), state.ExitStatus)
|
||||
}
|
||||
ch <- &state
|
||||
if err != nil || state.Exited {
|
||||
close(ch)
|
||||
return
|
||||
}
|
||||
|
||||
isbreakpoint := false
|
||||
istracepoint := true
|
||||
for i := range state.Threads {
|
||||
if state.Threads[i].Breakpoint != nil {
|
||||
isbreakpoint = true
|
||||
istracepoint = istracepoint && state.Threads[i].Breakpoint.Tracepoint
|
||||
}
|
||||
}
|
||||
|
||||
if !isbreakpoint || !istracepoint {
|
||||
close(ch)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
return ch
|
||||
}
|
||||
|
||||
func (c *RPCClient) Next() (*api.DebuggerState, error) {
|
||||
var out CommandOut
|
||||
err := c.call("Command", api.DebuggerCommand{Name: api.Next}, &out)
|
||||
return &out.State, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) Step() (*api.DebuggerState, error) {
|
||||
var out CommandOut
|
||||
err := c.call("Command", api.DebuggerCommand{Name: api.Step}, &out)
|
||||
return &out.State, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) StepOut() (*api.DebuggerState, error) {
|
||||
var out CommandOut
|
||||
err := c.call("Command", &api.DebuggerCommand{Name: api.StepOut}, &out)
|
||||
return &out.State, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) StepInstruction() (*api.DebuggerState, error) {
|
||||
var out CommandOut
|
||||
err := c.call("Command", api.DebuggerCommand{Name: api.StepInstruction}, &out)
|
||||
return &out.State, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) SwitchThread(threadID int) (*api.DebuggerState, error) {
|
||||
var out CommandOut
|
||||
cmd := api.DebuggerCommand{
|
||||
Name: api.SwitchThread,
|
||||
ThreadID: threadID,
|
||||
}
|
||||
err := c.call("Command", cmd, &out)
|
||||
return &out.State, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) SwitchGoroutine(goroutineID int) (*api.DebuggerState, error) {
|
||||
var out CommandOut
|
||||
cmd := api.DebuggerCommand{
|
||||
Name: api.SwitchGoroutine,
|
||||
GoroutineID: goroutineID,
|
||||
}
|
||||
err := c.call("Command", cmd, &out)
|
||||
return &out.State, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) Halt() (*api.DebuggerState, error) {
|
||||
var out CommandOut
|
||||
err := c.call("Command", api.DebuggerCommand{Name: api.Halt}, &out)
|
||||
return &out.State, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) GetBreakpoint(id int) (*api.Breakpoint, error) {
|
||||
var out GetBreakpointOut
|
||||
err := c.call("GetBreakpoint", GetBreakpointIn{id, ""}, &out)
|
||||
return &out.Breakpoint, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) GetBreakpointByName(name string) (*api.Breakpoint, error) {
|
||||
var out GetBreakpointOut
|
||||
err := c.call("GetBreakpoint", GetBreakpointIn{0, name}, &out)
|
||||
return &out.Breakpoint, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) CreateBreakpoint(breakPoint *api.Breakpoint) (*api.Breakpoint, error) {
|
||||
var out CreateBreakpointOut
|
||||
err := c.call("CreateBreakpoint", CreateBreakpointIn{*breakPoint}, &out)
|
||||
return &out.Breakpoint, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListBreakpoints() ([]*api.Breakpoint, error) {
|
||||
var out ListBreakpointsOut
|
||||
err := c.call("ListBreakpoints", ListBreakpointsIn{}, &out)
|
||||
return out.Breakpoints, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ClearBreakpoint(id int) (*api.Breakpoint, error) {
|
||||
var out ClearBreakpointOut
|
||||
err := c.call("ClearBreakpoint", ClearBreakpointIn{id, ""}, &out)
|
||||
return out.Breakpoint, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ClearBreakpointByName(name string) (*api.Breakpoint, error) {
|
||||
var out ClearBreakpointOut
|
||||
err := c.call("ClearBreakpoint", ClearBreakpointIn{0, name}, &out)
|
||||
return out.Breakpoint, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) AmendBreakpoint(bp *api.Breakpoint) error {
|
||||
out := new(AmendBreakpointOut)
|
||||
err := c.call("AmendBreakpoint", AmendBreakpointIn{*bp}, out)
|
||||
return err
|
||||
}
|
||||
|
||||
func (c *RPCClient) CancelNext() error {
|
||||
var out CancelNextOut
|
||||
return c.call("CancelNext", CancelNextIn{}, &out)
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListThreads() ([]*api.Thread, error) {
|
||||
var out ListThreadsOut
|
||||
err := c.call("ListThreads", ListThreadsIn{}, &out)
|
||||
return out.Threads, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) GetThread(id int) (*api.Thread, error) {
|
||||
var out GetThreadOut
|
||||
err := c.call("GetThread", GetThreadIn{id}, &out)
|
||||
return out.Thread, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) EvalVariable(scope api.EvalScope, expr string, cfg api.LoadConfig) (*api.Variable, error) {
|
||||
var out EvalOut
|
||||
err := c.call("Eval", EvalIn{scope, expr, &cfg}, &out)
|
||||
return out.Variable, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) SetVariable(scope api.EvalScope, symbol, value string) error {
|
||||
out := new(SetOut)
|
||||
return c.call("Set", SetIn{scope, symbol, value}, out)
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListSources(filter string) ([]string, error) {
|
||||
sources := new(ListSourcesOut)
|
||||
err := c.call("ListSources", ListSourcesIn{filter}, sources)
|
||||
return sources.Sources, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListFunctions(filter string) ([]string, error) {
|
||||
funcs := new(ListFunctionsOut)
|
||||
err := c.call("ListFunctions", ListFunctionsIn{filter}, funcs)
|
||||
return funcs.Funcs, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListTypes(filter string) ([]string, error) {
|
||||
types := new(ListTypesOut)
|
||||
err := c.call("ListTypes", ListTypesIn{filter}, types)
|
||||
return types.Types, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListPackageVariables(filter string, cfg api.LoadConfig) ([]api.Variable, error) {
|
||||
var out ListPackageVarsOut
|
||||
err := c.call("ListPackageVars", ListPackageVarsIn{filter, cfg}, &out)
|
||||
return out.Variables, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListLocalVariables(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) {
|
||||
var out ListLocalVarsOut
|
||||
err := c.call("ListLocalVars", ListLocalVarsIn{scope, cfg}, &out)
|
||||
return out.Variables, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListRegisters(threadID int, includeFp bool) (api.Registers, error) {
|
||||
out := new(ListRegistersOut)
|
||||
err := c.call("ListRegisters", ListRegistersIn{ThreadID: threadID, IncludeFp: includeFp}, out)
|
||||
return out.Regs, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListFunctionArgs(scope api.EvalScope, cfg api.LoadConfig) ([]api.Variable, error) {
|
||||
var out ListFunctionArgsOut
|
||||
err := c.call("ListFunctionArgs", ListFunctionArgsIn{scope, cfg}, &out)
|
||||
return out.Args, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) ListGoroutines() ([]*api.Goroutine, error) {
|
||||
var out ListGoroutinesOut
|
||||
err := c.call("ListGoroutines", ListGoroutinesIn{}, &out)
|
||||
return out.Goroutines, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) Stacktrace(goroutineId, depth int, cfg *api.LoadConfig) ([]api.Stackframe, error) {
|
||||
var out StacktraceOut
|
||||
err := c.call("Stacktrace", StacktraceIn{goroutineId, depth, false, cfg}, &out)
|
||||
return out.Locations, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) AttachedToExistingProcess() bool {
|
||||
out := new(AttachedToExistingProcessOut)
|
||||
c.call("AttachedToExistingProcess", AttachedToExistingProcessIn{}, out)
|
||||
return out.Answer
|
||||
}
|
||||
|
||||
func (c *RPCClient) FindLocation(scope api.EvalScope, loc string) ([]api.Location, error) {
|
||||
var out FindLocationOut
|
||||
err := c.call("FindLocation", FindLocationIn{scope, loc}, &out)
|
||||
return out.Locations, err
|
||||
}
|
||||
|
||||
// Disassemble code between startPC and endPC
|
||||
func (c *RPCClient) DisassembleRange(scope api.EvalScope, startPC, endPC uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
|
||||
var out DisassembleOut
|
||||
err := c.call("Disassemble", DisassembleIn{scope, startPC, endPC, flavour}, &out)
|
||||
return out.Disassemble, err
|
||||
}
|
||||
|
||||
// Disassemble function containing pc
|
||||
func (c *RPCClient) DisassemblePC(scope api.EvalScope, pc uint64, flavour api.AssemblyFlavour) (api.AsmInstructions, error) {
|
||||
var out DisassembleOut
|
||||
err := c.call("Disassemble", DisassembleIn{scope, pc, 0, flavour}, &out)
|
||||
return out.Disassemble, err
|
||||
}
|
||||
|
||||
func (c *RPCClient) url(path string) string {
|
||||
return fmt.Sprintf("http://%s%s", c.addr, path)
|
||||
}
|
||||
|
||||
func (c *RPCClient) call(method string, args, reply interface{}) error {
|
||||
return c.client.Call("RPCServer."+method, args, reply)
|
||||
}
|
575
vendor/github.com/derekparker/delve/service/rpc2/server.go
generated
vendored
Normal file
575
vendor/github.com/derekparker/delve/service/rpc2/server.go
generated
vendored
Normal file
@ -0,0 +1,575 @@
|
||||
package rpc2
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
"github.com/derekparker/delve/service/debugger"
|
||||
)
|
||||
|
||||
type RPCServer struct {
|
||||
// config is all the information necessary to start the debugger and server.
|
||||
config *service.Config
|
||||
// debugger is a debugger service.
|
||||
debugger *debugger.Debugger
|
||||
}
|
||||
|
||||
func NewServer(config *service.Config, debugger *debugger.Debugger) *RPCServer {
|
||||
return &RPCServer{config, debugger}
|
||||
}
|
||||
|
||||
type ProcessPidIn struct {
|
||||
}
|
||||
|
||||
type ProcessPidOut struct {
|
||||
Pid int
|
||||
}
|
||||
|
||||
// ProcessPid returns the pid of the process we are debugging.
|
||||
func (s *RPCServer) ProcessPid(arg ProcessPidIn, out *ProcessPidOut) error {
|
||||
out.Pid = s.debugger.ProcessPid()
|
||||
return nil
|
||||
}
|
||||
|
||||
type LastModifiedIn struct {
|
||||
}
|
||||
|
||||
type LastModifiedOut struct {
|
||||
Time time.Time
|
||||
}
|
||||
|
||||
func (s *RPCServer) LastModified(arg LastModifiedIn, out *LastModifiedOut) error {
|
||||
out.Time = s.debugger.LastModified()
|
||||
return nil
|
||||
}
|
||||
|
||||
type DetachIn struct {
|
||||
Kill bool
|
||||
}
|
||||
|
||||
type DetachOut struct {
|
||||
}
|
||||
|
||||
// Detach detaches the debugger, optionally killing the process.
|
||||
func (s *RPCServer) Detach(arg DetachIn, out *DetachOut) error {
|
||||
return s.debugger.Detach(arg.Kill)
|
||||
}
|
||||
|
||||
type RestartIn struct {
|
||||
}
|
||||
|
||||
type RestartOut struct {
|
||||
DiscardedBreakpoints []api.DiscardedBreakpoint
|
||||
}
|
||||
|
||||
// Restart restarts program.
|
||||
func (s *RPCServer) Restart(arg RestartIn, out *RestartOut) error {
|
||||
if s.config.AttachPid != 0 {
|
||||
return errors.New("cannot restart process Delve did not create")
|
||||
}
|
||||
var err error
|
||||
out.DiscardedBreakpoints, err = s.debugger.Restart()
|
||||
return err
|
||||
}
|
||||
|
||||
type StateIn struct {
|
||||
}
|
||||
|
||||
type StateOut struct {
|
||||
State *api.DebuggerState
|
||||
}
|
||||
|
||||
// State returns the current debugger state.
|
||||
func (s *RPCServer) State(arg StateIn, out *StateOut) error {
|
||||
st, err := s.debugger.State()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.State = st
|
||||
return nil
|
||||
}
|
||||
|
||||
type CommandOut struct {
|
||||
State api.DebuggerState
|
||||
}
|
||||
|
||||
// Command interrupts, continues and steps through the program.
|
||||
func (s *RPCServer) Command(command api.DebuggerCommand, cb service.RPCCallback) {
|
||||
st, err := s.debugger.Command(&command)
|
||||
if err != nil {
|
||||
cb.Return(nil, err)
|
||||
return
|
||||
}
|
||||
var out CommandOut
|
||||
out.State = *st
|
||||
cb.Return(out, nil)
|
||||
return
|
||||
}
|
||||
|
||||
type GetBreakpointIn struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
|
||||
type GetBreakpointOut struct {
|
||||
Breakpoint api.Breakpoint
|
||||
}
|
||||
|
||||
// GetBreakpoint gets a breakpoint by Name (if Name is not an empty string) or by ID.
|
||||
func (s *RPCServer) GetBreakpoint(arg GetBreakpointIn, out *GetBreakpointOut) error {
|
||||
var bp *api.Breakpoint
|
||||
if arg.Name != "" {
|
||||
bp = s.debugger.FindBreakpointByName(arg.Name)
|
||||
if bp == nil {
|
||||
return fmt.Errorf("no breakpoint with name %s", arg.Name)
|
||||
}
|
||||
} else {
|
||||
bp = s.debugger.FindBreakpoint(arg.Id)
|
||||
if bp == nil {
|
||||
return fmt.Errorf("no breakpoint with id %d", arg.Id)
|
||||
}
|
||||
}
|
||||
out.Breakpoint = *bp
|
||||
return nil
|
||||
}
|
||||
|
||||
type StacktraceIn struct {
|
||||
Id int
|
||||
Depth int
|
||||
Full bool
|
||||
Cfg *api.LoadConfig
|
||||
}
|
||||
|
||||
type StacktraceOut struct {
|
||||
Locations []api.Stackframe
|
||||
}
|
||||
|
||||
// Stacktrace returns stacktrace of goroutine Id up to the specified Depth.
|
||||
//
|
||||
// If Full is set it will also the variable of all local variables
|
||||
// and function arguments of all stack frames.
|
||||
func (s *RPCServer) Stacktrace(arg StacktraceIn, out *StacktraceOut) error {
|
||||
cfg := arg.Cfg
|
||||
if cfg == nil && arg.Full {
|
||||
cfg = &api.LoadConfig{true, 1, 64, 64, -1}
|
||||
}
|
||||
locs, err := s.debugger.Stacktrace(arg.Id, arg.Depth, api.LoadConfigToProc(cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Locations = locs
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListBreakpointsIn struct {
|
||||
}
|
||||
|
||||
type ListBreakpointsOut struct {
|
||||
Breakpoints []*api.Breakpoint
|
||||
}
|
||||
|
||||
// ListBreakpoints gets all breakpoints.
|
||||
func (s *RPCServer) ListBreakpoints(arg ListBreakpointsIn, out *ListBreakpointsOut) error {
|
||||
out.Breakpoints = s.debugger.Breakpoints()
|
||||
return nil
|
||||
}
|
||||
|
||||
type CreateBreakpointIn struct {
|
||||
Breakpoint api.Breakpoint
|
||||
}
|
||||
|
||||
type CreateBreakpointOut struct {
|
||||
Breakpoint api.Breakpoint
|
||||
}
|
||||
|
||||
// CreateBreakpoint creates a new breakpoint.
|
||||
//
|
||||
// - If arg.Breakpoint.File is not an empty string the breakpoint
|
||||
// will be created on the specified file:line location
|
||||
//
|
||||
// - If arg.Breakpoint.FunctionName is not an empty string
|
||||
// the breakpoint will be created on the specified function:line
|
||||
// location. Note that setting a breakpoint on a function's entry point
|
||||
// (line == 0) can have surprising consequences, it is advisable to
|
||||
// use line = -1 instead which will skip the prologue.
|
||||
//
|
||||
// - Otherwise the value specified by arg.Breakpoint.Addr will be used.
|
||||
func (s *RPCServer) CreateBreakpoint(arg CreateBreakpointIn, out *CreateBreakpointOut) error {
|
||||
createdbp, err := s.debugger.CreateBreakpoint(&arg.Breakpoint)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Breakpoint = *createdbp
|
||||
return nil
|
||||
}
|
||||
|
||||
type ClearBreakpointIn struct {
|
||||
Id int
|
||||
Name string
|
||||
}
|
||||
|
||||
type ClearBreakpointOut struct {
|
||||
Breakpoint *api.Breakpoint
|
||||
}
|
||||
|
||||
// ClearBreakpoint deletes a breakpoint by Name (if Name is not an
|
||||
// empty string) or by ID.
|
||||
func (s *RPCServer) ClearBreakpoint(arg ClearBreakpointIn, out *ClearBreakpointOut) error {
|
||||
var bp *api.Breakpoint
|
||||
if arg.Name != "" {
|
||||
bp = s.debugger.FindBreakpointByName(arg.Name)
|
||||
if bp == nil {
|
||||
return fmt.Errorf("no breakpoint with name %s", arg.Name)
|
||||
}
|
||||
} else {
|
||||
bp = s.debugger.FindBreakpoint(arg.Id)
|
||||
if bp == nil {
|
||||
return fmt.Errorf("no breakpoint with id %d", arg.Id)
|
||||
}
|
||||
}
|
||||
deleted, err := s.debugger.ClearBreakpoint(bp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Breakpoint = deleted
|
||||
return nil
|
||||
}
|
||||
|
||||
type AmendBreakpointIn struct {
|
||||
Breakpoint api.Breakpoint
|
||||
}
|
||||
|
||||
type AmendBreakpointOut struct {
|
||||
}
|
||||
|
||||
// AmendBreakpoint allows user to update an existing breakpoint
|
||||
// for example to change the information retrieved when the
|
||||
// breakpoint is hit or to change, add or remove the break condition.
|
||||
//
|
||||
// arg.Breakpoint.ID must be a valid breakpoint ID
|
||||
func (s *RPCServer) AmendBreakpoint(arg AmendBreakpointIn, out *AmendBreakpointOut) error {
|
||||
return s.debugger.AmendBreakpoint(&arg.Breakpoint)
|
||||
}
|
||||
|
||||
type CancelNextIn struct {
|
||||
}
|
||||
|
||||
type CancelNextOut struct {
|
||||
}
|
||||
|
||||
func (s *RPCServer) CancelNext(arg CancelNextIn, out *CancelNextOut) error {
|
||||
return s.debugger.CancelNext()
|
||||
}
|
||||
|
||||
type ListThreadsIn struct {
|
||||
}
|
||||
|
||||
type ListThreadsOut struct {
|
||||
Threads []*api.Thread
|
||||
}
|
||||
|
||||
// ListThreads lists all threads.
|
||||
func (s *RPCServer) ListThreads(arg ListThreadsIn, out *ListThreadsOut) (err error) {
|
||||
out.Threads, err = s.debugger.Threads()
|
||||
return err
|
||||
}
|
||||
|
||||
type GetThreadIn struct {
|
||||
Id int
|
||||
}
|
||||
|
||||
type GetThreadOut struct {
|
||||
Thread *api.Thread
|
||||
}
|
||||
|
||||
// GetThread gets a thread by its ID.
|
||||
func (s *RPCServer) GetThread(arg GetThreadIn, out *GetThreadOut) error {
|
||||
t, err := s.debugger.FindThread(arg.Id)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if t == nil {
|
||||
return fmt.Errorf("no thread with id %d", arg.Id)
|
||||
}
|
||||
out.Thread = t
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListPackageVarsIn struct {
|
||||
Filter string
|
||||
Cfg api.LoadConfig
|
||||
}
|
||||
|
||||
type ListPackageVarsOut struct {
|
||||
Variables []api.Variable
|
||||
}
|
||||
|
||||
// ListPackageVars lists all package variables in the context of the current thread.
|
||||
func (s *RPCServer) ListPackageVars(arg ListPackageVarsIn, out *ListPackageVarsOut) error {
|
||||
state, err := s.debugger.State()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
current := state.CurrentThread
|
||||
if current == nil {
|
||||
return fmt.Errorf("no current thread")
|
||||
}
|
||||
|
||||
vars, err := s.debugger.PackageVariables(current.ID, arg.Filter, *api.LoadConfigToProc(&arg.Cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Variables = vars
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListRegistersIn struct {
|
||||
ThreadID int
|
||||
IncludeFp bool
|
||||
}
|
||||
|
||||
type ListRegistersOut struct {
|
||||
Registers string
|
||||
Regs api.Registers
|
||||
}
|
||||
|
||||
// ListRegisters lists registers and their values.
|
||||
func (s *RPCServer) ListRegisters(arg ListRegistersIn, out *ListRegistersOut) error {
|
||||
if arg.ThreadID == 0 {
|
||||
state, err := s.debugger.State()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
arg.ThreadID = state.CurrentThread.ID
|
||||
}
|
||||
|
||||
regs, err := s.debugger.Registers(arg.ThreadID, arg.IncludeFp)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Regs = regs
|
||||
out.Registers = out.Regs.String()
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListLocalVarsIn struct {
|
||||
Scope api.EvalScope
|
||||
Cfg api.LoadConfig
|
||||
}
|
||||
|
||||
type ListLocalVarsOut struct {
|
||||
Variables []api.Variable
|
||||
}
|
||||
|
||||
// ListLocalVars lists all local variables in scope.
|
||||
func (s *RPCServer) ListLocalVars(arg ListLocalVarsIn, out *ListLocalVarsOut) error {
|
||||
vars, err := s.debugger.LocalVariables(arg.Scope, *api.LoadConfigToProc(&arg.Cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Variables = vars
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListFunctionArgsIn struct {
|
||||
Scope api.EvalScope
|
||||
Cfg api.LoadConfig
|
||||
}
|
||||
|
||||
type ListFunctionArgsOut struct {
|
||||
Args []api.Variable
|
||||
}
|
||||
|
||||
// ListFunctionArgs lists all arguments to the current function
|
||||
func (s *RPCServer) ListFunctionArgs(arg ListFunctionArgsIn, out *ListFunctionArgsOut) error {
|
||||
vars, err := s.debugger.FunctionArguments(arg.Scope, *api.LoadConfigToProc(&arg.Cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Args = vars
|
||||
return nil
|
||||
}
|
||||
|
||||
type EvalIn struct {
|
||||
Scope api.EvalScope
|
||||
Expr string
|
||||
Cfg *api.LoadConfig
|
||||
}
|
||||
|
||||
type EvalOut struct {
|
||||
Variable *api.Variable
|
||||
}
|
||||
|
||||
// EvalVariable returns a variable in the specified context.
|
||||
//
|
||||
// See https://github.com/derekparker/delve/wiki/Expressions for
|
||||
// a description of acceptable values of arg.Expr.
|
||||
func (s *RPCServer) Eval(arg EvalIn, out *EvalOut) error {
|
||||
cfg := arg.Cfg
|
||||
if cfg == nil {
|
||||
cfg = &api.LoadConfig{true, 1, 64, 64, -1}
|
||||
}
|
||||
v, err := s.debugger.EvalVariableInScope(arg.Scope, arg.Expr, *api.LoadConfigToProc(cfg))
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Variable = v
|
||||
return nil
|
||||
}
|
||||
|
||||
type SetIn struct {
|
||||
Scope api.EvalScope
|
||||
Symbol string
|
||||
Value string
|
||||
}
|
||||
|
||||
type SetOut struct {
|
||||
}
|
||||
|
||||
// Set sets the value of a variable. Only numerical types and
|
||||
// pointers are currently supported.
|
||||
func (s *RPCServer) Set(arg SetIn, out *SetOut) error {
|
||||
return s.debugger.SetVariableInScope(arg.Scope, arg.Symbol, arg.Value)
|
||||
}
|
||||
|
||||
type ListSourcesIn struct {
|
||||
Filter string
|
||||
}
|
||||
|
||||
type ListSourcesOut struct {
|
||||
Sources []string
|
||||
}
|
||||
|
||||
// ListSources lists all source files in the process matching filter.
|
||||
func (s *RPCServer) ListSources(arg ListSourcesIn, out *ListSourcesOut) error {
|
||||
ss, err := s.debugger.Sources(arg.Filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Sources = ss
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListFunctionsIn struct {
|
||||
Filter string
|
||||
}
|
||||
|
||||
type ListFunctionsOut struct {
|
||||
Funcs []string
|
||||
}
|
||||
|
||||
// ListFunctions lists all functions in the process matching filter.
|
||||
func (s *RPCServer) ListFunctions(arg ListFunctionsIn, out *ListFunctionsOut) error {
|
||||
fns, err := s.debugger.Functions(arg.Filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Funcs = fns
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListTypesIn struct {
|
||||
Filter string
|
||||
}
|
||||
|
||||
type ListTypesOut struct {
|
||||
Types []string
|
||||
}
|
||||
|
||||
// ListTypes lists all types in the process matching filter.
|
||||
func (s *RPCServer) ListTypes(arg ListTypesIn, out *ListTypesOut) error {
|
||||
tps, err := s.debugger.Types(arg.Filter)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Types = tps
|
||||
return nil
|
||||
}
|
||||
|
||||
type ListGoroutinesIn struct {
|
||||
}
|
||||
|
||||
type ListGoroutinesOut struct {
|
||||
Goroutines []*api.Goroutine
|
||||
}
|
||||
|
||||
// ListGoroutines lists all goroutines.
|
||||
func (s *RPCServer) ListGoroutines(arg ListGoroutinesIn, out *ListGoroutinesOut) error {
|
||||
gs, err := s.debugger.Goroutines()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
out.Goroutines = gs
|
||||
return nil
|
||||
}
|
||||
|
||||
type AttachedToExistingProcessIn struct {
|
||||
}
|
||||
|
||||
type AttachedToExistingProcessOut struct {
|
||||
Answer bool
|
||||
}
|
||||
|
||||
// AttachedToExistingProcess returns whether we attached to a running process or not
|
||||
func (c *RPCServer) AttachedToExistingProcess(arg AttachedToExistingProcessIn, out *AttachedToExistingProcessOut) error {
|
||||
if c.config.AttachPid != 0 {
|
||||
out.Answer = true
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
type FindLocationIn struct {
|
||||
Scope api.EvalScope
|
||||
Loc string
|
||||
}
|
||||
|
||||
type FindLocationOut struct {
|
||||
Locations []api.Location
|
||||
}
|
||||
|
||||
// FindLocation returns concrete location information described by a location expression
|
||||
//
|
||||
// loc ::= <filename>:<line> | <function>[:<line>] | /<regex>/ | (+|-)<offset> | <line> | *<address>
|
||||
// * <filename> can be the full path of a file or just a suffix
|
||||
// * <function> ::= <package>.<receiver type>.<name> | <package>.(*<receiver type>).<name> | <receiver type>.<name> | <package>.<name> | (*<receiver type>).<name> | <name>
|
||||
// * <function> must be unambiguous
|
||||
// * /<regex>/ will return a location for each function matched by regex
|
||||
// * +<offset> returns a location for the line that is <offset> lines after the current line
|
||||
// * -<offset> returns a location for the line that is <offset> lines before the current line
|
||||
// * <line> returns a location for a line in the current file
|
||||
// * *<address> returns the location corresponding to the specified address
|
||||
//
|
||||
// NOTE: this function does not actually set breakpoints.
|
||||
func (c *RPCServer) FindLocation(arg FindLocationIn, out *FindLocationOut) error {
|
||||
var err error
|
||||
out.Locations, err = c.debugger.FindLocation(arg.Scope, arg.Loc)
|
||||
return err
|
||||
}
|
||||
|
||||
type DisassembleIn struct {
|
||||
Scope api.EvalScope
|
||||
StartPC, EndPC uint64
|
||||
Flavour api.AssemblyFlavour
|
||||
}
|
||||
|
||||
type DisassembleOut struct {
|
||||
Disassemble api.AsmInstructions
|
||||
}
|
||||
|
||||
// Disassemble code.
|
||||
//
|
||||
// If both StartPC and EndPC are non-zero the specified range will be disassembled, otherwise the function containing StartPC will be disassembled.
|
||||
//
|
||||
// Scope is used to mark the instruction the specified gorutine is stopped at.
|
||||
//
|
||||
// Disassemble will also try to calculate the destination address of an absolute indirect CALL if it happens to be the instruction the selected goroutine is stopped at.
|
||||
func (c *RPCServer) Disassemble(arg DisassembleIn, out *DisassembleOut) error {
|
||||
var err error
|
||||
out.Disassemble, err = c.debugger.Disassemble(arg.Scope, arg.StartPC, arg.EndPC, arg.Flavour)
|
||||
return err
|
||||
}
|
6
vendor/github.com/derekparker/delve/service/rpccallback.go
generated
vendored
Normal file
6
vendor/github.com/derekparker/delve/service/rpccallback.go
generated
vendored
Normal file
@ -0,0 +1,6 @@
|
||||
package service
|
||||
|
||||
// RPCCallback is used by RPC methods to return their result asynchronously.
|
||||
type RPCCallback interface {
|
||||
Return(out interface{}, err error)
|
||||
}
|
409
vendor/github.com/derekparker/delve/service/rpccommon/server.go
generated
vendored
Normal file
409
vendor/github.com/derekparker/delve/service/rpccommon/server.go
generated
vendored
Normal file
@ -0,0 +1,409 @@
|
||||
package rpccommon
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"io/ioutil"
|
||||
"log"
|
||||
"net"
|
||||
"net/rpc"
|
||||
"net/rpc/jsonrpc"
|
||||
"reflect"
|
||||
"runtime"
|
||||
"sync"
|
||||
"unicode"
|
||||
"unicode/utf8"
|
||||
|
||||
"github.com/derekparker/delve/pkg/version"
|
||||
"github.com/derekparker/delve/service"
|
||||
"github.com/derekparker/delve/service/api"
|
||||
"github.com/derekparker/delve/service/debugger"
|
||||
"github.com/derekparker/delve/service/rpc1"
|
||||
"github.com/derekparker/delve/service/rpc2"
|
||||
)
|
||||
|
||||
// ServerImpl implements a JSON-RPC server that can switch between two
|
||||
// versions of the API.
|
||||
type ServerImpl struct {
|
||||
// config is all the information necessary to start the debugger and server.
|
||||
config *service.Config
|
||||
// listener is used to serve HTTP.
|
||||
listener net.Listener
|
||||
// stopChan is used to stop the listener goroutine.
|
||||
stopChan chan struct{}
|
||||
// debugger is the debugger service.
|
||||
debugger *debugger.Debugger
|
||||
// s1 is APIv1 server.
|
||||
s1 *rpc1.RPCServer
|
||||
// s2 is APIv2 server.
|
||||
s2 *rpc2.RPCServer
|
||||
// maps of served methods, one for each supported API.
|
||||
methodMaps []map[string]*methodType
|
||||
}
|
||||
|
||||
type RPCCallback struct {
|
||||
s *ServerImpl
|
||||
sending *sync.Mutex
|
||||
codec rpc.ServerCodec
|
||||
req rpc.Request
|
||||
}
|
||||
|
||||
// RPCServer implements the RPC method calls common to all versions of the API.
|
||||
type RPCServer struct {
|
||||
s *ServerImpl
|
||||
}
|
||||
|
||||
type methodType struct {
|
||||
method reflect.Method
|
||||
Rcvr reflect.Value
|
||||
ArgType reflect.Type
|
||||
ReplyType reflect.Type
|
||||
Synchronous bool
|
||||
}
|
||||
|
||||
// NewServer creates a new RPCServer.
|
||||
func NewServer(config *service.Config, logEnabled bool) *ServerImpl {
|
||||
log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
|
||||
if !logEnabled {
|
||||
log.SetOutput(ioutil.Discard)
|
||||
}
|
||||
if config.APIVersion < 2 {
|
||||
log.Printf("Using API v1")
|
||||
}
|
||||
return &ServerImpl{
|
||||
config: config,
|
||||
listener: config.Listener,
|
||||
stopChan: make(chan struct{}),
|
||||
}
|
||||
}
|
||||
|
||||
// Stop stops the JSON-RPC server.
|
||||
func (s *ServerImpl) Stop(kill bool) error {
|
||||
if s.config.AcceptMulti {
|
||||
close(s.stopChan)
|
||||
s.listener.Close()
|
||||
}
|
||||
return s.debugger.Detach(kill)
|
||||
}
|
||||
|
||||
// Restart restarts the debugger.
|
||||
func (s *ServerImpl) Restart() error {
|
||||
if s.config.AttachPid != 0 {
|
||||
return errors.New("cannot restart process Delve did not create")
|
||||
}
|
||||
return s.s2.Restart(rpc2.RestartIn{}, nil)
|
||||
}
|
||||
|
||||
// Run starts a debugger and exposes it with an HTTP server. The debugger
|
||||
// itself can be stopped with the `detach` API. Run blocks until the HTTP
|
||||
// server stops.
|
||||
func (s *ServerImpl) Run() error {
|
||||
var err error
|
||||
|
||||
if s.config.APIVersion < 2 {
|
||||
s.config.APIVersion = 1
|
||||
}
|
||||
if s.config.APIVersion > 2 {
|
||||
return fmt.Errorf("unknown API version")
|
||||
}
|
||||
|
||||
// Create and start the debugger
|
||||
if s.debugger, err = debugger.New(&debugger.Config{
|
||||
ProcessArgs: s.config.ProcessArgs,
|
||||
AttachPid: s.config.AttachPid,
|
||||
WorkingDir: s.config.WorkingDir,
|
||||
}); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
s.s1 = rpc1.NewServer(s.config, s.debugger)
|
||||
s.s2 = rpc2.NewServer(s.config, s.debugger)
|
||||
|
||||
rpcServer := &RPCServer{s}
|
||||
|
||||
s.methodMaps = make([]map[string]*methodType, 2)
|
||||
|
||||
s.methodMaps[0] = map[string]*methodType{}
|
||||
s.methodMaps[1] = map[string]*methodType{}
|
||||
suitableMethods(s.s1, s.methodMaps[0])
|
||||
suitableMethods(rpcServer, s.methodMaps[0])
|
||||
suitableMethods(s.s2, s.methodMaps[1])
|
||||
suitableMethods(rpcServer, s.methodMaps[1])
|
||||
|
||||
go func() {
|
||||
defer s.listener.Close()
|
||||
for {
|
||||
c, err := s.listener.Accept()
|
||||
if err != nil {
|
||||
select {
|
||||
case <-s.stopChan:
|
||||
// We were supposed to exit, do nothing and return
|
||||
return
|
||||
default:
|
||||
panic(err)
|
||||
}
|
||||
}
|
||||
go s.serveJSONCodec(c)
|
||||
if !s.config.AcceptMulti {
|
||||
break
|
||||
}
|
||||
}
|
||||
}()
|
||||
return nil
|
||||
}
|
||||
|
||||
// Precompute the reflect type for error. Can't use error directly
|
||||
// because Typeof takes an empty interface value. This is annoying.
|
||||
var typeOfError = reflect.TypeOf((*error)(nil)).Elem()
|
||||
|
||||
// Is this an exported - upper case - name?
|
||||
func isExported(name string) bool {
|
||||
rune, _ := utf8.DecodeRuneInString(name)
|
||||
return unicode.IsUpper(rune)
|
||||
}
|
||||
|
||||
// Is this type exported or a builtin?
|
||||
func isExportedOrBuiltinType(t reflect.Type) bool {
|
||||
for t.Kind() == reflect.Ptr {
|
||||
t = t.Elem()
|
||||
}
|
||||
// PkgPath will be non-empty even for an exported type,
|
||||
// so we need to check the type name as well.
|
||||
return isExported(t.Name()) || t.PkgPath() == ""
|
||||
}
|
||||
|
||||
// Fills methods map with the methods of receiver that should be made
|
||||
// available through the RPC interface.
|
||||
// These are all the public methods of rcvr that have one of those
|
||||
// two signatures:
|
||||
// func (rcvr ReceiverType) Method(in InputType, out *ReplyType) error
|
||||
// func (rcvr ReceiverType) Method(in InputType, cb service.RPCCallback)
|
||||
func suitableMethods(rcvr interface{}, methods map[string]*methodType) {
|
||||
typ := reflect.TypeOf(rcvr)
|
||||
rcvrv := reflect.ValueOf(rcvr)
|
||||
sname := reflect.Indirect(rcvrv).Type().Name()
|
||||
if sname == "" {
|
||||
log.Printf("rpc.Register: no service name for type %s", typ)
|
||||
return
|
||||
}
|
||||
for m := 0; m < typ.NumMethod(); m++ {
|
||||
method := typ.Method(m)
|
||||
mname := method.Name
|
||||
mtype := method.Type
|
||||
// method must be exported
|
||||
if method.PkgPath != "" {
|
||||
continue
|
||||
}
|
||||
// Method needs three ins: (receive, *args, *reply) or (receiver, *args, *RPCCallback)
|
||||
if mtype.NumIn() != 3 {
|
||||
log.Println("method", mname, "has wrong number of ins:", mtype.NumIn())
|
||||
continue
|
||||
}
|
||||
// First arg need not be a pointer.
|
||||
argType := mtype.In(1)
|
||||
if !isExportedOrBuiltinType(argType) {
|
||||
log.Println(mname, "argument type not exported:", argType)
|
||||
continue
|
||||
}
|
||||
|
||||
replyType := mtype.In(2)
|
||||
synchronous := replyType.String() != "service.RPCCallback"
|
||||
|
||||
if synchronous {
|
||||
// Second arg must be a pointer.
|
||||
if replyType.Kind() != reflect.Ptr {
|
||||
log.Println("method", mname, "reply type not a pointer:", replyType)
|
||||
continue
|
||||
}
|
||||
// Reply type must be exported.
|
||||
if !isExportedOrBuiltinType(replyType) {
|
||||
log.Println("method", mname, "reply type not exported:", replyType)
|
||||
continue
|
||||
}
|
||||
|
||||
// Method needs one out.
|
||||
if mtype.NumOut() != 1 {
|
||||
log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
|
||||
continue
|
||||
}
|
||||
// The return type of the method must be error.
|
||||
if returnType := mtype.Out(0); returnType != typeOfError {
|
||||
log.Println("method", mname, "returns", returnType.String(), "not error")
|
||||
continue
|
||||
}
|
||||
} else {
|
||||
// Method needs zero outs.
|
||||
if mtype.NumOut() != 0 {
|
||||
log.Println("method", mname, "has wrong number of outs:", mtype.NumOut())
|
||||
continue
|
||||
}
|
||||
}
|
||||
methods[sname+"."+mname] = &methodType{method: method, ArgType: argType, ReplyType: replyType, Synchronous: synchronous, Rcvr: rcvrv}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (s *ServerImpl) serveJSONCodec(conn io.ReadWriteCloser) {
|
||||
sending := new(sync.Mutex)
|
||||
codec := jsonrpc.NewServerCodec(conn)
|
||||
var req rpc.Request
|
||||
var resp rpc.Response
|
||||
for {
|
||||
req = rpc.Request{}
|
||||
err := codec.ReadRequestHeader(&req)
|
||||
if err != nil {
|
||||
if err != io.EOF {
|
||||
log.Println("rpc:", err)
|
||||
}
|
||||
break
|
||||
}
|
||||
|
||||
mtype, ok := s.methodMaps[s.config.APIVersion-1][req.ServiceMethod]
|
||||
if !ok {
|
||||
log.Printf("rpc: can't find method %s", req.ServiceMethod)
|
||||
continue
|
||||
}
|
||||
|
||||
var argv, replyv reflect.Value
|
||||
|
||||
// Decode the argument value.
|
||||
argIsValue := false // if true, need to indirect before calling.
|
||||
if mtype.ArgType.Kind() == reflect.Ptr {
|
||||
argv = reflect.New(mtype.ArgType.Elem())
|
||||
} else {
|
||||
argv = reflect.New(mtype.ArgType)
|
||||
argIsValue = true
|
||||
}
|
||||
// argv guaranteed to be a pointer now.
|
||||
if err = codec.ReadRequestBody(argv.Interface()); err != nil {
|
||||
return
|
||||
}
|
||||
if argIsValue {
|
||||
argv = argv.Elem()
|
||||
}
|
||||
|
||||
if mtype.Synchronous {
|
||||
replyv = reflect.New(mtype.ReplyType.Elem())
|
||||
function := mtype.method.Func
|
||||
var returnValues []reflect.Value
|
||||
var errInter interface{}
|
||||
func() {
|
||||
defer func() {
|
||||
if ierr := recover(); ierr != nil {
|
||||
errInter = newInternalError(ierr, 2)
|
||||
}
|
||||
}()
|
||||
returnValues = function.Call([]reflect.Value{mtype.Rcvr, argv, replyv})
|
||||
errInter = returnValues[0].Interface()
|
||||
}()
|
||||
|
||||
errmsg := ""
|
||||
if errInter != nil {
|
||||
errmsg = errInter.(error).Error()
|
||||
}
|
||||
resp = rpc.Response{}
|
||||
s.sendResponse(sending, &req, &resp, replyv.Interface(), codec, errmsg)
|
||||
} else {
|
||||
function := mtype.method.Func
|
||||
ctl := &RPCCallback{s, sending, codec, req}
|
||||
go func() {
|
||||
defer func() {
|
||||
if ierr := recover(); ierr != nil {
|
||||
ctl.Return(nil, newInternalError(ierr, 2))
|
||||
}
|
||||
}()
|
||||
function.Call([]reflect.Value{mtype.Rcvr, argv, reflect.ValueOf(ctl)})
|
||||
}()
|
||||
}
|
||||
}
|
||||
codec.Close()
|
||||
}
|
||||
|
||||
// A value sent as a placeholder for the server's response value when the server
|
||||
// receives an invalid request. It is never decoded by the client since the Response
|
||||
// contains an error when it is used.
|
||||
var invalidRequest = struct{}{}
|
||||
|
||||
func (s *ServerImpl) sendResponse(sending *sync.Mutex, req *rpc.Request, resp *rpc.Response, reply interface{}, codec rpc.ServerCodec, errmsg string) {
|
||||
resp.ServiceMethod = req.ServiceMethod
|
||||
if errmsg != "" {
|
||||
resp.Error = errmsg
|
||||
reply = invalidRequest
|
||||
}
|
||||
resp.Seq = req.Seq
|
||||
sending.Lock()
|
||||
defer sending.Unlock()
|
||||
err := codec.WriteResponse(resp, reply)
|
||||
if err != nil {
|
||||
log.Println("rpc: writing response:", err)
|
||||
}
|
||||
}
|
||||
|
||||
func (cb *RPCCallback) Return(out interface{}, err error) {
|
||||
errmsg := ""
|
||||
if err != nil {
|
||||
errmsg = err.Error()
|
||||
}
|
||||
var resp rpc.Response
|
||||
cb.s.sendResponse(cb.sending, &cb.req, &resp, out, cb.codec, errmsg)
|
||||
}
|
||||
|
||||
// GetVersion returns the version of delve as well as the API version
|
||||
// currently served.
|
||||
func (s *RPCServer) GetVersion(args api.GetVersionIn, out *api.GetVersionOut) error {
|
||||
out.DelveVersion = version.DelveVersion.String()
|
||||
out.APIVersion = s.s.config.APIVersion
|
||||
return nil
|
||||
}
|
||||
|
||||
// Changes version of the API being served.
|
||||
func (s *RPCServer) SetApiVersion(args api.SetAPIVersionIn, out *api.SetAPIVersionOut) error {
|
||||
if args.APIVersion < 2 {
|
||||
args.APIVersion = 1
|
||||
}
|
||||
if args.APIVersion > 2 {
|
||||
return fmt.Errorf("unknown API version")
|
||||
}
|
||||
s.s.config.APIVersion = args.APIVersion
|
||||
return nil
|
||||
}
|
||||
|
||||
type internalError struct {
|
||||
Err interface{}
|
||||
Stack []internalErrorFrame
|
||||
}
|
||||
|
||||
type internalErrorFrame struct {
|
||||
Pc uintptr
|
||||
Func string
|
||||
File string
|
||||
Line int
|
||||
}
|
||||
|
||||
func newInternalError(ierr interface{}, skip int) *internalError {
|
||||
r := &internalError{ierr, nil}
|
||||
for i := skip; ; i++ {
|
||||
pc, file, line, ok := runtime.Caller(i)
|
||||
if !ok {
|
||||
break
|
||||
}
|
||||
fname := "<unknown>"
|
||||
fn := runtime.FuncForPC(pc)
|
||||
if fn != nil {
|
||||
fname = fn.Name()
|
||||
}
|
||||
r.Stack = append(r.Stack, internalErrorFrame{pc, fname, file, line})
|
||||
}
|
||||
return r
|
||||
}
|
||||
|
||||
func (err *internalError) Error() string {
|
||||
var out bytes.Buffer
|
||||
fmt.Fprintf(&out, "Internal debugger error: %v\n", err.Err)
|
||||
for _, frame := range err.Stack {
|
||||
fmt.Fprintf(&out, "%s (%#x)\n\t%s:%d\n", frame.Func, frame.Pc, frame.File, frame.Line)
|
||||
}
|
||||
return out.String()
|
||||
}
|
8
vendor/github.com/derekparker/delve/service/server.go
generated
vendored
Normal file
8
vendor/github.com/derekparker/delve/service/server.go
generated
vendored
Normal file
@ -0,0 +1,8 @@
|
||||
package service
|
||||
|
||||
// Server represents a server for a remote client
|
||||
// to connect to.
|
||||
type Server interface {
|
||||
Run() error
|
||||
Stop(bool) error
|
||||
}
|
21
vendor/github.com/mattn/go-colorable/LICENSE
generated
vendored
Normal file
21
vendor/github.com/mattn/go-colorable/LICENSE
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
The MIT License (MIT)
|
||||
|
||||
Copyright (c) 2016 Yasuhiro Matsumoto
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all
|
||||
copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
||||
SOFTWARE.
|
43
vendor/github.com/mattn/go-colorable/README.md
generated
vendored
Normal file
43
vendor/github.com/mattn/go-colorable/README.md
generated
vendored
Normal file
@ -0,0 +1,43 @@
|
||||
# go-colorable
|
||||
|
||||
Colorable writer for windows.
|
||||
|
||||
For example, most of logger packages doesn't show colors on windows. (I know we can do it with ansicon. But I don't want.)
|
||||
This package is possible to handle escape sequence for ansi color on windows.
|
||||
|
||||
## Too Bad!
|
||||
|
||||
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/bad.png)
|
||||
|
||||
|
||||
## So Good!
|
||||
|
||||
![](https://raw.githubusercontent.com/mattn/go-colorable/gh-pages/good.png)
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
logrus.SetFormatter(&logrus.TextFormatter{ForceColors: true})
|
||||
logrus.SetOutput(colorable.NewColorableStdout())
|
||||
|
||||
logrus.Info("succeeded")
|
||||
logrus.Warn("not correct")
|
||||
logrus.Error("something error")
|
||||
logrus.Fatal("panic")
|
||||
```
|
||||
|
||||
You can compile above code on non-windows OSs.
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ go get github.com/mattn/go-colorable
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
MIT
|
||||
|
||||
# Author
|
||||
|
||||
Yasuhiro Matsumoto (a.k.a mattn)
|
24
vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
Normal file
24
vendor/github.com/mattn/go-colorable/colorable_others.go
generated
vendored
Normal file
@ -0,0 +1,24 @@
|
||||
// +build !windows
|
||||
|
||||
package colorable
|
||||
|
||||
import (
|
||||
"io"
|
||||
"os"
|
||||
)
|
||||
|
||||
func NewColorable(file *os.File) io.Writer {
|
||||
if file == nil {
|
||||
panic("nil passed instead of *os.File to NewColorable()")
|
||||
}
|
||||
|
||||
return file
|
||||
}
|
||||
|
||||
func NewColorableStdout() io.Writer {
|
||||
return os.Stdout
|
||||
}
|
||||
|
||||
func NewColorableStderr() io.Writer {
|
||||
return os.Stderr
|
||||
}
|
783
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
Normal file
783
vendor/github.com/mattn/go-colorable/colorable_windows.go
generated
vendored
Normal file
@ -0,0 +1,783 @@
|
||||
package colorable
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"io"
|
||||
"math"
|
||||
"os"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"unsafe"
|
||||
|
||||
"github.com/mattn/go-isatty"
|
||||
)
|
||||
|
||||
const (
|
||||
foregroundBlue = 0x1
|
||||
foregroundGreen = 0x2
|
||||
foregroundRed = 0x4
|
||||
foregroundIntensity = 0x8
|
||||
foregroundMask = (foregroundRed | foregroundBlue | foregroundGreen | foregroundIntensity)
|
||||
backgroundBlue = 0x10
|
||||
backgroundGreen = 0x20
|
||||
backgroundRed = 0x40
|
||||
backgroundIntensity = 0x80
|
||||
backgroundMask = (backgroundRed | backgroundBlue | backgroundGreen | backgroundIntensity)
|
||||
)
|
||||
|
||||
type wchar uint16
|
||||
type short int16
|
||||
type dword uint32
|
||||
type word uint16
|
||||
|
||||
type coord struct {
|
||||
x short
|
||||
y short
|
||||
}
|
||||
|
||||
type smallRect struct {
|
||||
left short
|
||||
top short
|
||||
right short
|
||||
bottom short
|
||||
}
|
||||
|
||||
type consoleScreenBufferInfo struct {
|
||||
size coord
|
||||
cursorPosition coord
|
||||
attributes word
|
||||
window smallRect
|
||||
maximumWindowSize coord
|
||||
}
|
||||
|
||||
var (
|
||||
kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
procGetConsoleScreenBufferInfo = kernel32.NewProc("GetConsoleScreenBufferInfo")
|
||||
procSetConsoleTextAttribute = kernel32.NewProc("SetConsoleTextAttribute")
|
||||
procSetConsoleCursorPosition = kernel32.NewProc("SetConsoleCursorPosition")
|
||||
procFillConsoleOutputCharacter = kernel32.NewProc("FillConsoleOutputCharacterW")
|
||||
procFillConsoleOutputAttribute = kernel32.NewProc("FillConsoleOutputAttribute")
|
||||
)
|
||||
|
||||
type Writer struct {
|
||||
out io.Writer
|
||||
handle syscall.Handle
|
||||
lastbuf bytes.Buffer
|
||||
oldattr word
|
||||
}
|
||||
|
||||
func NewColorable(file *os.File) io.Writer {
|
||||
if file == nil {
|
||||
panic("nil passed instead of *os.File to NewColorable()")
|
||||
}
|
||||
|
||||
if isatty.IsTerminal(file.Fd()) {
|
||||
var csbi consoleScreenBufferInfo
|
||||
handle := syscall.Handle(file.Fd())
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
return &Writer{out: file, handle: handle, oldattr: csbi.attributes}
|
||||
} else {
|
||||
return file
|
||||
}
|
||||
}
|
||||
|
||||
func NewColorableStdout() io.Writer {
|
||||
return NewColorable(os.Stdout)
|
||||
}
|
||||
|
||||
func NewColorableStderr() io.Writer {
|
||||
return NewColorable(os.Stderr)
|
||||
}
|
||||
|
||||
var color256 = map[int]int{
|
||||
0: 0x000000,
|
||||
1: 0x800000,
|
||||
2: 0x008000,
|
||||
3: 0x808000,
|
||||
4: 0x000080,
|
||||
5: 0x800080,
|
||||
6: 0x008080,
|
||||
7: 0xc0c0c0,
|
||||
8: 0x808080,
|
||||
9: 0xff0000,
|
||||
10: 0x00ff00,
|
||||
11: 0xffff00,
|
||||
12: 0x0000ff,
|
||||
13: 0xff00ff,
|
||||
14: 0x00ffff,
|
||||
15: 0xffffff,
|
||||
16: 0x000000,
|
||||
17: 0x00005f,
|
||||
18: 0x000087,
|
||||
19: 0x0000af,
|
||||
20: 0x0000d7,
|
||||
21: 0x0000ff,
|
||||
22: 0x005f00,
|
||||
23: 0x005f5f,
|
||||
24: 0x005f87,
|
||||
25: 0x005faf,
|
||||
26: 0x005fd7,
|
||||
27: 0x005fff,
|
||||
28: 0x008700,
|
||||
29: 0x00875f,
|
||||
30: 0x008787,
|
||||
31: 0x0087af,
|
||||
32: 0x0087d7,
|
||||
33: 0x0087ff,
|
||||
34: 0x00af00,
|
||||
35: 0x00af5f,
|
||||
36: 0x00af87,
|
||||
37: 0x00afaf,
|
||||
38: 0x00afd7,
|
||||
39: 0x00afff,
|
||||
40: 0x00d700,
|
||||
41: 0x00d75f,
|
||||
42: 0x00d787,
|
||||
43: 0x00d7af,
|
||||
44: 0x00d7d7,
|
||||
45: 0x00d7ff,
|
||||
46: 0x00ff00,
|
||||
47: 0x00ff5f,
|
||||
48: 0x00ff87,
|
||||
49: 0x00ffaf,
|
||||
50: 0x00ffd7,
|
||||
51: 0x00ffff,
|
||||
52: 0x5f0000,
|
||||
53: 0x5f005f,
|
||||
54: 0x5f0087,
|
||||
55: 0x5f00af,
|
||||
56: 0x5f00d7,
|
||||
57: 0x5f00ff,
|
||||
58: 0x5f5f00,
|
||||
59: 0x5f5f5f,
|
||||
60: 0x5f5f87,
|
||||
61: 0x5f5faf,
|
||||
62: 0x5f5fd7,
|
||||
63: 0x5f5fff,
|
||||
64: 0x5f8700,
|
||||
65: 0x5f875f,
|
||||
66: 0x5f8787,
|
||||
67: 0x5f87af,
|
||||
68: 0x5f87d7,
|
||||
69: 0x5f87ff,
|
||||
70: 0x5faf00,
|
||||
71: 0x5faf5f,
|
||||
72: 0x5faf87,
|
||||
73: 0x5fafaf,
|
||||
74: 0x5fafd7,
|
||||
75: 0x5fafff,
|
||||
76: 0x5fd700,
|
||||
77: 0x5fd75f,
|
||||
78: 0x5fd787,
|
||||
79: 0x5fd7af,
|
||||
80: 0x5fd7d7,
|
||||
81: 0x5fd7ff,
|
||||
82: 0x5fff00,
|
||||
83: 0x5fff5f,
|
||||
84: 0x5fff87,
|
||||
85: 0x5fffaf,
|
||||
86: 0x5fffd7,
|
||||
87: 0x5fffff,
|
||||
88: 0x870000,
|
||||
89: 0x87005f,
|
||||
90: 0x870087,
|
||||
91: 0x8700af,
|
||||
92: 0x8700d7,
|
||||
93: 0x8700ff,
|
||||
94: 0x875f00,
|
||||
95: 0x875f5f,
|
||||
96: 0x875f87,
|
||||
97: 0x875faf,
|
||||
98: 0x875fd7,
|
||||
99: 0x875fff,
|
||||
100: 0x878700,
|
||||
101: 0x87875f,
|
||||
102: 0x878787,
|
||||
103: 0x8787af,
|
||||
104: 0x8787d7,
|
||||
105: 0x8787ff,
|
||||
106: 0x87af00,
|
||||
107: 0x87af5f,
|
||||
108: 0x87af87,
|
||||
109: 0x87afaf,
|
||||
110: 0x87afd7,
|
||||
111: 0x87afff,
|
||||
112: 0x87d700,
|
||||
113: 0x87d75f,
|
||||
114: 0x87d787,
|
||||
115: 0x87d7af,
|
||||
116: 0x87d7d7,
|
||||
117: 0x87d7ff,
|
||||
118: 0x87ff00,
|
||||
119: 0x87ff5f,
|
||||
120: 0x87ff87,
|
||||
121: 0x87ffaf,
|
||||
122: 0x87ffd7,
|
||||
123: 0x87ffff,
|
||||
124: 0xaf0000,
|
||||
125: 0xaf005f,
|
||||
126: 0xaf0087,
|
||||
127: 0xaf00af,
|
||||
128: 0xaf00d7,
|
||||
129: 0xaf00ff,
|
||||
130: 0xaf5f00,
|
||||
131: 0xaf5f5f,
|
||||
132: 0xaf5f87,
|
||||
133: 0xaf5faf,
|
||||
134: 0xaf5fd7,
|
||||
135: 0xaf5fff,
|
||||
136: 0xaf8700,
|
||||
137: 0xaf875f,
|
||||
138: 0xaf8787,
|
||||
139: 0xaf87af,
|
||||
140: 0xaf87d7,
|
||||
141: 0xaf87ff,
|
||||
142: 0xafaf00,
|
||||
143: 0xafaf5f,
|
||||
144: 0xafaf87,
|
||||
145: 0xafafaf,
|
||||
146: 0xafafd7,
|
||||
147: 0xafafff,
|
||||
148: 0xafd700,
|
||||
149: 0xafd75f,
|
||||
150: 0xafd787,
|
||||
151: 0xafd7af,
|
||||
152: 0xafd7d7,
|
||||
153: 0xafd7ff,
|
||||
154: 0xafff00,
|
||||
155: 0xafff5f,
|
||||
156: 0xafff87,
|
||||
157: 0xafffaf,
|
||||
158: 0xafffd7,
|
||||
159: 0xafffff,
|
||||
160: 0xd70000,
|
||||
161: 0xd7005f,
|
||||
162: 0xd70087,
|
||||
163: 0xd700af,
|
||||
164: 0xd700d7,
|
||||
165: 0xd700ff,
|
||||
166: 0xd75f00,
|
||||
167: 0xd75f5f,
|
||||
168: 0xd75f87,
|
||||
169: 0xd75faf,
|
||||
170: 0xd75fd7,
|
||||
171: 0xd75fff,
|
||||
172: 0xd78700,
|
||||
173: 0xd7875f,
|
||||
174: 0xd78787,
|
||||
175: 0xd787af,
|
||||
176: 0xd787d7,
|
||||
177: 0xd787ff,
|
||||
178: 0xd7af00,
|
||||
179: 0xd7af5f,
|
||||
180: 0xd7af87,
|
||||
181: 0xd7afaf,
|
||||
182: 0xd7afd7,
|
||||
183: 0xd7afff,
|
||||
184: 0xd7d700,
|
||||
185: 0xd7d75f,
|
||||
186: 0xd7d787,
|
||||
187: 0xd7d7af,
|
||||
188: 0xd7d7d7,
|
||||
189: 0xd7d7ff,
|
||||
190: 0xd7ff00,
|
||||
191: 0xd7ff5f,
|
||||
192: 0xd7ff87,
|
||||
193: 0xd7ffaf,
|
||||
194: 0xd7ffd7,
|
||||
195: 0xd7ffff,
|
||||
196: 0xff0000,
|
||||
197: 0xff005f,
|
||||
198: 0xff0087,
|
||||
199: 0xff00af,
|
||||
200: 0xff00d7,
|
||||
201: 0xff00ff,
|
||||
202: 0xff5f00,
|
||||
203: 0xff5f5f,
|
||||
204: 0xff5f87,
|
||||
205: 0xff5faf,
|
||||
206: 0xff5fd7,
|
||||
207: 0xff5fff,
|
||||
208: 0xff8700,
|
||||
209: 0xff875f,
|
||||
210: 0xff8787,
|
||||
211: 0xff87af,
|
||||
212: 0xff87d7,
|
||||
213: 0xff87ff,
|
||||
214: 0xffaf00,
|
||||
215: 0xffaf5f,
|
||||
216: 0xffaf87,
|
||||
217: 0xffafaf,
|
||||
218: 0xffafd7,
|
||||
219: 0xffafff,
|
||||
220: 0xffd700,
|
||||
221: 0xffd75f,
|
||||
222: 0xffd787,
|
||||
223: 0xffd7af,
|
||||
224: 0xffd7d7,
|
||||
225: 0xffd7ff,
|
||||
226: 0xffff00,
|
||||
227: 0xffff5f,
|
||||
228: 0xffff87,
|
||||
229: 0xffffaf,
|
||||
230: 0xffffd7,
|
||||
231: 0xffffff,
|
||||
232: 0x080808,
|
||||
233: 0x121212,
|
||||
234: 0x1c1c1c,
|
||||
235: 0x262626,
|
||||
236: 0x303030,
|
||||
237: 0x3a3a3a,
|
||||
238: 0x444444,
|
||||
239: 0x4e4e4e,
|
||||
240: 0x585858,
|
||||
241: 0x626262,
|
||||
242: 0x6c6c6c,
|
||||
243: 0x767676,
|
||||
244: 0x808080,
|
||||
245: 0x8a8a8a,
|
||||
246: 0x949494,
|
||||
247: 0x9e9e9e,
|
||||
248: 0xa8a8a8,
|
||||
249: 0xb2b2b2,
|
||||
250: 0xbcbcbc,
|
||||
251: 0xc6c6c6,
|
||||
252: 0xd0d0d0,
|
||||
253: 0xdadada,
|
||||
254: 0xe4e4e4,
|
||||
255: 0xeeeeee,
|
||||
}
|
||||
|
||||
func (w *Writer) Write(data []byte) (n int, err error) {
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
|
||||
er := bytes.NewBuffer(data)
|
||||
loop:
|
||||
for {
|
||||
r1, _, err := procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
if r1 == 0 {
|
||||
break loop
|
||||
}
|
||||
|
||||
c1, _, err := er.ReadRune()
|
||||
if err != nil {
|
||||
break loop
|
||||
}
|
||||
if c1 != 0x1b {
|
||||
fmt.Fprint(w.out, string(c1))
|
||||
continue
|
||||
}
|
||||
c2, _, err := er.ReadRune()
|
||||
if err != nil {
|
||||
w.lastbuf.WriteRune(c1)
|
||||
break loop
|
||||
}
|
||||
if c2 != 0x5b {
|
||||
w.lastbuf.WriteRune(c1)
|
||||
w.lastbuf.WriteRune(c2)
|
||||
continue
|
||||
}
|
||||
|
||||
var buf bytes.Buffer
|
||||
var m rune
|
||||
for {
|
||||
c, _, err := er.ReadRune()
|
||||
if err != nil {
|
||||
w.lastbuf.WriteRune(c1)
|
||||
w.lastbuf.WriteRune(c2)
|
||||
w.lastbuf.Write(buf.Bytes())
|
||||
break loop
|
||||
}
|
||||
if ('a' <= c && c <= 'z') || ('A' <= c && c <= 'Z') || c == '@' {
|
||||
m = c
|
||||
break
|
||||
}
|
||||
buf.Write([]byte(string(c)))
|
||||
}
|
||||
|
||||
var csbi consoleScreenBufferInfo
|
||||
switch m {
|
||||
case 'A':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.y -= short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'B':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.y += short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'C':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x -= short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'D':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
if n, err = strconv.Atoi(buf.String()); err == nil {
|
||||
var csbi consoleScreenBufferInfo
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x += short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
}
|
||||
case 'E':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x = 0
|
||||
csbi.cursorPosition.y += short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'F':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x = 0
|
||||
csbi.cursorPosition.y -= short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'G':
|
||||
n, err = strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
procGetConsoleScreenBufferInfo.Call(uintptr(w.handle), uintptr(unsafe.Pointer(&csbi)))
|
||||
csbi.cursorPosition.x = short(n)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'H':
|
||||
token := strings.Split(buf.String(), ";")
|
||||
if len(token) != 2 {
|
||||
continue
|
||||
}
|
||||
n1, err := strconv.Atoi(token[0])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
n2, err := strconv.Atoi(token[1])
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
csbi.cursorPosition.x = short(n2)
|
||||
csbi.cursorPosition.x = short(n1)
|
||||
procSetConsoleCursorPosition.Call(uintptr(w.handle), *(*uintptr)(unsafe.Pointer(&csbi.cursorPosition)))
|
||||
case 'J':
|
||||
n, err := strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var cursor coord
|
||||
switch n {
|
||||
case 0:
|
||||
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
||||
case 1:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
||||
case 2:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top}
|
||||
}
|
||||
var count, written dword
|
||||
count = dword(csbi.size.x - csbi.cursorPosition.x + (csbi.size.y-csbi.cursorPosition.y)*csbi.size.x)
|
||||
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
case 'K':
|
||||
n, err := strconv.Atoi(buf.String())
|
||||
if err != nil {
|
||||
continue
|
||||
}
|
||||
var cursor coord
|
||||
switch n {
|
||||
case 0:
|
||||
cursor = coord{x: csbi.cursorPosition.x, y: csbi.cursorPosition.y}
|
||||
case 1:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
||||
case 2:
|
||||
cursor = coord{x: csbi.window.left, y: csbi.window.top + csbi.cursorPosition.y}
|
||||
}
|
||||
var count, written dword
|
||||
count = dword(csbi.size.x - csbi.cursorPosition.x)
|
||||
procFillConsoleOutputCharacter.Call(uintptr(w.handle), uintptr(' '), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
procFillConsoleOutputAttribute.Call(uintptr(w.handle), uintptr(csbi.attributes), uintptr(count), *(*uintptr)(unsafe.Pointer(&cursor)), uintptr(unsafe.Pointer(&written)))
|
||||
case 'm':
|
||||
attr := csbi.attributes
|
||||
cs := buf.String()
|
||||
if cs == "" {
|
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(w.oldattr))
|
||||
continue
|
||||
}
|
||||
token := strings.Split(cs, ";")
|
||||
for i := 0; i < len(token); i += 1 {
|
||||
ns := token[i]
|
||||
if n, err = strconv.Atoi(ns); err == nil {
|
||||
switch {
|
||||
case n == 0 || n == 100:
|
||||
attr = w.oldattr
|
||||
case 1 <= n && n <= 5:
|
||||
attr |= foregroundIntensity
|
||||
case n == 7:
|
||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
||||
case 22 == n || n == 25 || n == 25:
|
||||
attr |= foregroundIntensity
|
||||
case n == 27:
|
||||
attr = ((attr & foregroundMask) << 4) | ((attr & backgroundMask) >> 4)
|
||||
case 30 <= n && n <= 37:
|
||||
attr = (attr & backgroundMask)
|
||||
if (n-30)&1 != 0 {
|
||||
attr |= foregroundRed
|
||||
}
|
||||
if (n-30)&2 != 0 {
|
||||
attr |= foregroundGreen
|
||||
}
|
||||
if (n-30)&4 != 0 {
|
||||
attr |= foregroundBlue
|
||||
}
|
||||
case n == 38: // set foreground color.
|
||||
if i < len(token)-2 && (token[i+1] == "5" || token[i+1] == "05") {
|
||||
if n256, err := strconv.Atoi(token[i+2]); err == nil {
|
||||
if n256foreAttr == nil {
|
||||
n256setup()
|
||||
}
|
||||
attr &= backgroundMask
|
||||
attr |= n256foreAttr[n256]
|
||||
i += 2
|
||||
}
|
||||
} else {
|
||||
attr = attr & (w.oldattr & backgroundMask)
|
||||
}
|
||||
case n == 39: // reset foreground color.
|
||||
attr &= backgroundMask
|
||||
attr |= w.oldattr & foregroundMask
|
||||
case 40 <= n && n <= 47:
|
||||
attr = (attr & foregroundMask)
|
||||
if (n-40)&1 != 0 {
|
||||
attr |= backgroundRed
|
||||
}
|
||||
if (n-40)&2 != 0 {
|
||||
attr |= backgroundGreen
|
||||
}
|
||||
if (n-40)&4 != 0 {
|
||||
attr |= backgroundBlue
|
||||
}
|
||||
case n == 48: // set background color.
|
||||
if i < len(token)-2 && token[i+1] == "5" {
|
||||
if n256, err := strconv.Atoi(token[i+2]); err == nil {
|
||||
if n256backAttr == nil {
|
||||
n256setup()
|
||||
}
|
||||
attr &= foregroundMask
|
||||
attr |= n256backAttr[n256]
|
||||
i += 2
|
||||
}
|
||||
} else {
|
||||
attr = attr & (w.oldattr & foregroundMask)
|
||||
}
|
||||
case n == 49: // reset foreground color.
|
||||
attr &= foregroundMask
|
||||
attr |= w.oldattr & backgroundMask
|
||||
case 90 <= n && n <= 97:
|
||||
attr = (attr & backgroundMask)
|
||||
attr |= foregroundIntensity
|
||||
if (n-90)&1 != 0 {
|
||||
attr |= foregroundRed
|
||||
}
|
||||
if (n-90)&2 != 0 {
|
||||
attr |= foregroundGreen
|
||||
}
|
||||
if (n-90)&4 != 0 {
|
||||
attr |= foregroundBlue
|
||||
}
|
||||
case 100 <= n && n <= 107:
|
||||
attr = (attr & foregroundMask)
|
||||
attr |= backgroundIntensity
|
||||
if (n-100)&1 != 0 {
|
||||
attr |= backgroundRed
|
||||
}
|
||||
if (n-100)&2 != 0 {
|
||||
attr |= backgroundGreen
|
||||
}
|
||||
if (n-100)&4 != 0 {
|
||||
attr |= backgroundBlue
|
||||
}
|
||||
}
|
||||
procSetConsoleTextAttribute.Call(uintptr(w.handle), uintptr(attr))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return len(data) - w.lastbuf.Len(), nil
|
||||
}
|
||||
|
||||
type consoleColor struct {
|
||||
rgb int
|
||||
red bool
|
||||
green bool
|
||||
blue bool
|
||||
intensity bool
|
||||
}
|
||||
|
||||
func (c consoleColor) foregroundAttr() (attr word) {
|
||||
if c.red {
|
||||
attr |= foregroundRed
|
||||
}
|
||||
if c.green {
|
||||
attr |= foregroundGreen
|
||||
}
|
||||
if c.blue {
|
||||
attr |= foregroundBlue
|
||||
}
|
||||
if c.intensity {
|
||||
attr |= foregroundIntensity
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (c consoleColor) backgroundAttr() (attr word) {
|
||||
if c.red {
|
||||
attr |= backgroundRed
|
||||
}
|
||||
if c.green {
|
||||
attr |= backgroundGreen
|
||||
}
|
||||
if c.blue {
|
||||
attr |= backgroundBlue
|
||||
}
|
||||
if c.intensity {
|
||||
attr |= backgroundIntensity
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
var color16 = []consoleColor{
|
||||
consoleColor{0x000000, false, false, false, false},
|
||||
consoleColor{0x000080, false, false, true, false},
|
||||
consoleColor{0x008000, false, true, false, false},
|
||||
consoleColor{0x008080, false, true, true, false},
|
||||
consoleColor{0x800000, true, false, false, false},
|
||||
consoleColor{0x800080, true, false, true, false},
|
||||
consoleColor{0x808000, true, true, false, false},
|
||||
consoleColor{0xc0c0c0, true, true, true, false},
|
||||
consoleColor{0x808080, false, false, false, true},
|
||||
consoleColor{0x0000ff, false, false, true, true},
|
||||
consoleColor{0x00ff00, false, true, false, true},
|
||||
consoleColor{0x00ffff, false, true, true, true},
|
||||
consoleColor{0xff0000, true, false, false, true},
|
||||
consoleColor{0xff00ff, true, false, true, true},
|
||||
consoleColor{0xffff00, true, true, false, true},
|
||||
consoleColor{0xffffff, true, true, true, true},
|
||||
}
|
||||
|
||||
type hsv struct {
|
||||
h, s, v float32
|
||||
}
|
||||
|
||||
func (a hsv) dist(b hsv) float32 {
|
||||
dh := a.h - b.h
|
||||
switch {
|
||||
case dh > 0.5:
|
||||
dh = 1 - dh
|
||||
case dh < -0.5:
|
||||
dh = -1 - dh
|
||||
}
|
||||
ds := a.s - b.s
|
||||
dv := a.v - b.v
|
||||
return float32(math.Sqrt(float64(dh*dh + ds*ds + dv*dv)))
|
||||
}
|
||||
|
||||
func toHSV(rgb int) hsv {
|
||||
r, g, b := float32((rgb&0xFF0000)>>16)/256.0,
|
||||
float32((rgb&0x00FF00)>>8)/256.0,
|
||||
float32(rgb&0x0000FF)/256.0
|
||||
min, max := minmax3f(r, g, b)
|
||||
h := max - min
|
||||
if h > 0 {
|
||||
if max == r {
|
||||
h = (g - b) / h
|
||||
if h < 0 {
|
||||
h += 6
|
||||
}
|
||||
} else if max == g {
|
||||
h = 2 + (b-r)/h
|
||||
} else {
|
||||
h = 4 + (r-g)/h
|
||||
}
|
||||
}
|
||||
h /= 6.0
|
||||
s := max - min
|
||||
if max != 0 {
|
||||
s /= max
|
||||
}
|
||||
v := max
|
||||
return hsv{h: h, s: s, v: v}
|
||||
}
|
||||
|
||||
type hsvTable []hsv
|
||||
|
||||
func toHSVTable(rgbTable []consoleColor) hsvTable {
|
||||
t := make(hsvTable, len(rgbTable))
|
||||
for i, c := range rgbTable {
|
||||
t[i] = toHSV(c.rgb)
|
||||
}
|
||||
return t
|
||||
}
|
||||
|
||||
func (t hsvTable) find(rgb int) consoleColor {
|
||||
hsv := toHSV(rgb)
|
||||
n := 7
|
||||
l := float32(5.0)
|
||||
for i, p := range t {
|
||||
d := hsv.dist(p)
|
||||
if d < l {
|
||||
l, n = d, i
|
||||
}
|
||||
}
|
||||
return color16[n]
|
||||
}
|
||||
|
||||
func minmax3f(a, b, c float32) (min, max float32) {
|
||||
if a < b {
|
||||
if b < c {
|
||||
return a, c
|
||||
} else if a < c {
|
||||
return a, b
|
||||
} else {
|
||||
return c, b
|
||||
}
|
||||
} else {
|
||||
if a < c {
|
||||
return b, c
|
||||
} else if b < c {
|
||||
return b, a
|
||||
} else {
|
||||
return c, a
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var n256foreAttr []word
|
||||
var n256backAttr []word
|
||||
|
||||
func n256setup() {
|
||||
n256foreAttr = make([]word, 256)
|
||||
n256backAttr = make([]word, 256)
|
||||
t := toHSVTable(color16)
|
||||
for i, rgb := range color256 {
|
||||
c := t.find(rgb)
|
||||
n256foreAttr[i] = c.foregroundAttr()
|
||||
n256backAttr[i] = c.backgroundAttr()
|
||||
}
|
||||
}
|
9
vendor/github.com/mattn/go-isatty/LICENSE
generated
vendored
Normal file
9
vendor/github.com/mattn/go-isatty/LICENSE
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
Copyright (c) Yasuhiro MATSUMOTO <mattn.jp@gmail.com>
|
||||
|
||||
MIT License (Expat)
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
37
vendor/github.com/mattn/go-isatty/README.md
generated
vendored
Normal file
37
vendor/github.com/mattn/go-isatty/README.md
generated
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
# go-isatty
|
||||
|
||||
isatty for golang
|
||||
|
||||
## Usage
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/mattn/go-isatty"
|
||||
"os"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if isatty.IsTerminal(os.Stdout.Fd()) {
|
||||
fmt.Println("Is Terminal")
|
||||
} else {
|
||||
fmt.Println("Is Not Terminal")
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
## Installation
|
||||
|
||||
```
|
||||
$ go get github.com/mattn/go-isatty
|
||||
```
|
||||
|
||||
# License
|
||||
|
||||
MIT
|
||||
|
||||
# Author
|
||||
|
||||
Yasuhiro Matsumoto (a.k.a mattn)
|
2
vendor/github.com/mattn/go-isatty/doc.go
generated
vendored
Normal file
2
vendor/github.com/mattn/go-isatty/doc.go
generated
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
// Package isatty implements interface to isatty
|
||||
package isatty
|
9
vendor/github.com/mattn/go-isatty/isatty_appengine.go
generated
vendored
Normal file
9
vendor/github.com/mattn/go-isatty/isatty_appengine.go
generated
vendored
Normal file
@ -0,0 +1,9 @@
|
||||
// +build appengine
|
||||
|
||||
package isatty
|
||||
|
||||
// IsTerminal returns true if the file descriptor is terminal which
|
||||
// is always false on on appengine classic which is a sandboxed PaaS.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
return false
|
||||
}
|
18
vendor/github.com/mattn/go-isatty/isatty_bsd.go
generated
vendored
Normal file
18
vendor/github.com/mattn/go-isatty/isatty_bsd.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// +build darwin freebsd openbsd netbsd
|
||||
// +build !appengine
|
||||
|
||||
package isatty
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const ioctlReadTermios = syscall.TIOCGETA
|
||||
|
||||
// IsTerminal return true if the file descriptor is terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
var termios syscall.Termios
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
}
|
18
vendor/github.com/mattn/go-isatty/isatty_linux.go
generated
vendored
Normal file
18
vendor/github.com/mattn/go-isatty/isatty_linux.go
generated
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
// +build linux
|
||||
// +build !appengine
|
||||
|
||||
package isatty
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
const ioctlReadTermios = syscall.TCGETS
|
||||
|
||||
// IsTerminal return true if the file descriptor is terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
var termios syscall.Termios
|
||||
_, _, err := syscall.Syscall6(syscall.SYS_IOCTL, fd, ioctlReadTermios, uintptr(unsafe.Pointer(&termios)), 0, 0, 0)
|
||||
return err == 0
|
||||
}
|
16
vendor/github.com/mattn/go-isatty/isatty_solaris.go
generated
vendored
Normal file
16
vendor/github.com/mattn/go-isatty/isatty_solaris.go
generated
vendored
Normal file
@ -0,0 +1,16 @@
|
||||
// +build solaris
|
||||
// +build !appengine
|
||||
|
||||
package isatty
|
||||
|
||||
import (
|
||||
"golang.org/x/sys/unix"
|
||||
)
|
||||
|
||||
// IsTerminal returns true if the given file descriptor is a terminal.
|
||||
// see: http://src.illumos.org/source/xref/illumos-gate/usr/src/lib/libbc/libc/gen/common/isatty.c
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
var termio unix.Termio
|
||||
err := unix.IoctlSetTermio(int(fd), unix.TCGETA, &termio)
|
||||
return err == nil
|
||||
}
|
19
vendor/github.com/mattn/go-isatty/isatty_windows.go
generated
vendored
Normal file
19
vendor/github.com/mattn/go-isatty/isatty_windows.go
generated
vendored
Normal file
@ -0,0 +1,19 @@
|
||||
// +build windows
|
||||
// +build !appengine
|
||||
|
||||
package isatty
|
||||
|
||||
import (
|
||||
"syscall"
|
||||
"unsafe"
|
||||
)
|
||||
|
||||
var kernel32 = syscall.NewLazyDLL("kernel32.dll")
|
||||
var procGetConsoleMode = kernel32.NewProc("GetConsoleMode")
|
||||
|
||||
// IsTerminal return true if the file descriptor is terminal.
|
||||
func IsTerminal(fd uintptr) bool {
|
||||
var st uint32
|
||||
r, _, e := syscall.Syscall(procGetConsoleMode.Addr(), 2, fd, uintptr(unsafe.Pointer(&st)), 0)
|
||||
return r != 0 && e == 0
|
||||
}
|
21
vendor/github.com/peterh/liner/COPYING
generated
vendored
Normal file
21
vendor/github.com/peterh/liner/COPYING
generated
vendored
Normal file
@ -0,0 +1,21 @@
|
||||
Copyright © 2012 Peter Harris
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a
|
||||
copy of this software and associated documentation files (the "Software"),
|
||||
to deal in the Software without restriction, including without limitation
|
||||
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||
and/or sell copies of the Software, and to permit persons to whom the
|
||||
Software is furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice (including the next
|
||||
paragraph) shall be included in all copies or substantial portions of the
|
||||
Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||
THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER
|
||||
DEALINGS IN THE SOFTWARE.
|
||||
|
100
vendor/github.com/peterh/liner/README.md
generated
vendored
Normal file
100
vendor/github.com/peterh/liner/README.md
generated
vendored
Normal file
@ -0,0 +1,100 @@
|
||||
Liner
|
||||
=====
|
||||
|
||||
Liner is a command line editor with history. It was inspired by linenoise;
|
||||
everything Unix-like is a VT100 (or is trying very hard to be). If your
|
||||
terminal is not pretending to be a VT100, change it. Liner also support
|
||||
Windows.
|
||||
|
||||
Liner is released under the X11 license (which is similar to the new BSD
|
||||
license).
|
||||
|
||||
Line Editing
|
||||
------------
|
||||
|
||||
The following line editing commands are supported on platforms and terminals
|
||||
that Liner supports:
|
||||
|
||||
Keystroke | Action
|
||||
--------- | ------
|
||||
Ctrl-A, Home | Move cursor to beginning of line
|
||||
Ctrl-E, End | Move cursor to end of line
|
||||
Ctrl-B, Left | Move cursor one character left
|
||||
Ctrl-F, Right| Move cursor one character right
|
||||
Ctrl-Left, Alt-B | Move cursor to previous word
|
||||
Ctrl-Right, Alt-F | Move cursor to next word
|
||||
Ctrl-D, Del | (if line is *not* empty) Delete character under cursor
|
||||
Ctrl-D | (if line *is* empty) End of File - usually quits application
|
||||
Ctrl-C | Reset input (create new empty prompt)
|
||||
Ctrl-L | Clear screen (line is unmodified)
|
||||
Ctrl-T | Transpose previous character with current character
|
||||
Ctrl-H, BackSpace | Delete character before cursor
|
||||
Ctrl-W | Delete word leading up to cursor
|
||||
Ctrl-K | Delete from cursor to end of line
|
||||
Ctrl-U | Delete from start of line to cursor
|
||||
Ctrl-P, Up | Previous match from history
|
||||
Ctrl-N, Down | Next match from history
|
||||
Ctrl-R | Reverse Search history (Ctrl-S forward, Ctrl-G cancel)
|
||||
Ctrl-Y | Paste from Yank buffer (Alt-Y to paste next yank instead)
|
||||
Tab | Next completion
|
||||
Shift-Tab | (after Tab) Previous completion
|
||||
|
||||
Getting started
|
||||
-----------------
|
||||
|
||||
```go
|
||||
package main
|
||||
|
||||
import (
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
|
||||
"github.com/peterh/liner"
|
||||
)
|
||||
|
||||
var (
|
||||
history_fn = filepath.Join(os.TempDir(), ".liner_example_history")
|
||||
names = []string{"john", "james", "mary", "nancy"}
|
||||
)
|
||||
|
||||
func main() {
|
||||
line := liner.NewLiner()
|
||||
defer line.Close()
|
||||
|
||||
line.SetCtrlCAborts(true)
|
||||
|
||||
line.SetCompleter(func(line string) (c []string) {
|
||||
for _, n := range names {
|
||||
if strings.HasPrefix(n, strings.ToLower(line)) {
|
||||
c = append(c, n)
|
||||
}
|
||||
}
|
||||
return
|
||||
})
|
||||
|
||||
if f, err := os.Open(history_fn); err == nil {
|
||||
line.ReadHistory(f)
|
||||
f.Close()
|
||||
}
|
||||
|
||||
if name, err := line.Prompt("What is your name? "); err == nil {
|
||||
log.Print("Got: ", name)
|
||||
line.AppendHistory(name)
|
||||
} else if err == liner.ErrPromptAborted {
|
||||
log.Print("Aborted")
|
||||
} else {
|
||||
log.Print("Error reading line: ", err)
|
||||
}
|
||||
|
||||
if f, err := os.Create(history_fn); err != nil {
|
||||
log.Print("Error writing history file: ", err)
|
||||
} else {
|
||||
line.WriteHistory(f)
|
||||
f.Close()
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
For documentation, see http://godoc.org/github.com/peterh/liner
|
41
vendor/github.com/peterh/liner/bsdinput.go
generated
vendored
Normal file
41
vendor/github.com/peterh/liner/bsdinput.go
generated
vendored
Normal file
@ -0,0 +1,41 @@
|
||||
// +build openbsd freebsd netbsd
|
||||
|
||||
package liner
|
||||
|
||||
import "syscall"
|
||||
|
||||
const (
|
||||
getTermios = syscall.TIOCGETA
|
||||
setTermios = syscall.TIOCSETA
|
||||
)
|
||||
|
||||
const (
|
||||
// Input flags
|
||||
inpck = 0x010
|
||||
istrip = 0x020
|
||||
icrnl = 0x100
|
||||
ixon = 0x200
|
||||
|
||||
// Output flags
|
||||
opost = 0x1
|
||||
|
||||
// Control flags
|
||||
cs8 = 0x300
|
||||
|
||||
// Local flags
|
||||
isig = 0x080
|
||||
icanon = 0x100
|
||||
iexten = 0x400
|
||||
)
|
||||
|
||||
type termios struct {
|
||||
Iflag uint32
|
||||
Oflag uint32
|
||||
Cflag uint32
|
||||
Lflag uint32
|
||||
Cc [20]byte
|
||||
Ispeed int32
|
||||
Ospeed int32
|
||||
}
|
||||
|
||||
const cursorColumn = false
|
237
vendor/github.com/peterh/liner/common.go
generated
vendored
Normal file
237
vendor/github.com/peterh/liner/common.go
generated
vendored
Normal file
@ -0,0 +1,237 @@
|
||||
/*
|
||||
Package liner implements a simple command line editor, inspired by linenoise
|
||||
(https://github.com/antirez/linenoise/). This package supports WIN32 in
|
||||
addition to the xterm codes supported by everything else.
|
||||
*/
|
||||
package liner
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"container/ring"
|
||||
"errors"
|
||||
"fmt"
|
||||
"io"
|
||||
"strings"
|
||||
"sync"
|
||||
"unicode/utf8"
|
||||
)
|
||||
|
||||
type commonState struct {
|
||||
terminalSupported bool
|
||||
outputRedirected bool
|
||||
inputRedirected bool
|
||||
history []string
|
||||
historyMutex sync.RWMutex
|
||||
completer WordCompleter
|
||||
columns int
|
||||
killRing *ring.Ring
|
||||
ctrlCAborts bool
|
||||
r *bufio.Reader
|
||||
tabStyle TabStyle
|
||||
multiLineMode bool
|
||||
cursorRows int
|
||||
maxRows int
|
||||
shouldRestart ShouldRestart
|
||||
}
|
||||
|
||||
// TabStyle is used to select how tab completions are displayed.
|
||||
type TabStyle int
|
||||
|
||||
// Two tab styles are currently available:
|
||||
//
|
||||
// TabCircular cycles through each completion item and displays it directly on
|
||||
// the prompt
|
||||
//
|
||||
// TabPrints prints the list of completion items to the screen after a second
|
||||
// tab key is pressed. This behaves similar to GNU readline and BASH (which
|
||||
// uses readline)
|
||||
const (
|
||||
TabCircular TabStyle = iota
|
||||
TabPrints
|
||||
)
|
||||
|
||||
// ErrPromptAborted is returned from Prompt or PasswordPrompt when the user presses Ctrl-C
|
||||
// if SetCtrlCAborts(true) has been called on the State
|
||||
var ErrPromptAborted = errors.New("prompt aborted")
|
||||
|
||||
// ErrNotTerminalOutput is returned from Prompt or PasswordPrompt if the
|
||||
// platform is normally supported, but stdout has been redirected
|
||||
var ErrNotTerminalOutput = errors.New("standard output is not a terminal")
|
||||
|
||||
// Max elements to save on the killring
|
||||
const KillRingMax = 60
|
||||
|
||||
// HistoryLimit is the maximum number of entries saved in the scrollback history.
|
||||
const HistoryLimit = 1000
|
||||
|
||||
// ReadHistory reads scrollback history from r. Returns the number of lines
|
||||
// read, and any read error (except io.EOF).
|
||||
func (s *State) ReadHistory(r io.Reader) (num int, err error) {
|
||||
s.historyMutex.Lock()
|
||||
defer s.historyMutex.Unlock()
|
||||
|
||||
in := bufio.NewReader(r)
|
||||
num = 0
|
||||
for {
|
||||
line, part, err := in.ReadLine()
|
||||
if err == io.EOF {
|
||||
break
|
||||
}
|
||||
if err != nil {
|
||||
return num, err
|
||||
}
|
||||
if part {
|
||||
return num, fmt.Errorf("line %d is too long", num+1)
|
||||
}
|
||||
if !utf8.Valid(line) {
|
||||
return num, fmt.Errorf("invalid string at line %d", num+1)
|
||||
}
|
||||
num++
|
||||
s.history = append(s.history, string(line))
|
||||
if len(s.history) > HistoryLimit {
|
||||
s.history = s.history[1:]
|
||||
}
|
||||
}
|
||||
return num, nil
|
||||
}
|
||||
|
||||
// WriteHistory writes scrollback history to w. Returns the number of lines
|
||||
// successfully written, and any write error.
|
||||
//
|
||||
// Unlike the rest of liner's API, WriteHistory is safe to call
|
||||
// from another goroutine while Prompt is in progress.
|
||||
// This exception is to facilitate the saving of the history buffer
|
||||
// during an unexpected exit (for example, due to Ctrl-C being invoked)
|
||||
func (s *State) WriteHistory(w io.Writer) (num int, err error) {
|
||||
s.historyMutex.RLock()
|
||||
defer s.historyMutex.RUnlock()
|
||||
|
||||
for _, item := range s.history {
|
||||
_, err := fmt.Fprintln(w, item)
|
||||
if err != nil {
|
||||
return num, err
|
||||
}
|
||||
num++
|
||||
}
|
||||
return num, nil
|
||||
}
|
||||
|
||||
// AppendHistory appends an entry to the scrollback history. AppendHistory
|
||||
// should be called iff Prompt returns a valid command.
|
||||
func (s *State) AppendHistory(item string) {
|
||||
s.historyMutex.Lock()
|
||||
defer s.historyMutex.Unlock()
|
||||
|
||||
if len(s.history) > 0 {
|
||||
if item == s.history[len(s.history)-1] {
|
||||
return
|
||||
}
|
||||
}
|
||||
s.history = append(s.history, item)
|
||||
if len(s.history) > HistoryLimit {
|
||||
s.history = s.history[1:]
|
||||
}
|
||||
}
|
||||
|
||||
// Returns the history lines starting with prefix
|
||||
func (s *State) getHistoryByPrefix(prefix string) (ph []string) {
|
||||
for _, h := range s.history {
|
||||
if strings.HasPrefix(h, prefix) {
|
||||
ph = append(ph, h)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns the history lines matching the inteligent search
|
||||
func (s *State) getHistoryByPattern(pattern string) (ph []string, pos []int) {
|
||||
if pattern == "" {
|
||||
return
|
||||
}
|
||||
for _, h := range s.history {
|
||||
if i := strings.Index(h, pattern); i >= 0 {
|
||||
ph = append(ph, h)
|
||||
pos = append(pos, i)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Completer takes the currently edited line content at the left of the cursor
|
||||
// and returns a list of completion candidates.
|
||||
// If the line is "Hello, wo!!!" and the cursor is before the first '!', "Hello, wo" is passed
|
||||
// to the completer which may return {"Hello, world", "Hello, Word"} to have "Hello, world!!!".
|
||||
type Completer func(line string) []string
|
||||
|
||||
// WordCompleter takes the currently edited line with the cursor position and
|
||||
// returns the completion candidates for the partial word to be completed.
|
||||
// If the line is "Hello, wo!!!" and the cursor is before the first '!', ("Hello, wo!!!", 9) is passed
|
||||
// to the completer which may returns ("Hello, ", {"world", "Word"}, "!!!") to have "Hello, world!!!".
|
||||
type WordCompleter func(line string, pos int) (head string, completions []string, tail string)
|
||||
|
||||
// SetCompleter sets the completion function that Liner will call to
|
||||
// fetch completion candidates when the user presses tab.
|
||||
func (s *State) SetCompleter(f Completer) {
|
||||
if f == nil {
|
||||
s.completer = nil
|
||||
return
|
||||
}
|
||||
s.completer = func(line string, pos int) (string, []string, string) {
|
||||
return "", f(string([]rune(line)[:pos])), string([]rune(line)[pos:])
|
||||
}
|
||||
}
|
||||
|
||||
// SetWordCompleter sets the completion function that Liner will call to
|
||||
// fetch completion candidates when the user presses tab.
|
||||
func (s *State) SetWordCompleter(f WordCompleter) {
|
||||
s.completer = f
|
||||
}
|
||||
|
||||
// SetTabCompletionStyle sets the behvavior when the Tab key is pressed
|
||||
// for auto-completion. TabCircular is the default behavior and cycles
|
||||
// through the list of candidates at the prompt. TabPrints will print
|
||||
// the available completion candidates to the screen similar to BASH
|
||||
// and GNU Readline
|
||||
func (s *State) SetTabCompletionStyle(tabStyle TabStyle) {
|
||||
s.tabStyle = tabStyle
|
||||
}
|
||||
|
||||
// ModeApplier is the interface that wraps a representation of the terminal
|
||||
// mode. ApplyMode sets the terminal to this mode.
|
||||
type ModeApplier interface {
|
||||
ApplyMode() error
|
||||
}
|
||||
|
||||
// SetCtrlCAborts sets whether Prompt on a supported terminal will return an
|
||||
// ErrPromptAborted when Ctrl-C is pressed. The default is false (will not
|
||||
// return when Ctrl-C is pressed). Unsupported terminals typically raise SIGINT
|
||||
// (and Prompt does not return) regardless of the value passed to SetCtrlCAborts.
|
||||
func (s *State) SetCtrlCAborts(aborts bool) {
|
||||
s.ctrlCAborts = aborts
|
||||
}
|
||||
|
||||
// SetMultiLineMode sets whether line is auto-wrapped. The default is false (single line).
|
||||
func (s *State) SetMultiLineMode(mlmode bool) {
|
||||
s.multiLineMode = mlmode
|
||||
}
|
||||
|
||||
// ShouldRestart is passed the error generated by readNext and returns true if
|
||||
// the the read should be restarted or false if the error should be returned.
|
||||
type ShouldRestart func(err error) bool
|
||||
|
||||
// SetShouldRestart sets the restart function that Liner will call to determine
|
||||
// whether to retry the call to, or return the error returned by, readNext.
|
||||
func (s *State) SetShouldRestart(f ShouldRestart) {
|
||||
s.shouldRestart = f
|
||||
}
|
||||
|
||||
func (s *State) promptUnsupported(p string) (string, error) {
|
||||
if !s.inputRedirected || !s.terminalSupported {
|
||||
fmt.Print(p)
|
||||
}
|
||||
linebuf, _, err := s.r.ReadLine()
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
return string(linebuf), nil
|
||||
}
|
57
vendor/github.com/peterh/liner/fallbackinput.go
generated
vendored
Normal file
57
vendor/github.com/peterh/liner/fallbackinput.go
generated
vendored
Normal file
@ -0,0 +1,57 @@
|
||||
// +build !windows,!linux,!darwin,!openbsd,!freebsd,!netbsd
|
||||
|
||||
package liner
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"os"
|
||||
)
|
||||
|
||||
// State represents an open terminal
|
||||
type State struct {
|
||||
commonState
|
||||
}
|
||||
|
||||
// Prompt displays p, and then waits for user input. Prompt does not support
|
||||
// line editing on this operating system.
|
||||
func (s *State) Prompt(p string) (string, error) {
|
||||
return s.promptUnsupported(p)
|
||||
}
|
||||
|
||||
// PasswordPrompt is not supported in this OS.
|
||||
func (s *State) PasswordPrompt(p string) (string, error) {
|
||||
return "", errors.New("liner: function not supported in this terminal")
|
||||
}
|
||||
|
||||
// NewLiner initializes a new *State
|
||||
//
|
||||
// Note that this operating system uses a fallback mode without line
|
||||
// editing. Patches welcome.
|
||||
func NewLiner() *State {
|
||||
var s State
|
||||
s.r = bufio.NewReader(os.Stdin)
|
||||
return &s
|
||||
}
|
||||
|
||||
// Close returns the terminal to its previous mode
|
||||
func (s *State) Close() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TerminalSupported returns false because line editing is not
|
||||
// supported on this platform.
|
||||
func TerminalSupported() bool {
|
||||
return false
|
||||
}
|
||||
|
||||
type noopMode struct{}
|
||||
|
||||
func (n noopMode) ApplyMode() error {
|
||||
return nil
|
||||
}
|
||||
|
||||
// TerminalMode returns a noop InputModeSetter on this platform.
|
||||
func TerminalMode() (ModeApplier, error) {
|
||||
return noopMode{}, nil
|
||||
}
|
367
vendor/github.com/peterh/liner/input.go
generated
vendored
Normal file
367
vendor/github.com/peterh/liner/input.go
generated
vendored
Normal file
@ -0,0 +1,367 @@
|
||||
// +build linux darwin openbsd freebsd netbsd
|
||||
|
||||
package liner
|
||||
|
||||
import (
|
||||
"bufio"
|
||||
"errors"
|
||||
"os"
|
||||
"os/signal"
|
||||
"strconv"
|
||||
"strings"
|
||||
"syscall"
|
||||
"time"
|
||||
)
|
||||
|
||||
type nexter struct {
|
||||
r rune
|
||||
err error
|
||||
}
|
||||
|
||||
// State represents an open terminal
|
||||
type State struct {
|
||||
commonState
|
||||
origMode termios
|
||||
defaultMode termios
|
||||
next <-chan nexter
|
||||
winch chan os.Signal
|
||||
pending []rune
|
||||
useCHA bool
|
||||
}
|
||||
|
||||
// NewLiner initializes a new *State, and sets the terminal into raw mode. To
|
||||
// restore the terminal to its previous state, call State.Close().
|
||||
//
|
||||
// Note if you are still using Go 1.0: NewLiner handles SIGWINCH, so it will
|
||||
// leak a channel every time you call it. Therefore, it is recommened that you
|
||||
// upgrade to a newer release of Go, or ensure that NewLiner is only called
|
||||
// once.
|
||||
func NewLiner() *State {
|
||||
var s State
|
||||
s.r = bufio.NewReader(os.Stdin)
|
||||
|
||||
s.terminalSupported = TerminalSupported()
|
||||
if m, err := TerminalMode(); err == nil {
|
||||
s.origMode = *m.(*termios)
|
||||
} else {
|
||||
s.inputRedirected = true
|
||||
}
|
||||
if _, err := getMode(syscall.Stdout); err != 0 {
|
||||
s.outputRedirected = true
|
||||
}
|
||||
if s.inputRedirected && s.outputRedirected {
|
||||
s.terminalSupported = false
|
||||
}
|
||||
if s.terminalSupported && !s.inputRedirected && !s.outputRedirected {
|
||||
mode := s.origMode
|
||||
mode.Iflag &^= icrnl | inpck | istrip | ixon
|
||||
mode.Cflag |= cs8
|
||||
mode.Lflag &^= syscall.ECHO | icanon | iexten
|
||||
mode.ApplyMode()
|
||||
|
||||
winch := make(chan os.Signal, 1)
|
||||
signal.Notify(winch, syscall.SIGWINCH)
|
||||
s.winch = winch
|
||||
|
||||
s.checkOutput()
|
||||
}
|
||||
|
||||
if !s.outputRedirected {
|
||||
s.outputRedirected = !s.getColumns()
|
||||
}
|
||||
|
||||
return &s
|
||||
}
|
||||
|
||||
var errTimedOut = errors.New("timeout")
|
||||
|
||||
func (s *State) startPrompt() {
|
||||
if s.terminalSupported {
|
||||
if m, err := TerminalMode(); err == nil {
|
||||
s.defaultMode = *m.(*termios)
|
||||
mode := s.defaultMode
|
||||
mode.Lflag &^= isig
|
||||
mode.ApplyMode()
|
||||
}
|
||||
}
|
||||
s.restartPrompt()
|
||||
}
|
||||
|
||||
func (s *State) restartPrompt() {
|
||||
next := make(chan nexter)
|
||||
go func() {
|
||||
for {
|
||||
var n nexter
|
||||
n.r, _, n.err = s.r.ReadRune()
|
||||
next <- n
|
||||
// Shut down nexter loop when an end condition has been reached
|
||||
if n.err != nil || n.r == '\n' || n.r == '\r' || n.r == ctrlC || n.r == ctrlD {
|
||||
close(next)
|
||||
return
|
||||
}
|
||||
}
|
||||
}()
|
||||
s.next = next
|
||||
}
|
||||
|
||||
func (s *State) stopPrompt() {
|
||||
if s.terminalSupported {
|
||||
s.defaultMode.ApplyMode()
|
||||
}
|
||||
}
|
||||
|
||||
func (s *State) nextPending(timeout <-chan time.Time) (rune, error) {
|
||||
select {
|
||||
case thing, ok := <-s.next:
|
||||
if !ok {
|
||||
return 0, errors.New("liner: internal error")
|
||||
}
|
||||
if thing.err != nil {
|
||||
return 0, thing.err
|
||||
}
|
||||
s.pending = append(s.pending, thing.r)
|
||||
return thing.r, nil
|
||||
case <-timeout:
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, errTimedOut
|
||||
}
|
||||
// not reached
|
||||
return 0, nil
|
||||
}
|
||||
|
||||
func (s *State) readNext() (interface{}, error) {
|
||||
if len(s.pending) > 0 {
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
var r rune
|
||||
select {
|
||||
case thing, ok := <-s.next:
|
||||
if !ok {
|
||||
return 0, errors.New("liner: internal error")
|
||||
}
|
||||
if thing.err != nil {
|
||||
return nil, thing.err
|
||||
}
|
||||
r = thing.r
|
||||
case <-s.winch:
|
||||
s.getColumns()
|
||||
return winch, nil
|
||||
}
|
||||
if r != esc {
|
||||
return r, nil
|
||||
}
|
||||
s.pending = append(s.pending, r)
|
||||
|
||||
// Wait at most 50 ms for the rest of the escape sequence
|
||||
// If nothing else arrives, it was an actual press of the esc key
|
||||
timeout := time.After(50 * time.Millisecond)
|
||||
flag, err := s.nextPending(timeout)
|
||||
if err != nil {
|
||||
if err == errTimedOut {
|
||||
return flag, nil
|
||||
}
|
||||
return unknown, err
|
||||
}
|
||||
|
||||
switch flag {
|
||||
case '[':
|
||||
code, err := s.nextPending(timeout)
|
||||
if err != nil {
|
||||
if err == errTimedOut {
|
||||
return code, nil
|
||||
}
|
||||
return unknown, err
|
||||
}
|
||||
switch code {
|
||||
case 'A':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return up, nil
|
||||
case 'B':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return down, nil
|
||||
case 'C':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return right, nil
|
||||
case 'D':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return left, nil
|
||||
case 'F':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return end, nil
|
||||
case 'H':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return home, nil
|
||||
case 'Z':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return shiftTab, nil
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
num := []rune{code}
|
||||
for {
|
||||
code, err := s.nextPending(timeout)
|
||||
if err != nil {
|
||||
if err == errTimedOut {
|
||||
return code, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
switch code {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
num = append(num, code)
|
||||
case ';':
|
||||
// Modifier code to follow
|
||||
// This only supports Ctrl-left and Ctrl-right for now
|
||||
x, _ := strconv.ParseInt(string(num), 10, 32)
|
||||
if x != 1 {
|
||||
// Can't be left or right
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
num = num[:0]
|
||||
for {
|
||||
code, err = s.nextPending(timeout)
|
||||
if err != nil {
|
||||
if err == errTimedOut {
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
switch code {
|
||||
case '0', '1', '2', '3', '4', '5', '6', '7', '8', '9':
|
||||
num = append(num, code)
|
||||
case 'C', 'D':
|
||||
// right, left
|
||||
mod, _ := strconv.ParseInt(string(num), 10, 32)
|
||||
if mod != 5 {
|
||||
// Not bare Ctrl
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
if code == 'C' {
|
||||
return wordRight, nil
|
||||
}
|
||||
return wordLeft, nil
|
||||
default:
|
||||
// Not left or right
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
}
|
||||
case '~':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
x, _ := strconv.ParseInt(string(num), 10, 32)
|
||||
switch x {
|
||||
case 2:
|
||||
return insert, nil
|
||||
case 3:
|
||||
return del, nil
|
||||
case 5:
|
||||
return pageUp, nil
|
||||
case 6:
|
||||
return pageDown, nil
|
||||
case 7:
|
||||
return home, nil
|
||||
case 8:
|
||||
return end, nil
|
||||
case 15:
|
||||
return f5, nil
|
||||
case 17:
|
||||
return f6, nil
|
||||
case 18:
|
||||
return f7, nil
|
||||
case 19:
|
||||
return f8, nil
|
||||
case 20:
|
||||
return f9, nil
|
||||
case 21:
|
||||
return f10, nil
|
||||
case 23:
|
||||
return f11, nil
|
||||
case 24:
|
||||
return f12, nil
|
||||
default:
|
||||
return unknown, nil
|
||||
}
|
||||
default:
|
||||
// unrecognized escape code
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
case 'O':
|
||||
code, err := s.nextPending(timeout)
|
||||
if err != nil {
|
||||
if err == errTimedOut {
|
||||
return code, nil
|
||||
}
|
||||
return nil, err
|
||||
}
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
switch code {
|
||||
case 'c':
|
||||
return wordRight, nil
|
||||
case 'd':
|
||||
return wordLeft, nil
|
||||
case 'H':
|
||||
return home, nil
|
||||
case 'F':
|
||||
return end, nil
|
||||
case 'P':
|
||||
return f1, nil
|
||||
case 'Q':
|
||||
return f2, nil
|
||||
case 'R':
|
||||
return f3, nil
|
||||
case 'S':
|
||||
return f4, nil
|
||||
default:
|
||||
return unknown, nil
|
||||
}
|
||||
case 'b':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return altB, nil
|
||||
case 'f':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return altF, nil
|
||||
case 'y':
|
||||
s.pending = s.pending[:0] // escape code complete
|
||||
return altY, nil
|
||||
default:
|
||||
rv := s.pending[0]
|
||||
s.pending = s.pending[1:]
|
||||
return rv, nil
|
||||
}
|
||||
|
||||
// not reached
|
||||
return r, nil
|
||||
}
|
||||
|
||||
// Close returns the terminal to its previous mode
|
||||
func (s *State) Close() error {
|
||||
stopSignal(s.winch)
|
||||
if !s.inputRedirected {
|
||||
s.origMode.ApplyMode()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// TerminalSupported returns true if the current terminal supports
|
||||
// line editing features, and false if liner will use the 'dumb'
|
||||
// fallback for input.
|
||||
// Note that TerminalSupported does not check all factors that may
|
||||
// cause liner to not fully support the terminal (such as stdin redirection)
|
||||
func TerminalSupported() bool {
|
||||
bad := map[string]bool{"": true, "dumb": true, "cons25": true}
|
||||
return !bad[strings.ToLower(os.Getenv("TERM"))]
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user