mirror of
				https://github.com/beego/bee.git
				synced 2025-11-04 04:13:26 +00:00 
			
		
		
		
	Updated vendor
This commit is contained in:
		@@ -228,7 +228,7 @@ func startWatcher(paths []string, ch chan int) {
 | 
			
		||||
 | 
			
		||||
					// Wait 1s before re-build until there is no file change
 | 
			
		||||
					scheduleTime := time.Now().Add(1 * time.Second)
 | 
			
		||||
					time.Sleep(scheduleTime.Sub(time.Now()))
 | 
			
		||||
					time.Sleep(time.Until(scheduleTime))
 | 
			
		||||
					_, err := buildDebug()
 | 
			
		||||
					if err != nil {
 | 
			
		||||
						utils.Notify("Build Failed: "+err.Error(), "bee")
 | 
			
		||||
 
 | 
			
		||||
@@ -85,7 +85,7 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
 | 
			
		||||
					go func() {
 | 
			
		||||
						// Wait 1s before autobuild until there is no file change.
 | 
			
		||||
						scheduleTime = time.Now().Add(1 * time.Second)
 | 
			
		||||
						time.Sleep(scheduleTime.Sub(time.Now()))
 | 
			
		||||
						time.Sleep(time.Until(scheduleTime))
 | 
			
		||||
						AutoBuild(files, isgenerate)
 | 
			
		||||
 | 
			
		||||
						if config.Conf.EnableReload {
 | 
			
		||||
 
 | 
			
		||||
@@ -116,7 +116,7 @@ func ParsePackagesFromDir(dirpath string) {
 | 
			
		||||
				err = parsePackageFromDir(fpath)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					// Send the error to through the channel and continue walking
 | 
			
		||||
					c <- fmt.Errorf("Error while parsing directory: %s", err.Error())
 | 
			
		||||
					c <- fmt.Errorf("error while parsing directory: %s", err.Error())
 | 
			
		||||
					return nil
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 
 | 
			
		||||
@@ -328,7 +328,7 @@ func CheckEnv(appname string) (apppath, packpath string, err error) {
 | 
			
		||||
	apppath = filepath.Join(gosrcpath, appname)
 | 
			
		||||
 | 
			
		||||
	if _, e := os.Stat(apppath); !os.IsNotExist(e) {
 | 
			
		||||
		err = fmt.Errorf("Cannot create application without removing '%s' first", apppath)
 | 
			
		||||
		err = fmt.Errorf("cannot create application without removing '%s' first", apppath)
 | 
			
		||||
		beeLogger.Log.Errorf("Path '%s' already exists", apppath)
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										13
									
								
								vendor/github.com/astaxie/beego/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										13
									
								
								vendor/github.com/astaxie/beego/LICENSE
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,13 @@
 | 
			
		||||
Copyright 2014 astaxie
 | 
			
		||||
 | 
			
		||||
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.
 | 
			
		||||
							
								
								
									
										31
									
								
								vendor/github.com/cosiner/argv/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										31
									
								
								vendor/github.com/cosiner/argv/README.md
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,31 @@
 | 
			
		||||
# Argv
 | 
			
		||||
 | 
			
		||||
[](https://godoc.org/github.com/cosiner/argv) 
 | 
			
		||||
[](https://travis-ci.org/cosiner/argv)
 | 
			
		||||
[](https://coveralls.io/github/cosiner/argv)
 | 
			
		||||
[](https://goreportcard.com/report/github.com/cosiner/argv)
 | 
			
		||||
 | 
			
		||||
Argv is a  library for [Go](https://golang.org) to split command line string into arguments array. 
 | 
			
		||||
 | 
			
		||||
# Documentation
 | 
			
		||||
Documentation can be found at [Godoc](https://godoc.org/github.com/cosiner/argv)
 | 
			
		||||
 | 
			
		||||
# Example
 | 
			
		||||
```Go
 | 
			
		||||
func TestArgv(t *testing.T) {
 | 
			
		||||
	args, err := argv.Argv([]rune(" ls   `echo /`   |  wc  -l "), os.Environ(), argv.Run)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
	    t.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
	expects := [][]string{
 | 
			
		||||
	    []string{"ls", "/"},
 | 
			
		||||
	    []string{"wc", "-l"},
 | 
			
		||||
	}
 | 
			
		||||
	if !reflect.DeepDqual(args, expects) {
 | 
			
		||||
	    t.Fatal(args)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
```
 | 
			
		||||
 | 
			
		||||
# LICENSE
 | 
			
		||||
MIT.
 | 
			
		||||
							
								
								
									
										83
									
								
								vendor/github.com/derekparker/delve/pkg/logflags/logflags.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										83
									
								
								vendor/github.com/derekparker/delve/pkg/logflags/logflags.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,83 +0,0 @@
 | 
			
		||||
package logflags
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"strings"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var debugger = false
 | 
			
		||||
var gdbWire = false
 | 
			
		||||
var lldbServerOutput = false
 | 
			
		||||
var debugLineErrors = false
 | 
			
		||||
var rpc = false
 | 
			
		||||
var fnCall = false
 | 
			
		||||
 | 
			
		||||
// GdbWire returns true if the gdbserial package should log all the packets
 | 
			
		||||
// exchanged with the stub.
 | 
			
		||||
func GdbWire() bool {
 | 
			
		||||
	return gdbWire
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debugger returns true if the debugger package should log.
 | 
			
		||||
func Debugger() bool {
 | 
			
		||||
	return debugger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LLDBServerOutput returns true if the output of the LLDB server should be
 | 
			
		||||
// redirected to standard output instead of suppressed.
 | 
			
		||||
func LLDBServerOutput() bool {
 | 
			
		||||
	return lldbServerOutput
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DebugLineErrors returns true if pkg/dwarf/line should log its recoverable
 | 
			
		||||
// errors.
 | 
			
		||||
func DebugLineErrors() bool {
 | 
			
		||||
	return debugLineErrors
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RPC returns true if rpc messages should be logged.
 | 
			
		||||
func RPC() bool {
 | 
			
		||||
	return rpc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FnCall returns true if the function call protocol should be logged.
 | 
			
		||||
func FnCall() bool {
 | 
			
		||||
	return fnCall
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errLogstrWithoutLog = errors.New("--log-output specified without --log")
 | 
			
		||||
 | 
			
		||||
// Setup sets debugger flags based on the contents of logstr.
 | 
			
		||||
func Setup(logFlag bool, logstr string) error {
 | 
			
		||||
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
 | 
			
		||||
	if !logFlag {
 | 
			
		||||
		log.SetOutput(ioutil.Discard)
 | 
			
		||||
		if logstr != "" {
 | 
			
		||||
			return errLogstrWithoutLog
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if logstr == "" {
 | 
			
		||||
		logstr = "debugger"
 | 
			
		||||
	}
 | 
			
		||||
	v := strings.Split(logstr, ",")
 | 
			
		||||
	for _, logcmd := range v {
 | 
			
		||||
		switch logcmd {
 | 
			
		||||
		case "debugger":
 | 
			
		||||
			debugger = true
 | 
			
		||||
		case "gdbwire":
 | 
			
		||||
			gdbWire = true
 | 
			
		||||
		case "lldbout":
 | 
			
		||||
			lldbServerOutput = true
 | 
			
		||||
		case "debuglineerr":
 | 
			
		||||
			debugLineErrors = true
 | 
			
		||||
		case "rpc":
 | 
			
		||||
			rpc = true
 | 
			
		||||
		case "fncall":
 | 
			
		||||
			fnCall = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										119
									
								
								vendor/github.com/derekparker/delve/pkg/proc/native/mach_exc.defs
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										119
									
								
								vendor/github.com/derekparker/delve/pkg/proc/native/mach_exc.defs
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,119 +0,0 @@
 | 
			
		||||
/*
 | 
			
		||||
 * 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 : */
 | 
			
		||||
							
								
								
									
										342
									
								
								vendor/github.com/derekparker/delve/pkg/proc/native/registers_linux_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										342
									
								
								vendor/github.com/derekparker/delve/pkg/proc/native/registers_linux_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,342 +0,0 @@
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/arch/x86/x86asm"
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Regs is a wrapper for sys.PtraceRegs.
 | 
			
		||||
type Regs struct {
 | 
			
		||||
	regs     *sys.PtraceRegs
 | 
			
		||||
	fpregs   []proc.Register
 | 
			
		||||
	fpregset *proc.LinuxX86Xstate
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) Slice() []proc.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([]proc.Register, 0, len(regs)+len(r.fpregs))
 | 
			
		||||
	for _, reg := range regs {
 | 
			
		||||
		if reg.k == "Eflags" {
 | 
			
		||||
			out = proc.AppendEflagReg(out, reg.k, reg.v)
 | 
			
		||||
		} else {
 | 
			
		||||
			out = proc.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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) GAddr() (uint64, bool) {
 | 
			
		||||
	return 0, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPC sets RIP to the value specified by 'pc'.
 | 
			
		||||
func (thread *Thread) SetPC(pc uint64) error {
 | 
			
		||||
	ir, err := registers(thread, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	r := ir.(*Regs)
 | 
			
		||||
	r.regs.SetPC(pc)
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) })
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSP sets RSP to the value specified by 'sp'
 | 
			
		||||
func (thread *Thread) SetSP(sp uint64) (err error) {
 | 
			
		||||
	var ir proc.Registers
 | 
			
		||||
	ir, err = registers(thread, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	r := ir.(*Regs)
 | 
			
		||||
	r.regs.Rsp = sp
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, r.regs) })
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) SetDX(dx uint64) (err error) {
 | 
			
		||||
	var ir proc.Registers
 | 
			
		||||
	ir, err = registers(thread, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	r := ir.(*Regs)
 | 
			
		||||
	r.regs.Rdx = dx
 | 
			
		||||
	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, proc.ErrUnknownRegister
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func registers(thread *Thread, floatingPoint bool) (proc.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, nil}
 | 
			
		||||
	if floatingPoint {
 | 
			
		||||
		var fpregset proc.LinuxX86Xstate
 | 
			
		||||
		r.fpregs, fpregset, err = thread.fpRegisters()
 | 
			
		||||
		r.fpregset = &fpregset
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 []proc.Register, fpregs proc.LinuxX86Xstate, err error) {
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) })
 | 
			
		||||
	regs = fpregs.Decode()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("could not get floating point registers: %v", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Copy returns a copy of these registers that is
 | 
			
		||||
// guarenteed not to change.
 | 
			
		||||
func (r *Regs) Copy() proc.Registers {
 | 
			
		||||
	var rr Regs
 | 
			
		||||
	rr.regs = &sys.PtraceRegs{}
 | 
			
		||||
	rr.fpregset = &proc.LinuxX86Xstate{}
 | 
			
		||||
	*(rr.regs) = *(r.regs)
 | 
			
		||||
	if r.fpregset != nil {
 | 
			
		||||
		*(rr.fpregset) = *(r.fpregset)
 | 
			
		||||
	}
 | 
			
		||||
	if r.fpregs != nil {
 | 
			
		||||
		rr.fpregs = make([]proc.Register, len(r.fpregs))
 | 
			
		||||
		copy(rr.fpregs, r.fpregs)
 | 
			
		||||
	}
 | 
			
		||||
	return &rr
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										114
									
								
								vendor/github.com/derekparker/delve/pkg/proc/native/syscall_windows_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
								
							
							
						
						
									
										114
									
								
								vendor/github.com/derekparker/delve/pkg/proc/native/syscall_windows_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
								
							@@ -1,114 +0,0 @@
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
@@ -2,20 +2,23 @@ package config
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/user"
 | 
			
		||||
	"path"
 | 
			
		||||
	"runtime"
 | 
			
		||||
 | 
			
		||||
	"gopkg.in/yaml.v2"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	configDir  string = ".dlv"
 | 
			
		||||
	configFile string = "config.yml"
 | 
			
		||||
	configDir       string = "dlv"
 | 
			
		||||
	configDirHidden string = ".dlv"
 | 
			
		||||
	configFile      string = "config.yml"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Describes a rule for substitution of path to source code file.
 | 
			
		||||
// SubstitutePathRule 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
 | 
			
		||||
@@ -23,7 +26,7 @@ type SubstitutePathRule struct {
 | 
			
		||||
	To string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Slice of source code path substitution rules.
 | 
			
		||||
// SubstitutePathRules is a slice of source code path substitution rules.
 | 
			
		||||
type SubstitutePathRules []SubstitutePathRule
 | 
			
		||||
 | 
			
		||||
// Config defines all configuration options available to be set through the config file.
 | 
			
		||||
@@ -47,6 +50,10 @@ type Config struct {
 | 
			
		||||
	// Source list line-number color (3/4 bit color codes as defined
 | 
			
		||||
	// here: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors)
 | 
			
		||||
	SourceListLineColor int `yaml:"source-list-line-color"`
 | 
			
		||||
 | 
			
		||||
	// DebugFileDirectories is the list of directories Delve will use
 | 
			
		||||
	// in order to resolve external debug info files.
 | 
			
		||||
	DebugInfoDirectories []string `yaml:"debug-info-directories"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadConfig attempts to populate a Config object from the config.yml file.
 | 
			
		||||
@@ -54,12 +61,32 @@ func LoadConfig() *Config {
 | 
			
		||||
	err := createConfigPath()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Could not create config directory: %v.", err)
 | 
			
		||||
		return nil
 | 
			
		||||
		return &Config{}
 | 
			
		||||
	}
 | 
			
		||||
	fullConfigFile, err := GetConfigFilePath(configFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Unable to get config file path: %v.", err)
 | 
			
		||||
		return nil
 | 
			
		||||
		return &Config{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	hasOldConfig, err := hasOldConfig()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "Unable to determine if old config exists: %v\n", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if hasOldConfig {
 | 
			
		||||
		userHomeDir := getUserHomeDir()
 | 
			
		||||
		oldLocation := path.Join(userHomeDir, configDirHidden)
 | 
			
		||||
		if err := moveOldConfig(); err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "Unable to move old config: %v\n", err)
 | 
			
		||||
			return &Config{}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := os.RemoveAll(oldLocation); err != nil {
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "Unable to remove old config location: %v\n", err)
 | 
			
		||||
			return &Config{}
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Fprintf(os.Stderr, "Successfully moved config from: %s to: %s\n", oldLocation, fullConfigFile)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f, err := os.Open(fullConfigFile)
 | 
			
		||||
@@ -67,7 +94,7 @@ func LoadConfig() *Config {
 | 
			
		||||
		f, err = createDefaultConfig(fullConfigFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			fmt.Printf("Error creating default config file: %v", err)
 | 
			
		||||
			return nil
 | 
			
		||||
			return &Config{}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	defer func() {
 | 
			
		||||
@@ -80,19 +107,25 @@ func LoadConfig() *Config {
 | 
			
		||||
	data, err := ioutil.ReadAll(f)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Unable to read config data: %v.", err)
 | 
			
		||||
		return nil
 | 
			
		||||
		return &Config{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var c Config
 | 
			
		||||
	err = yaml.Unmarshal(data, &c)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		fmt.Printf("Unable to decode config file: %v.", err)
 | 
			
		||||
		return nil
 | 
			
		||||
		return &Config{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if len(c.DebugInfoDirectories) == 0 {
 | 
			
		||||
		c.DebugInfoDirectories = []string{"/usr/lib/debug/.build-id"}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &c
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SaveConfig will marshal and save the config struct
 | 
			
		||||
// to disk.
 | 
			
		||||
func SaveConfig(conf *Config) error {
 | 
			
		||||
	fullConfigFile, err := GetConfigFilePath(configFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -114,15 +147,42 @@ func SaveConfig(conf *Config) error {
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// moveOldConfig attempts to move config to new location
 | 
			
		||||
// $HOME/.dlv to $XDG_CONFIG_HOME/dlv
 | 
			
		||||
func moveOldConfig() error {
 | 
			
		||||
	if os.Getenv("XDG_CONFIG_HOME") == "" && runtime.GOOS != "linux" {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	userHomeDir := getUserHomeDir()
 | 
			
		||||
 | 
			
		||||
	p := path.Join(userHomeDir, configDirHidden, configFile)
 | 
			
		||||
	_, err := os.Stat(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to read config file located at: %s", p)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newFile, err := GetConfigFilePath(configFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to read config file located at: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := os.Rename(p, newFile); err != nil {
 | 
			
		||||
		return fmt.Errorf("unable to move %s to %s", p, newFile)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createDefaultConfig(path string) (*os.File, error) {
 | 
			
		||||
	f, err := os.Create(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Unable to create config file: %v.", err)
 | 
			
		||||
		return nil, fmt.Errorf("unable to create config file: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	err = writeDefaultConfig(f)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("Unable to write default configuration: %v.", err)
 | 
			
		||||
		return nil, fmt.Errorf("unable to write default configuration: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	f.Seek(0, io.SeekStart)
 | 
			
		||||
	return f, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -158,6 +218,9 @@ substitute-path:
 | 
			
		||||
 | 
			
		||||
# Uncomment the following line to make the whatis command also print the DWARF location expression of its argument.
 | 
			
		||||
# show-location-expr: true
 | 
			
		||||
 | 
			
		||||
# List of directories to use when searching for separate debug info files.
 | 
			
		||||
debug-info-directories: ["/usr/lib/debug/.build-id"]
 | 
			
		||||
`)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
@@ -173,9 +236,43 @@ func createConfigPath() error {
 | 
			
		||||
 | 
			
		||||
// 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
 | 
			
		||||
	if configPath := os.Getenv("XDG_CONFIG_HOME"); configPath != "" {
 | 
			
		||||
		return path.Join(configPath, configDir, file), nil
 | 
			
		||||
	}
 | 
			
		||||
	return path.Join(usr.HomeDir, configDir, file), nil
 | 
			
		||||
 | 
			
		||||
	userHomeDir := getUserHomeDir()
 | 
			
		||||
 | 
			
		||||
	if runtime.GOOS == "linux" {
 | 
			
		||||
		return path.Join(userHomeDir, ".config", configDir, file), nil
 | 
			
		||||
	}
 | 
			
		||||
	return path.Join(userHomeDir, configDirHidden, file), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Checks if the user has a config at the old location: $HOME/.dlv
 | 
			
		||||
func hasOldConfig() (bool, error) {
 | 
			
		||||
	// If you don't have XDG_CONFIG_HOME set and aren't on Linux you have nothing to move
 | 
			
		||||
	if os.Getenv("XDG_CONFIG_HOME") == "" && runtime.GOOS != "linux" {
 | 
			
		||||
		return false, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	userHomeDir := getUserHomeDir()
 | 
			
		||||
 | 
			
		||||
	o := path.Join(userHomeDir, configDirHidden, configFile)
 | 
			
		||||
	_, err := os.Stat(o)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if os.IsNotExist(err) {
 | 
			
		||||
			return false, nil
 | 
			
		||||
		}
 | 
			
		||||
		return false, err
 | 
			
		||||
	}
 | 
			
		||||
	return true, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func getUserHomeDir() string {
 | 
			
		||||
	userHomeDir := "."
 | 
			
		||||
	usr, err := user.Current()
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		userHomeDir = usr.HomeDir
 | 
			
		||||
	}
 | 
			
		||||
	return userHomeDir
 | 
			
		||||
}
 | 
			
		||||
@@ -5,7 +5,7 @@ import (
 | 
			
		||||
	"unicode"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Like strings.Fields but ignores spaces inside areas surrounded
 | 
			
		||||
// SplitQuotedFields is like strings.Fields but ignores spaces inside areas surrounded
 | 
			
		||||
// by the specified quote character.
 | 
			
		||||
// To specify a single quote use backslash to escape it: '\''
 | 
			
		||||
func SplitQuotedFields(in string, quote rune) []string {
 | 
			
		||||
@@ -7,7 +7,7 @@ import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/util"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type parsefunc func(*parseContext) parsefunc
 | 
			
		||||
@@ -5,7 +5,7 @@ import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/util"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DWRule struct {
 | 
			
		||||
@@ -16,8 +16,8 @@ import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/util"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -41,6 +41,20 @@ const (
 | 
			
		||||
	encImaginaryFloat = 0x09
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const cyclicalTypeStop = "<cyclical>" // guard value printed for types with a cyclical definition, to avoid inifinite recursion in Type.String
 | 
			
		||||
 | 
			
		||||
type recCheck map[dwarf.Offset]struct{}
 | 
			
		||||
 | 
			
		||||
func (recCheck recCheck) acquire(off dwarf.Offset) (release func()) {
 | 
			
		||||
	if _, rec := recCheck[off]; rec {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	recCheck[off] = struct{}{}
 | 
			
		||||
	return func() {
 | 
			
		||||
		delete(recCheck, off)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A Type conventionally represents a pointer to any of the
 | 
			
		||||
// specific Type structures (CharType, StructType, etc.).
 | 
			
		||||
//TODO: remove this use dwarf.Type
 | 
			
		||||
@@ -48,6 +62,9 @@ type Type interface {
 | 
			
		||||
	Common() *CommonType
 | 
			
		||||
	String() string
 | 
			
		||||
	Size() int64
 | 
			
		||||
 | 
			
		||||
	stringIntl(recCheck) string
 | 
			
		||||
	sizeIntl(recCheck) int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A CommonType holds fields common to multiple types.
 | 
			
		||||
@@ -62,7 +79,8 @@ type CommonType struct {
 | 
			
		||||
 | 
			
		||||
func (c *CommonType) Common() *CommonType { return c }
 | 
			
		||||
 | 
			
		||||
func (c *CommonType) Size() int64 { return c.ByteSize }
 | 
			
		||||
func (c *CommonType) Size() int64             { return c.ByteSize }
 | 
			
		||||
func (c *CommonType) sizeIntl(recCheck) int64 { return c.ByteSize }
 | 
			
		||||
 | 
			
		||||
// Basic types
 | 
			
		||||
 | 
			
		||||
@@ -75,7 +93,9 @@ type BasicType struct {
 | 
			
		||||
 | 
			
		||||
func (b *BasicType) Basic() *BasicType { return b }
 | 
			
		||||
 | 
			
		||||
func (t *BasicType) String() string {
 | 
			
		||||
func (t *BasicType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *BasicType) stringIntl(recCheck) string {
 | 
			
		||||
	if t.Name != "" {
 | 
			
		||||
		return t.Name
 | 
			
		||||
	}
 | 
			
		||||
@@ -136,9 +156,27 @@ type QualType struct {
 | 
			
		||||
	Type Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *QualType) String() string { return t.Qual + " " + t.Type.String() }
 | 
			
		||||
func (t *QualType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *QualType) Size() int64 { return t.Type.Size() }
 | 
			
		||||
func (t *QualType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	return t.Qual + " " + t.Type.stringIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *QualType) Size() int64 { return t.sizeIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *QualType) sizeIntl(recCheck recCheck) int64 {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return t.CommonType.ByteSize
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	return t.Type.sizeIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// An ArrayType represents a fixed size array type.
 | 
			
		||||
type ArrayType struct {
 | 
			
		||||
@@ -148,18 +186,36 @@ type ArrayType struct {
 | 
			
		||||
	Count         int64 // if == -1, an incomplete array, like char x[].
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *ArrayType) String() string {
 | 
			
		||||
	return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.String()
 | 
			
		||||
func (t *ArrayType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *ArrayType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.stringIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *ArrayType) Size() int64 { return t.Count * t.Type.Size() }
 | 
			
		||||
func (t *ArrayType) Size() int64 { return t.sizeIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *ArrayType) sizeIntl(recCheck recCheck) int64 {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return t.CommonType.ByteSize
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	return t.Count * t.Type.sizeIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A VoidType represents the C void type.
 | 
			
		||||
type VoidType struct {
 | 
			
		||||
	CommonType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *VoidType) String() string { return "void" }
 | 
			
		||||
func (t *VoidType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *VoidType) stringIntl(recCheck) string { return "void" }
 | 
			
		||||
 | 
			
		||||
// A PtrType represents a pointer type.
 | 
			
		||||
type PtrType struct {
 | 
			
		||||
@@ -167,7 +223,16 @@ type PtrType struct {
 | 
			
		||||
	Type Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *PtrType) String() string { return "*" + t.Type.String() }
 | 
			
		||||
func (t *PtrType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *PtrType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	return "*" + t.Type.stringIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A StructType represents a struct, union, or C++ class type.
 | 
			
		||||
type StructType struct {
 | 
			
		||||
@@ -189,14 +254,21 @@ type StructField struct {
 | 
			
		||||
	Embedded   bool
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *StructType) String() string {
 | 
			
		||||
func (t *StructType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *StructType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	if t.StructName != "" {
 | 
			
		||||
		return t.Kind + " " + t.StructName
 | 
			
		||||
	}
 | 
			
		||||
	return t.Defn()
 | 
			
		||||
	return t.Defn(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *StructType) Defn() string {
 | 
			
		||||
func (t *StructType) Defn(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	s := t.Kind
 | 
			
		||||
	if t.StructName != "" {
 | 
			
		||||
		s += " " + t.StructName
 | 
			
		||||
@@ -210,7 +282,7 @@ func (t *StructType) Defn() string {
 | 
			
		||||
		if i > 0 {
 | 
			
		||||
			s += "; "
 | 
			
		||||
		}
 | 
			
		||||
		s += f.Name + " " + f.Type.String()
 | 
			
		||||
		s += f.Name + " " + f.Type.stringIntl(recCheck)
 | 
			
		||||
		s += "@" + strconv.FormatInt(f.ByteOffset, 10)
 | 
			
		||||
		if f.BitSize > 0 {
 | 
			
		||||
			s += " : " + strconv.FormatInt(f.BitSize, 10)
 | 
			
		||||
@@ -228,11 +300,18 @@ type SliceType struct {
 | 
			
		||||
	ElemType Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *SliceType) String() string {
 | 
			
		||||
func (t *SliceType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *SliceType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	if t.Name != "" {
 | 
			
		||||
		return t.Name
 | 
			
		||||
	}
 | 
			
		||||
	return "[]" + t.ElemType.String()
 | 
			
		||||
	return "[]" + t.ElemType.stringIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A StringType represents a Go string type. It looks like a StructType, describing
 | 
			
		||||
@@ -241,7 +320,9 @@ type StringType struct {
 | 
			
		||||
	StructType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *StringType) String() string {
 | 
			
		||||
func (t *StringType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *StringType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	if t.Name != "" {
 | 
			
		||||
		return t.Name
 | 
			
		||||
	}
 | 
			
		||||
@@ -253,7 +334,9 @@ type InterfaceType struct {
 | 
			
		||||
	TypedefType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *InterfaceType) String() string {
 | 
			
		||||
func (t *InterfaceType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *InterfaceType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	if t.Name != "" {
 | 
			
		||||
		return t.Name
 | 
			
		||||
	}
 | 
			
		||||
@@ -275,7 +358,9 @@ type EnumValue struct {
 | 
			
		||||
	Val  int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *EnumType) String() string {
 | 
			
		||||
func (t *EnumType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *EnumType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	s := "enum"
 | 
			
		||||
	if t.EnumName != "" {
 | 
			
		||||
		s += " " + t.EnumName
 | 
			
		||||
@@ -298,17 +383,24 @@ type FuncType struct {
 | 
			
		||||
	ParamType  []Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *FuncType) String() string {
 | 
			
		||||
func (t *FuncType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *FuncType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	s := "func("
 | 
			
		||||
	for i, t := range t.ParamType {
 | 
			
		||||
		if i > 0 {
 | 
			
		||||
			s += ", "
 | 
			
		||||
		}
 | 
			
		||||
		s += t.String()
 | 
			
		||||
		s += t.stringIntl(recCheck)
 | 
			
		||||
	}
 | 
			
		||||
	s += ")"
 | 
			
		||||
	if t.ReturnType != nil {
 | 
			
		||||
		s += " " + t.ReturnType.String()
 | 
			
		||||
		s += " " + t.ReturnType.stringIntl(recCheck)
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
@@ -318,7 +410,9 @@ type DotDotDotType struct {
 | 
			
		||||
	CommonType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *DotDotDotType) String() string { return "..." }
 | 
			
		||||
func (t *DotDotDotType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *DotDotDotType) stringIntl(recCheck recCheck) string { return "..." }
 | 
			
		||||
 | 
			
		||||
// A TypedefType represents a named type.
 | 
			
		||||
type TypedefType struct {
 | 
			
		||||
@@ -326,9 +420,20 @@ type TypedefType struct {
 | 
			
		||||
	Type Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *TypedefType) String() string { return t.Name }
 | 
			
		||||
func (t *TypedefType) String() string { return t.stringIntl(nil) }
 | 
			
		||||
 | 
			
		||||
func (t *TypedefType) Size() int64 { return t.Type.Size() }
 | 
			
		||||
func (t *TypedefType) stringIntl(recCheck recCheck) string { return t.Name }
 | 
			
		||||
 | 
			
		||||
func (t *TypedefType) Size() int64 { return t.sizeIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *TypedefType) sizeIntl(recCheck recCheck) int64 {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return t.CommonType.ByteSize
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	return t.Type.sizeIntl(recCheck)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// A MapType represents a Go map type. It looks like a TypedefType, describing
 | 
			
		||||
// the runtime-internal structure, with extra fields.
 | 
			
		||||
@@ -338,7 +443,14 @@ type MapType struct {
 | 
			
		||||
	ElemType Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *MapType) String() string {
 | 
			
		||||
func (t *MapType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *MapType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	if t.Name != "" {
 | 
			
		||||
		return t.Name
 | 
			
		||||
	}
 | 
			
		||||
@@ -351,7 +463,14 @@ type ChanType struct {
 | 
			
		||||
	ElemType Type
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *ChanType) String() string {
 | 
			
		||||
func (t *ChanType) String() string { return t.stringIntl(make(recCheck)) }
 | 
			
		||||
 | 
			
		||||
func (t *ChanType) stringIntl(recCheck recCheck) string {
 | 
			
		||||
	release := recCheck.acquire(t.CommonType.Offset)
 | 
			
		||||
	if release == nil {
 | 
			
		||||
		return cyclicalTypeStop
 | 
			
		||||
	}
 | 
			
		||||
	defer release()
 | 
			
		||||
	if t.Name != "" {
 | 
			
		||||
		return t.Name
 | 
			
		||||
	}
 | 
			
		||||
@@ -586,9 +705,10 @@ func readType(d *dwarf.Data, name string, r *dwarf.Reader, off dwarf.Offset, typ
 | 
			
		||||
		switch t.ReflectKind {
 | 
			
		||||
		case reflect.Slice:
 | 
			
		||||
			slice := new(SliceType)
 | 
			
		||||
			typ = slice
 | 
			
		||||
			typeCache[off] = slice
 | 
			
		||||
			slice.ElemType = typeOf(e, AttrGoElem)
 | 
			
		||||
			t = &slice.StructType
 | 
			
		||||
			typ = slice
 | 
			
		||||
		case reflect.String:
 | 
			
		||||
			str := new(StringType)
 | 
			
		||||
			t = &str.StructType
 | 
			
		||||
@@ -798,19 +918,22 @@ func readType(d *dwarf.Data, name string, r *dwarf.Reader, off dwarf.Offset, typ
 | 
			
		||||
		switch t.ReflectKind {
 | 
			
		||||
		case reflect.Map:
 | 
			
		||||
			m := new(MapType)
 | 
			
		||||
			typ = m
 | 
			
		||||
			typeCache[off] = typ
 | 
			
		||||
			m.KeyType = typeOf(e, AttrGoKey)
 | 
			
		||||
			m.ElemType = typeOf(e, AttrGoElem)
 | 
			
		||||
			t = &m.TypedefType
 | 
			
		||||
			typ = m
 | 
			
		||||
		case reflect.Chan:
 | 
			
		||||
			c := new(ChanType)
 | 
			
		||||
			typ = c
 | 
			
		||||
			typeCache[off] = typ
 | 
			
		||||
			c.ElemType = typeOf(e, AttrGoElem)
 | 
			
		||||
			t = &c.TypedefType
 | 
			
		||||
			typ = c
 | 
			
		||||
		case reflect.Interface:
 | 
			
		||||
			it := new(InterfaceType)
 | 
			
		||||
			t = &it.TypedefType
 | 
			
		||||
			typ = it
 | 
			
		||||
			typeCache[off] = it
 | 
			
		||||
			t = &it.TypedefType
 | 
			
		||||
		default:
 | 
			
		||||
			typ = t
 | 
			
		||||
		}
 | 
			
		||||
@@ -876,3 +999,13 @@ func zeroArray(t Type) {
 | 
			
		||||
		t = at.Type
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resolveTypedef(typ Type) Type {
 | 
			
		||||
	for {
 | 
			
		||||
		if tt, ok := typ.(*TypedefType); ok {
 | 
			
		||||
			typ = tt.Type
 | 
			
		||||
		} else {
 | 
			
		||||
			return typ
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -5,7 +5,7 @@ import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/util"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type DebugLinePrologue struct {
 | 
			
		||||
@@ -7,7 +7,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/util"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Location struct {
 | 
			
		||||
@@ -7,7 +7,7 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/util"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/util"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Opcode byte
 | 
			
		||||
							
								
								
									
										2
									
								
								vendor/github.com/derekparker/delve/pkg/dwarf/reader/reader.go → vendor/github.com/go-delve/delve/pkg/dwarf/reader/reader.go
									
									
									
										generated
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							
							
						
						
									
										2
									
								
								vendor/github.com/derekparker/delve/pkg/dwarf/reader/reader.go → vendor/github.com/go-delve/delve/pkg/dwarf/reader/reader.go
									
									
									
										generated
									
									
										vendored
									
									
										
										
										Executable file → Normal file
									
								
							@@ -5,7 +5,7 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Reader struct {
 | 
			
		||||
							
								
								
									
										227
									
								
								vendor/github.com/go-delve/delve/pkg/logflags/logflags.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										227
									
								
								vendor/github.com/go-delve/delve/pkg/logflags/logflags.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,227 @@
 | 
			
		||||
package logflags
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"log"
 | 
			
		||||
	"os"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var debugger = false
 | 
			
		||||
var gdbWire = false
 | 
			
		||||
var lldbServerOutput = false
 | 
			
		||||
var debugLineErrors = false
 | 
			
		||||
var rpc = false
 | 
			
		||||
var fnCall = false
 | 
			
		||||
var minidump = false
 | 
			
		||||
 | 
			
		||||
var logOut io.WriteCloser
 | 
			
		||||
 | 
			
		||||
func makeLogger(flag bool, fields logrus.Fields) *logrus.Entry {
 | 
			
		||||
	logger := logrus.New().WithFields(fields)
 | 
			
		||||
	logger.Logger.Formatter = &textFormatter{}
 | 
			
		||||
	if logOut != nil {
 | 
			
		||||
		logger.Logger.Out = logOut
 | 
			
		||||
	}
 | 
			
		||||
	logger.Logger.Level = logrus.DebugLevel
 | 
			
		||||
	if !flag {
 | 
			
		||||
		logger.Logger.Level = logrus.PanicLevel
 | 
			
		||||
	}
 | 
			
		||||
	return logger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GdbWire returns true if the gdbserial package should log all the packets
 | 
			
		||||
// exchanged with the stub.
 | 
			
		||||
func GdbWire() bool {
 | 
			
		||||
	return gdbWire
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GdbWireLogger returns a configured logger for the gdbserial wire protocol.
 | 
			
		||||
func GdbWireLogger() *logrus.Entry {
 | 
			
		||||
	return makeLogger(gdbWire, logrus.Fields{"layer": "gdbconn"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Debugger returns true if the debugger package should log.
 | 
			
		||||
func Debugger() bool {
 | 
			
		||||
	return debugger
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DebuggerLogger returns a logger for the debugger package.
 | 
			
		||||
func DebuggerLogger() *logrus.Entry {
 | 
			
		||||
	return makeLogger(debugger, logrus.Fields{"layer": "debugger"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LLDBServerOutput returns true if the output of the LLDB server should be
 | 
			
		||||
// redirected to standard output instead of suppressed.
 | 
			
		||||
func LLDBServerOutput() bool {
 | 
			
		||||
	return lldbServerOutput
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// DebugLineErrors returns true if pkg/dwarf/line should log its recoverable
 | 
			
		||||
// errors.
 | 
			
		||||
func DebugLineErrors() bool {
 | 
			
		||||
	return debugLineErrors
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RPC returns true if RPC messages should be logged.
 | 
			
		||||
func RPC() bool {
 | 
			
		||||
	return rpc
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RPCLogger returns a logger for RPC messages.
 | 
			
		||||
func RPCLogger() *logrus.Entry {
 | 
			
		||||
	return makeLogger(rpc, logrus.Fields{"layer": "rpc"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FnCall returns true if the function call protocol should be logged.
 | 
			
		||||
func FnCall() bool {
 | 
			
		||||
	return fnCall
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func FnCallLogger() *logrus.Entry {
 | 
			
		||||
	return makeLogger(fnCall, logrus.Fields{"layer": "proc", "kind": "fncall"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Minidump returns true if the minidump loader should be logged.
 | 
			
		||||
func Minidump() bool {
 | 
			
		||||
	return minidump
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func MinidumpLogger() *logrus.Entry {
 | 
			
		||||
	return makeLogger(minidump, logrus.Fields{"layer": "core", "kind": "minidump"})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// WriteAPIListeningMessage writes the "API server listening" message in headless mode.
 | 
			
		||||
func WriteAPIListeningMessage(addr string) {
 | 
			
		||||
	if logOut != nil {
 | 
			
		||||
		fmt.Fprintf(logOut, "API server listening at: %s\n", addr)
 | 
			
		||||
	} else {
 | 
			
		||||
		fmt.Printf("API server listening at: %s\n", addr)
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errLogstrWithoutLog = errors.New("--log-output specified without --log")
 | 
			
		||||
 | 
			
		||||
// Setup sets debugger flags based on the contents of logstr.
 | 
			
		||||
// If logDest is not empty logs will be redirected to the file descriptor or
 | 
			
		||||
// file path specified by logDest.
 | 
			
		||||
func Setup(logFlag bool, logstr string, logDest string) error {
 | 
			
		||||
	if logDest != "" {
 | 
			
		||||
		n, err := strconv.Atoi(logDest)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			logOut = os.NewFile(uintptr(n), "delve-logs")
 | 
			
		||||
		} else {
 | 
			
		||||
			fh, err := os.Create(logDest)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return fmt.Errorf("could not create log file: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
			logOut = fh
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	log.SetFlags(log.Ldate | log.Ltime | log.Lshortfile)
 | 
			
		||||
	if !logFlag {
 | 
			
		||||
		log.SetOutput(ioutil.Discard)
 | 
			
		||||
		if logstr != "" {
 | 
			
		||||
			return errLogstrWithoutLog
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if logstr == "" {
 | 
			
		||||
		logstr = "debugger"
 | 
			
		||||
	}
 | 
			
		||||
	v := strings.Split(logstr, ",")
 | 
			
		||||
	for _, logcmd := range v {
 | 
			
		||||
		switch logcmd {
 | 
			
		||||
		case "debugger":
 | 
			
		||||
			debugger = true
 | 
			
		||||
		case "gdbwire":
 | 
			
		||||
			gdbWire = true
 | 
			
		||||
		case "lldbout":
 | 
			
		||||
			lldbServerOutput = true
 | 
			
		||||
		case "debuglineerr":
 | 
			
		||||
			debugLineErrors = true
 | 
			
		||||
		case "rpc":
 | 
			
		||||
			rpc = true
 | 
			
		||||
		case "fncall":
 | 
			
		||||
			fnCall = true
 | 
			
		||||
		case "minidump":
 | 
			
		||||
			minidump = true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close closes the logger output.
 | 
			
		||||
func Close() {
 | 
			
		||||
	if logOut != nil {
 | 
			
		||||
		logOut.Close()
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// textFormatter is a simplified version of logrus.TextFormatter that
 | 
			
		||||
// doesn't make logs unreadable when they are output to a text file or to a
 | 
			
		||||
// terminal that doesn't support colors.
 | 
			
		||||
type textFormatter struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *textFormatter) Format(entry *logrus.Entry) ([]byte, error) {
 | 
			
		||||
	var b *bytes.Buffer
 | 
			
		||||
	if entry.Buffer != nil {
 | 
			
		||||
		b = entry.Buffer
 | 
			
		||||
	} else {
 | 
			
		||||
		b = &bytes.Buffer{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	keys := make([]string, 0, len(entry.Data))
 | 
			
		||||
	for k := range entry.Data {
 | 
			
		||||
		keys = append(keys, k)
 | 
			
		||||
	}
 | 
			
		||||
	sort.Strings(keys)
 | 
			
		||||
 | 
			
		||||
	b.WriteString(entry.Time.Format(time.RFC3339))
 | 
			
		||||
	b.WriteByte(' ')
 | 
			
		||||
	b.WriteString(entry.Level.String())
 | 
			
		||||
	b.WriteByte(' ')
 | 
			
		||||
	for i, key := range keys {
 | 
			
		||||
		b.WriteString(key)
 | 
			
		||||
		b.WriteByte('=')
 | 
			
		||||
		stringVal, ok := entry.Data[key].(string)
 | 
			
		||||
		if !ok {
 | 
			
		||||
			stringVal = fmt.Sprint(entry.Data[key])
 | 
			
		||||
		}
 | 
			
		||||
		if f.needsQuoting(stringVal) {
 | 
			
		||||
			fmt.Fprintf(b, "%q", stringVal)
 | 
			
		||||
		} else {
 | 
			
		||||
			b.WriteString(stringVal)
 | 
			
		||||
		}
 | 
			
		||||
		if i != len(keys)-1 {
 | 
			
		||||
			b.WriteByte(',')
 | 
			
		||||
		} else {
 | 
			
		||||
			b.WriteByte(' ')
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	b.WriteString(entry.Message)
 | 
			
		||||
	b.WriteByte('\n')
 | 
			
		||||
	return b.Bytes(), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (f *textFormatter) needsQuoting(text string) bool {
 | 
			
		||||
	for _, ch := range text {
 | 
			
		||||
		if !((ch >= 'a' && ch <= 'z') ||
 | 
			
		||||
			(ch >= 'A' && ch <= 'Z') ||
 | 
			
		||||
			(ch >= '0' && ch <= '9') ||
 | 
			
		||||
			ch == '-' || ch == '.' || ch == '_' || ch == '/' || ch == '@' || ch == '^' || ch == '+') {
 | 
			
		||||
			return true
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
@@ -3,8 +3,8 @@ package proc
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/frame"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/frame"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
	"golang.org/x/arch/x86/x86asm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -284,7 +284,7 @@ func (a *AMD64) RegistersToDwarfRegisters(regs Registers, staticBase uint64) op.
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, reg := range regs.Slice() {
 | 
			
		||||
	for _, reg := range regs.Slice(true) {
 | 
			
		||||
		for dwarfReg, regName := range amd64DwarfToName {
 | 
			
		||||
			if regName == reg.Name {
 | 
			
		||||
				dregs[dwarfReg] = op.DwarfRegisterFromBytes(reg.Bytes)
 | 
			
		||||
@@ -12,40 +12,30 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sort"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/frame"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/line"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/goversion"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/frame"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/line"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/goversion"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// BinaryInfo holds information on the binary being executed.
 | 
			
		||||
// BinaryInfo holds information on the binaries being executed (this
 | 
			
		||||
// includes both the executable and also any loaded libraries).
 | 
			
		||||
type BinaryInfo struct {
 | 
			
		||||
	lastModified time.Time // Time the executable of this process was last modified
 | 
			
		||||
	// Path on disk of the binary being executed.
 | 
			
		||||
	Path string
 | 
			
		||||
	// Architecture of this binary.
 | 
			
		||||
	Arch Arch
 | 
			
		||||
 | 
			
		||||
	GOOS           string
 | 
			
		||||
	closer         io.Closer
 | 
			
		||||
	sepDebugCloser io.Closer
 | 
			
		||||
 | 
			
		||||
	staticBase uint64
 | 
			
		||||
 | 
			
		||||
	// Maps package names to package paths, needed to lookup types inside DWARF info
 | 
			
		||||
	packageMap map[string]string
 | 
			
		||||
 | 
			
		||||
	Arch          Arch
 | 
			
		||||
	dwarf         *dwarf.Data
 | 
			
		||||
	frameEntries  frame.FrameDescriptionEntries
 | 
			
		||||
	loclist       loclistReader
 | 
			
		||||
	compileUnits  []*compileUnit
 | 
			
		||||
	types         map[string]dwarf.Offset
 | 
			
		||||
	packageVars   []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
 | 
			
		||||
	gStructOffset uint64
 | 
			
		||||
	// GOOS operating system this binary is executing on.
 | 
			
		||||
	GOOS string
 | 
			
		||||
 | 
			
		||||
	// Functions is a list of all DW_TAG_subprogram entries in debug_info, sorted by entry point
 | 
			
		||||
	Functions []Function
 | 
			
		||||
@@ -54,7 +44,32 @@ type BinaryInfo struct {
 | 
			
		||||
	// LookupFunc maps function names to a description of the function.
 | 
			
		||||
	LookupFunc map[string]*Function
 | 
			
		||||
 | 
			
		||||
	typeCache map[dwarf.Offset]godwarf.Type
 | 
			
		||||
	// Images is a list of loaded shared libraries (also known as
 | 
			
		||||
	// shared objects on linux or DLLs on windws).
 | 
			
		||||
	Images []*Image
 | 
			
		||||
 | 
			
		||||
	ElfDynamicSection ElfDynamicSection
 | 
			
		||||
 | 
			
		||||
	lastModified time.Time // Time the executable of this process was last modified
 | 
			
		||||
 | 
			
		||||
	closer         io.Closer
 | 
			
		||||
	sepDebugCloser io.Closer
 | 
			
		||||
 | 
			
		||||
	staticBase uint64
 | 
			
		||||
 | 
			
		||||
	// Maps package names to package paths, needed to lookup types inside DWARF info
 | 
			
		||||
	packageMap map[string]string
 | 
			
		||||
 | 
			
		||||
	dwarf        *dwarf.Data
 | 
			
		||||
	dwarfReader  *dwarf.Reader
 | 
			
		||||
	frameEntries frame.FrameDescriptionEntries
 | 
			
		||||
	loclist      loclistReader
 | 
			
		||||
	compileUnits []*compileUnit
 | 
			
		||||
	types        map[string]dwarf.Offset
 | 
			
		||||
	packageVars  []packageVar // packageVars is a list of all global/package variables in debug_info, sorted by address
 | 
			
		||||
	typeCache    map[dwarf.Offset]godwarf.Type
 | 
			
		||||
 | 
			
		||||
	gStructOffset uint64
 | 
			
		||||
 | 
			
		||||
	loadModuleDataOnce sync.Once
 | 
			
		||||
	moduleData         []moduleData
 | 
			
		||||
@@ -71,8 +86,6 @@ type BinaryInfo struct {
 | 
			
		||||
 | 
			
		||||
	loadErrMu sync.Mutex
 | 
			
		||||
	loadErr   error
 | 
			
		||||
 | 
			
		||||
	dwarfReader *dwarf.Reader
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrUnsupportedLinuxArch is returned when attempting to debug a binary compiled for an unsupported architecture.
 | 
			
		||||
@@ -84,14 +97,20 @@ var ErrUnsupportedWindowsArch = errors.New("unsupported architecture of windows/
 | 
			
		||||
// ErrUnsupportedDarwinArch is returned when attempting to debug a binary compiled for an unsupported architecture.
 | 
			
		||||
var ErrUnsupportedDarwinArch = errors.New("unsupported architecture - only darwin/amd64 is supported")
 | 
			
		||||
 | 
			
		||||
// ErrCouldNotDetermineRelocation is an error returned when Delve could not determine the base address of a
 | 
			
		||||
// position independant executable.
 | 
			
		||||
var ErrCouldNotDetermineRelocation = errors.New("could not determine the base address of a PIE")
 | 
			
		||||
 | 
			
		||||
// ErrNoDebugInfoFound is returned when Delve cannot open the debug_info
 | 
			
		||||
// section or find an external debug info file.
 | 
			
		||||
var ErrNoDebugInfoFound = errors.New("could not open debug info")
 | 
			
		||||
 | 
			
		||||
const dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
 | 
			
		||||
 | 
			
		||||
type compileUnit struct {
 | 
			
		||||
	Name   string // univocal name for non-go compile units
 | 
			
		||||
	LowPC  uint64
 | 
			
		||||
	Ranges [][2]uint64
 | 
			
		||||
	name   string // univocal name for non-go compile units
 | 
			
		||||
	lowPC  uint64
 | 
			
		||||
	ranges [][2]uint64
 | 
			
		||||
 | 
			
		||||
	entry              *dwarf.Entry        // debug_info entry describing this compile unit
 | 
			
		||||
	isgo               bool                // true if this is the go compile unit
 | 
			
		||||
@@ -277,6 +296,12 @@ type buildIDHeader struct {
 | 
			
		||||
	Type   uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ElfDynamicSection describes the .dynamic section of an ELF executable.
 | 
			
		||||
type ElfDynamicSection struct {
 | 
			
		||||
	Addr uint64 // relocated address of where the .dynamic section is mapped in memory
 | 
			
		||||
	Size uint64 // size of the .dynamic section of the executable
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NewBinaryInfo returns an initialized but unloaded BinaryInfo struct.
 | 
			
		||||
func NewBinaryInfo(goos, goarch string) *BinaryInfo {
 | 
			
		||||
	r := &BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry), typeCache: make(map[dwarf.Offset]godwarf.Type)}
 | 
			
		||||
@@ -293,22 +318,24 @@ func NewBinaryInfo(goos, goarch string) *BinaryInfo {
 | 
			
		||||
// LoadBinaryInfo will load and store the information from the binary at 'path'.
 | 
			
		||||
// It is expected this will be called in parallel with other initialization steps
 | 
			
		||||
// so a sync.WaitGroup must be provided.
 | 
			
		||||
func (bi *BinaryInfo) LoadBinaryInfo(path string, entryPoint uint64, wg *sync.WaitGroup) error {
 | 
			
		||||
func (bi *BinaryInfo) LoadBinaryInfo(path string, entryPoint uint64, debugInfoDirs []string) error {
 | 
			
		||||
	fi, err := os.Stat(path)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		bi.lastModified = fi.ModTime()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	defer wg.Wait()
 | 
			
		||||
	bi.Path = path
 | 
			
		||||
	switch bi.GOOS {
 | 
			
		||||
	case "linux":
 | 
			
		||||
		return bi.LoadBinaryInfoElf(path, entryPoint, wg)
 | 
			
		||||
		return bi.LoadBinaryInfoElf(path, entryPoint, debugInfoDirs, &wg)
 | 
			
		||||
	case "windows":
 | 
			
		||||
		return bi.LoadBinaryInfoPE(path, entryPoint, wg)
 | 
			
		||||
		return bi.LoadBinaryInfoPE(path, entryPoint, &wg)
 | 
			
		||||
	case "darwin":
 | 
			
		||||
		return bi.LoadBinaryInfoMacho(path, entryPoint, wg)
 | 
			
		||||
		return bi.LoadBinaryInfoMacho(path, entryPoint, &wg)
 | 
			
		||||
	}
 | 
			
		||||
	return errors.New("unsupported operating system")
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GStructOffset returns the offset of the G
 | 
			
		||||
@@ -398,12 +425,35 @@ func (bi *BinaryInfo) PCToFunc(pc uint64) *Function {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Image represents a loaded library file (shared object on linux, DLL on windows).
 | 
			
		||||
type Image struct {
 | 
			
		||||
	Path string
 | 
			
		||||
	addr uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AddImage adds the specified image to bi.
 | 
			
		||||
func (bi *BinaryInfo) AddImage(path string, addr uint64) {
 | 
			
		||||
	if !strings.HasPrefix(path, "/") {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	for _, image := range bi.Images {
 | 
			
		||||
		if image.Path == path && image.addr == addr {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	//TODO(aarzilli): actually load informations about the image here
 | 
			
		||||
	bi.Images = append(bi.Images, &Image{Path: path, addr: addr})
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Close closes all internal readers.
 | 
			
		||||
func (bi *BinaryInfo) Close() error {
 | 
			
		||||
	if bi.sepDebugCloser != nil {
 | 
			
		||||
		bi.sepDebugCloser.Close()
 | 
			
		||||
	}
 | 
			
		||||
	return bi.closer.Close()
 | 
			
		||||
	if bi.closer != nil {
 | 
			
		||||
		return bi.closer.Close()
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (bi *BinaryInfo) setLoadError(fmtstr string, args ...interface{}) {
 | 
			
		||||
@@ -488,7 +538,7 @@ func (bi *BinaryInfo) Location(entry reader.Entry, attr dwarf.Attr, pc uint64, r
 | 
			
		||||
func (bi *BinaryInfo) loclistEntry(off int64, pc uint64) []byte {
 | 
			
		||||
	var base uint64
 | 
			
		||||
	if cu := bi.findCompileUnit(pc); cu != nil {
 | 
			
		||||
		base = cu.LowPC
 | 
			
		||||
		base = cu.lowPC
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bi.loclist.Seek(int(off))
 | 
			
		||||
@@ -509,7 +559,7 @@ func (bi *BinaryInfo) loclistEntry(off int64, pc uint64) []byte {
 | 
			
		||||
// findCompileUnit returns the compile unit containing address pc.
 | 
			
		||||
func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit {
 | 
			
		||||
	for _, cu := range bi.compileUnits {
 | 
			
		||||
		for _, rng := range cu.Ranges {
 | 
			
		||||
		for _, rng := range cu.ranges {
 | 
			
		||||
			if pc >= rng[0] && pc < rng[1] {
 | 
			
		||||
				return cu
 | 
			
		||||
			}
 | 
			
		||||
@@ -558,35 +608,32 @@ func (e *ErrNoBuildIDNote) Error() string {
 | 
			
		||||
// in GDB's documentation [1], and if found returns two handles, one
 | 
			
		||||
// for the bare file, and another for its corresponding elf.File.
 | 
			
		||||
// [1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
 | 
			
		||||
func (bi *BinaryInfo) openSeparateDebugInfo(exe *elf.File) (*os.File, *elf.File, error) {
 | 
			
		||||
	buildid := exe.Section(".note.gnu.build-id")
 | 
			
		||||
	if buildid == nil {
 | 
			
		||||
		return nil, nil, &ErrNoBuildIDNote{}
 | 
			
		||||
//
 | 
			
		||||
// Alternatively, if the debug file cannot be found be the build-id, Delve
 | 
			
		||||
// will look in directories specified by the debug-info-directories config value.
 | 
			
		||||
func (bi *BinaryInfo) openSeparateDebugInfo(exe *elf.File, debugInfoDirectories []string) (*os.File, *elf.File, error) {
 | 
			
		||||
	var debugFilePath string
 | 
			
		||||
	for _, dir := range debugInfoDirectories {
 | 
			
		||||
		var potentialDebugFilePath string
 | 
			
		||||
		if strings.Contains(dir, "build-id") {
 | 
			
		||||
			desc1, desc2, err := parseBuildID(exe)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			potentialDebugFilePath = fmt.Sprintf("%s/%s/%s.debug", dir, desc1, desc2)
 | 
			
		||||
		} else {
 | 
			
		||||
			potentialDebugFilePath = fmt.Sprintf("%s/%s.debug", dir, filepath.Base(bi.Path))
 | 
			
		||||
		}
 | 
			
		||||
		_, err := os.Stat(potentialDebugFilePath)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			debugFilePath = potentialDebugFilePath
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	br := buildid.Open()
 | 
			
		||||
	bh := new(buildIDHeader)
 | 
			
		||||
	if err := binary.Read(br, binary.LittleEndian, bh); err != nil {
 | 
			
		||||
		return nil, nil, errors.New("can't read build-id header: " + err.Error())
 | 
			
		||||
	if debugFilePath == "" {
 | 
			
		||||
		return nil, nil, ErrNoDebugInfoFound
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name := make([]byte, bh.Namesz)
 | 
			
		||||
	if err := binary.Read(br, binary.LittleEndian, name); err != nil {
 | 
			
		||||
		return nil, nil, errors.New("can't read build-id name: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if strings.TrimSpace(string(name)) != "GNU\x00" {
 | 
			
		||||
		return nil, nil, errors.New("invalid build-id signature")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	descBinary := make([]byte, bh.Descsz)
 | 
			
		||||
	if err := binary.Read(br, binary.LittleEndian, descBinary); err != nil {
 | 
			
		||||
		return nil, nil, errors.New("can't read build-id desc: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	desc := hex.EncodeToString(descBinary)
 | 
			
		||||
 | 
			
		||||
	debugPath := fmt.Sprintf("/usr/lib/debug/.build-id/%s/%s.debug", desc[:2], desc[2:])
 | 
			
		||||
	sepFile, err := os.OpenFile(debugPath, 0, os.ModePerm)
 | 
			
		||||
	sepFile, err := os.OpenFile(debugFilePath, 0, os.ModePerm)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, nil, errors.New("can't open separate debug file: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
@@ -594,19 +641,48 @@ func (bi *BinaryInfo) openSeparateDebugInfo(exe *elf.File) (*os.File, *elf.File,
 | 
			
		||||
	elfFile, err := elf.NewFile(sepFile)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		sepFile.Close()
 | 
			
		||||
		return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugPath, err.Error())
 | 
			
		||||
		return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if elfFile.Machine != elf.EM_X86_64 {
 | 
			
		||||
		sepFile.Close()
 | 
			
		||||
		return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugPath, ErrUnsupportedLinuxArch.Error())
 | 
			
		||||
		return nil, nil, fmt.Errorf("can't open separate debug file %q: %v", debugFilePath, ErrUnsupportedLinuxArch.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return sepFile, elfFile, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseBuildID(exe *elf.File) (string, string, error) {
 | 
			
		||||
	buildid := exe.Section(".note.gnu.build-id")
 | 
			
		||||
	if buildid == nil {
 | 
			
		||||
		return "", "", &ErrNoBuildIDNote{}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	br := buildid.Open()
 | 
			
		||||
	bh := new(buildIDHeader)
 | 
			
		||||
	if err := binary.Read(br, binary.LittleEndian, bh); err != nil {
 | 
			
		||||
		return "", "", errors.New("can't read build-id header: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	name := make([]byte, bh.Namesz)
 | 
			
		||||
	if err := binary.Read(br, binary.LittleEndian, name); err != nil {
 | 
			
		||||
		return "", "", errors.New("can't read build-id name: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if strings.TrimSpace(string(name)) != "GNU\x00" {
 | 
			
		||||
		return "", "", errors.New("invalid build-id signature")
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	descBinary := make([]byte, bh.Descsz)
 | 
			
		||||
	if err := binary.Read(br, binary.LittleEndian, descBinary); err != nil {
 | 
			
		||||
		return "", "", errors.New("can't read build-id desc: " + err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	desc := hex.EncodeToString(descBinary)
 | 
			
		||||
	return desc[:2], desc[2:], nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LoadBinaryInfoElf specifically loads information from an ELF binary.
 | 
			
		||||
func (bi *BinaryInfo) LoadBinaryInfoElf(path string, entryPoint uint64, wg *sync.WaitGroup) error {
 | 
			
		||||
func (bi *BinaryInfo) LoadBinaryInfoElf(path string, entryPoint uint64, debugInfoDirectories []string, wg *sync.WaitGroup) error {
 | 
			
		||||
	exe, err := os.OpenFile(path, 0, os.ModePerm)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
@@ -628,17 +704,19 @@ func (bi *BinaryInfo) LoadBinaryInfoElf(path string, entryPoint uint64, wg *sync
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if dynsec := elfFile.Section(".dynamic"); dynsec != nil {
 | 
			
		||||
		bi.ElfDynamicSection.Addr = dynsec.Addr + bi.staticBase
 | 
			
		||||
		bi.ElfDynamicSection.Size = dynsec.Size
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dwarfFile := elfFile
 | 
			
		||||
 | 
			
		||||
	bi.dwarf, err = elfFile.DWARF()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		var sepFile *os.File
 | 
			
		||||
		var serr error
 | 
			
		||||
		sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(elfFile)
 | 
			
		||||
		sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(elfFile, debugInfoDirectories)
 | 
			
		||||
		if serr != nil {
 | 
			
		||||
			if _, ok := serr.(*ErrNoBuildIDNote); ok {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			return serr
 | 
			
		||||
		}
 | 
			
		||||
		bi.sepDebugCloser = sepFile
 | 
			
		||||
@@ -714,9 +792,16 @@ func (bi *BinaryInfo) setGStructOffsetElf(exe *elf.File, wg *sync.WaitGroup) {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if tls == nil {
 | 
			
		||||
		bi.gStructOffset = ^uint64(8) + 1 // -8
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	memsz := tls.Memsz
 | 
			
		||||
 | 
			
		||||
	memsz = (memsz + uint64(bi.Arch.PtrSize()) - 1) & ^uint64(bi.Arch.PtrSize()-1) // align to pointer-sized-boundary
 | 
			
		||||
	// The TLS register points to the end of the TLS block, which is
 | 
			
		||||
	// tls.Memsz long. runtime.tlsg is an offset from the beginning of that block.
 | 
			
		||||
	bi.gStructOffset = ^(tls.Memsz) + 1 + tlsg.Value // -tls.Memsz + tlsg.Value
 | 
			
		||||
	bi.gStructOffset = ^(memsz) + 1 + tlsg.Value // -tls.Memsz + tlsg.Value
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PE ////////////////////////////////////////////////////////////////
 | 
			
		||||
@@ -30,7 +30,8 @@ type Breakpoint struct {
 | 
			
		||||
	Kind BreakpointKind
 | 
			
		||||
 | 
			
		||||
	// Breakpoint information
 | 
			
		||||
	Tracepoint    bool     // Tracepoint flag
 | 
			
		||||
	Tracepoint    bool // Tracepoint flag
 | 
			
		||||
	TraceReturn   bool
 | 
			
		||||
	Goroutine     bool     // Retrieve goroutine information
 | 
			
		||||
	Stacktrace    int      // Number of stack frames to retrieve
 | 
			
		||||
	Variables     []string // Variables to evaluate
 | 
			
		||||
@@ -45,7 +46,7 @@ type Breakpoint struct {
 | 
			
		||||
	// 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. This ensures 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
 | 
			
		||||
@@ -220,13 +221,16 @@ func (bpmap *BreakpointMap) ResetBreakpointIDCounter() {
 | 
			
		||||
	bpmap.breakpointIDCounter = 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type writeBreakpointFn func(addr uint64) (file string, line int, fn *Function, originalData []byte, err error)
 | 
			
		||||
// WriteBreakpointFn is a type that represents a function to be used for
 | 
			
		||||
// writting breakpoings into the target.
 | 
			
		||||
type WriteBreakpointFn func(addr uint64) (file string, line int, fn *Function, originalData []byte, err error)
 | 
			
		||||
 | 
			
		||||
type clearBreakpointFn func(*Breakpoint) error
 | 
			
		||||
 | 
			
		||||
// Set creates a breakpoint at addr calling writeBreakpoint. Do not call this
 | 
			
		||||
// function, call proc.Process.SetBreakpoint instead, this function exists
 | 
			
		||||
// to implement proc.Process.SetBreakpoint.
 | 
			
		||||
func (bpmap *BreakpointMap) Set(addr uint64, kind BreakpointKind, cond ast.Expr, writeBreakpoint writeBreakpointFn) (*Breakpoint, error) {
 | 
			
		||||
func (bpmap *BreakpointMap) Set(addr uint64, kind BreakpointKind, cond ast.Expr, writeBreakpoint WriteBreakpointFn) (*Breakpoint, error) {
 | 
			
		||||
	if bp, ok := bpmap.M[addr]; ok {
 | 
			
		||||
		// We can overlap one internal breakpoint with one user breakpoint, we
 | 
			
		||||
		// need to support this otherwise a conditional breakpoint can mask a
 | 
			
		||||
@@ -248,8 +252,13 @@ func (bpmap *BreakpointMap) Set(addr uint64, kind BreakpointKind, cond ast.Expr,
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	fnName := ""
 | 
			
		||||
	if fn != nil {
 | 
			
		||||
		fnName = fn.Name
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	newBreakpoint := &Breakpoint{
 | 
			
		||||
		FunctionName: fn.Name,
 | 
			
		||||
		FunctionName: fnName,
 | 
			
		||||
		File:         f,
 | 
			
		||||
		Line:         l,
 | 
			
		||||
		Addr:         addr,
 | 
			
		||||
@@ -274,7 +283,7 @@ func (bpmap *BreakpointMap) Set(addr uint64, kind BreakpointKind, cond ast.Expr,
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetWithID creates a breakpoint at addr, with the specified ID.
 | 
			
		||||
func (bpmap *BreakpointMap) SetWithID(id int, addr uint64, writeBreakpoint writeBreakpointFn) (*Breakpoint, error) {
 | 
			
		||||
func (bpmap *BreakpointMap) SetWithID(id int, addr uint64, writeBreakpoint WriteBreakpointFn) (*Breakpoint, error) {
 | 
			
		||||
	bp, err := bpmap.Set(addr, UserBreakpoint, nil, writeBreakpoint)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		bp.ID = id
 | 
			
		||||
@@ -421,12 +430,6 @@ func (rbpi *returnBreakpointInfo) Collect(thread Thread) []*Variable {
 | 
			
		||||
		return (v.Flags & VariableReturnArgument) != 0
 | 
			
		||||
	})
 | 
			
		||||
 | 
			
		||||
	// Go saves the return variables in the opposite order that the user
 | 
			
		||||
	// specifies them so here we reverse the slice to make it easier to
 | 
			
		||||
	// understand.
 | 
			
		||||
	for i := 0; i < len(vars)/2; i++ {
 | 
			
		||||
		vars[i], vars[len(vars)-i-1] = vars[len(vars)-i-1], vars[i]
 | 
			
		||||
	}
 | 
			
		||||
	return vars
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -5,9 +5,8 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"io"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// A SplicedMemory represents a memory space formed from multiple regions,
 | 
			
		||||
@@ -144,8 +143,13 @@ func (r *OffsetReaderAt) ReadMemory(buf []byte, addr uintptr) (n int, err error)
 | 
			
		||||
 | 
			
		||||
// Process represents a core file.
 | 
			
		||||
type Process struct {
 | 
			
		||||
	mem     proc.MemoryReader
 | 
			
		||||
	Threads map[int]*Thread
 | 
			
		||||
	pid     int
 | 
			
		||||
 | 
			
		||||
	entryPoint uint64
 | 
			
		||||
 | 
			
		||||
	bi                *proc.BinaryInfo
 | 
			
		||||
	core              *Core
 | 
			
		||||
	breakpoints       proc.BreakpointMap
 | 
			
		||||
	currentThread     *Thread
 | 
			
		||||
	selectedGoroutine *proc.G
 | 
			
		||||
@@ -154,12 +158,16 @@ type Process struct {
 | 
			
		||||
 | 
			
		||||
// Thread represents a thread in the core file being debugged.
 | 
			
		||||
type Thread struct {
 | 
			
		||||
	th     *LinuxPrStatus
 | 
			
		||||
	fpregs []proc.Register
 | 
			
		||||
	th     osThread
 | 
			
		||||
	p      *Process
 | 
			
		||||
	common proc.CommonThread
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type osThread interface {
 | 
			
		||||
	registers(floatingPoint bool) (proc.Registers, error)
 | 
			
		||||
	pid() int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	// ErrWriteCore is returned when attempting to write to the core
 | 
			
		||||
	// process memory.
 | 
			
		||||
@@ -175,45 +183,66 @@ var (
 | 
			
		||||
	ErrChangeRegisterCore = errors.New("can not change register values of core process")
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type openFn func(string, string) (*Process, error)
 | 
			
		||||
 | 
			
		||||
var openFns = []openFn{readLinuxAMD64Core, readAMD64Minidump}
 | 
			
		||||
 | 
			
		||||
// ErrUnrecognizedFormat is returned when the core file is not recognized as
 | 
			
		||||
// any of the supported formats.
 | 
			
		||||
var ErrUnrecognizedFormat = errors.New("unrecognized core format")
 | 
			
		||||
 | 
			
		||||
// OpenCore will open the core file and return a Process struct.
 | 
			
		||||
func OpenCore(corePath, exePath string) (*Process, error) {
 | 
			
		||||
	core, err := readCore(corePath, exePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	p := &Process{
 | 
			
		||||
		core:        core,
 | 
			
		||||
		breakpoints: proc.NewBreakpointMap(),
 | 
			
		||||
		bi:          proc.NewBinaryInfo("linux", "amd64"),
 | 
			
		||||
	}
 | 
			
		||||
	for _, thread := range core.Threads {
 | 
			
		||||
		thread.p = p
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	err = p.bi.LoadBinaryInfo(exePath, core.entryPoint, &wg)
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		err = p.bi.LoadError()
 | 
			
		||||
// If the DWARF information cannot be found in the binary, Delve will look
 | 
			
		||||
// for external debug files in the directories passed in.
 | 
			
		||||
func OpenCore(corePath, exePath string, debugInfoDirs []string) (*Process, error) {
 | 
			
		||||
	var p *Process
 | 
			
		||||
	var err error
 | 
			
		||||
	for _, openFn := range openFns {
 | 
			
		||||
		p, err = openFn(corePath, exePath)
 | 
			
		||||
		if err != ErrUnrecognizedFormat {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, th := range p.core.Threads {
 | 
			
		||||
		p.currentThread = th
 | 
			
		||||
		break
 | 
			
		||||
	if err := p.initialize(exePath, debugInfoDirs); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
 | 
			
		||||
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// initialize for core files doesn't do much
 | 
			
		||||
// aside from call the post initialization setup.
 | 
			
		||||
func (p *Process) initialize(path string, debugInfoDirs []string) error {
 | 
			
		||||
	return proc.PostInitializationSetup(p, path, debugInfoDirs, p.writeBreakpoint)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BinInfo will return the binary info.
 | 
			
		||||
func (p *Process) BinInfo() *proc.BinaryInfo {
 | 
			
		||||
	return p.bi
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSelectedGoroutine will set internally the goroutine that should be
 | 
			
		||||
// the default for any command executed, the goroutine being actively
 | 
			
		||||
// followed.
 | 
			
		||||
func (p *Process) SetSelectedGoroutine(g *proc.G) {
 | 
			
		||||
	p.selectedGoroutine = g
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EntryPoint will return the entry point address for this core file.
 | 
			
		||||
func (p *Process) EntryPoint() (uint64, error) {
 | 
			
		||||
	return p.entryPoint, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writeBreakpoint is a noop function since you
 | 
			
		||||
// cannot write breakpoints into core files.
 | 
			
		||||
func (p *Process) writeBreakpoint(addr uint64) (file string, line int, fn *proc.Function, originalData []byte, err error) {
 | 
			
		||||
	return "", 0, nil, nil, errors.New("cannot write a breakpoint to a core file")
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Recorded returns whether this is a live or recorded process. Always returns true for core files.
 | 
			
		||||
func (p *Process) Recorded() (bool, string) { return true, "" }
 | 
			
		||||
 | 
			
		||||
@@ -239,7 +268,7 @@ func (p *Process) ClearCheckpoint(int) error { return errors.New("checkpoint not
 | 
			
		||||
// read memory into `data`, returning the length read, and returning an error if
 | 
			
		||||
// the length read is shorter than the length of the `data` buffer.
 | 
			
		||||
func (t *Thread) ReadMemory(data []byte, addr uintptr) (n int, err error) {
 | 
			
		||||
	n, err = t.p.core.ReadMemory(data, addr)
 | 
			
		||||
	n, err = t.p.mem.ReadMemory(data, addr)
 | 
			
		||||
	if err == nil && n != len(data) {
 | 
			
		||||
		err = ErrShortRead
 | 
			
		||||
	}
 | 
			
		||||
@@ -255,8 +284,13 @@ func (t *Thread) WriteMemory(addr uintptr, data []byte) (int, error) {
 | 
			
		||||
// Location returns the location of this thread based on
 | 
			
		||||
// the value of the instruction pointer register.
 | 
			
		||||
func (t *Thread) Location() (*proc.Location, error) {
 | 
			
		||||
	f, l, fn := t.p.bi.PCToLine(t.th.Reg.Rip)
 | 
			
		||||
	return &proc.Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil
 | 
			
		||||
	regs, err := t.th.registers(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	pc := regs.PC()
 | 
			
		||||
	f, l, fn := t.p.bi.PCToLine(pc)
 | 
			
		||||
	return &proc.Location{PC: pc, File: f, Line: l, Fn: fn}, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Breakpoint returns the current breakpoint this thread is stopped at.
 | 
			
		||||
@@ -268,16 +302,12 @@ func (t *Thread) Breakpoint() proc.BreakpointState {
 | 
			
		||||
 | 
			
		||||
// ThreadID returns the ID for this thread.
 | 
			
		||||
func (t *Thread) ThreadID() int {
 | 
			
		||||
	return int(t.th.Pid)
 | 
			
		||||
	return int(t.th.pid())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registers returns the current value of the registers for this thread.
 | 
			
		||||
func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
 | 
			
		||||
	r := &Registers{&t.th.Reg, nil}
 | 
			
		||||
	if floatingPoint {
 | 
			
		||||
		r.fpregs = t.fpregs
 | 
			
		||||
	}
 | 
			
		||||
	return r, nil
 | 
			
		||||
	return t.th.registers(floatingPoint)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RestoreRegisters will only return an error for core files,
 | 
			
		||||
@@ -406,7 +436,7 @@ func (p *Process) Common() *proc.CommonProcess {
 | 
			
		||||
 | 
			
		||||
// Pid returns the process ID of this process.
 | 
			
		||||
func (p *Process) Pid() int {
 | 
			
		||||
	return p.core.Pid
 | 
			
		||||
	return p.pid
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ResumeNotify is a no-op on core files as we cannot
 | 
			
		||||
@@ -444,7 +474,7 @@ func (p *Process) SwitchGoroutine(gid int) error {
 | 
			
		||||
 | 
			
		||||
// SwitchThread will change the selected and active thread.
 | 
			
		||||
func (p *Process) SwitchThread(tid int) error {
 | 
			
		||||
	if th, ok := p.core.Threads[tid]; ok {
 | 
			
		||||
	if th, ok := p.Threads[tid]; ok {
 | 
			
		||||
		p.currentThread = th
 | 
			
		||||
		p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
 | 
			
		||||
		return nil
 | 
			
		||||
@@ -454,8 +484,8 @@ func (p *Process) SwitchThread(tid int) error {
 | 
			
		||||
 | 
			
		||||
// ThreadList will return a list of all threads currently in the process.
 | 
			
		||||
func (p *Process) ThreadList() []proc.Thread {
 | 
			
		||||
	r := make([]proc.Thread, 0, len(p.core.Threads))
 | 
			
		||||
	for _, v := range p.core.Threads {
 | 
			
		||||
	r := make([]proc.Thread, 0, len(p.Threads))
 | 
			
		||||
	for _, v := range p.Threads {
 | 
			
		||||
		r = append(r, v)
 | 
			
		||||
	}
 | 
			
		||||
	return r
 | 
			
		||||
@@ -463,64 +493,6 @@ func (p *Process) ThreadList() []proc.Thread {
 | 
			
		||||
 | 
			
		||||
// FindThread will return the thread with the corresponding thread ID.
 | 
			
		||||
func (p *Process) FindThread(threadID int) (proc.Thread, bool) {
 | 
			
		||||
	t, ok := p.core.Threads[threadID]
 | 
			
		||||
	t, ok := p.Threads[threadID]
 | 
			
		||||
	return t, ok
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Registers represents the CPU registers.
 | 
			
		||||
type Registers struct {
 | 
			
		||||
	*LinuxCoreRegisters
 | 
			
		||||
	fpregs []proc.Register
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Slice will return a slice containing all registers and their values.
 | 
			
		||||
func (r *Registers) Slice() []proc.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},
 | 
			
		||||
		{"Orig_rax", r.Orig_rax},
 | 
			
		||||
		{"Cs", r.Cs},
 | 
			
		||||
		{"Eflags", r.Eflags},
 | 
			
		||||
		{"Ss", r.Ss},
 | 
			
		||||
		{"Fs_base", r.Fs_base},
 | 
			
		||||
		{"Gs_base", r.Gs_base},
 | 
			
		||||
		{"Ds", r.Ds},
 | 
			
		||||
		{"Es", r.Es},
 | 
			
		||||
		{"Fs", r.Fs},
 | 
			
		||||
		{"Gs", r.Gs},
 | 
			
		||||
	}
 | 
			
		||||
	out := make([]proc.Register, 0, len(regs))
 | 
			
		||||
	for _, reg := range regs {
 | 
			
		||||
		if reg.k == "Eflags" {
 | 
			
		||||
			out = proc.AppendEflagReg(out, reg.k, reg.v)
 | 
			
		||||
		} else {
 | 
			
		||||
			out = proc.AppendQwordReg(out, reg.k, reg.v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	out = append(out, r.fpregs...)
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Copy will return a copy of the registers that is guarenteed
 | 
			
		||||
// not to change.
 | 
			
		||||
func (r *Registers) Copy() proc.Registers {
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
@@ -7,45 +7,12 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/arch/x86/x86asm"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc/linutil"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/linutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Copied from golang.org/x/sys/unix.PtraceRegs since it's not available on
 | 
			
		||||
// all systems.
 | 
			
		||||
type LinuxCoreRegisters struct {
 | 
			
		||||
	R15      uint64
 | 
			
		||||
	R14      uint64
 | 
			
		||||
	R13      uint64
 | 
			
		||||
	R12      uint64
 | 
			
		||||
	Rbp      uint64
 | 
			
		||||
	Rbx      uint64
 | 
			
		||||
	R11      uint64
 | 
			
		||||
	R10      uint64
 | 
			
		||||
	R9       uint64
 | 
			
		||||
	R8       uint64
 | 
			
		||||
	Rax      uint64
 | 
			
		||||
	Rcx      uint64
 | 
			
		||||
	Rdx      uint64
 | 
			
		||||
	Rsi      uint64
 | 
			
		||||
	Rdi      uint64
 | 
			
		||||
	Orig_rax uint64
 | 
			
		||||
	Rip      uint64
 | 
			
		||||
	Cs       uint64
 | 
			
		||||
	Eflags   uint64
 | 
			
		||||
	Rsp      uint64
 | 
			
		||||
	Ss       uint64
 | 
			
		||||
	Fs_base  uint64
 | 
			
		||||
	Gs_base  uint64
 | 
			
		||||
	Ds       uint64
 | 
			
		||||
	Es       uint64
 | 
			
		||||
	Fs       uint64
 | 
			
		||||
	Gs       uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Copied from golang.org/x/sys/unix.Timeval since it's not available on all
 | 
			
		||||
// systems.
 | 
			
		||||
type LinuxCoreTimeval struct {
 | 
			
		||||
@@ -62,207 +29,21 @@ const NT_X86_XSTATE elf.NType = 0x202 // Note type for notes containing X86 XSAV
 | 
			
		||||
// NT_AUXV is the note type for notes containing a copy of the Auxv array
 | 
			
		||||
const NT_AUXV elf.NType = 0x6
 | 
			
		||||
 | 
			
		||||
// PC returns the value of RIP.
 | 
			
		||||
func (r *LinuxCoreRegisters) PC() uint64 {
 | 
			
		||||
	return r.Rip
 | 
			
		||||
}
 | 
			
		||||
const elfErrorBadMagicNumber = "bad magic number"
 | 
			
		||||
 | 
			
		||||
// SP returns the value of RSP.
 | 
			
		||||
func (r *LinuxCoreRegisters) SP() uint64 {
 | 
			
		||||
	return r.Rsp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// BP returns the value of RBP.
 | 
			
		||||
func (r *LinuxCoreRegisters) BP() uint64 {
 | 
			
		||||
	return r.Rbp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CX returns the value of RCX.
 | 
			
		||||
func (r *LinuxCoreRegisters) CX() uint64 {
 | 
			
		||||
	return r.Rcx
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TLS returns the location of the thread local storate,
 | 
			
		||||
// which will be the value of Fs_base.
 | 
			
		||||
func (r *LinuxCoreRegisters) TLS() uint64 {
 | 
			
		||||
	return r.Fs_base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GAddr returns the address of the G struct. Always returns 0
 | 
			
		||||
// and false for core files.
 | 
			
		||||
func (r *LinuxCoreRegisters) GAddr() (uint64, bool) {
 | 
			
		||||
	return 0, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get returns the value of the register requested via the
 | 
			
		||||
// register number, returning an error if that register
 | 
			
		||||
// could not be found.
 | 
			
		||||
func (r *LinuxCoreRegisters) 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, proc.ErrUnknownRegister
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readCore reads a core file from corePath corresponding to the executable at
 | 
			
		||||
// readLinuxAMD64Core reads a core file from corePath corresponding to the executable at
 | 
			
		||||
// exePath. For details on the Linux ELF core format, see:
 | 
			
		||||
// http://www.gabriel.urdhr.fr/2015/05/29/core-file/,
 | 
			
		||||
// http://uhlo.blogspot.fr/2012/05/brief-look-into-core-dumps.html,
 | 
			
		||||
// elf_core_dump in http://lxr.free-electrons.com/source/fs/binfmt_elf.c,
 | 
			
		||||
// and, if absolutely desperate, readelf.c from the binutils source.
 | 
			
		||||
func readCore(corePath, exePath string) (*Core, error) {
 | 
			
		||||
func readLinuxAMD64Core(corePath, exePath string) (*Process, error) {
 | 
			
		||||
	coreFile, err := elf.Open(corePath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if _, isfmterr := err.(*elf.FormatError); isfmterr && (strings.Contains(err.Error(), elfErrorBadMagicNumber) || strings.Contains(err.Error(), " at offset 0x0: too short")) {
 | 
			
		||||
			// Go >=1.11 and <1.11 produce different errors when reading a non-elf file.
 | 
			
		||||
			return nil, ErrUnrecognizedFormat
 | 
			
		||||
		}
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	exe, err := os.Open(exePath)
 | 
			
		||||
@@ -288,37 +69,51 @@ func readCore(corePath, exePath string) (*Core, error) {
 | 
			
		||||
	memory := buildMemory(coreFile, exeELF, exe, notes)
 | 
			
		||||
	entryPoint := findEntryPoint(notes)
 | 
			
		||||
 | 
			
		||||
	core := &Core{
 | 
			
		||||
		MemoryReader: memory,
 | 
			
		||||
		Threads:      map[int]*Thread{},
 | 
			
		||||
		entryPoint:   entryPoint,
 | 
			
		||||
	p := &Process{
 | 
			
		||||
		mem:         memory,
 | 
			
		||||
		Threads:     map[int]*Thread{},
 | 
			
		||||
		entryPoint:  entryPoint,
 | 
			
		||||
		bi:          proc.NewBinaryInfo("linux", "amd64"),
 | 
			
		||||
		breakpoints: proc.NewBreakpointMap(),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var lastThread *Thread
 | 
			
		||||
	var lastThread *linuxAMD64Thread
 | 
			
		||||
	for _, note := range notes {
 | 
			
		||||
		switch note.Type {
 | 
			
		||||
		case elf.NT_PRSTATUS:
 | 
			
		||||
			t := note.Desc.(*LinuxPrStatus)
 | 
			
		||||
			lastThread = &Thread{t, nil, nil, proc.CommonThread{}}
 | 
			
		||||
			core.Threads[int(t.Pid)] = lastThread
 | 
			
		||||
			lastThread = &linuxAMD64Thread{linutil.AMD64Registers{Regs: &t.Reg}, t}
 | 
			
		||||
			p.Threads[int(t.Pid)] = &Thread{lastThread, p, proc.CommonThread{}}
 | 
			
		||||
			if p.currentThread == nil {
 | 
			
		||||
				p.currentThread = p.Threads[int(t.Pid)]
 | 
			
		||||
			}
 | 
			
		||||
		case NT_X86_XSTATE:
 | 
			
		||||
			if lastThread != nil {
 | 
			
		||||
				lastThread.fpregs = note.Desc.(*proc.LinuxX86Xstate).Decode()
 | 
			
		||||
				lastThread.regs.Fpregs = note.Desc.(*linutil.AMD64Xstate).Decode()
 | 
			
		||||
			}
 | 
			
		||||
		case elf.NT_PRPSINFO:
 | 
			
		||||
			core.Pid = int(note.Desc.(*LinuxPrPsInfo).Pid)
 | 
			
		||||
			p.pid = int(note.Desc.(*LinuxPrPsInfo).Pid)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return core, nil
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Core represents a core file.
 | 
			
		||||
type Core struct {
 | 
			
		||||
	proc.MemoryReader
 | 
			
		||||
	Threads map[int]*Thread
 | 
			
		||||
	Pid     int
 | 
			
		||||
type linuxAMD64Thread struct {
 | 
			
		||||
	regs linutil.AMD64Registers
 | 
			
		||||
	t    *LinuxPrStatus
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	entryPoint uint64
 | 
			
		||||
func (t *linuxAMD64Thread) registers(floatingPoint bool) (proc.Registers, error) {
 | 
			
		||||
	var r linutil.AMD64Registers
 | 
			
		||||
	r.Regs = t.regs.Regs
 | 
			
		||||
	if floatingPoint {
 | 
			
		||||
		r.Fpregs = t.regs.Fpregs
 | 
			
		||||
	}
 | 
			
		||||
	return &r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *linuxAMD64Thread) pid() int {
 | 
			
		||||
	return int(t.t.Pid)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Note is a note from the PT_NOTE prog.
 | 
			
		||||
@@ -415,8 +210,8 @@ func readNote(r io.ReadSeeker) (*Note, error) {
 | 
			
		||||
		}
 | 
			
		||||
		note.Desc = data
 | 
			
		||||
	case NT_X86_XSTATE:
 | 
			
		||||
		var fpregs proc.LinuxX86Xstate
 | 
			
		||||
		if err := proc.LinuxX86XstateRead(desc, true, &fpregs); err != nil {
 | 
			
		||||
		var fpregs linutil.AMD64Xstate
 | 
			
		||||
		if err := linutil.AMD64XstateRead(desc, true, &fpregs); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		note.Desc = &fpregs
 | 
			
		||||
@@ -516,7 +311,7 @@ type LinuxPrStatus struct {
 | 
			
		||||
	Sighold                      uint64
 | 
			
		||||
	Pid, Ppid, Pgrp, Sid         int32
 | 
			
		||||
	Utime, Stime, CUtime, CStime LinuxCoreTimeval
 | 
			
		||||
	Reg                          LinuxCoreRegisters
 | 
			
		||||
	Reg                          linutil.AMD64PtraceRegs
 | 
			
		||||
	Fpvalid                      int32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										154
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/minidump/fileflags_string.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										154
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/minidump/fileflags_string.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,154 @@
 | 
			
		||||
// Code generated by "stringer -type FileFlags,StreamType,Arch,MemoryState,MemoryType,MemoryProtection"; DO NOT EDIT.
 | 
			
		||||
 | 
			
		||||
package minidump
 | 
			
		||||
 | 
			
		||||
import "strconv"
 | 
			
		||||
 | 
			
		||||
const _FileFlags_name = "FileNormalFileWithDataSegsFileWithFullMemoryFileWithHandleDataFileFilterMemoryFileScanMemoryFileWithUnloadedModulesFileWithIncorrectlyReferencedMemoryFileFilterModulePathsFileWithProcessThreadDataFileWithPrivateReadWriteMemoryFileWithoutOptionalDataFileWithFullMemoryInfoFileWithThreadInfoFileWithCodeSegsFileWithoutAuxilliarySegsFileWithFullAuxilliaryStateFileWithPrivateCopyMemoryFileIgnoreInaccessibleMemoryFileWithTokenInformation"
 | 
			
		||||
 | 
			
		||||
var _FileFlags_map = map[FileFlags]string{
 | 
			
		||||
	0:      _FileFlags_name[0:10],
 | 
			
		||||
	1:      _FileFlags_name[10:26],
 | 
			
		||||
	2:      _FileFlags_name[26:44],
 | 
			
		||||
	4:      _FileFlags_name[44:62],
 | 
			
		||||
	8:      _FileFlags_name[62:78],
 | 
			
		||||
	16:     _FileFlags_name[78:92],
 | 
			
		||||
	32:     _FileFlags_name[92:115],
 | 
			
		||||
	64:     _FileFlags_name[115:150],
 | 
			
		||||
	128:    _FileFlags_name[150:171],
 | 
			
		||||
	256:    _FileFlags_name[171:196],
 | 
			
		||||
	512:    _FileFlags_name[196:226],
 | 
			
		||||
	1024:   _FileFlags_name[226:249],
 | 
			
		||||
	2048:   _FileFlags_name[249:271],
 | 
			
		||||
	4096:   _FileFlags_name[271:289],
 | 
			
		||||
	8192:   _FileFlags_name[289:305],
 | 
			
		||||
	16384:  _FileFlags_name[305:330],
 | 
			
		||||
	32768:  _FileFlags_name[330:357],
 | 
			
		||||
	65536:  _FileFlags_name[357:382],
 | 
			
		||||
	131072: _FileFlags_name[382:410],
 | 
			
		||||
	262144: _FileFlags_name[410:434],
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (i FileFlags) String() string {
 | 
			
		||||
	if str, ok := _FileFlags_map[i]; ok {
 | 
			
		||||
		return str
 | 
			
		||||
	}
 | 
			
		||||
	return "FileFlags(" + strconv.FormatInt(int64(i), 10) + ")"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const _StreamType_name = "UnusedStreamReservedStream0ReservedStream1ThreadListStreamModuleListStreamMemoryListStreamExceptionStreamSystemInfoStreamThreadExListStreamMemory64ListStreamCommentStreamACommentStreamWHandleDataStreamFunctionTableStreamUnloadedModuleStreamMiscInfoStreamMemoryInfoListStreamThreadInfoListStreamHandleOperationListStreamTokenStreamJavascriptDataStreamSystemMemoryInfoStreamProcessVMCounterStream"
 | 
			
		||||
 | 
			
		||||
var _StreamType_index = [...]uint16{0, 12, 27, 42, 58, 74, 90, 105, 121, 139, 157, 171, 185, 201, 220, 240, 254, 274, 294, 319, 330, 350, 372, 394}
 | 
			
		||||
 | 
			
		||||
func (i StreamType) String() string {
 | 
			
		||||
	if i >= StreamType(len(_StreamType_index)-1) {
 | 
			
		||||
		return "StreamType(" + strconv.FormatInt(int64(i), 10) + ")"
 | 
			
		||||
	}
 | 
			
		||||
	return _StreamType_name[_StreamType_index[i]:_StreamType_index[i+1]]
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_Arch_name_0 = "CpuArchitectureX86CpuArchitectureMipsCpuArchitectureAlphaCpuArchitecturePPCCpuArchitectureSHXCpuArchitectureARMCpuArchitectureIA64CpuArchitectureAlpha64CpuArchitectureMSILCpuArchitectureAMD64CpuArchitectureWoW64"
 | 
			
		||||
	_Arch_name_1 = "CpuArchitectureARM64"
 | 
			
		||||
	_Arch_name_2 = "CpuArchitectureUnknown"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	_Arch_index_0 = [...]uint8{0, 18, 37, 57, 75, 93, 111, 130, 152, 171, 191, 211}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (i Arch) String() string {
 | 
			
		||||
	switch {
 | 
			
		||||
	case 0 <= i && i <= 10:
 | 
			
		||||
		return _Arch_name_0[_Arch_index_0[i]:_Arch_index_0[i+1]]
 | 
			
		||||
	case i == 12:
 | 
			
		||||
		return _Arch_name_1
 | 
			
		||||
	case i == 65535:
 | 
			
		||||
		return _Arch_name_2
 | 
			
		||||
	default:
 | 
			
		||||
		return "Arch(" + strconv.FormatInt(int64(i), 10) + ")"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_MemoryState_name_0 = "MemoryStateCommit"
 | 
			
		||||
	_MemoryState_name_1 = "MemoryStateReserve"
 | 
			
		||||
	_MemoryState_name_2 = "MemoryStateFree"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (i MemoryState) String() string {
 | 
			
		||||
	switch {
 | 
			
		||||
	case i == 4096:
 | 
			
		||||
		return _MemoryState_name_0
 | 
			
		||||
	case i == 8192:
 | 
			
		||||
		return _MemoryState_name_1
 | 
			
		||||
	case i == 65536:
 | 
			
		||||
		return _MemoryState_name_2
 | 
			
		||||
	default:
 | 
			
		||||
		return "MemoryState(" + strconv.FormatInt(int64(i), 10) + ")"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_MemoryType_name_0 = "MemoryTypePrivate"
 | 
			
		||||
	_MemoryType_name_1 = "MemoryTypeMapped"
 | 
			
		||||
	_MemoryType_name_2 = "MemoryTypeImage"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (i MemoryType) String() string {
 | 
			
		||||
	switch {
 | 
			
		||||
	case i == 131072:
 | 
			
		||||
		return _MemoryType_name_0
 | 
			
		||||
	case i == 262144:
 | 
			
		||||
		return _MemoryType_name_1
 | 
			
		||||
	case i == 16777216:
 | 
			
		||||
		return _MemoryType_name_2
 | 
			
		||||
	default:
 | 
			
		||||
		return "MemoryType(" + strconv.FormatInt(int64(i), 10) + ")"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_MemoryProtection_name_0 = "MemoryProtectNoAccessMemoryProtectReadOnly"
 | 
			
		||||
	_MemoryProtection_name_1 = "MemoryProtectReadWrite"
 | 
			
		||||
	_MemoryProtection_name_2 = "MemoryProtectWriteCopy"
 | 
			
		||||
	_MemoryProtection_name_3 = "MemoryProtectExecute"
 | 
			
		||||
	_MemoryProtection_name_4 = "MemoryProtectExecuteRead"
 | 
			
		||||
	_MemoryProtection_name_5 = "MemoryProtectExecuteReadWrite"
 | 
			
		||||
	_MemoryProtection_name_6 = "MemoryProtectExecuteWriteCopy"
 | 
			
		||||
	_MemoryProtection_name_7 = "MemoryProtectPageGuard"
 | 
			
		||||
	_MemoryProtection_name_8 = "MemoryProtectNoCache"
 | 
			
		||||
	_MemoryProtection_name_9 = "MemoryProtectWriteCombine"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var (
 | 
			
		||||
	_MemoryProtection_index_0 = [...]uint8{0, 21, 42}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func (i MemoryProtection) String() string {
 | 
			
		||||
	switch {
 | 
			
		||||
	case 1 <= i && i <= 2:
 | 
			
		||||
		i -= 1
 | 
			
		||||
		return _MemoryProtection_name_0[_MemoryProtection_index_0[i]:_MemoryProtection_index_0[i+1]]
 | 
			
		||||
	case i == 4:
 | 
			
		||||
		return _MemoryProtection_name_1
 | 
			
		||||
	case i == 8:
 | 
			
		||||
		return _MemoryProtection_name_2
 | 
			
		||||
	case i == 16:
 | 
			
		||||
		return _MemoryProtection_name_3
 | 
			
		||||
	case i == 32:
 | 
			
		||||
		return _MemoryProtection_name_4
 | 
			
		||||
	case i == 64:
 | 
			
		||||
		return _MemoryProtection_name_5
 | 
			
		||||
	case i == 128:
 | 
			
		||||
		return _MemoryProtection_name_6
 | 
			
		||||
	case i == 256:
 | 
			
		||||
		return _MemoryProtection_name_7
 | 
			
		||||
	case i == 512:
 | 
			
		||||
		return _MemoryProtection_name_8
 | 
			
		||||
	case i == 1024:
 | 
			
		||||
		return _MemoryProtection_name_9
 | 
			
		||||
	default:
 | 
			
		||||
		return "MemoryProtection(" + strconv.FormatInt(int64(i), 10) + ")"
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										686
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/minidump/minidump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										686
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/minidump/minidump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,686 @@
 | 
			
		||||
package minidump
 | 
			
		||||
 | 
			
		||||
// Package minidump provides a loader for Windows Minidump files.
 | 
			
		||||
// Minidump files are the Windows equivalent of unix core dumps.
 | 
			
		||||
// They can be created by the kernel when a program crashes (however this is
 | 
			
		||||
// disabled for Go programs) or programmatically using either WinDbg or the
 | 
			
		||||
// ProcDump utility.
 | 
			
		||||
//
 | 
			
		||||
// The file format is described on MSDN starting at:
 | 
			
		||||
//  https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_header
 | 
			
		||||
// which is the structure found at offset 0 on a minidump file.
 | 
			
		||||
//
 | 
			
		||||
// Further information on the format can be found reading
 | 
			
		||||
// chromium-breakpad's minidump loading code, specifically:
 | 
			
		||||
//  https://chromium.googlesource.com/breakpad/breakpad/+/master/src/google_breakpad/common/minidump_cpu_amd64.h
 | 
			
		||||
// and:
 | 
			
		||||
//  https://chromium.googlesource.com/breakpad/breakpad/+/master/src/google_breakpad/common/minidump_format.h
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"io"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"unicode/utf16"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/winutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type minidumpBuf struct {
 | 
			
		||||
	buf  []byte
 | 
			
		||||
	kind string
 | 
			
		||||
	off  int
 | 
			
		||||
	err  error
 | 
			
		||||
	ctx  string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (buf *minidumpBuf) u16() uint16 {
 | 
			
		||||
	const stride = 2
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	if buf.off+stride >= len(buf.buf) {
 | 
			
		||||
		buf.err = fmt.Errorf("minidump %s truncated at offset %#x while %s", buf.kind, buf.off, buf.ctx)
 | 
			
		||||
	}
 | 
			
		||||
	r := binary.LittleEndian.Uint16(buf.buf[buf.off : buf.off+stride])
 | 
			
		||||
	buf.off += stride
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (buf *minidumpBuf) u32() uint32 {
 | 
			
		||||
	const stride = 4
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	if buf.off+stride >= len(buf.buf) {
 | 
			
		||||
		buf.err = fmt.Errorf("minidump %s truncated at offset %#x while %s", buf.kind, buf.off, buf.ctx)
 | 
			
		||||
	}
 | 
			
		||||
	r := binary.LittleEndian.Uint32(buf.buf[buf.off : buf.off+stride])
 | 
			
		||||
	buf.off += stride
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (buf *minidumpBuf) u64() uint64 {
 | 
			
		||||
	const stride = 8
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return 0
 | 
			
		||||
	}
 | 
			
		||||
	if buf.off+stride >= len(buf.buf) {
 | 
			
		||||
		buf.err = fmt.Errorf("minidump %s truncated at offset %#x while %s", buf.kind, buf.off, buf.ctx)
 | 
			
		||||
	}
 | 
			
		||||
	r := binary.LittleEndian.Uint64(buf.buf[buf.off : buf.off+stride])
 | 
			
		||||
	buf.off += stride
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func streamBuf(stream *Stream, buf *minidumpBuf, name string) *minidumpBuf {
 | 
			
		||||
	return &minidumpBuf{
 | 
			
		||||
		buf:  buf.buf,
 | 
			
		||||
		kind: "stream",
 | 
			
		||||
		off:  stream.Offset,
 | 
			
		||||
		err:  nil,
 | 
			
		||||
		ctx:  fmt.Sprintf("reading %s stream at %#x", name, stream.Offset),
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrNotAMinidump is the error returned when the file being loaded is not a
 | 
			
		||||
// minidump file.
 | 
			
		||||
type ErrNotAMinidump struct {
 | 
			
		||||
	what string
 | 
			
		||||
	got  uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (err ErrNotAMinidump) Error() string {
 | 
			
		||||
	return fmt.Sprintf("not a minidump, invalid %s %#x", err.what, err.got)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	minidumpSignature = 0x504d444d // 'MDMP'
 | 
			
		||||
	minidumpVersion   = 0xa793
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Minidump represents a minidump file
 | 
			
		||||
type Minidump struct {
 | 
			
		||||
	Timestamp uint32
 | 
			
		||||
	Flags     FileFlags
 | 
			
		||||
 | 
			
		||||
	Streams []Stream
 | 
			
		||||
 | 
			
		||||
	Threads []Thread
 | 
			
		||||
	Modules []Module
 | 
			
		||||
 | 
			
		||||
	Pid uint32
 | 
			
		||||
 | 
			
		||||
	MemoryRanges []MemoryRange
 | 
			
		||||
	MemoryInfo   []MemoryInfo
 | 
			
		||||
 | 
			
		||||
	streamNum uint32
 | 
			
		||||
	streamOff uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stream represents one (uninterpreted) stream in a minidump file.
 | 
			
		||||
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_directory
 | 
			
		||||
type Stream struct {
 | 
			
		||||
	Type    StreamType
 | 
			
		||||
	Offset  int
 | 
			
		||||
	RawData []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Thread represents an entry in the ThreadList stream.
 | 
			
		||||
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_thread
 | 
			
		||||
type Thread struct {
 | 
			
		||||
	ID            uint32
 | 
			
		||||
	SuspendCount  uint32
 | 
			
		||||
	PriorityClass uint32
 | 
			
		||||
	Priority      uint32
 | 
			
		||||
	TEB           uint64
 | 
			
		||||
	Context       winutil.CONTEXT
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Module represents an entry in the ModuleList stream.
 | 
			
		||||
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_module
 | 
			
		||||
type Module struct {
 | 
			
		||||
	BaseOfImage   uint64
 | 
			
		||||
	SizeOfImage   uint32
 | 
			
		||||
	Checksum      uint32
 | 
			
		||||
	TimeDateStamp uint32
 | 
			
		||||
	Name          string
 | 
			
		||||
	VersionInfo   VSFixedFileInfo
 | 
			
		||||
 | 
			
		||||
	// CVRecord stores a CodeView record and is populated when a module's debug information resides in a PDB file.  It identifies the PDB file.
 | 
			
		||||
	CVRecord []byte
 | 
			
		||||
 | 
			
		||||
	// MiscRecord is populated when a module's debug information resides in a DBG file.  It identifies the DBG file.  This field is effectively obsolete with modules built by recent toolchains.
 | 
			
		||||
	MiscRecord []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// VSFixedFileInfo: Visual Studio Fixed File Info.
 | 
			
		||||
// See: https://docs.microsoft.com/en-us/windows/desktop/api/verrsrc/ns-verrsrc-tagvs_fixedfileinfo
 | 
			
		||||
type VSFixedFileInfo struct {
 | 
			
		||||
	Signature        uint32
 | 
			
		||||
	StructVersion    uint32
 | 
			
		||||
	FileVersionHi    uint32
 | 
			
		||||
	FileVersionLo    uint32
 | 
			
		||||
	ProductVersionHi uint32
 | 
			
		||||
	ProductVersionLo uint32
 | 
			
		||||
	FileFlagsMask    uint32
 | 
			
		||||
	FileFlags        uint32
 | 
			
		||||
	FileOS           uint32
 | 
			
		||||
	FileType         uint32
 | 
			
		||||
	FileSubtype      uint32
 | 
			
		||||
	FileDateHi       uint32
 | 
			
		||||
	FileDateLo       uint32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MemoryRange represents a region of memory saved to the core file, it's constructed after either:
 | 
			
		||||
// 1. parsing an entry in the Memory64List stream.
 | 
			
		||||
// 2. parsing the stack field of an entry in the ThreadList stream.
 | 
			
		||||
type MemoryRange struct {
 | 
			
		||||
	Addr uint64
 | 
			
		||||
	Data []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ReadMemory reads len(buf) bytes of memory starting at addr into buf from this memory region.
 | 
			
		||||
func (m *MemoryRange) ReadMemory(buf []byte, addr uintptr) (int, error) {
 | 
			
		||||
	if len(buf) == 0 {
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	if (uint64(addr) < m.Addr) || (uint64(addr)+uint64(len(buf)) > m.Addr+uint64(len(m.Data))) {
 | 
			
		||||
		return 0, io.EOF
 | 
			
		||||
	}
 | 
			
		||||
	copy(buf, m.Data[uint64(addr)-m.Addr:])
 | 
			
		||||
	return len(buf), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// MemoryInfo reprents an entry in the MemoryInfoList stream.
 | 
			
		||||
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_memory_info
 | 
			
		||||
type MemoryInfo struct {
 | 
			
		||||
	Addr       uint64
 | 
			
		||||
	Size       uint64
 | 
			
		||||
	State      MemoryState
 | 
			
		||||
	Protection MemoryProtection
 | 
			
		||||
	Type       MemoryType
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
//go:generate stringer -type FileFlags,StreamType,Arch,MemoryState,MemoryType,MemoryProtection
 | 
			
		||||
 | 
			
		||||
// MemoryState is the type of the State field of MINIDUMP_MEMORY_INFO
 | 
			
		||||
type MemoryState uint32
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	MemoryStateCommit  MemoryState = 0x1000
 | 
			
		||||
	MemoryStateReserve MemoryState = 0x2000
 | 
			
		||||
	MemoryStateFree    MemoryState = 0x10000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MemoryType is the type of the Type field of MINIDUMP_MEMORY_INFO
 | 
			
		||||
type MemoryType uint32
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	MemoryTypePrivate MemoryType = 0x20000
 | 
			
		||||
	MemoryTypeMapped  MemoryType = 0x40000
 | 
			
		||||
	MemoryTypeImage   MemoryType = 0x1000000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// MemoryProtection is the type of the Protection field of MINIDUMP_MEMORY_INFO
 | 
			
		||||
type MemoryProtection uint32
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	MemoryProtectNoAccess         MemoryProtection = 0x01 // PAGE_NOACCESS
 | 
			
		||||
	MemoryProtectReadOnly         MemoryProtection = 0x02 // PAGE_READONLY
 | 
			
		||||
	MemoryProtectReadWrite        MemoryProtection = 0x04 // PAGE_READWRITE
 | 
			
		||||
	MemoryProtectWriteCopy        MemoryProtection = 0x08 // PAGE_WRITECOPY
 | 
			
		||||
	MemoryProtectExecute          MemoryProtection = 0x10 // PAGE_EXECUTE
 | 
			
		||||
	MemoryProtectExecuteRead      MemoryProtection = 0x20 // PAGE_EXECUTE_READ
 | 
			
		||||
	MemoryProtectExecuteReadWrite MemoryProtection = 0x40 // PAGE_EXECUTE_READWRITE
 | 
			
		||||
	MemoryProtectExecuteWriteCopy MemoryProtection = 0x80 // PAGE_EXECUTE_WRITECOPY
 | 
			
		||||
	// These options can be combined with the previous flags
 | 
			
		||||
	MemoryProtectPageGuard    MemoryProtection = 0x100 // PAGE_GUARD
 | 
			
		||||
	MemoryProtectNoCache      MemoryProtection = 0x200 // PAGE_NOCACHE
 | 
			
		||||
	MemoryProtectWriteCombine MemoryProtection = 0x400 // PAGE_WRITECOMBINE
 | 
			
		||||
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// FileFlags is the type of the Flags field of MINIDUMP_HEADER
 | 
			
		||||
type FileFlags uint64
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	FileNormal                          FileFlags = 0x00000000
 | 
			
		||||
	FileWithDataSegs                    FileFlags = 0x00000001
 | 
			
		||||
	FileWithFullMemory                  FileFlags = 0x00000002
 | 
			
		||||
	FileWithHandleData                  FileFlags = 0x00000004
 | 
			
		||||
	FileFilterMemory                    FileFlags = 0x00000008
 | 
			
		||||
	FileScanMemory                      FileFlags = 0x00000010
 | 
			
		||||
	FileWithUnloadedModules             FileFlags = 0x00000020
 | 
			
		||||
	FileWithIncorrectlyReferencedMemory FileFlags = 0x00000040
 | 
			
		||||
	FileFilterModulePaths               FileFlags = 0x00000080
 | 
			
		||||
	FileWithProcessThreadData           FileFlags = 0x00000100
 | 
			
		||||
	FileWithPrivateReadWriteMemory      FileFlags = 0x00000200
 | 
			
		||||
	FileWithoutOptionalData             FileFlags = 0x00000400
 | 
			
		||||
	FileWithFullMemoryInfo              FileFlags = 0x00000800
 | 
			
		||||
	FileWithThreadInfo                  FileFlags = 0x00001000
 | 
			
		||||
	FileWithCodeSegs                    FileFlags = 0x00002000
 | 
			
		||||
	FileWithoutAuxilliarySegs           FileFlags = 0x00004000
 | 
			
		||||
	FileWithFullAuxilliaryState         FileFlags = 0x00008000
 | 
			
		||||
	FileWithPrivateCopyMemory           FileFlags = 0x00010000
 | 
			
		||||
	FileIgnoreInaccessibleMemory        FileFlags = 0x00020000
 | 
			
		||||
	FileWithTokenInformation            FileFlags = 0x00040000
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// StreamType is the type of the StreamType field of MINIDUMP_DIRECTORY
 | 
			
		||||
type StreamType uint32
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	UnusedStream              StreamType = 0
 | 
			
		||||
	ReservedStream0           StreamType = 1
 | 
			
		||||
	ReservedStream1           StreamType = 2
 | 
			
		||||
	ThreadListStream          StreamType = 3
 | 
			
		||||
	ModuleListStream          StreamType = 4
 | 
			
		||||
	MemoryListStream          StreamType = 5
 | 
			
		||||
	ExceptionStream           StreamType = 6
 | 
			
		||||
	SystemInfoStream          StreamType = 7
 | 
			
		||||
	ThreadExListStream        StreamType = 8
 | 
			
		||||
	Memory64ListStream        StreamType = 9
 | 
			
		||||
	CommentStreamA            StreamType = 10
 | 
			
		||||
	CommentStreamW            StreamType = 11
 | 
			
		||||
	HandleDataStream          StreamType = 12
 | 
			
		||||
	FunctionTableStream       StreamType = 13
 | 
			
		||||
	UnloadedModuleStream      StreamType = 14
 | 
			
		||||
	MiscInfoStream            StreamType = 15
 | 
			
		||||
	MemoryInfoListStream      StreamType = 16
 | 
			
		||||
	ThreadInfoListStream      StreamType = 17
 | 
			
		||||
	HandleOperationListStream StreamType = 18
 | 
			
		||||
	TokenStream               StreamType = 19
 | 
			
		||||
	JavascriptDataStream      StreamType = 20
 | 
			
		||||
	SystemMemoryInfoStream    StreamType = 21
 | 
			
		||||
	ProcessVMCounterStream    StreamType = 22
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Arch is the type of the ProcessorArchitecture field of MINIDUMP_SYSTEM_INFO.
 | 
			
		||||
type Arch uint16
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	CpuArchitectureX86     Arch = 0
 | 
			
		||||
	CpuArchitectureMips    Arch = 1
 | 
			
		||||
	CpuArchitectureAlpha   Arch = 2
 | 
			
		||||
	CpuArchitecturePPC     Arch = 3
 | 
			
		||||
	CpuArchitectureSHX     Arch = 4 // Super-H
 | 
			
		||||
	CpuArchitectureARM     Arch = 5
 | 
			
		||||
	CpuArchitectureIA64    Arch = 6
 | 
			
		||||
	CpuArchitectureAlpha64 Arch = 7
 | 
			
		||||
	CpuArchitectureMSIL    Arch = 8 // Microsoft Intermediate Language
 | 
			
		||||
	CpuArchitectureAMD64   Arch = 9
 | 
			
		||||
	CpuArchitectureWoW64   Arch = 10
 | 
			
		||||
	CpuArchitectureARM64   Arch = 12
 | 
			
		||||
	CpuArchitectureUnknown Arch = 0xffff
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Open reads the minidump file at path and returns it as a Minidump structure.
 | 
			
		||||
func Open(path string, logfn func(fmt string, args ...interface{})) (*Minidump, error) {
 | 
			
		||||
	rawbuf, err := ioutil.ReadFile(path) //TODO(aarzilli): mmap?
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf := &minidumpBuf{buf: rawbuf, kind: "file"}
 | 
			
		||||
 | 
			
		||||
	var mdmp Minidump
 | 
			
		||||
 | 
			
		||||
	readMinidumpHeader(&mdmp, buf)
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return nil, buf.err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if logfn != nil {
 | 
			
		||||
		logfn("Minidump Header\n")
 | 
			
		||||
		logfn("Num Streams: %d\n", mdmp.streamNum)
 | 
			
		||||
		logfn("Streams offset: %#x\n", mdmp.streamOff)
 | 
			
		||||
		logfn("File flags: %s\n", fileFlagsToString(mdmp.Flags))
 | 
			
		||||
		logfn("Offset after header %#x\n", buf.off)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	readDirectory(&mdmp, buf)
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return nil, buf.err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.Streams {
 | 
			
		||||
		stream := &mdmp.Streams[i]
 | 
			
		||||
		if stream.Type != SystemInfoStream {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		sb := streamBuf(stream, buf, "system info")
 | 
			
		||||
		if buf.err != nil {
 | 
			
		||||
			return nil, buf.err
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		arch := Arch(sb.u16())
 | 
			
		||||
 | 
			
		||||
		if logfn != nil {
 | 
			
		||||
			logfn("Found processor architecture %s\n", arch.String())
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if arch != CpuArchitectureAMD64 {
 | 
			
		||||
			return nil, fmt.Errorf("unsupported architecture %s", arch.String())
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.Streams {
 | 
			
		||||
		stream := &mdmp.Streams[i]
 | 
			
		||||
		if logfn != nil {
 | 
			
		||||
			logfn("Stream %d: type:%s off:%#x size:%#x\n", i, stream.Type, stream.Offset, len(stream.RawData))
 | 
			
		||||
		}
 | 
			
		||||
		switch stream.Type {
 | 
			
		||||
		case ThreadListStream:
 | 
			
		||||
			readThreadList(&mdmp, streamBuf(stream, buf, "thread list"))
 | 
			
		||||
			if logfn != nil {
 | 
			
		||||
				for i := range mdmp.Threads {
 | 
			
		||||
					logfn("\tID:%#x TEB:%#x\n", mdmp.Threads[i].ID, mdmp.Threads[i].TEB)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case ModuleListStream:
 | 
			
		||||
			readModuleList(&mdmp, streamBuf(stream, buf, "module list"))
 | 
			
		||||
			if logfn != nil {
 | 
			
		||||
				for i := range mdmp.Modules {
 | 
			
		||||
					logfn("\tName:%q BaseOfImage:%#x SizeOfImage:%#x\n", mdmp.Modules[i].Name, mdmp.Modules[i].BaseOfImage, mdmp.Modules[i].SizeOfImage)
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
		case ExceptionStream:
 | 
			
		||||
			//TODO(aarzilli): this stream contains the exception that made the
 | 
			
		||||
			//process stop and caused the minidump to be taken. If we ever start
 | 
			
		||||
			//caring about this we should parse this.
 | 
			
		||||
		case Memory64ListStream:
 | 
			
		||||
			readMemory64List(&mdmp, streamBuf(stream, buf, "memory64 list"), logfn)
 | 
			
		||||
		case MemoryInfoListStream:
 | 
			
		||||
			readMemoryInfoList(&mdmp, streamBuf(stream, buf, "memory info list"), logfn)
 | 
			
		||||
		case MiscInfoStream:
 | 
			
		||||
			readMiscInfo(&mdmp, streamBuf(stream, buf, "misc info"))
 | 
			
		||||
			if logfn != nil {
 | 
			
		||||
				logfn("\tPid: %#x\n", mdmp.Pid)
 | 
			
		||||
			}
 | 
			
		||||
		case CommentStreamW:
 | 
			
		||||
			if logfn != nil {
 | 
			
		||||
				logfn("\t%q\n", decodeUTF16(stream.RawData))
 | 
			
		||||
			}
 | 
			
		||||
		case CommentStreamA:
 | 
			
		||||
			if logfn != nil {
 | 
			
		||||
				logfn("\t%s\n", string(stream.RawData))
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		if buf.err != nil {
 | 
			
		||||
			return nil, buf.err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return &mdmp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// decodeUTF16 converts a NUL-terminated UTF16LE string to (non NUL-terminated) UTF8.
 | 
			
		||||
func decodeUTF16(in []byte) string {
 | 
			
		||||
	utf16encoded := []uint16{}
 | 
			
		||||
	for i := 0; i+1 < len(in); i += 2 {
 | 
			
		||||
		var ch uint16
 | 
			
		||||
		ch = uint16(in[i]) + uint16(in[i+1])<<8
 | 
			
		||||
		utf16encoded = append(utf16encoded, ch)
 | 
			
		||||
	}
 | 
			
		||||
	s := string(utf16.Decode(utf16encoded))
 | 
			
		||||
	if len(s) > 0 && s[len(s)-1] == 0 {
 | 
			
		||||
		s = s[:len(s)-1]
 | 
			
		||||
	}
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fileFlagsToString(flags FileFlags) string {
 | 
			
		||||
	out := []byte{}
 | 
			
		||||
	for i, name := range _FileFlags_map {
 | 
			
		||||
		if i == 0 {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if flags&i != 0 {
 | 
			
		||||
			if len(out) > 0 {
 | 
			
		||||
				out = append(out, '|')
 | 
			
		||||
			}
 | 
			
		||||
			out = append(out, name...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if len(out) == 0 {
 | 
			
		||||
		return flags.String()
 | 
			
		||||
	}
 | 
			
		||||
	return string(out)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readMinidumpHeader reads the minidump file header
 | 
			
		||||
func readMinidumpHeader(mdmp *Minidump, buf *minidumpBuf) {
 | 
			
		||||
	buf.ctx = "reading minidump header"
 | 
			
		||||
 | 
			
		||||
	if sig := buf.u32(); sig != minidumpSignature {
 | 
			
		||||
		buf.err = ErrNotAMinidump{"signature", sig}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if ver := buf.u16(); ver != minidumpVersion {
 | 
			
		||||
		buf.err = ErrNotAMinidump{"version", uint32(ver)}
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	buf.u16() // implementation specific version
 | 
			
		||||
	mdmp.streamNum = buf.u32()
 | 
			
		||||
	mdmp.streamOff = buf.u32()
 | 
			
		||||
	buf.u32() // checksum, but it's always 0
 | 
			
		||||
	mdmp.Timestamp = buf.u32()
 | 
			
		||||
	mdmp.Flags = FileFlags(buf.u64())
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readDirectory reads the list of streams (i.e. the minidum "directory")
 | 
			
		||||
func readDirectory(mdmp *Minidump, buf *minidumpBuf) {
 | 
			
		||||
	buf.off = int(mdmp.streamOff)
 | 
			
		||||
 | 
			
		||||
	mdmp.Streams = make([]Stream, mdmp.streamNum)
 | 
			
		||||
	for i := range mdmp.Streams {
 | 
			
		||||
		buf.ctx = fmt.Sprintf("reading stream directory entry %d", i)
 | 
			
		||||
		stream := &mdmp.Streams[i]
 | 
			
		||||
		stream.Type = StreamType(buf.u32())
 | 
			
		||||
		stream.Offset, stream.RawData = readLocationDescriptor(buf)
 | 
			
		||||
		if buf.err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readLocationDescriptor reads a location descriptor structure (a structure
 | 
			
		||||
// which describes a subregion of the file), and returns the destination
 | 
			
		||||
// offset and a slice into the minidump file's buffer.
 | 
			
		||||
func readLocationDescriptor(buf *minidumpBuf) (off int, rawData []byte) {
 | 
			
		||||
	sz := buf.u32()
 | 
			
		||||
	off = int(buf.u32())
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return off, nil
 | 
			
		||||
	}
 | 
			
		||||
	end := off + int(sz)
 | 
			
		||||
	if off >= len(buf.buf) || end > len(buf.buf) {
 | 
			
		||||
		buf.err = fmt.Errorf("location starting at %#x of size %#x is past the end of file, while %s", off, sz, buf.ctx)
 | 
			
		||||
		return 0, nil
 | 
			
		||||
	}
 | 
			
		||||
	rawData = buf.buf[off:end]
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readString(buf *minidumpBuf) string {
 | 
			
		||||
	startOff := buf.off
 | 
			
		||||
	sz := buf.u32()
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	end := buf.off + int(sz)
 | 
			
		||||
	if buf.off >= len(buf.buf) || end > len(buf.buf) {
 | 
			
		||||
		buf.err = fmt.Errorf("string starting at %#x of size %#x is past the end of file, while %s", startOff, sz, buf.ctx)
 | 
			
		||||
		return ""
 | 
			
		||||
	}
 | 
			
		||||
	return decodeUTF16(buf.buf[buf.off:end])
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readThreadList reads a thread list stream and adds the threads to the minidump.
 | 
			
		||||
func readThreadList(mdmp *Minidump, buf *minidumpBuf) {
 | 
			
		||||
	threadNum := buf.u32()
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mdmp.Threads = make([]Thread, threadNum)
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.Threads {
 | 
			
		||||
		buf.ctx = fmt.Sprintf("reading thread list entry %d", i)
 | 
			
		||||
		thread := &mdmp.Threads[i]
 | 
			
		||||
 | 
			
		||||
		thread.ID = buf.u32()
 | 
			
		||||
		thread.SuspendCount = buf.u32()
 | 
			
		||||
		thread.PriorityClass = buf.u32()
 | 
			
		||||
		thread.Priority = buf.u32()
 | 
			
		||||
		thread.TEB = buf.u64()
 | 
			
		||||
		if buf.err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		readMemoryDescriptor(mdmp, buf)                    // thread stack
 | 
			
		||||
		_, rawThreadContext := readLocationDescriptor(buf) // thread context
 | 
			
		||||
		thread.Context = *((*winutil.CONTEXT)(unsafe.Pointer(&rawThreadContext[0])))
 | 
			
		||||
		if buf.err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readModuleList reads a module list stream and adds the modules to the minidump.
 | 
			
		||||
func readModuleList(mdmp *Minidump, buf *minidumpBuf) {
 | 
			
		||||
	moduleNum := buf.u32()
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mdmp.Modules = make([]Module, moduleNum)
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.Modules {
 | 
			
		||||
		buf.ctx = fmt.Sprintf("reading module list entry %d", i)
 | 
			
		||||
		module := &mdmp.Modules[i]
 | 
			
		||||
 | 
			
		||||
		module.BaseOfImage = buf.u64()
 | 
			
		||||
		module.SizeOfImage = buf.u32()
 | 
			
		||||
		module.Checksum = buf.u32()
 | 
			
		||||
		module.TimeDateStamp = buf.u32()
 | 
			
		||||
		nameOff := int(buf.u32())
 | 
			
		||||
 | 
			
		||||
		versionInfoVec := make([]uint32, unsafe.Sizeof(VSFixedFileInfo{})/unsafe.Sizeof(uint32(0)))
 | 
			
		||||
		for j := range versionInfoVec {
 | 
			
		||||
			versionInfoVec[j] = buf.u32()
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		module.VersionInfo = *(*VSFixedFileInfo)(unsafe.Pointer(&versionInfoVec[0]))
 | 
			
		||||
 | 
			
		||||
		_, module.CVRecord = readLocationDescriptor(buf)
 | 
			
		||||
		_, module.MiscRecord = readLocationDescriptor(buf)
 | 
			
		||||
 | 
			
		||||
		if buf.err != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		nameBuf := minidumpBuf{buf: buf.buf, kind: "file", off: nameOff, err: nil, ctx: buf.ctx}
 | 
			
		||||
		module.Name = readString(&nameBuf)
 | 
			
		||||
		if nameBuf.err != nil {
 | 
			
		||||
			buf.err = nameBuf.err
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readMemory64List reads a _MINIDUMP_MEMORY64_LIST structure, containing
 | 
			
		||||
// the description of the process memory.
 | 
			
		||||
// See: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_memory64_list
 | 
			
		||||
// And: https://docs.microsoft.com/en-us/windows/desktop/api/minidumpapiset/ns-minidumpapiset-_minidump_memory_descriptor
 | 
			
		||||
func readMemory64List(mdmp *Minidump, buf *minidumpBuf, logfn func(fmt string, args ...interface{})) {
 | 
			
		||||
	rangesNum := buf.u64()
 | 
			
		||||
	baseOff := int(buf.u64())
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := uint64(0); i < rangesNum; i++ {
 | 
			
		||||
		addr := buf.u64()
 | 
			
		||||
		sz := buf.u64()
 | 
			
		||||
 | 
			
		||||
		end := baseOff + int(sz)
 | 
			
		||||
		if baseOff >= len(buf.buf) || end > len(buf.buf) {
 | 
			
		||||
			buf.err = fmt.Errorf("memory range at %#x of size %#x is past the end of file, while %s", baseOff, sz, buf.ctx)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		mdmp.addMemory(addr, buf.buf[baseOff:end])
 | 
			
		||||
 | 
			
		||||
		if logfn != nil {
 | 
			
		||||
			logfn("\tMemory %d addr:%#x size:%#x FileOffset:%#x\n", i, addr, sz, baseOff)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		baseOff = end
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readMemoryInfoList(mdmp *Minidump, buf *minidumpBuf, logfn func(fmt string, args ...interface{})) {
 | 
			
		||||
	startOff := buf.off
 | 
			
		||||
	sizeOfHeader := int(buf.u32())
 | 
			
		||||
	sizeOfEntry := int(buf.u32())
 | 
			
		||||
	numEntries := buf.u64()
 | 
			
		||||
 | 
			
		||||
	buf.off = startOff + sizeOfHeader
 | 
			
		||||
 | 
			
		||||
	mdmp.MemoryInfo = make([]MemoryInfo, numEntries)
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.MemoryInfo {
 | 
			
		||||
		memInfo := &mdmp.MemoryInfo[i]
 | 
			
		||||
		startOff := buf.off
 | 
			
		||||
 | 
			
		||||
		memInfo.Addr = buf.u64()
 | 
			
		||||
		buf.u64() // allocation_base
 | 
			
		||||
 | 
			
		||||
		buf.u32() // allocation_protection
 | 
			
		||||
		buf.u32() // alignment
 | 
			
		||||
 | 
			
		||||
		memInfo.Size = buf.u64()
 | 
			
		||||
 | 
			
		||||
		memInfo.State = MemoryState(buf.u32())
 | 
			
		||||
		memInfo.Protection = MemoryProtection(buf.u32())
 | 
			
		||||
		memInfo.Type = MemoryType(buf.u32())
 | 
			
		||||
 | 
			
		||||
		if logfn != nil {
 | 
			
		||||
			logfn("\tMemoryInfo %d Addr:%#x Size:%#x %s %s %s\n", i, memInfo.Addr, memInfo.Size, memInfo.State, memInfo.Protection, memInfo.Type)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		buf.off = startOff + sizeOfEntry
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readMiscInfo reads the process_id from a MiscInfo stream.
 | 
			
		||||
func readMiscInfo(mdmp *Minidump, buf *minidumpBuf) {
 | 
			
		||||
	buf.u32() // size of info
 | 
			
		||||
	buf.u32() // flags1
 | 
			
		||||
 | 
			
		||||
	mdmp.Pid = buf.u32() // process_id
 | 
			
		||||
	// there are more fields here, but we don't care about them
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readMemoryDescriptor reads a memory descriptor struct and adds it to the memory map of the minidump.
 | 
			
		||||
func readMemoryDescriptor(mdmp *Minidump, buf *minidumpBuf) {
 | 
			
		||||
	addr := buf.u64()
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	_, rawData := readLocationDescriptor(buf)
 | 
			
		||||
	if buf.err != nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	mdmp.addMemory(addr, rawData)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mdmp *Minidump) addMemory(addr uint64, data []byte) {
 | 
			
		||||
	mdmp.MemoryRanges = append(mdmp.MemoryRanges, MemoryRange{addr, data})
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										59
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/windows_amd64_minidump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										59
									
								
								vendor/github.com/go-delve/delve/pkg/proc/core/windows_amd64_minidump.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,59 @@
 | 
			
		||||
package core
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"github.com/go-delve/delve/pkg/logflags"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/core/minidump"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/winutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func readAMD64Minidump(minidumpPath, exePath string) (*Process, error) {
 | 
			
		||||
	var logfn func(string, ...interface{})
 | 
			
		||||
	if logflags.Minidump() {
 | 
			
		||||
		logfn = logflags.MinidumpLogger().Infof
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	mdmp, err := minidump.Open(minidumpPath, logfn)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if _, isNotAMinidump := err.(minidump.ErrNotAMinidump); isNotAMinidump {
 | 
			
		||||
			return nil, ErrUnrecognizedFormat
 | 
			
		||||
		}
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	memory := &SplicedMemory{}
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.MemoryRanges {
 | 
			
		||||
		m := &mdmp.MemoryRanges[i]
 | 
			
		||||
		memory.Add(m, uintptr(m.Addr), uintptr(len(m.Data)))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := &Process{
 | 
			
		||||
		mem:         memory,
 | 
			
		||||
		Threads:     map[int]*Thread{},
 | 
			
		||||
		bi:          proc.NewBinaryInfo("windows", "amd64"),
 | 
			
		||||
		breakpoints: proc.NewBreakpointMap(),
 | 
			
		||||
		pid:         int(mdmp.Pid),
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for i := range mdmp.Threads {
 | 
			
		||||
		th := &mdmp.Threads[i]
 | 
			
		||||
		p.Threads[int(th.ID)] = &Thread{&windowsAMD64Thread{th}, p, proc.CommonThread{}}
 | 
			
		||||
		if p.currentThread == nil {
 | 
			
		||||
			p.currentThread = p.Threads[int(th.ID)]
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type windowsAMD64Thread struct {
 | 
			
		||||
	th *minidump.Thread
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (th *windowsAMD64Thread) pid() int {
 | 
			
		||||
	return int(th.th.ID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (th *windowsAMD64Thread) registers(floatingPoint bool) (proc.Registers, error) {
 | 
			
		||||
	return winutil.NewAMD64Registers(&th.th.Context, th.th.TEB, floatingPoint), nil
 | 
			
		||||
}
 | 
			
		||||
@@ -65,6 +65,14 @@ func (inst *AsmInstruction) IsCall() bool {
 | 
			
		||||
	return inst.Inst.Op == x86asm.CALL || inst.Inst.Op == x86asm.LCALL
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// IsRet returns true if the instruction is a RET or LRET instruction.
 | 
			
		||||
func (inst *AsmInstruction) IsRet() bool {
 | 
			
		||||
	if inst.Inst == nil {
 | 
			
		||||
		return false
 | 
			
		||||
	}
 | 
			
		||||
	return inst.Inst.Op == x86asm.RET || inst.Inst.Op == x86asm.LRET
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func resolveCallArg(inst *archInst, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
 | 
			
		||||
	if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
 | 
			
		||||
		return nil
 | 
			
		||||
@@ -110,7 +118,7 @@ func resolveCallArg(inst *archInst, currentGoroutine bool, regs Registers, mem M
 | 
			
		||||
 | 
			
		||||
	file, line, fn := bininfo.PCToLine(pc)
 | 
			
		||||
	if fn == nil {
 | 
			
		||||
		return nil
 | 
			
		||||
		return &Location{PC: pc}
 | 
			
		||||
	}
 | 
			
		||||
	return &Location{PC: pc, File: file, Line: line, Fn: fn}
 | 
			
		||||
}
 | 
			
		||||
@@ -14,9 +14,9 @@ import (
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/goversion"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/goversion"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var errOperationOnSpecialFloat = errors.New("operations on non-finite floats not implemented")
 | 
			
		||||
@@ -114,15 +114,25 @@ func (scope *EvalScope) evalToplevelTypeCast(t ast.Expr, cfg LoadConfig) (*Varia
 | 
			
		||||
 | 
			
		||||
	case "string":
 | 
			
		||||
		switch argv.Kind {
 | 
			
		||||
		case reflect.String:
 | 
			
		||||
			s := constant.StringVal(argv.Value)
 | 
			
		||||
			v.Value = constant.MakeString(s)
 | 
			
		||||
			v.Len = int64(len(s))
 | 
			
		||||
			return v, nil
 | 
			
		||||
		case reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64, reflect.Int, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64, reflect.Uint, reflect.Uintptr:
 | 
			
		||||
			b, _ := constant.Int64Val(argv.Value)
 | 
			
		||||
			s := string(b)
 | 
			
		||||
			v.Value = constant.MakeString(s)
 | 
			
		||||
			v.Len = int64(len(s))
 | 
			
		||||
			return v, nil
 | 
			
		||||
 | 
			
		||||
		case reflect.Slice:
 | 
			
		||||
			switch elemType := argv.RealType.(*godwarf.SliceType).ElemType.(type) {
 | 
			
		||||
		case reflect.Slice, reflect.Array:
 | 
			
		||||
			var elem godwarf.Type
 | 
			
		||||
			if argv.Kind == reflect.Slice {
 | 
			
		||||
				elem = argv.RealType.(*godwarf.SliceType).ElemType
 | 
			
		||||
			} else {
 | 
			
		||||
				elem = argv.RealType.(*godwarf.ArrayType).Type
 | 
			
		||||
			}
 | 
			
		||||
			switch elemType := elem.(type) {
 | 
			
		||||
			case *godwarf.UintType:
 | 
			
		||||
				if elemType.Name != "uint8" && elemType.Name != "byte" {
 | 
			
		||||
					return nil, nil
 | 
			
		||||
@@ -1198,7 +1208,7 @@ func (err *typeConvErr) Error() string {
 | 
			
		||||
 | 
			
		||||
func (v *Variable) isType(typ godwarf.Type, kind reflect.Kind) error {
 | 
			
		||||
	if v.DwarfType != nil {
 | 
			
		||||
		if typ != nil && typ.String() != v.RealType.String() {
 | 
			
		||||
		if typ == nil || !sameType(typ, v.RealType) {
 | 
			
		||||
			return &typeConvErr{v.DwarfType, typ}
 | 
			
		||||
		}
 | 
			
		||||
		return nil
 | 
			
		||||
@@ -1255,6 +1265,34 @@ func (v *Variable) isType(typ godwarf.Type, kind reflect.Kind) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func sameType(t1, t2 godwarf.Type) bool {
 | 
			
		||||
	// Because of a bug in the go linker a type that refers to another type
 | 
			
		||||
	// (for example a pointer type) will usually use the typedef but rarely use
 | 
			
		||||
	// the non-typedef entry directly.
 | 
			
		||||
	// For types that we read directly from go this is fine because it's
 | 
			
		||||
	// consistent, however we also synthesize some types ourselves
 | 
			
		||||
	// (specifically pointers and slices) and we always use a reference through
 | 
			
		||||
	// a typedef.
 | 
			
		||||
	t1 = resolveTypedef(t1)
 | 
			
		||||
	t2 = resolveTypedef(t2)
 | 
			
		||||
 | 
			
		||||
	if tt1, isptr1 := t1.(*godwarf.PtrType); isptr1 {
 | 
			
		||||
		tt2, isptr2 := t2.(*godwarf.PtrType)
 | 
			
		||||
		if !isptr2 {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		return sameType(tt1.Type, tt2.Type)
 | 
			
		||||
	}
 | 
			
		||||
	if tt1, isslice1 := t1.(*godwarf.SliceType); isslice1 {
 | 
			
		||||
		tt2, isslice2 := t2.(*godwarf.SliceType)
 | 
			
		||||
		if !isslice2 {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
		return sameType(tt1.ElemType, tt2.ElemType)
 | 
			
		||||
	}
 | 
			
		||||
	return t1.String() == t2.String()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (v *Variable) sliceAccess(idx int) (*Variable, error) {
 | 
			
		||||
	if idx < 0 || int64(idx) >= v.Len {
 | 
			
		||||
		return nil, fmt.Errorf("index out of bounds")
 | 
			
		||||
@@ -11,11 +11,10 @@ import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"sort"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/logflags"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/logflags"
 | 
			
		||||
	"golang.org/x/arch/x86/x86asm"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -81,7 +80,7 @@ type functionCallState struct {
 | 
			
		||||
// CallFunction starts a debugger injected function call on the current thread of p.
 | 
			
		||||
// See runtime.debugCallV1 in $GOROOT/src/runtime/asm_amd64.s for a
 | 
			
		||||
// description of the protocol.
 | 
			
		||||
func CallFunction(p Process, expr string, retLoadCfg *LoadConfig) error {
 | 
			
		||||
func CallFunction(p Process, expr string, retLoadCfg *LoadConfig, checkEscape bool) error {
 | 
			
		||||
	bi := p.BinInfo()
 | 
			
		||||
	if !p.Common().fncallEnabled {
 | 
			
		||||
		return errFuncCallUnsupportedBackend
 | 
			
		||||
@@ -126,7 +125,7 @@ func CallFunction(p Process, expr string, retLoadCfg *LoadConfig) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	argmem, err := funcCallArgFrame(fn, argvars, g, bi)
 | 
			
		||||
	argmem, err := funcCallArgFrame(fn, argvars, g, bi, checkEscape)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
@@ -153,10 +152,7 @@ func CallFunction(p Process, expr string, retLoadCfg *LoadConfig) error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func fncallLog(fmtstr string, args ...interface{}) {
 | 
			
		||||
	if !logflags.FnCall() {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	logrus.WithFields(logrus.Fields{"layer": "proc", "kind": "fncall"}).Infof(fmtstr, args...)
 | 
			
		||||
	logflags.FnCallLogger().Infof(fmtstr, args...)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// writePointer writes val as an architecture pointer at addr in mem.
 | 
			
		||||
@@ -218,7 +214,7 @@ func funcCallEvalExpr(p Process, expr string) (fn *Function, closureAddr uint64,
 | 
			
		||||
	if fnvar.Kind != reflect.Func {
 | 
			
		||||
		return nil, 0, nil, fmt.Errorf("expression %q is not a function", exprToString(callexpr.Fun))
 | 
			
		||||
	}
 | 
			
		||||
	fnvar.loadValue(LoadConfig{false, 0, 0, 0, 0})
 | 
			
		||||
	fnvar.loadValue(LoadConfig{false, 0, 0, 0, 0, 0})
 | 
			
		||||
	if fnvar.Unreadable != nil {
 | 
			
		||||
		return nil, 0, nil, fnvar.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
@@ -259,7 +255,7 @@ type funcCallArg struct {
 | 
			
		||||
 | 
			
		||||
// funcCallArgFrame checks type and pointer escaping for the arguments and
 | 
			
		||||
// returns the argument frame.
 | 
			
		||||
func funcCallArgFrame(fn *Function, actualArgs []*Variable, g *G, bi *BinaryInfo) (argmem []byte, err error) {
 | 
			
		||||
func funcCallArgFrame(fn *Function, actualArgs []*Variable, g *G, bi *BinaryInfo, checkEscape bool) (argmem []byte, err error) {
 | 
			
		||||
	argFrameSize, formalArgs, err := funcCallArgs(fn, bi, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -278,9 +274,11 @@ func funcCallArgFrame(fn *Function, actualArgs []*Variable, g *G, bi *BinaryInfo
 | 
			
		||||
		formalArg := &formalArgs[i]
 | 
			
		||||
		actualArg := actualArgs[i]
 | 
			
		||||
 | 
			
		||||
		//TODO(aarzilli): only apply the escapeCheck to leaking parameters.
 | 
			
		||||
		if err := escapeCheck(actualArg, formalArg.name, g); err != nil {
 | 
			
		||||
			return nil, fmt.Errorf("can not pass %s to %s: %v", actualArg.Name, formalArg.name, err)
 | 
			
		||||
		if checkEscape {
 | 
			
		||||
			//TODO(aarzilli): only apply the escapeCheck to leaking parameters.
 | 
			
		||||
			if err := escapeCheck(actualArg, formalArg.name, g); err != nil {
 | 
			
		||||
				return nil, fmt.Errorf("cannot use %s as argument %s in function %s: %v", actualArg.Name, formalArg.name, fn.Name, err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		//TODO(aarzilli): autmoatic wrapping in interfaces for cases not handled
 | 
			
		||||
@@ -343,7 +341,13 @@ func funcCallArgs(fn *Function, bi *BinaryInfo, includeRet bool) (argFrameSize i
 | 
			
		||||
func escapeCheck(v *Variable, name string, g *G) error {
 | 
			
		||||
	switch v.Kind {
 | 
			
		||||
	case reflect.Ptr:
 | 
			
		||||
		w := v.maybeDereference()
 | 
			
		||||
		var w *Variable
 | 
			
		||||
		if len(v.Children) == 1 {
 | 
			
		||||
			// this branch is here to support pointers constructed with typecasts from ints or the '&' operator
 | 
			
		||||
			w = &v.Children[0]
 | 
			
		||||
		} else {
 | 
			
		||||
			w = v.maybeDereference()
 | 
			
		||||
		}
 | 
			
		||||
		return escapeCheckPointer(w.Addr, name, g)
 | 
			
		||||
	case reflect.Chan, reflect.String, reflect.Slice:
 | 
			
		||||
		return escapeCheckPointer(v.Base, name, g)
 | 
			
		||||
@@ -68,23 +68,20 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"io/ioutil"
 | 
			
		||||
	"net"
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/arch/x86/x86asm"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/logflags"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc/linutil"
 | 
			
		||||
	"github.com/mattn/go-isatty"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/logflags"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/linutil"
 | 
			
		||||
	isatty "github.com/mattn/go-isatty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -130,7 +127,7 @@ type Process struct {
 | 
			
		||||
	common proc.CommonProcess
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Thread is a thread.
 | 
			
		||||
// Thread represents an operating system thread.
 | 
			
		||||
type Thread struct {
 | 
			
		||||
	ID                int
 | 
			
		||||
	strID             string
 | 
			
		||||
@@ -142,8 +139,7 @@ type Thread struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ErrBackendUnavailable is returned when the stub program can not be found.
 | 
			
		||||
type ErrBackendUnavailable struct {
 | 
			
		||||
}
 | 
			
		||||
type ErrBackendUnavailable struct{}
 | 
			
		||||
 | 
			
		||||
func (err *ErrBackendUnavailable) Error() string {
 | 
			
		||||
	return "backend unavailable"
 | 
			
		||||
@@ -172,11 +168,7 @@ type gdbRegister struct {
 | 
			
		||||
// Detach.
 | 
			
		||||
// Use Listen, Dial or Connect to complete connection.
 | 
			
		||||
func New(process *os.Process) *Process {
 | 
			
		||||
	logger := logrus.New().WithFields(logrus.Fields{"layer": "gdbconn"})
 | 
			
		||||
	logger.Logger.Level = logrus.DebugLevel
 | 
			
		||||
	if !logflags.GdbWire() {
 | 
			
		||||
		logger.Logger.Out = ioutil.Discard
 | 
			
		||||
	}
 | 
			
		||||
	logger := logflags.GdbWireLogger()
 | 
			
		||||
	p := &Process{
 | 
			
		||||
		conn: gdbConn{
 | 
			
		||||
			maxTransmitAttempts: maxTransmitAttempts,
 | 
			
		||||
@@ -205,7 +197,7 @@ func New(process *os.Process) *Process {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Listen waits for a connection from the stub.
 | 
			
		||||
func (p *Process) Listen(listener net.Listener, path string, pid int) error {
 | 
			
		||||
func (p *Process) Listen(listener net.Listener, path string, pid int, debugInfoDirs []string) error {
 | 
			
		||||
	acceptChan := make(chan net.Conn)
 | 
			
		||||
 | 
			
		||||
	go func() {
 | 
			
		||||
@@ -219,7 +211,7 @@ func (p *Process) Listen(listener net.Listener, path string, pid int) error {
 | 
			
		||||
		if conn == nil {
 | 
			
		||||
			return errors.New("could not connect")
 | 
			
		||||
		}
 | 
			
		||||
		return p.Connect(conn, path, pid)
 | 
			
		||||
		return p.Connect(conn, path, pid, debugInfoDirs)
 | 
			
		||||
	case status := <-p.waitChan:
 | 
			
		||||
		listener.Close()
 | 
			
		||||
		return fmt.Errorf("stub exited while waiting for connection: %v", status)
 | 
			
		||||
@@ -227,11 +219,11 @@ func (p *Process) Listen(listener net.Listener, path string, pid int) error {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Dial attempts to connect to the stub.
 | 
			
		||||
func (p *Process) Dial(addr string, path string, pid int) error {
 | 
			
		||||
func (p *Process) Dial(addr string, path string, pid int, debugInfoDirs []string) error {
 | 
			
		||||
	for {
 | 
			
		||||
		conn, err := net.Dial("tcp", addr)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			return p.Connect(conn, path, pid)
 | 
			
		||||
			return p.Connect(conn, path, pid, debugInfoDirs)
 | 
			
		||||
		}
 | 
			
		||||
		select {
 | 
			
		||||
		case status := <-p.waitChan:
 | 
			
		||||
@@ -248,9 +240,8 @@ func (p *Process) Dial(addr string, path string, pid int) error {
 | 
			
		||||
// program and the PID of the target process, both are optional, however
 | 
			
		||||
// some stubs do not provide ways to determine path and pid automatically
 | 
			
		||||
// and Connect will be unable to function without knowing them.
 | 
			
		||||
func (p *Process) Connect(conn net.Conn, path string, pid int) error {
 | 
			
		||||
func (p *Process) Connect(conn net.Conn, path string, pid int, debugInfoDirs []string) error {
 | 
			
		||||
	p.conn.conn = conn
 | 
			
		||||
 | 
			
		||||
	p.conn.pid = pid
 | 
			
		||||
	err := p.conn.handshake()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -270,55 +261,7 @@ func (p *Process) Connect(conn net.Conn, path string, pid int) error {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if path == "" {
 | 
			
		||||
		// If we are attaching to a running process and the user didn't specify
 | 
			
		||||
		// the executable file manually we must ask the stub for it.
 | 
			
		||||
		// We support both qXfer:exec-file:read:: (the gdb way) and calling
 | 
			
		||||
		// qProcessInfo (the lldb way).
 | 
			
		||||
		// Unfortunately debugserver on macOS supports neither.
 | 
			
		||||
		path, err = p.conn.readExecFile()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if isProtocolErrorUnsupported(err) {
 | 
			
		||||
				_, path, err = p.loadProcessInfo(pid)
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					conn.Close()
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				conn.Close()
 | 
			
		||||
				return fmt.Errorf("could not determine executable path: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if path == "" {
 | 
			
		||||
		// try using jGetLoadedDynamicLibrariesInfos which is the only way to do
 | 
			
		||||
		// this supported on debugserver (but only on macOS >= 12.10)
 | 
			
		||||
		images, _ := p.conn.getLoadedDynamicLibraries()
 | 
			
		||||
		for _, image := range images {
 | 
			
		||||
			if image.MachHeader.FileType == macho.TypeExec {
 | 
			
		||||
				path = image.Pathname
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var entryPoint uint64
 | 
			
		||||
	if auxv, err := p.conn.readAuxv(); err == nil {
 | 
			
		||||
		// If we can't read the auxiliary vector it just means it's not supported
 | 
			
		||||
		// by the OS or by the stub. If we are debugging a PIE and the entry point
 | 
			
		||||
		// is needed proc.LoadBinaryInfo will complain about it.
 | 
			
		||||
		entryPoint = linutil.EntryPointFromAuxvAMD64(auxv)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var wg sync.WaitGroup
 | 
			
		||||
	err = p.bi.LoadBinaryInfo(path, entryPoint, &wg)
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		err = p.bi.LoadError()
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		conn.Close()
 | 
			
		||||
	if err := p.initialize(path, debugInfoDirs); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -335,36 +278,6 @@ func (p *Process) Connect(conn net.Conn, path string, pid int) error {
 | 
			
		||||
			p.loadGInstrAddr = addr
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = p.updateThreadList(&threadUpdater{p: p})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		conn.Close()
 | 
			
		||||
		p.bi.Close()
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.conn.pid <= 0 {
 | 
			
		||||
		p.conn.pid, _, err = p.loadProcessInfo(0)
 | 
			
		||||
		if err != nil && !isProtocolErrorUnsupported(err) {
 | 
			
		||||
			conn.Close()
 | 
			
		||||
			p.bi.Close()
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
 | 
			
		||||
 | 
			
		||||
	proc.CreateUnrecoveredPanicBreakpoint(p, p.writeBreakpoint, &p.breakpoints)
 | 
			
		||||
 | 
			
		||||
	panicpc, err := proc.FindFunctionLocation(p, "runtime.startpanic", true, 0)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		bp, err := p.breakpoints.SetWithID(-1, panicpc, p.writeBreakpoint)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			bp.Name = proc.UnrecoveredPanic
 | 
			
		||||
			bp.Variables = []string{"runtime.curg._panic.arg"}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -405,7 +318,7 @@ func getLdEnvVars() []string {
 | 
			
		||||
// LLDBLaunch starts an instance of lldb-server and connects to it, asking
 | 
			
		||||
// it to launch the specified target program with the specified arguments
 | 
			
		||||
// (cmd) on the specified directory wd.
 | 
			
		||||
func LLDBLaunch(cmd []string, wd string, foreground bool) (*Process, error) {
 | 
			
		||||
func LLDBLaunch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*Process, error) {
 | 
			
		||||
	switch runtime.GOOS {
 | 
			
		||||
	case "windows":
 | 
			
		||||
		return nil, ErrUnsupportedOS
 | 
			
		||||
@@ -482,9 +395,9 @@ func LLDBLaunch(cmd []string, wd string, foreground bool) (*Process, error) {
 | 
			
		||||
	p.conn.isDebugserver = isDebugserver
 | 
			
		||||
 | 
			
		||||
	if listener != nil {
 | 
			
		||||
		err = p.Listen(listener, cmd[0], 0)
 | 
			
		||||
		err = p.Listen(listener, cmd[0], 0, debugInfoDirs)
 | 
			
		||||
	} else {
 | 
			
		||||
		err = p.Dial(port, cmd[0], 0)
 | 
			
		||||
		err = p.Dial(port, cmd[0], 0, debugInfoDirs)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -497,13 +410,13 @@ func LLDBLaunch(cmd []string, wd string, foreground bool) (*Process, error) {
 | 
			
		||||
// Path is path to the target's executable, path only needs to be specified
 | 
			
		||||
// for some stubs that do not provide an automated way of determining it
 | 
			
		||||
// (for example debugserver).
 | 
			
		||||
func LLDBAttach(pid int, path string) (*Process, error) {
 | 
			
		||||
func LLDBAttach(pid int, path string, debugInfoDirs []string) (*Process, error) {
 | 
			
		||||
	if runtime.GOOS == "windows" {
 | 
			
		||||
		return nil, ErrUnsupportedOS
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	isDebugserver := false
 | 
			
		||||
	var proc *exec.Cmd
 | 
			
		||||
	var process *exec.Cmd
 | 
			
		||||
	var listener net.Listener
 | 
			
		||||
	var port string
 | 
			
		||||
	if _, err := os.Stat(debugserverExecutable); err == nil {
 | 
			
		||||
@@ -512,32 +425,31 @@ func LLDBAttach(pid int, path string) (*Process, error) {
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		proc = exec.Command(debugserverExecutable, "-R", fmt.Sprintf("127.0.0.1:%d", listener.Addr().(*net.TCPAddr).Port), "--attach="+strconv.Itoa(pid))
 | 
			
		||||
		process = exec.Command(debugserverExecutable, "-R", fmt.Sprintf("127.0.0.1:%d", listener.Addr().(*net.TCPAddr).Port), "--attach="+strconv.Itoa(pid))
 | 
			
		||||
	} else {
 | 
			
		||||
		if _, err := exec.LookPath("lldb-server"); err != nil {
 | 
			
		||||
			return nil, &ErrBackendUnavailable{}
 | 
			
		||||
		}
 | 
			
		||||
		port = unusedPort()
 | 
			
		||||
		proc = exec.Command("lldb-server", "gdbserver", "--attach", strconv.Itoa(pid), port)
 | 
			
		||||
		process = exec.Command("lldb-server", "gdbserver", "--attach", strconv.Itoa(pid), port)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	proc.Stdout = os.Stdout
 | 
			
		||||
	proc.Stderr = os.Stderr
 | 
			
		||||
	process.Stdout = os.Stdout
 | 
			
		||||
	process.Stderr = os.Stderr
 | 
			
		||||
	process.SysProcAttr = sysProcAttr(false)
 | 
			
		||||
 | 
			
		||||
	proc.SysProcAttr = sysProcAttr(false)
 | 
			
		||||
 | 
			
		||||
	err := proc.Start()
 | 
			
		||||
	err := process.Start()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	p := New(proc.Process)
 | 
			
		||||
	p := New(process.Process)
 | 
			
		||||
	p.conn.isDebugserver = isDebugserver
 | 
			
		||||
 | 
			
		||||
	if listener != nil {
 | 
			
		||||
		err = p.Listen(listener, path, pid)
 | 
			
		||||
		err = p.Listen(listener, path, pid, debugInfoDirs)
 | 
			
		||||
	} else {
 | 
			
		||||
		err = p.Dial(port, path, pid)
 | 
			
		||||
		err = p.Dial(port, path, pid, debugInfoDirs)
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -545,10 +457,80 @@ func LLDBAttach(pid int, path string) (*Process, error) {
 | 
			
		||||
	return p, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// loadProcessInfo uses qProcessInfo to load the inferior's PID and
 | 
			
		||||
// EntryPoint will return the process entry point address, useful for
 | 
			
		||||
// debugging PIEs.
 | 
			
		||||
func (p *Process) EntryPoint() (uint64, error) {
 | 
			
		||||
	var entryPoint uint64
 | 
			
		||||
	if auxv, err := p.conn.readAuxv(); err == nil {
 | 
			
		||||
		// If we can't read the auxiliary vector it just means it's not supported
 | 
			
		||||
		// by the OS or by the stub. If we are debugging a PIE and the entry point
 | 
			
		||||
		// is needed proc.LoadBinaryInfo will complain about it.
 | 
			
		||||
		entryPoint = linutil.EntryPointFromAuxvAMD64(auxv)
 | 
			
		||||
	}
 | 
			
		||||
	return entryPoint, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// initialize uses qProcessInfo to load the inferior's PID and
 | 
			
		||||
// executable path. This command is not supported by all stubs and not all
 | 
			
		||||
// stubs will report both the PID and executable path.
 | 
			
		||||
func (p *Process) loadProcessInfo(pid int) (int, string, error) {
 | 
			
		||||
func (p *Process) initialize(path string, debugInfoDirs []string) error {
 | 
			
		||||
	var err error
 | 
			
		||||
	if path == "" {
 | 
			
		||||
		// If we are attaching to a running process and the user didn't specify
 | 
			
		||||
		// the executable file manually we must ask the stub for it.
 | 
			
		||||
		// We support both qXfer:exec-file:read:: (the gdb way) and calling
 | 
			
		||||
		// qProcessInfo (the lldb way).
 | 
			
		||||
		// Unfortunately debugserver on macOS supports neither.
 | 
			
		||||
		path, err = p.conn.readExecFile()
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if isProtocolErrorUnsupported(err) {
 | 
			
		||||
				_, path, err = queryProcessInfo(p, p.Pid())
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					p.conn.conn.Close()
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
				p.conn.conn.Close()
 | 
			
		||||
				return fmt.Errorf("could not determine executable path: %v", err)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if path == "" {
 | 
			
		||||
		// try using jGetLoadedDynamicLibrariesInfos which is the only way to do
 | 
			
		||||
		// this supported on debugserver (but only on macOS >= 12.10)
 | 
			
		||||
		images, _ := p.conn.getLoadedDynamicLibraries()
 | 
			
		||||
		for _, image := range images {
 | 
			
		||||
			if image.MachHeader.FileType == macho.TypeExec {
 | 
			
		||||
				path = image.Pathname
 | 
			
		||||
				break
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = p.updateThreadList(&threadUpdater{p: p})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		p.conn.conn.Close()
 | 
			
		||||
		p.bi.Close()
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.conn.pid <= 0 {
 | 
			
		||||
		p.conn.pid, _, err = queryProcessInfo(p, 0)
 | 
			
		||||
		if err != nil && !isProtocolErrorUnsupported(err) {
 | 
			
		||||
			p.conn.conn.Close()
 | 
			
		||||
			p.bi.Close()
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if err = proc.PostInitializationSetup(p, path, debugInfoDirs, p.writeBreakpoint); err != nil {
 | 
			
		||||
		p.conn.conn.Close()
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func queryProcessInfo(p *Process, pid int) (int, string, error) {
 | 
			
		||||
	pi, err := p.conn.queryProcessInfo(pid)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, "", err
 | 
			
		||||
@@ -659,7 +641,7 @@ func (p *Process) ContinueOnce() (proc.Thread, error) {
 | 
			
		||||
 | 
			
		||||
	// resume all threads
 | 
			
		||||
	var threadID string
 | 
			
		||||
	var sig uint8 = 0
 | 
			
		||||
	var sig uint8
 | 
			
		||||
	var tu = threadUpdater{p: p}
 | 
			
		||||
	var err error
 | 
			
		||||
continueLoop:
 | 
			
		||||
@@ -697,6 +679,14 @@ continueLoop:
 | 
			
		||||
		// process so we must stop here.
 | 
			
		||||
		case 0x91, 0x92, 0x93, 0x94, 0x95, 0x96: /* TARGET_EXC_BAD_ACCESS */
 | 
			
		||||
			break continueLoop
 | 
			
		||||
 | 
			
		||||
		// Signal 0 is returned by rr when it reaches the start of the process
 | 
			
		||||
		// in backward continue mode.
 | 
			
		||||
		case 0:
 | 
			
		||||
			if p.conn.direction == proc.Backward {
 | 
			
		||||
				break continueLoop
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		default:
 | 
			
		||||
			// any other signal is always propagated to inferior
 | 
			
		||||
		}
 | 
			
		||||
@@ -706,13 +696,19 @@ continueLoop:
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if p.BinInfo().GOOS == "linux" {
 | 
			
		||||
		if err := linutil.ElfUpdateSharedObjects(p); err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := p.setCurrentBreakpoints(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for _, thread := range p.threads {
 | 
			
		||||
		if thread.strID == threadID {
 | 
			
		||||
			var err error = nil
 | 
			
		||||
			var err error
 | 
			
		||||
			switch sig {
 | 
			
		||||
			case 0x91:
 | 
			
		||||
				err = errors.New("bad access")
 | 
			
		||||
@@ -734,6 +730,13 @@ continueLoop:
 | 
			
		||||
	return nil, fmt.Errorf("could not find thread %s", threadID)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSelectedGoroutine will set internally the goroutine that should be
 | 
			
		||||
// the default for any command executed, the goroutine being actively
 | 
			
		||||
// followed.
 | 
			
		||||
func (p *Process) SetSelectedGoroutine(g *proc.G) {
 | 
			
		||||
	p.selectedGoroutine = g
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// StepInstruction will step exactly one CPU instruction.
 | 
			
		||||
func (p *Process) StepInstruction() error {
 | 
			
		||||
	thread := p.currentThread
 | 
			
		||||
@@ -1025,9 +1028,6 @@ func (p *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) {
 | 
			
		||||
 | 
			
		||||
func (p *Process) writeBreakpoint(addr uint64) (string, int, *proc.Function, []byte, error) {
 | 
			
		||||
	f, l, fn := p.bi.PCToLine(uint64(addr))
 | 
			
		||||
	if fn == nil {
 | 
			
		||||
		return "", 0, nil, nil, proc.InvalidAddressError{Address: addr}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := p.conn.setBreakpoint(addr); err != nil {
 | 
			
		||||
		return "", 0, nil, nil, err
 | 
			
		||||
@@ -1310,7 +1310,6 @@ func (t *Thread) Blocked() bool {
 | 
			
		||||
	default:
 | 
			
		||||
		return strings.HasPrefix(fn.Name, "syscall.Syscall") || strings.HasPrefix(fn.Name, "syscall.RawSyscall")
 | 
			
		||||
	}
 | 
			
		||||
	return false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// loadGInstr returns the correct MOV instruction for the current
 | 
			
		||||
@@ -1569,25 +1568,25 @@ func (t *Thread) clearBreakpointState() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetCurrentBreakpoint will find and set the threads current breakpoint.
 | 
			
		||||
func (thread *Thread) SetCurrentBreakpoint() error {
 | 
			
		||||
	thread.clearBreakpointState()
 | 
			
		||||
	regs, err := thread.Registers(false)
 | 
			
		||||
func (t *Thread) SetCurrentBreakpoint() error {
 | 
			
		||||
	t.clearBreakpointState()
 | 
			
		||||
	regs, err := t.Registers(false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	pc := regs.PC()
 | 
			
		||||
	if bp, ok := thread.p.FindBreakpoint(pc); ok {
 | 
			
		||||
		if thread.regs.PC() != bp.Addr {
 | 
			
		||||
			if err := thread.SetPC(bp.Addr); err != nil {
 | 
			
		||||
	if bp, ok := t.p.FindBreakpoint(pc); ok {
 | 
			
		||||
		if t.regs.PC() != bp.Addr {
 | 
			
		||||
			if err := t.SetPC(bp.Addr); err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		thread.CurrentBreakpoint = bp.CheckCondition(thread)
 | 
			
		||||
		if thread.CurrentBreakpoint.Breakpoint != nil && thread.CurrentBreakpoint.Active {
 | 
			
		||||
			if g, err := proc.GetG(thread); err == nil {
 | 
			
		||||
				thread.CurrentBreakpoint.HitCount[g.ID]++
 | 
			
		||||
		t.CurrentBreakpoint = bp.CheckCondition(t)
 | 
			
		||||
		if t.CurrentBreakpoint.Breakpoint != nil && t.CurrentBreakpoint.Active {
 | 
			
		||||
			if g, err := proc.GetG(t); err == nil {
 | 
			
		||||
				t.CurrentBreakpoint.HitCount[g.ID]++
 | 
			
		||||
			}
 | 
			
		||||
			thread.CurrentBreakpoint.TotalHitCount++
 | 
			
		||||
			t.CurrentBreakpoint.TotalHitCount++
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
@@ -1827,9 +1826,12 @@ func (t *Thread) SetDX(dx uint64) error {
 | 
			
		||||
	return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (regs *gdbRegisters) Slice() []proc.Register {
 | 
			
		||||
func (regs *gdbRegisters) Slice(floatingPoint bool) []proc.Register {
 | 
			
		||||
	r := make([]proc.Register, 0, len(regs.regsInfo))
 | 
			
		||||
	for _, reginfo := range regs.regsInfo {
 | 
			
		||||
		if reginfo.Group == "float" && !floatingPoint {
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		switch {
 | 
			
		||||
		case reginfo.Name == "eflags":
 | 
			
		||||
			r = proc.AppendEflagReg(r, reginfo.Name, uint64(binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value)))
 | 
			
		||||
@@ -1842,6 +1844,9 @@ func (regs *gdbRegisters) Slice() []proc.Register {
 | 
			
		||||
		case reginfo.Bitsize == 64:
 | 
			
		||||
			r = proc.AppendQwordReg(r, reginfo.Name, binary.LittleEndian.Uint64(regs.regs[reginfo.Name].value))
 | 
			
		||||
		case reginfo.Bitsize == 80:
 | 
			
		||||
			if !floatingPoint {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			idx := 0
 | 
			
		||||
			for _, stprefix := range []string{"stmm", "st"} {
 | 
			
		||||
				if strings.HasPrefix(reginfo.Name, stprefix) {
 | 
			
		||||
@@ -1853,10 +1858,12 @@ func (regs *gdbRegisters) Slice() []proc.Register {
 | 
			
		||||
			r = proc.AppendX87Reg(r, idx, binary.LittleEndian.Uint16(value[8:]), binary.LittleEndian.Uint64(value[:8]))
 | 
			
		||||
 | 
			
		||||
		case reginfo.Bitsize == 128:
 | 
			
		||||
			r = proc.AppendSSEReg(r, strings.ToUpper(reginfo.Name), regs.regs[reginfo.Name].value)
 | 
			
		||||
			if floatingPoint {
 | 
			
		||||
				r = proc.AppendSSEReg(r, strings.ToUpper(reginfo.Name), regs.regs[reginfo.Name].value)
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
		case reginfo.Bitsize == 256:
 | 
			
		||||
			if !strings.HasPrefix(strings.ToLower(reginfo.Name), "ymm") {
 | 
			
		||||
			if !strings.HasPrefix(strings.ToLower(reginfo.Name), "ymm") || !floatingPoint {
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
@@ -16,8 +16,8 @@ import (
 | 
			
		||||
	"sync"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/logflags"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/logflags"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -227,7 +227,8 @@ type gdbRegisterInfo struct {
 | 
			
		||||
	Name    string `xml:"name,attr"`
 | 
			
		||||
	Bitsize int    `xml:"bitsize,attr"`
 | 
			
		||||
	Offset  int
 | 
			
		||||
	Regnum  int `xml:"regnum,attr"`
 | 
			
		||||
	Regnum  int    `xml:"regnum,attr"`
 | 
			
		||||
	Group   string `xml:"group,attr"`
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// readTargetXml reads target.xml file from stub using qXfer:features:read,
 | 
			
		||||
@@ -53,7 +53,7 @@ func Record(cmd []string, wd string, quiet bool) (tracedir string, err error) {
 | 
			
		||||
 | 
			
		||||
// Replay starts an instance of rr in replay mode, with the specified trace
 | 
			
		||||
// directory, and connects to it.
 | 
			
		||||
func Replay(tracedir string, quiet bool) (*Process, error) {
 | 
			
		||||
func Replay(tracedir string, quiet bool, debugInfoDirs []string) (*Process, error) {
 | 
			
		||||
	if err := checkRRAvailabe(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -82,7 +82,7 @@ func Replay(tracedir string, quiet bool) (*Process, error) {
 | 
			
		||||
 | 
			
		||||
	p := New(rrcmd.Process)
 | 
			
		||||
	p.tracedir = tracedir
 | 
			
		||||
	err = p.Dial(init.port, init.exe, 0)
 | 
			
		||||
	err = p.Dial(init.port, init.exe, 0, debugInfoDirs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		rrcmd.Process.Kill()
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -257,11 +257,11 @@ func splitQuotedFields(in string) []string {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RecordAndReplay acts like calling Record and then Replay.
 | 
			
		||||
func RecordAndReplay(cmd []string, wd string, quiet bool) (p *Process, tracedir string, err error) {
 | 
			
		||||
func RecordAndReplay(cmd []string, wd string, quiet bool, debugInfoDirs []string) (p *Process, tracedir string, err error) {
 | 
			
		||||
	tracedir, err = Record(cmd, wd, quiet)
 | 
			
		||||
	if tracedir == "" {
 | 
			
		||||
		return nil, "", err
 | 
			
		||||
	}
 | 
			
		||||
	p, err = Replay(tracedir, quiet)
 | 
			
		||||
	p, err = Replay(tracedir, quiet, debugInfoDirs)
 | 
			
		||||
	return p, tracedir, err
 | 
			
		||||
}
 | 
			
		||||
@@ -68,6 +68,7 @@ type Info interface {
 | 
			
		||||
	// ErrProcessExited or ProcessDetachedError).
 | 
			
		||||
	Valid() (bool, error)
 | 
			
		||||
	BinInfo() *BinaryInfo
 | 
			
		||||
	EntryPoint() (uint64, error)
 | 
			
		||||
	// Common returns a struct with fields common to all backends
 | 
			
		||||
	Common() *CommonProcess
 | 
			
		||||
 | 
			
		||||
@@ -86,6 +87,7 @@ type ThreadInfo interface {
 | 
			
		||||
// GoroutineInfo is an interface for getting information on running goroutines.
 | 
			
		||||
type GoroutineInfo interface {
 | 
			
		||||
	SelectedGoroutine() *G
 | 
			
		||||
	SetSelectedGoroutine(*G)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ProcessManipulation is an interface for changing the execution state of a process.
 | 
			
		||||
							
								
								
									
										172
									
								
								vendor/github.com/go-delve/delve/pkg/proc/linutil/dynamic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										172
									
								
								vendor/github.com/go-delve/delve/pkg/proc/linutil/dynamic.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,172 @@
 | 
			
		||||
package linutil
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	maxNumLibraries      = 1000000 // maximum number of loaded libraries, to avoid loading forever on corrupted memory
 | 
			
		||||
	maxLibraryPathLength = 1000000 // maximum length for the path of a library, to avoid loading forever on corrupted memory
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ErrTooManyLibraries = errors.New("number of loaded libraries exceeds maximum")
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_DT_NULL  = 0  // DT_NULL as defined by SysV ABI specification
 | 
			
		||||
	_DT_DEBUG = 21 // DT_DEBUG as defined by SysV ABI specification
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// dynamicSearchDebug searches for the DT_DEBUG entry in the .dynamic section
 | 
			
		||||
func dynamicSearchDebug(p proc.Process) (uint64, error) {
 | 
			
		||||
	bi := p.BinInfo()
 | 
			
		||||
	mem := p.CurrentThread()
 | 
			
		||||
 | 
			
		||||
	dynbuf := make([]byte, bi.ElfDynamicSection.Size)
 | 
			
		||||
	_, err := mem.ReadMemory(dynbuf, uintptr(bi.ElfDynamicSection.Addr))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	rd := bytes.NewReader(dynbuf)
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		var tag, val uint64
 | 
			
		||||
		if err := binary.Read(rd, binary.LittleEndian, &tag); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		if err := binary.Read(rd, binary.LittleEndian, &val); err != nil {
 | 
			
		||||
			return 0, err
 | 
			
		||||
		}
 | 
			
		||||
		switch tag {
 | 
			
		||||
		case _DT_NULL:
 | 
			
		||||
			return 0, nil
 | 
			
		||||
		case _DT_DEBUG:
 | 
			
		||||
			return val, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// hard-coded offsets of the fields of the r_debug and link_map structs, see
 | 
			
		||||
// /usr/include/elf/link.h for a full description of those structs.
 | 
			
		||||
const (
 | 
			
		||||
	_R_DEBUG_MAP_OFFSET   = 8
 | 
			
		||||
	_LINK_MAP_ADDR_OFFSET = 0  // offset of link_map.l_addr field (base address shared object is loaded at)
 | 
			
		||||
	_LINK_MAP_NAME_OFFSET = 8  // offset of link_map.l_name field (absolute file name object was found in)
 | 
			
		||||
	_LINK_MAP_LD          = 16 // offset of link_map.l_ld field (dynamic section of the shared object)
 | 
			
		||||
	_LINK_MAP_NEXT        = 24 // offset of link_map.l_next field
 | 
			
		||||
	_LINK_MAP_PREV        = 32 // offset of link_map.l_prev field
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func readPtr(p proc.Process, addr uint64) (uint64, error) {
 | 
			
		||||
	ptrbuf := make([]byte, p.BinInfo().Arch.PtrSize())
 | 
			
		||||
	_, err := p.CurrentThread().ReadMemory(ptrbuf, uintptr(addr))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, err
 | 
			
		||||
	}
 | 
			
		||||
	return binary.LittleEndian.Uint64(ptrbuf), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type linkMap struct {
 | 
			
		||||
	addr       uint64
 | 
			
		||||
	name       string
 | 
			
		||||
	ld         uint64
 | 
			
		||||
	next, prev uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readLinkMapNode(p proc.Process, r_map uint64) (*linkMap, error) {
 | 
			
		||||
	bi := p.BinInfo()
 | 
			
		||||
 | 
			
		||||
	var lm linkMap
 | 
			
		||||
	var ptrs [5]uint64
 | 
			
		||||
	for i := range ptrs {
 | 
			
		||||
		var err error
 | 
			
		||||
		ptrs[i], err = readPtr(p, r_map+uint64(bi.Arch.PtrSize()*i))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	lm.addr = ptrs[0]
 | 
			
		||||
	var err error
 | 
			
		||||
	lm.name, err = readCString(p, ptrs[1])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	lm.ld = ptrs[2]
 | 
			
		||||
	lm.next = ptrs[3]
 | 
			
		||||
	lm.prev = ptrs[4]
 | 
			
		||||
	return &lm, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func readCString(p proc.Process, addr uint64) (string, error) {
 | 
			
		||||
	if addr == 0 {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
	mem := p.CurrentThread()
 | 
			
		||||
	buf := make([]byte, 1)
 | 
			
		||||
	r := []byte{}
 | 
			
		||||
	for {
 | 
			
		||||
		if len(r) > maxLibraryPathLength {
 | 
			
		||||
			return "", fmt.Errorf("error reading libraries: string too long (%d)", len(r))
 | 
			
		||||
		}
 | 
			
		||||
		_, err := mem.ReadMemory(buf, uintptr(addr))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return "", err
 | 
			
		||||
		}
 | 
			
		||||
		if buf[0] == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		r = append(r, buf[0])
 | 
			
		||||
		addr++
 | 
			
		||||
	}
 | 
			
		||||
	return string(r), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ElfUpdateSharedObjects reads the list of dynamic libraries loaded by the
 | 
			
		||||
// dynamic linker from the .dynamic section and uses it to update p.BinInfo().
 | 
			
		||||
// See the SysV ABI for a description of how the .dynamic section works:
 | 
			
		||||
// http://www.sco.com/developers/gabi/latest/contents.html
 | 
			
		||||
func ElfUpdateSharedObjects(p proc.Process) error {
 | 
			
		||||
	bi := p.BinInfo()
 | 
			
		||||
	if bi.ElfDynamicSection.Addr == 0 {
 | 
			
		||||
		// no dynamic section, therefore nothing to do here
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	debugAddr, err := dynamicSearchDebug(p)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if debugAddr == 0 {
 | 
			
		||||
		// no DT_DEBUG entry
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r_map, err := readPtr(p, debugAddr+_R_DEBUG_MAP_OFFSET)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	libs := []string{}
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		if r_map == 0 {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if len(libs) > maxNumLibraries {
 | 
			
		||||
			return ErrTooManyLibraries
 | 
			
		||||
		}
 | 
			
		||||
		lm, err := readLinkMapNode(p, r_map)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		bi.AddImage(lm.name, lm.addr)
 | 
			
		||||
		libs = append(libs, lm.name)
 | 
			
		||||
		r_map = lm.next
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										399
									
								
								vendor/github.com/go-delve/delve/pkg/proc/linutil/regs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										399
									
								
								vendor/github.com/go-delve/delve/pkg/proc/linutil/regs.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,399 @@
 | 
			
		||||
package linutil
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"encoding/binary"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/arch/x86/x86asm"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// AMD64Registers implements the proc.Registers interface for the native/linux
 | 
			
		||||
// backend and core/linux backends, on AMD64.
 | 
			
		||||
type AMD64Registers struct {
 | 
			
		||||
	Regs     *AMD64PtraceRegs
 | 
			
		||||
	Fpregs   []proc.Register
 | 
			
		||||
	Fpregset *AMD64Xstate
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AMD64PtraceRegs is the struct used by the linux kernel to return the
 | 
			
		||||
// general purpose registers for AMD64 CPUs.
 | 
			
		||||
type AMD64PtraceRegs struct {
 | 
			
		||||
	R15      uint64
 | 
			
		||||
	R14      uint64
 | 
			
		||||
	R13      uint64
 | 
			
		||||
	R12      uint64
 | 
			
		||||
	Rbp      uint64
 | 
			
		||||
	Rbx      uint64
 | 
			
		||||
	R11      uint64
 | 
			
		||||
	R10      uint64
 | 
			
		||||
	R9       uint64
 | 
			
		||||
	R8       uint64
 | 
			
		||||
	Rax      uint64
 | 
			
		||||
	Rcx      uint64
 | 
			
		||||
	Rdx      uint64
 | 
			
		||||
	Rsi      uint64
 | 
			
		||||
	Rdi      uint64
 | 
			
		||||
	Orig_rax uint64
 | 
			
		||||
	Rip      uint64
 | 
			
		||||
	Cs       uint64
 | 
			
		||||
	Eflags   uint64
 | 
			
		||||
	Rsp      uint64
 | 
			
		||||
	Ss       uint64
 | 
			
		||||
	Fs_base  uint64
 | 
			
		||||
	Gs_base  uint64
 | 
			
		||||
	Ds       uint64
 | 
			
		||||
	Es       uint64
 | 
			
		||||
	Fs       uint64
 | 
			
		||||
	Gs       uint64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Slice returns the registers as a list of (name, value) pairs.
 | 
			
		||||
func (r *AMD64Registers) Slice(floatingPoint bool) []proc.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([]proc.Register, 0, len(regs)+len(r.Fpregs))
 | 
			
		||||
	for _, reg := range regs {
 | 
			
		||||
		if reg.k == "Eflags" {
 | 
			
		||||
			out = proc.AppendEflagReg(out, reg.k, reg.v)
 | 
			
		||||
		} else {
 | 
			
		||||
			out = proc.AppendQwordReg(out, reg.k, reg.v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if floatingPoint {
 | 
			
		||||
		out = append(out, r.Fpregs...)
 | 
			
		||||
	}
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PC returns the value of RIP register.
 | 
			
		||||
func (r *AMD64Registers) PC() uint64 {
 | 
			
		||||
	return r.Regs.Rip
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SP returns the value of RSP register.
 | 
			
		||||
func (r *AMD64Registers) SP() uint64 {
 | 
			
		||||
	return r.Regs.Rsp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *AMD64Registers) BP() uint64 {
 | 
			
		||||
	return r.Regs.Rbp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CX returns the value of RCX register.
 | 
			
		||||
func (r *AMD64Registers) CX() uint64 {
 | 
			
		||||
	return r.Regs.Rcx
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// TLS returns the address of the thread local storage memory segment.
 | 
			
		||||
func (r *AMD64Registers) TLS() uint64 {
 | 
			
		||||
	return r.Regs.Fs_base
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GAddr returns the address of the G variable if it is known, 0 and false
 | 
			
		||||
// otherwise.
 | 
			
		||||
func (r *AMD64Registers) GAddr() (uint64, bool) {
 | 
			
		||||
	return 0, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Get returns the value of the n-th register (in x86asm order).
 | 
			
		||||
func (r *AMD64Registers) 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, proc.ErrUnknownRegister
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Copy returns a copy of these registers that is guarenteed not to change.
 | 
			
		||||
func (r *AMD64Registers) Copy() proc.Registers {
 | 
			
		||||
	var rr AMD64Registers
 | 
			
		||||
	rr.Regs = &AMD64PtraceRegs{}
 | 
			
		||||
	rr.Fpregset = &AMD64Xstate{}
 | 
			
		||||
	*(rr.Regs) = *(r.Regs)
 | 
			
		||||
	if r.Fpregset != nil {
 | 
			
		||||
		*(rr.Fpregset) = *(r.Fpregset)
 | 
			
		||||
	}
 | 
			
		||||
	if r.Fpregs != nil {
 | 
			
		||||
		rr.Fpregs = make([]proc.Register, len(r.Fpregs))
 | 
			
		||||
		copy(rr.Fpregs, r.Fpregs)
 | 
			
		||||
	}
 | 
			
		||||
	return &rr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AMD64PtraceFpRegs tracks user_fpregs_struct in /usr/include/x86_64-linux-gnu/sys/user.h
 | 
			
		||||
type AMD64PtraceFpRegs 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// AMD64Xstate represents amd64 XSAVE area. See Section 13.1 (and
 | 
			
		||||
// following) of Intel® 64 and IA-32 Architectures Software Developer’s
 | 
			
		||||
// Manual, Volume 1: Basic Architecture.
 | 
			
		||||
type AMD64Xstate struct {
 | 
			
		||||
	AMD64PtraceFpRegs
 | 
			
		||||
	Xsave    []byte // raw xsave area
 | 
			
		||||
	AvxState bool   // contains AVX state
 | 
			
		||||
	YmmSpace [256]byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode decodes an XSAVE area to a list of name/value pairs of registers.
 | 
			
		||||
func (xsave *AMD64Xstate) Decode() (regs []proc.Register) {
 | 
			
		||||
	// x87 registers
 | 
			
		||||
	regs = proc.AppendWordReg(regs, "CW", xsave.Cwd)
 | 
			
		||||
	regs = proc.AppendWordReg(regs, "SW", xsave.Swd)
 | 
			
		||||
	regs = proc.AppendWordReg(regs, "TW", xsave.Ftw)
 | 
			
		||||
	regs = proc.AppendWordReg(regs, "FOP", xsave.Fop)
 | 
			
		||||
	regs = proc.AppendQwordReg(regs, "FIP", xsave.Rip)
 | 
			
		||||
	regs = proc.AppendQwordReg(regs, "FDP", xsave.Rdp)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(xsave.StSpace); i += 4 {
 | 
			
		||||
		regs = proc.AppendX87Reg(regs, i/4, uint16(xsave.StSpace[i+2]), uint64(xsave.StSpace[i+1])<<32|uint64(xsave.StSpace[i]))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// SSE registers
 | 
			
		||||
	regs = proc.AppendMxcsrReg(regs, "MXCSR", uint64(xsave.Mxcsr))
 | 
			
		||||
	regs = proc.AppendDwordReg(regs, "MXCSR_MASK", xsave.MxcrMask)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(xsave.XmmSpace); i += 16 {
 | 
			
		||||
		regs = proc.AppendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), xsave.XmmSpace[i:i+16])
 | 
			
		||||
		if xsave.AvxState {
 | 
			
		||||
			regs = proc.AppendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), xsave.YmmSpace[i:i+16])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_XSAVE_HEADER_START          = 512
 | 
			
		||||
	_XSAVE_HEADER_LEN            = 64
 | 
			
		||||
	_XSAVE_EXTENDED_REGION_START = 576
 | 
			
		||||
	_XSAVE_SSE_REGION_LEN        = 416
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LinuxX86XstateRead reads a byte array containing an XSAVE area into regset.
 | 
			
		||||
// If readLegacy is true regset.PtraceFpRegs will be filled with the
 | 
			
		||||
// contents of the legacy region of the XSAVE area.
 | 
			
		||||
// See Section 13.1 (and following) of Intel® 64 and IA-32 Architectures
 | 
			
		||||
// Software Developer’s Manual, Volume 1: Basic Architecture.
 | 
			
		||||
func AMD64XstateRead(xstateargs []byte, readLegacy bool, regset *AMD64Xstate) error {
 | 
			
		||||
	if _XSAVE_HEADER_START+_XSAVE_HEADER_LEN >= len(xstateargs) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if readLegacy {
 | 
			
		||||
		rdr := bytes.NewReader(xstateargs[:_XSAVE_HEADER_START])
 | 
			
		||||
		if err := binary.Read(rdr, binary.LittleEndian, ®set.AMD64PtraceFpRegs); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	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 nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if xstate_bv&(1<<2) == 0 {
 | 
			
		||||
		// AVX state not present
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	avxstate := xstateargs[_XSAVE_EXTENDED_REGION_START:]
 | 
			
		||||
	regset.AvxState = true
 | 
			
		||||
	copy(regset.YmmSpace[:], avxstate[:len(regset.YmmSpace)])
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -2,8 +2,9 @@ package proc
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const cacheEnabled = true
 | 
			
		||||
@@ -93,7 +94,7 @@ type compositeMemory struct {
 | 
			
		||||
	data    []byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []op.Piece) *compositeMemory {
 | 
			
		||||
func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []op.Piece) (*compositeMemory, error) {
 | 
			
		||||
	cmem := &compositeMemory{realmem: mem, regs: regs, pieces: pieces, data: []byte{}}
 | 
			
		||||
	for _, piece := range pieces {
 | 
			
		||||
		if piece.IsRegister {
 | 
			
		||||
@@ -102,6 +103,9 @@ func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []o
 | 
			
		||||
			if sz == 0 && len(pieces) == 1 {
 | 
			
		||||
				sz = len(reg)
 | 
			
		||||
			}
 | 
			
		||||
			if sz > len(reg) {
 | 
			
		||||
				return nil, fmt.Errorf("could not read %d bytes from register %d (size: %d)", sz, piece.RegNum, len(reg))
 | 
			
		||||
			}
 | 
			
		||||
			cmem.data = append(cmem.data, reg[:sz]...)
 | 
			
		||||
		} else {
 | 
			
		||||
			buf := make([]byte, piece.Size)
 | 
			
		||||
@@ -109,7 +113,7 @@ func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []o
 | 
			
		||||
			cmem.data = append(cmem.data, buf...)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return cmem
 | 
			
		||||
	return cmem, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (mem *compositeMemory) ReadMemory(data []byte, addr uintptr) (int, error) {
 | 
			
		||||
@@ -88,7 +88,7 @@ func resolveTypeOff(bi *BinaryInfo, typeAddr uintptr, off uintptr, mem MemoryRea
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		v.loadValue(LoadConfig{false, 1, 0, 0, -1})
 | 
			
		||||
		v.loadValue(LoadConfig{false, 1, 0, 0, -1, 0})
 | 
			
		||||
		addr, _ := constant.Int64Val(v.Value)
 | 
			
		||||
		return v.newVariable(v.Name, uintptr(addr), rtyp, mem), nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -6,18 +6,18 @@ import (
 | 
			
		||||
	"errors"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation")
 | 
			
		||||
 | 
			
		||||
// Launch returns ErrNativeBackendDisabled.
 | 
			
		||||
func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
 | 
			
		||||
func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, error) {
 | 
			
		||||
	return nil, ErrNativeBackendDisabled
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attach returns ErrNativeBackendDisabled.
 | 
			
		||||
func Attach(pid int) (*Process, error) {
 | 
			
		||||
func Attach(pid int, _ []string) (*Process, error) {
 | 
			
		||||
	return nil, ErrNativeBackendDisabled
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -75,7 +75,9 @@ func (dbp *Process) detach(kill bool) error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) entryPoint() (uint64, error) {
 | 
			
		||||
// EntryPoint returns the entry point for the process,
 | 
			
		||||
// useful for PIEs.
 | 
			
		||||
func (dbp *Process) EntryPoint() (uint64, error) {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -85,17 +87,17 @@ func (t *Thread) Blocked() bool {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPC sets the value of the PC register.
 | 
			
		||||
func (thread *Thread) SetPC(pc uint64) error {
 | 
			
		||||
func (t *Thread) SetPC(pc uint64) error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSP sets the value of the SP register.
 | 
			
		||||
func (thread *Thread) SetSP(sp uint64) error {
 | 
			
		||||
func (t *Thread) SetSP(sp uint64) error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetDX sets the value of the DX register.
 | 
			
		||||
func (thread *Thread) SetDX(dx uint64) error {
 | 
			
		||||
func (t *Thread) SetDX(dx uint64) error {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -126,3 +128,5 @@ func (t *Thread) restoreRegisters(sr proc.Registers) error {
 | 
			
		||||
func (t *Thread) Stopped() bool {
 | 
			
		||||
	panic(ErrNativeBackendDisabled)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initialize(dbp *Process) error { return nil }
 | 
			
		||||
@@ -6,13 +6,14 @@ import (
 | 
			
		||||
	"runtime"
 | 
			
		||||
	"sync"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Process represents all of the information the debugger
 | 
			
		||||
// is holding onto regarding the process we are debugging.
 | 
			
		||||
type Process struct {
 | 
			
		||||
	bi  *proc.BinaryInfo
 | 
			
		||||
	bi *proc.BinaryInfo
 | 
			
		||||
 | 
			
		||||
	pid int // Process Pid
 | 
			
		||||
 | 
			
		||||
	// Breakpoint table, holds information on breakpoints.
 | 
			
		||||
@@ -184,32 +185,6 @@ func (dbp *Process) Breakpoints() *proc.BreakpointMap {
 | 
			
		||||
	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
 | 
			
		||||
 | 
			
		||||
	path = findExecutable(path, dbp.pid)
 | 
			
		||||
 | 
			
		||||
	entryPoint, err := dbp.entryPoint()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	wg.Add(1)
 | 
			
		||||
	go dbp.loadProcessInformation(&wg)
 | 
			
		||||
	err = dbp.bi.LoadBinaryInfo(path, entryPoint, &wg)
 | 
			
		||||
	wg.Wait()
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		err = dbp.bi.LoadError()
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// RequestManualStop sets the `halt` flag and
 | 
			
		||||
// sends SIGSTOP to all threads.
 | 
			
		||||
func (dbp *Process) RequestManualStop() error {
 | 
			
		||||
@@ -236,9 +211,6 @@ func (dbp *Process) CheckAndClearManualStopRequest() bool {
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) writeBreakpoint(addr uint64) (string, int, *proc.Function, []byte, error) {
 | 
			
		||||
	f, l, fn := dbp.bi.PCToLine(uint64(addr))
 | 
			
		||||
	if fn == nil {
 | 
			
		||||
		return "", 0, nil, nil, proc.InvalidAddressError{Address: addr}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	originalData := make([]byte, dbp.bi.Arch.BreakpointSize())
 | 
			
		||||
	_, err := dbp.currentThread.ReadMemory(originalData, uintptr(addr))
 | 
			
		||||
@@ -379,26 +351,23 @@ func (dbp *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) {
 | 
			
		||||
	return nil, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns a new Process struct.
 | 
			
		||||
func initializeDebugProcess(dbp *Process, path string) (*Process, error) {
 | 
			
		||||
	err := dbp.LoadInformation(path)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return dbp, err
 | 
			
		||||
// initialize will ensure that all relevant information is loaded
 | 
			
		||||
// so the process is ready to be debugged.
 | 
			
		||||
func (dbp *Process) initialize(path string, debugInfoDirs []string) error {
 | 
			
		||||
	if err := initialize(dbp); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := dbp.updateThreadList(); err != nil {
 | 
			
		||||
		return dbp, err
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	return proc.PostInitializationSetup(dbp, path, debugInfoDirs, dbp.writeBreakpoint)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
	// 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, _ = proc.GetG(dbp.currentThread)
 | 
			
		||||
 | 
			
		||||
	proc.CreateUnrecoveredPanicBreakpoint(dbp, dbp.writeBreakpoint, &dbp.breakpoints)
 | 
			
		||||
 | 
			
		||||
	return dbp, nil
 | 
			
		||||
// SetSelectedGoroutine will set internally the goroutine that should be
 | 
			
		||||
// the default for any command executed, the goroutine being actively
 | 
			
		||||
// followed.
 | 
			
		||||
func (dbp *Process) SetSelectedGoroutine(g *proc.G) {
 | 
			
		||||
	dbp.selectedGoroutine = g
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// ClearInternalBreakpoints will clear all non-user set breakpoints. These
 | 
			
		||||
@@ -13,12 +13,11 @@ import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// OSProcessDetails holds Darwin specific information.
 | 
			
		||||
@@ -38,7 +37,7 @@ type OSProcessDetails struct {
 | 
			
		||||
// 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, foreground bool) (*Process, error) {
 | 
			
		||||
func Launch(cmd []string, wd string, foreground bool, _ []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, proc.ErrNotExecutable
 | 
			
		||||
@@ -119,7 +118,7 @@ func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp.os.initialized = true
 | 
			
		||||
	dbp, err = initializeDebugProcess(dbp, argv0Go)
 | 
			
		||||
	err = dbp.initialize(argv0Go, []string{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -132,7 +131,7 @@ func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attach to an existing process with the given PID.
 | 
			
		||||
func Attach(pid int) (*Process, error) {
 | 
			
		||||
func Attach(pid int, _ []string) (*Process, error) {
 | 
			
		||||
	dbp := New(pid)
 | 
			
		||||
 | 
			
		||||
	kret := C.acquire_mach_task(C.int(pid),
 | 
			
		||||
@@ -155,7 +154,7 @@ func Attach(pid int) (*Process, error) {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp, err = initializeDebugProcess(dbp, "")
 | 
			
		||||
	err = dbp.initialize("", []string{})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		dbp.Detach(false)
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -383,10 +382,6 @@ func (dbp *Process) waitForStop() ([]int, error) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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)
 | 
			
		||||
@@ -464,7 +459,9 @@ func (dbp *Process) detach(kill bool) error {
 | 
			
		||||
	return PtraceDetach(dbp.pid, 0)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) entryPoint() (uint64, error) {
 | 
			
		||||
func (dbp *Process) EntryPoint() (uint64, error) {
 | 
			
		||||
	//TODO(aarzilli): implement this
 | 
			
		||||
	return 0, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initialize(dbp *Process) error { return nil }
 | 
			
		||||
@@ -12,15 +12,15 @@ import (
 | 
			
		||||
	"regexp"
 | 
			
		||||
	"strconv"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc/linutil"
 | 
			
		||||
	"github.com/mattn/go-isatty"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/linutil"
 | 
			
		||||
 | 
			
		||||
	isatty "github.com/mattn/go-isatty"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Process statuses
 | 
			
		||||
@@ -46,7 +46,9 @@ type OSProcessDetails struct {
 | 
			
		||||
// 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, foreground bool) (*Process, error) {
 | 
			
		||||
// If the DWARF information cannot be found in the binary, Delve will look
 | 
			
		||||
// for external debug files in the directories passed in.
 | 
			
		||||
func Launch(cmd []string, wd string, foreground bool, debugInfoDirs []string) (*Process, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		process *exec.Cmd
 | 
			
		||||
		err     error
 | 
			
		||||
@@ -88,11 +90,16 @@ func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("waiting for target execve failed: %s", err)
 | 
			
		||||
	}
 | 
			
		||||
	return initializeDebugProcess(dbp, process.Path)
 | 
			
		||||
	if err = dbp.initialize(cmd[0], debugInfoDirs); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return dbp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attach to an existing process with the given PID.
 | 
			
		||||
func Attach(pid int) (*Process, error) {
 | 
			
		||||
// Attach to an existing process with the given PID. Once attached, if
 | 
			
		||||
// the DWARF information cannot be found in the binary, Delve will look
 | 
			
		||||
// for external debug files in the directories passed in.
 | 
			
		||||
func Attach(pid int, debugInfoDirs []string) (*Process, error) {
 | 
			
		||||
	dbp := New(pid)
 | 
			
		||||
	dbp.common = proc.NewCommonProcess(true)
 | 
			
		||||
 | 
			
		||||
@@ -106,7 +113,7 @@ func Attach(pid int) (*Process, error) {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	dbp, err = initializeDebugProcess(dbp, "")
 | 
			
		||||
	err = dbp.initialize(findExecutable("", dbp.pid), debugInfoDirs)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		dbp.Detach(false)
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -114,6 +121,33 @@ func Attach(pid int) (*Process, error) {
 | 
			
		||||
	return dbp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func initialize(dbp *Process) error {
 | 
			
		||||
	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 {
 | 
			
		||||
			return fmt.Errorf("could not read proc stat: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		expr := fmt.Sprintf("%d\\s*\\((.*)\\)", dbp.pid)
 | 
			
		||||
		rexp, err := regexp.Compile(expr)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return fmt.Errorf("regexp compile error: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
		match := rexp.FindSubmatch(stat)
 | 
			
		||||
		if match == nil {
 | 
			
		||||
			return fmt.Errorf("no match found using regexp '%s' in /proc/%d/stat", expr, dbp.pid)
 | 
			
		||||
		}
 | 
			
		||||
		comm = match[1]
 | 
			
		||||
	}
 | 
			
		||||
	dbp.os.comm = strings.Replace(string(comm), "%", "%%", -1)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// kill kills the target process.
 | 
			
		||||
func (dbp *Process) kill() (err error) {
 | 
			
		||||
	if dbp.exited {
 | 
			
		||||
@@ -199,7 +233,7 @@ func (dbp *Process) updateThreadList() error {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
	return linutil.ElfUpdateSharedObjects(dbp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func findExecutable(path string, pid int) string {
 | 
			
		||||
@@ -295,35 +329,7 @@ func (dbp *Process) trapWaitInternal(pid int, halt bool) (*Thread, error) {
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 (dbp *Process) loadProcessInformation() {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func status(pid int, comm string) rune {
 | 
			
		||||
@@ -447,6 +453,10 @@ func (dbp *Process) stop(trapthread *Thread) (err error) {
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := linutil.ElfUpdateSharedObjects(dbp); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// set breakpoints on all threads
 | 
			
		||||
	for _, th := range dbp.threads {
 | 
			
		||||
		if th.CurrentBreakpoint.Breakpoint == nil {
 | 
			
		||||
@@ -479,7 +489,9 @@ func (dbp *Process) detach(kill bool) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) entryPoint() (uint64, error) {
 | 
			
		||||
// EntryPoint will return the process entry point address, useful for
 | 
			
		||||
// debugging PIEs.
 | 
			
		||||
func (dbp *Process) EntryPoint() (uint64, error) {
 | 
			
		||||
	auxvbuf, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/auxv", dbp.pid))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return 0, fmt.Errorf("could not read auxiliary vector: %v", err)
 | 
			
		||||
@@ -7,13 +7,12 @@ import (
 | 
			
		||||
	"os"
 | 
			
		||||
	"os/exec"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"sync"
 | 
			
		||||
	"syscall"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/windows"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// OSProcessDetails holds Windows specific information.
 | 
			
		||||
@@ -37,7 +36,7 @@ func openExecutablePathPE(path string) (*pe.File, io.Closer, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Launch creates and begins debugging a new process.
 | 
			
		||||
func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
 | 
			
		||||
func Launch(cmd []string, wd string, foreground bool, _ []string) (*Process, error) {
 | 
			
		||||
	argv0Go, err := filepath.Abs(cmd[0])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -77,11 +76,14 @@ func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
 | 
			
		||||
	dbp.pid = p.Pid
 | 
			
		||||
	dbp.childProcess = true
 | 
			
		||||
 | 
			
		||||
	return newDebugProcess(dbp, argv0Go)
 | 
			
		||||
	if err = dbp.initialize(argv0Go, []string{}); err != nil {
 | 
			
		||||
		dbp.Detach(true)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return dbp, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// newDebugProcess prepares process pid for debugging.
 | 
			
		||||
func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
 | 
			
		||||
func initialize(dbp *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
 | 
			
		||||
@@ -93,29 +95,25 @@ func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
 | 
			
		||||
		tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
 | 
			
		||||
	})
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if tid == 0 {
 | 
			
		||||
		dbp.postExit()
 | 
			
		||||
		return nil, proc.ErrProcessExited{Pid: dbp.pid, Status: exitCode}
 | 
			
		||||
		return proc.ErrProcessExited{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
 | 
			
		||||
			return 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)
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// findExePath searches for process pid, and returns its executable path.
 | 
			
		||||
@@ -153,7 +151,7 @@ func findExePath(pid int) (string, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Attach to an existing process with the given PID.
 | 
			
		||||
func Attach(pid int) (*Process, error) {
 | 
			
		||||
func Attach(pid int, _ []string) (*Process, error) {
 | 
			
		||||
	// TODO: Probably should have SeDebugPrivilege before starting here.
 | 
			
		||||
	err := _DebugActiveProcess(uint32(pid))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -163,11 +161,9 @@ func Attach(pid int) (*Process, error) {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	dbp, err := newDebugProcess(New(pid), exepath)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		if dbp != nil {
 | 
			
		||||
			dbp.Detach(false)
 | 
			
		||||
		}
 | 
			
		||||
	dbp := New(pid)
 | 
			
		||||
	if err = dbp.initialize(exepath, []string{}); err != nil {
 | 
			
		||||
		dbp.Detach(true)
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	return dbp, nil
 | 
			
		||||
@@ -245,6 +241,8 @@ const (
 | 
			
		||||
	waitDontHandleExceptions
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const _MS_VC_EXCEPTION = 0x406D1388 // part of VisualC protocol to set thread names
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, exitCode int, err error) {
 | 
			
		||||
	var debugEvent _DEBUG_EVENT
 | 
			
		||||
	shouldExit := false
 | 
			
		||||
@@ -346,6 +344,10 @@ func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, e
 | 
			
		||||
			case _EXCEPTION_SINGLE_STEP:
 | 
			
		||||
				dbp.os.breakThread = tid
 | 
			
		||||
				return tid, 0, nil
 | 
			
		||||
			case _MS_VC_EXCEPTION:
 | 
			
		||||
				// This exception is sent to set the thread name in VisualC, we should
 | 
			
		||||
				// mask it or it might crash the program.
 | 
			
		||||
				continueStatus = _DBG_CONTINUE
 | 
			
		||||
			default:
 | 
			
		||||
				continueStatus = _DBG_EXCEPTION_NOT_HANDLED
 | 
			
		||||
			}
 | 
			
		||||
@@ -386,10 +388,6 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
			
		||||
	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")
 | 
			
		||||
}
 | 
			
		||||
@@ -482,7 +480,7 @@ func (dbp *Process) detach(kill bool) error {
 | 
			
		||||
	return _DebugActiveProcessStop(uint32(dbp.pid))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (dbp *Process) entryPoint() (uint64, error) {
 | 
			
		||||
func (dbp *Process) EntryPoint() (uint64, error) {
 | 
			
		||||
	return dbp.os.entryPoint, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -6,7 +6,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/linutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// PtraceAttach executes the sys.PtraceAttach call.
 | 
			
		||||
@@ -57,8 +57,8 @@ func PtracePeekUser(tid int, off uintptr) (uintptr, error) {
 | 
			
		||||
// 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 proc.LinuxX86Xstate, err error) {
 | 
			
		||||
	_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETFPREGS, uintptr(tid), uintptr(0), uintptr(unsafe.Pointer(®set.PtraceFpRegs)), 0, 0)
 | 
			
		||||
func PtraceGetRegset(tid int) (regset linutil.AMD64Xstate, err error) {
 | 
			
		||||
	_, _, err = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_GETFPREGS, uintptr(tid), uintptr(0), uintptr(unsafe.Pointer(®set.AMD64PtraceFpRegs)), 0, 0)
 | 
			
		||||
	if err == syscall.Errno(0) || err == syscall.ENODEV {
 | 
			
		||||
		// ignore ENODEV, it just means this CPU doesn't have X87 registers (??)
 | 
			
		||||
		err = nil
 | 
			
		||||
@@ -68,8 +68,9 @@ func PtraceGetRegset(tid int) (regset proc.LinuxX86Xstate, err error) {
 | 
			
		||||
	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) {
 | 
			
		||||
		if err == syscall.ENODEV {
 | 
			
		||||
			// ignore ENODEV, it just means this CPU or kernel doesn't support XSTATE, see https://github.com/derekparker/delve/issues/1022
 | 
			
		||||
		if err == syscall.ENODEV || err == syscall.EIO {
 | 
			
		||||
			// ignore ENODEV, it just means this CPU or kernel doesn't support XSTATE, see https://github.com/go-delve/delve/issues/1022
 | 
			
		||||
			// also ignore EIO, it means that we are running on an old kernel (pre 2.6.34) and PTRACE_GETREGSET is not implemented
 | 
			
		||||
			err = nil
 | 
			
		||||
		}
 | 
			
		||||
		return
 | 
			
		||||
@@ -78,6 +79,6 @@ func PtraceGetRegset(tid int) (regset proc.LinuxX86Xstate, err error) {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	regset.Xsave = xstateargs[:iov.Len]
 | 
			
		||||
	err = proc.LinuxX86XstateRead(regset.Xsave, false, ®set)
 | 
			
		||||
	err = linutil.AMD64XstateRead(regset.Xsave, false, ®set)
 | 
			
		||||
	return regset, err
 | 
			
		||||
}
 | 
			
		||||
@@ -12,7 +12,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/arch/x86/x86asm"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Regs represents CPU registers on an AMD64 processor.
 | 
			
		||||
@@ -42,7 +42,7 @@ type Regs struct {
 | 
			
		||||
	fpregs []proc.Register
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) Slice() []proc.Register {
 | 
			
		||||
func (r *Regs) Slice(floatingPoint bool) []proc.Register {
 | 
			
		||||
	var regs = []struct {
 | 
			
		||||
		k string
 | 
			
		||||
		v uint64
 | 
			
		||||
@@ -78,7 +78,9 @@ func (r *Regs) Slice() []proc.Register {
 | 
			
		||||
			out = proc.AppendQwordReg(out, reg.k, reg.v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	out = append(out, r.fpregs...)
 | 
			
		||||
	if floatingPoint {
 | 
			
		||||
		out = append(out, r.fpregs...)
 | 
			
		||||
	}
 | 
			
		||||
	return out
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
							
								
								
									
										87
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/registers_linux_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										87
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/registers_linux_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,87 @@
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/linutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SetPC sets RIP to the value specified by 'pc'.
 | 
			
		||||
func (thread *Thread) SetPC(pc uint64) error {
 | 
			
		||||
	ir, err := registers(thread, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	r := ir.(*linutil.AMD64Registers)
 | 
			
		||||
	r.Regs.Rip = pc
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
 | 
			
		||||
	return err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSP sets RSP to the value specified by 'sp'
 | 
			
		||||
func (thread *Thread) SetSP(sp uint64) (err error) {
 | 
			
		||||
	var ir proc.Registers
 | 
			
		||||
	ir, err = registers(thread, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	r := ir.(*linutil.AMD64Registers)
 | 
			
		||||
	r.Regs.Rsp = sp
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) SetDX(dx uint64) (err error) {
 | 
			
		||||
	var ir proc.Registers
 | 
			
		||||
	ir, err = registers(thread, false)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	r := ir.(*linutil.AMD64Registers)
 | 
			
		||||
	r.Regs.Rdx = dx
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { err = sys.PtraceSetRegs(thread.ID, (*sys.PtraceRegs)(r.Regs)) })
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
 | 
			
		||||
	var (
 | 
			
		||||
		regs linutil.AMD64PtraceRegs
 | 
			
		||||
		err  error
 | 
			
		||||
	)
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { err = sys.PtraceGetRegs(thread.ID, (*sys.PtraceRegs)(®s)) })
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	r := &linutil.AMD64Registers{®s, nil, nil}
 | 
			
		||||
	if floatingPoint {
 | 
			
		||||
		var fpregset linutil.AMD64Xstate
 | 
			
		||||
		r.Fpregs, fpregset, err = thread.fpRegisters()
 | 
			
		||||
		r.Fpregset = &fpregset
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
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 []proc.Register, fpregs linutil.AMD64Xstate, err error) {
 | 
			
		||||
	thread.dbp.execPtraceFunc(func() { fpregs, err = PtraceGetRegset(thread.ID) })
 | 
			
		||||
	regs = fpregs.Decode()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		err = fmt.Errorf("could not get floating point registers: %v", err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
							
								
								
									
										71
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/registers_windows_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/registers_windows_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,71 @@
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/winutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// SetPC sets the RIP register to the value specified by `pc`.
 | 
			
		||||
func (thread *Thread) SetPC(pc uint64) error {
 | 
			
		||||
	context := winutil.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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSP sets the RSP register to the value specified by `sp`.
 | 
			
		||||
func (thread *Thread) SetSP(sp uint64) error {
 | 
			
		||||
	context := winutil.NewCONTEXT()
 | 
			
		||||
	context.ContextFlags = _CONTEXT_ALL
 | 
			
		||||
 | 
			
		||||
	err := _GetThreadContext(thread.os.hThread, context)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	context.Rsp = sp
 | 
			
		||||
 | 
			
		||||
	return _SetThreadContext(thread.os.hThread, context)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) SetDX(dx uint64) error {
 | 
			
		||||
	context := winutil.NewCONTEXT()
 | 
			
		||||
	context.ContextFlags = _CONTEXT_ALL
 | 
			
		||||
 | 
			
		||||
	err := _GetThreadContext(thread.os.hThread, context)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	context.Rdx = dx
 | 
			
		||||
 | 
			
		||||
	return _SetThreadContext(thread.os.hThread, context)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
 | 
			
		||||
	context := winutil.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)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return winutil.NewAMD64Registers(context, uint64(threadInfo.TebBaseAddress), floatingPoint), nil
 | 
			
		||||
}
 | 
			
		||||
@@ -4,6 +4,8 @@ package native
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"syscall"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/winutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type _NTSTATUS int32
 | 
			
		||||
@@ -97,6 +99,11 @@ func _NT_SUCCESS(x _NTSTATUS) bool {
 | 
			
		||||
	return x >= 0
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// zsyscall_windows.go, an autogenerated file, wants to refer to the context
 | 
			
		||||
// structure as _CONTEXT, but we need to have it in pkg/proc/winutil.CONTEXT
 | 
			
		||||
// because it's also used on non-windows operating systems.
 | 
			
		||||
type _CONTEXT = winutil.CONTEXT
 | 
			
		||||
 | 
			
		||||
//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
 | 
			
		||||
							
								
								
									
										24
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/syscall_windows_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							
							
						
						
									
										24
									
								
								vendor/github.com/go-delve/delve/pkg/proc/native/syscall_windows_amd64.go
									
									
									
										generated
									
									
										vendored
									
									
										Normal file
									
								
							@@ -0,0 +1,24 @@
 | 
			
		||||
package native
 | 
			
		||||
 | 
			
		||||
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 _DEBUG_EVENT struct {
 | 
			
		||||
	DebugEventCode uint32
 | 
			
		||||
	ProcessId      uint32
 | 
			
		||||
	ThreadId       uint32
 | 
			
		||||
	_              uint32 // to align Union properly
 | 
			
		||||
	U              [160]byte
 | 
			
		||||
}
 | 
			
		||||
@@ -3,7 +3,7 @@ package native
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Thread represents a single thread in the traced process
 | 
			
		||||
@@ -12,7 +12,7 @@ import (
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// WaitStatus is a synonym for the platform-specific WaitStatus
 | 
			
		||||
@@ -7,7 +7,8 @@ import (
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/unix"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/linutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type WaitStatus sys.WaitStatus
 | 
			
		||||
@@ -83,21 +84,21 @@ func (t *Thread) Blocked() bool {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) restoreRegisters(savedRegs proc.Registers) error {
 | 
			
		||||
	sr := savedRegs.(*Regs)
 | 
			
		||||
	sr := savedRegs.(*linutil.AMD64Registers)
 | 
			
		||||
 | 
			
		||||
	var restoreRegistersErr error
 | 
			
		||||
	t.dbp.execPtraceFunc(func() {
 | 
			
		||||
		restoreRegistersErr = sys.PtraceSetRegs(t.ID, sr.regs)
 | 
			
		||||
		restoreRegistersErr = sys.PtraceSetRegs(t.ID, (*sys.PtraceRegs)(sr.Regs))
 | 
			
		||||
		if restoreRegistersErr != nil {
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
		if sr.fpregset.Xsave != nil {
 | 
			
		||||
			iov := sys.Iovec{Base: &sr.fpregset.Xsave[0], Len: uint64(len(sr.fpregset.Xsave))}
 | 
			
		||||
		if sr.Fpregset.Xsave != nil {
 | 
			
		||||
			iov := sys.Iovec{Base: &sr.Fpregset.Xsave[0], Len: uint64(len(sr.Fpregset.Xsave))}
 | 
			
		||||
			_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(t.ID), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0)
 | 
			
		||||
			return
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETFPREGS, uintptr(t.ID), uintptr(0), uintptr(unsafe.Pointer(&sr.fpregset.PtraceFpRegs)), 0, 0)
 | 
			
		||||
		_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETFPREGS, uintptr(t.ID), uintptr(0), uintptr(unsafe.Pointer(&sr.Fpregset.AMD64PtraceFpRegs)), 0, 0)
 | 
			
		||||
		return
 | 
			
		||||
	})
 | 
			
		||||
	if restoreRegistersErr == syscall.Errno(0) {
 | 
			
		||||
@@ -6,7 +6,8 @@ import (
 | 
			
		||||
 | 
			
		||||
	sys "golang.org/x/sys/windows"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc/winutil"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// WaitStatus is a synonym for the platform-specific WaitStatus
 | 
			
		||||
@@ -19,7 +20,7 @@ type OSSpecificDetails struct {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) singleStep() error {
 | 
			
		||||
	context := newCONTEXT()
 | 
			
		||||
	context := winutil.NewCONTEXT()
 | 
			
		||||
	context.ContextFlags = _CONTEXT_ALL
 | 
			
		||||
 | 
			
		||||
	// Set the processor TRAP flag
 | 
			
		||||
@@ -154,5 +155,5 @@ func (t *Thread) ReadMemory(buf []byte, addr uintptr) (int, error) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (t *Thread) restoreRegisters(savedRegs proc.Registers) error {
 | 
			
		||||
	return _SetThreadContext(t.os.hThread, savedRegs.(*Regs).context)
 | 
			
		||||
	return _SetThreadContext(t.os.hThread, savedRegs.(*winutil.AMD64Registers).Context)
 | 
			
		||||
}
 | 
			
		||||
@@ -19,8 +19,16 @@ var ErrNotExecutable = errors.New("not an executable file")
 | 
			
		||||
// only possible on recorded (traced) programs.
 | 
			
		||||
var ErrNotRecorded = errors.New("not a recording")
 | 
			
		||||
 | 
			
		||||
// UnrecoveredPanic is the name given to the unrecovered panic breakpoint.
 | 
			
		||||
const UnrecoveredPanic = "unrecovered-panic"
 | 
			
		||||
const (
 | 
			
		||||
	// UnrecoveredPanic is the name given to the unrecovered panic breakpoint.
 | 
			
		||||
	UnrecoveredPanic = "unrecovered-panic"
 | 
			
		||||
 | 
			
		||||
	// FatalThrow is the name given to the breakpoint triggered when the target process dies because of a fatal runtime error
 | 
			
		||||
	FatalThrow = "runtime-fatal-throw"
 | 
			
		||||
 | 
			
		||||
	unrecoveredPanicID = -1
 | 
			
		||||
	fatalThrowID       = -2
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ErrProcessExited indicates that the process has exited and contains both
 | 
			
		||||
// process id and exit status.
 | 
			
		||||
@@ -41,6 +49,31 @@ func (pe ProcessDetachedError) Error() string {
 | 
			
		||||
	return "detached from the process"
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PostInitializationSetup handles all of the initialization procedures
 | 
			
		||||
// that must happen after Delve creates or attaches to a process.
 | 
			
		||||
func PostInitializationSetup(p Process, path string, debugInfoDirs []string, writeBreakpoint WriteBreakpointFn) error {
 | 
			
		||||
	entryPoint, err := p.EntryPoint()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	err = p.BinInfo().LoadBinaryInfo(path, entryPoint, debugInfoDirs)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		err = p.BinInfo().LoadError()
 | 
			
		||||
	}
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	g, _ := GetG(p.CurrentThread())
 | 
			
		||||
	p.SetSelectedGoroutine(g)
 | 
			
		||||
 | 
			
		||||
	createUnrecoveredPanicBreakpoint(p, writeBreakpoint)
 | 
			
		||||
	createFatalThrowBreakpoint(p, writeBreakpoint)
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindFileLocation returns the PC for a given file:line.
 | 
			
		||||
// Assumes that `file` is normalized to lower case and '/' on Windows.
 | 
			
		||||
func FindFileLocation(p Process, fileName string, lineno int) (uint64, error) {
 | 
			
		||||
@@ -69,7 +102,7 @@ func (err *ErrFunctionNotFound) Error() string {
 | 
			
		||||
// 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
 | 
			
		||||
// https://github.com/go-delve/delve/issues/170
 | 
			
		||||
func FindFunctionLocation(p Process, funcName string, firstLine bool, lineOffset int) (uint64, error) {
 | 
			
		||||
	bi := p.BinInfo()
 | 
			
		||||
	origfn := bi.LookupFunc[funcName]
 | 
			
		||||
@@ -88,6 +121,33 @@ func FindFunctionLocation(p Process, funcName string, firstLine bool, lineOffset
 | 
			
		||||
	return origfn.Entry, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FunctionReturnLocations will return a list of addresses corresponding
 | 
			
		||||
// to 'ret' or 'call runtime.deferreturn'.
 | 
			
		||||
func FunctionReturnLocations(p Process, funcName string) ([]uint64, error) {
 | 
			
		||||
	const deferReturn = "runtime.deferreturn"
 | 
			
		||||
 | 
			
		||||
	g := p.SelectedGoroutine()
 | 
			
		||||
	fn, ok := p.BinInfo().LookupFunc[funcName]
 | 
			
		||||
	if !ok {
 | 
			
		||||
		return nil, fmt.Errorf("unable to find function %s", funcName)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	instructions, err := Disassemble(p, g, fn.Entry, fn.End)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var addrs []uint64
 | 
			
		||||
	for _, instruction := range instructions {
 | 
			
		||||
		if instruction.IsRet() {
 | 
			
		||||
			addrs = append(addrs, instruction.Loc.PC)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	addrs = append(addrs, findDeferReturnCalls(instructions)...)
 | 
			
		||||
 | 
			
		||||
	return addrs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Next continues execution until the next source line.
 | 
			
		||||
func Next(dbp Process) (err error) {
 | 
			
		||||
	if _, err := dbp.Valid(); err != nil {
 | 
			
		||||
@@ -184,7 +244,8 @@ func Continue(dbp Process) error {
 | 
			
		||||
				return conditionErrors(threads)
 | 
			
		||||
			}
 | 
			
		||||
		case curbp.Active && curbp.Internal:
 | 
			
		||||
			if curbp.Kind == StepBreakpoint {
 | 
			
		||||
			switch curbp.Kind {
 | 
			
		||||
			case StepBreakpoint:
 | 
			
		||||
				// See description of proc.(*Process).next for the meaning of StepBreakpoints
 | 
			
		||||
				if err := conditionErrors(threads); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
@@ -204,7 +265,7 @@ func Continue(dbp Process) error {
 | 
			
		||||
				if err = setStepIntoBreakpoint(dbp, text, SameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
				}
 | 
			
		||||
			} else {
 | 
			
		||||
			default:
 | 
			
		||||
				curthread.Common().returnValues = curbp.Breakpoint.returnInfo.Collect(curthread)
 | 
			
		||||
				if err := dbp.ClearInternalBreakpoints(); err != nil {
 | 
			
		||||
					return err
 | 
			
		||||
@@ -281,7 +342,7 @@ func stepInstructionOut(dbp Process, curthread Thread, fnname1, fnname2 string)
 | 
			
		||||
			if g := dbp.SelectedGoroutine(); g != nil {
 | 
			
		||||
				g.CurrentLoc = *loc
 | 
			
		||||
			}
 | 
			
		||||
			return nil
 | 
			
		||||
			return curthread.SetCurrentBreakpoint()
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
@@ -436,14 +497,22 @@ func StepOut(dbp Process) error {
 | 
			
		||||
	return Continue(dbp)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// GoroutinesInfo returns an array of G structures representing the information
 | 
			
		||||
// Delve cares about from the internal runtime G structure.
 | 
			
		||||
func GoroutinesInfo(dbp Process) ([]*G, error) {
 | 
			
		||||
// GoroutinesInfo searches for goroutines starting at index 'start', and
 | 
			
		||||
// returns an array of up to 'count' (or all found elements, if 'count' is 0)
 | 
			
		||||
// G structures representing the information Delve care about from the internal
 | 
			
		||||
// runtime G structure.
 | 
			
		||||
// GoroutinesInfo also returns the next index to be used as 'start' argument
 | 
			
		||||
// while scanning for all available goroutines, or -1 if there was an error
 | 
			
		||||
// or if the index already reached the last possible value.
 | 
			
		||||
func GoroutinesInfo(dbp Process, start, count int) ([]*G, int, error) {
 | 
			
		||||
	if _, err := dbp.Valid(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
		return nil, -1, err
 | 
			
		||||
	}
 | 
			
		||||
	if dbp.Common().allGCache != nil {
 | 
			
		||||
		return dbp.Common().allGCache, nil
 | 
			
		||||
		// We can't use the cached array to fulfill a subrange request
 | 
			
		||||
		if start == 0 && (count == 0 || count >= len(dbp.Common().allGCache)) {
 | 
			
		||||
			return dbp.Common().allGCache, -1, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	var (
 | 
			
		||||
@@ -465,12 +534,12 @@ func GoroutinesInfo(dbp Process) ([]*G, error) {
 | 
			
		||||
 | 
			
		||||
	addr, err := rdr.AddrFor("runtime.allglen", dbp.BinInfo().staticBase)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
		return nil, -1, err
 | 
			
		||||
	}
 | 
			
		||||
	allglenBytes := make([]byte, 8)
 | 
			
		||||
	_, err = dbp.CurrentThread().ReadMemory(allglenBytes, uintptr(addr))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
		return nil, -1, err
 | 
			
		||||
	}
 | 
			
		||||
	allglen := binary.LittleEndian.Uint64(allglenBytes)
 | 
			
		||||
 | 
			
		||||
@@ -480,17 +549,20 @@ func GoroutinesInfo(dbp Process) ([]*G, error) {
 | 
			
		||||
		// try old name (pre Go 1.6)
 | 
			
		||||
		allgentryaddr, err = rdr.AddrFor("runtime.allg", dbp.BinInfo().staticBase)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
			return nil, -1, err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	faddr := make([]byte, dbp.BinInfo().Arch.PtrSize())
 | 
			
		||||
	_, err = dbp.CurrentThread().ReadMemory(faddr, uintptr(allgentryaddr))
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
		return nil, -1, err
 | 
			
		||||
	}
 | 
			
		||||
	allgptr := binary.LittleEndian.Uint64(faddr)
 | 
			
		||||
 | 
			
		||||
	for i := uint64(0); i < allglen; i++ {
 | 
			
		||||
	for i := uint64(start); i < allglen; i++ {
 | 
			
		||||
		if count != 0 && len(allg) >= count {
 | 
			
		||||
			return allg, int(i), nil
 | 
			
		||||
		}
 | 
			
		||||
		gvar, err := newGVariable(dbp.CurrentThread(), uintptr(allgptr+(i*uint64(dbp.BinInfo().Arch.PtrSize()))), true)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			allg = append(allg, &G{Unreadable: err})
 | 
			
		||||
@@ -504,7 +576,7 @@ func GoroutinesInfo(dbp Process) ([]*G, error) {
 | 
			
		||||
		if thg, allocated := threadg[g.ID]; allocated {
 | 
			
		||||
			loc, err := thg.Thread.Location()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return nil, err
 | 
			
		||||
				return nil, -1, err
 | 
			
		||||
			}
 | 
			
		||||
			g.Thread = thg.Thread
 | 
			
		||||
			// Prefer actual thread location information.
 | 
			
		||||
@@ -515,36 +587,78 @@ func GoroutinesInfo(dbp Process) ([]*G, error) {
 | 
			
		||||
			allg = append(allg, g)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	dbp.Common().allGCache = allg
 | 
			
		||||
	if start == 0 {
 | 
			
		||||
		dbp.Common().allGCache = allg
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return allg, nil
 | 
			
		||||
	return allg, -1, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FindGoroutine returns a G struct representing the goroutine
 | 
			
		||||
// specified by `gid`.
 | 
			
		||||
func FindGoroutine(dbp Process, gid int) (*G, error) {
 | 
			
		||||
	if gid == -1 {
 | 
			
		||||
		return dbp.SelectedGoroutine(), nil
 | 
			
		||||
	if selg := dbp.SelectedGoroutine(); (gid == -1) || (selg != nil && selg.ID == gid) || (selg == nil && gid == 0) {
 | 
			
		||||
		// Return the currently selected goroutine in the following circumstances:
 | 
			
		||||
		//
 | 
			
		||||
		// 1. if the caller asks for gid == -1 (because that's what a goroutine ID of -1 means in our API).
 | 
			
		||||
		// 2. if gid == selg.ID.
 | 
			
		||||
		//    this serves two purposes: (a) it's an optimizations that allows us
 | 
			
		||||
		//    to avoid reading any other goroutine and, more importantly, (b) we
 | 
			
		||||
		//    could be reading an incorrect value for the goroutine ID of a thread.
 | 
			
		||||
		//    This condition usually happens when a goroutine calls runtime.clone
 | 
			
		||||
		//    and for a short period of time two threads will appear to be running
 | 
			
		||||
		//    the same goroutine.
 | 
			
		||||
		// 3. if the caller asks for gid == 0 and the selected goroutine is
 | 
			
		||||
		//    either 0 or nil.
 | 
			
		||||
		//    Goroutine 0 is special, it either means we have no current goroutine
 | 
			
		||||
		//    (for example, running C code), or that we are running on a speical
 | 
			
		||||
		//    stack (system stack, signal handling stack) and we didn't properly
 | 
			
		||||
		//    detect it.
 | 
			
		||||
		//    Since there could be multiple goroutines '0' running simultaneously
 | 
			
		||||
		//    if the user requests it return the one that's already selected or
 | 
			
		||||
		//    nil if there isn't a selected goroutine.
 | 
			
		||||
		return selg, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	gs, err := GoroutinesInfo(dbp)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	if gid == 0 {
 | 
			
		||||
		return nil, fmt.Errorf("Unknown goroutine %d", gid)
 | 
			
		||||
	}
 | 
			
		||||
	for i := range gs {
 | 
			
		||||
		if gs[i].ID == gid {
 | 
			
		||||
			if gs[i].Unreadable != nil {
 | 
			
		||||
				return nil, gs[i].Unreadable
 | 
			
		||||
			}
 | 
			
		||||
			return gs[i], nil
 | 
			
		||||
 | 
			
		||||
	// Calling GoroutinesInfo could be slow if there are many goroutines
 | 
			
		||||
	// running, check if a running goroutine has been requested first.
 | 
			
		||||
	for _, thread := range dbp.ThreadList() {
 | 
			
		||||
		g, _ := GetG(thread)
 | 
			
		||||
		if g != nil && g.ID == gid {
 | 
			
		||||
			return g, nil
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	const goroutinesInfoLimit = 10
 | 
			
		||||
	nextg := 0
 | 
			
		||||
	for nextg >= 0 {
 | 
			
		||||
		var gs []*G
 | 
			
		||||
		var err error
 | 
			
		||||
		gs, nextg, err = GoroutinesInfo(dbp, nextg, goroutinesInfoLimit)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, err
 | 
			
		||||
		}
 | 
			
		||||
		for i := range gs {
 | 
			
		||||
			if gs[i].ID == gid {
 | 
			
		||||
				if gs[i].Unreadable != nil {
 | 
			
		||||
					return nil, gs[i].Unreadable
 | 
			
		||||
				}
 | 
			
		||||
				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 ConvertEvalScope(dbp Process, gid, frame int) (*EvalScope, error) {
 | 
			
		||||
// If deferCall is > 0 the eval scope will be relative to the specified deferred call.
 | 
			
		||||
func ConvertEvalScope(dbp Process, gid, frame, deferCall int) (*EvalScope, error) {
 | 
			
		||||
	if _, err := dbp.Valid(); err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -564,7 +678,7 @@ func ConvertEvalScope(dbp Process, gid, frame int) (*EvalScope, error) {
 | 
			
		||||
		thread = g.Thread
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	locs, err := g.Stacktrace(frame+1, false)
 | 
			
		||||
	locs, err := g.Stacktrace(frame+1, deferCall > 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
@@ -573,6 +687,19 @@ func ConvertEvalScope(dbp Process, gid, frame int) (*EvalScope, error) {
 | 
			
		||||
		return nil, fmt.Errorf("Frame %d does not exist in goroutine %d", frame, gid)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if deferCall > 0 {
 | 
			
		||||
		if deferCall-1 >= len(locs[frame].Defers) {
 | 
			
		||||
			return nil, fmt.Errorf("Frame %d only has %d deferred calls", frame, len(locs[frame].Defers))
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		d := locs[frame].Defers[deferCall-1]
 | 
			
		||||
		if d.Unreadable != nil {
 | 
			
		||||
			return nil, d.Unreadable
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return d.EvalScope(ct)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return FrameToScope(dbp.BinInfo(), thread, g, locs[frame:]...), nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -606,21 +733,30 @@ func FrameToScope(bi *BinaryInfo, thread MemoryReadWriter, g *G, frames ...Stack
 | 
			
		||||
	return s
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CreateUnrecoveredPanicBreakpoint creates the unrecoverable-panic breakpoint.
 | 
			
		||||
// createUnrecoveredPanicBreakpoint creates the unrecoverable-panic breakpoint.
 | 
			
		||||
// This function is meant to be called by implementations of the Process interface.
 | 
			
		||||
func CreateUnrecoveredPanicBreakpoint(p Process, writeBreakpoint writeBreakpointFn, breakpoints *BreakpointMap) {
 | 
			
		||||
func createUnrecoveredPanicBreakpoint(p Process, writeBreakpoint WriteBreakpointFn) {
 | 
			
		||||
	panicpc, err := FindFunctionLocation(p, "runtime.startpanic", true, 0)
 | 
			
		||||
	if _, isFnNotFound := err.(*ErrFunctionNotFound); isFnNotFound {
 | 
			
		||||
		panicpc, err = FindFunctionLocation(p, "runtime.fatalpanic", true, 0)
 | 
			
		||||
	}
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		bp, err := breakpoints.SetWithID(-1, panicpc, writeBreakpoint)
 | 
			
		||||
		bp, err := p.Breakpoints().SetWithID(unrecoveredPanicID, panicpc, writeBreakpoint)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			bp.Name = UnrecoveredPanic
 | 
			
		||||
			bp.Variables = []string{"runtime.curg._panic.arg"}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func createFatalThrowBreakpoint(p Process, writeBreakpoint WriteBreakpointFn) {
 | 
			
		||||
	fatalpc, err := FindFunctionLocation(p, "runtime.fatalthrow", true, 0)
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		bp, err := p.Breakpoints().SetWithID(fatalThrowID, fatalpc, writeBreakpoint)
 | 
			
		||||
		if err == nil {
 | 
			
		||||
			bp.Name = FatalThrow
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// FirstPCAfterPrologue returns the address of the first
 | 
			
		||||
@@ -23,7 +23,7 @@ type Registers interface {
 | 
			
		||||
	// GAddr returns the address of the G variable if it is known, 0 and false otherwise
 | 
			
		||||
	GAddr() (uint64, bool)
 | 
			
		||||
	Get(int) (uint64, error)
 | 
			
		||||
	Slice() []Register
 | 
			
		||||
	Slice(floatingPoint bool) []Register
 | 
			
		||||
	// Copy returns a copy of the registers that is guaranteed not to change
 | 
			
		||||
	// when the registers of the associated thread change.
 | 
			
		||||
	Copy() Registers
 | 
			
		||||
@@ -250,99 +250,3 @@ func (descr flagRegisterDescr) Describe(reg uint64, bitsize int) string {
 | 
			
		||||
	}
 | 
			
		||||
	return fmt.Sprintf("%#0*x\t[%s]", bitsize/4, reg, strings.Join(r, " "))
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// PtraceFpRegs 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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// LinuxX86Xstate represents amd64 XSAVE area. See Section 13.1 (and
 | 
			
		||||
// following) of Intel® 64 and IA-32 Architectures Software Developer’s
 | 
			
		||||
// Manual, Volume 1: Basic Architecture.
 | 
			
		||||
type LinuxX86Xstate struct {
 | 
			
		||||
	PtraceFpRegs
 | 
			
		||||
	Xsave    []byte // raw xsave area
 | 
			
		||||
	AvxState bool   // contains AVX state
 | 
			
		||||
	YmmSpace [256]byte
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Decode decodes an XSAVE area to a list of name/value pairs of registers.
 | 
			
		||||
func (xsave *LinuxX86Xstate) Decode() (regs []Register) {
 | 
			
		||||
	// x87 registers
 | 
			
		||||
	regs = AppendWordReg(regs, "CW", xsave.Cwd)
 | 
			
		||||
	regs = AppendWordReg(regs, "SW", xsave.Swd)
 | 
			
		||||
	regs = AppendWordReg(regs, "TW", xsave.Ftw)
 | 
			
		||||
	regs = AppendWordReg(regs, "FOP", xsave.Fop)
 | 
			
		||||
	regs = AppendQwordReg(regs, "FIP", xsave.Rip)
 | 
			
		||||
	regs = AppendQwordReg(regs, "FDP", xsave.Rdp)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(xsave.StSpace); i += 4 {
 | 
			
		||||
		regs = AppendX87Reg(regs, i/4, uint16(xsave.StSpace[i+2]), uint64(xsave.StSpace[i+1])<<32|uint64(xsave.StSpace[i]))
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// SSE registers
 | 
			
		||||
	regs = AppendMxcsrReg(regs, "MXCSR", uint64(xsave.Mxcsr))
 | 
			
		||||
	regs = AppendDwordReg(regs, "MXCSR_MASK", xsave.MxcrMask)
 | 
			
		||||
 | 
			
		||||
	for i := 0; i < len(xsave.XmmSpace); i += 16 {
 | 
			
		||||
		regs = AppendSSEReg(regs, fmt.Sprintf("XMM%d", i/16), xsave.XmmSpace[i:i+16])
 | 
			
		||||
		if xsave.AvxState {
 | 
			
		||||
			regs = AppendSSEReg(regs, fmt.Sprintf("YMM%d", i/16), xsave.YmmSpace[i:i+16])
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
	_XSAVE_HEADER_START          = 512
 | 
			
		||||
	_XSAVE_HEADER_LEN            = 64
 | 
			
		||||
	_XSAVE_EXTENDED_REGION_START = 576
 | 
			
		||||
	_XSAVE_SSE_REGION_LEN        = 416
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// LinuxX86XstateRead reads a byte array containing an XSAVE area into regset.
 | 
			
		||||
// If readLegacy is true regset.PtraceFpRegs will be filled with the
 | 
			
		||||
// contents of the legacy region of the XSAVE area.
 | 
			
		||||
// See Section 13.1 (and following) of Intel® 64 and IA-32 Architectures
 | 
			
		||||
// Software Developer’s Manual, Volume 1: Basic Architecture.
 | 
			
		||||
func LinuxX86XstateRead(xstateargs []byte, readLegacy bool, regset *LinuxX86Xstate) error {
 | 
			
		||||
	if _XSAVE_HEADER_START+_XSAVE_HEADER_LEN >= len(xstateargs) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
	if readLegacy {
 | 
			
		||||
		rdr := bytes.NewReader(xstateargs[:_XSAVE_HEADER_START])
 | 
			
		||||
		if err := binary.Read(rdr, binary.LittleEndian, ®set.PtraceFpRegs); err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	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 nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if xstate_bv&(1<<2) == 0 {
 | 
			
		||||
		// AVX state not present
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	avxstate := xstateargs[_XSAVE_EXTENDED_REGION_START:]
 | 
			
		||||
	regset.AvxState = true
 | 
			
		||||
	copy(regset.YmmSpace[:], avxstate[:len(regset.YmmSpace)])
 | 
			
		||||
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
@@ -79,7 +79,7 @@ func GetDwarfRegister(regs Registers, i int) []byte {
 | 
			
		||||
		return buf.Bytes()
 | 
			
		||||
	}
 | 
			
		||||
	if regname, ok := dwarfToName[i]; ok {
 | 
			
		||||
		regslice := regs.Slice()
 | 
			
		||||
		regslice := regs.Slice(true)
 | 
			
		||||
		for _, reg := range regslice {
 | 
			
		||||
			if reg.Name == regname {
 | 
			
		||||
				return reg.Bytes
 | 
			
		||||
@@ -7,9 +7,9 @@ import (
 | 
			
		||||
	"go/constant"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/frame"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/frame"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// This code is partly adapted from runtime.gentraceback in
 | 
			
		||||
@@ -375,36 +375,21 @@ func (it *stackIterator) newStackframe(ret, retaddr uint64) Stackframe {
 | 
			
		||||
		it.regs.FrameBase = it.frameBase(fn)
 | 
			
		||||
	}
 | 
			
		||||
	r := Stackframe{Current: Location{PC: it.pc, File: f, Line: l, Fn: fn}, Regs: it.regs, Ret: ret, addrret: retaddr, stackHi: it.stackhi, SystemStack: it.systemstack, lastpc: it.pc}
 | 
			
		||||
	if !it.top {
 | 
			
		||||
		fnname := ""
 | 
			
		||||
		if r.Current.Fn != nil {
 | 
			
		||||
			fnname = r.Current.Fn.Name
 | 
			
		||||
		}
 | 
			
		||||
		switch fnname {
 | 
			
		||||
	r.Call = r.Current
 | 
			
		||||
	if !it.top && r.Current.Fn != nil && it.pc != r.Current.Fn.Entry {
 | 
			
		||||
		// if the return address is the entry point of the function that
 | 
			
		||||
		// contains it then this is some kind of fake return frame (for example
 | 
			
		||||
		// runtime.sigreturn) that didn't actually call the current frame,
 | 
			
		||||
		// attempting to get the location of the CALL instruction would just
 | 
			
		||||
		// obfuscate what's going on, since there is no CALL instruction.
 | 
			
		||||
		switch r.Current.Fn.Name {
 | 
			
		||||
		case "runtime.mstart", "runtime.systemstack_switch":
 | 
			
		||||
			// these frames are inserted by runtime.systemstack and there is no CALL
 | 
			
		||||
			// instruction to look for at pc - 1
 | 
			
		||||
			r.Call = r.Current
 | 
			
		||||
		default:
 | 
			
		||||
			if r.Current.Fn != nil && it.pc == r.Current.Fn.Entry {
 | 
			
		||||
				// if the return address is the entry point of the function that
 | 
			
		||||
				// contains it then this is some kind of fake return frame (for example
 | 
			
		||||
				// runtime.sigreturn) that didn't actually call the current frame,
 | 
			
		||||
				// attempting to get the location of the CALL instruction would just
 | 
			
		||||
				// obfuscate what's going on, since there is no CALL instruction.
 | 
			
		||||
				r.Call = r.Current
 | 
			
		||||
			} else {
 | 
			
		||||
				r.lastpc = it.pc - 1
 | 
			
		||||
				r.Call.File, r.Call.Line, r.Call.Fn = it.bi.PCToLine(it.pc - 1)
 | 
			
		||||
				if r.Call.Fn == nil {
 | 
			
		||||
					r.Call.File = "?"
 | 
			
		||||
					r.Call.Line = -1
 | 
			
		||||
				}
 | 
			
		||||
				r.Call.PC = r.Current.PC
 | 
			
		||||
			}
 | 
			
		||||
			r.lastpc = it.pc - 1
 | 
			
		||||
			r.Call.File, r.Call.Line = r.Current.Fn.cu.lineInfo.PCToLine(r.Current.Fn.Entry, it.pc-1)
 | 
			
		||||
		}
 | 
			
		||||
	} else {
 | 
			
		||||
		r.Call = r.Current
 | 
			
		||||
	}
 | 
			
		||||
	return r
 | 
			
		||||
}
 | 
			
		||||
@@ -594,6 +579,7 @@ type Defer struct {
 | 
			
		||||
	DeferPC    uint64 // PC address of instruction that added this defer
 | 
			
		||||
	SP         uint64 // Value of SP register when this function was deferred (this field gets adjusted when the stack is moved to match the new stack space)
 | 
			
		||||
	link       *Defer // Next deferred function
 | 
			
		||||
	argSz      int64
 | 
			
		||||
 | 
			
		||||
	variable   *Variable
 | 
			
		||||
	Unreadable error
 | 
			
		||||
@@ -644,7 +630,7 @@ func (g *G) readDefers(frames []Stackframe) {
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (d *Defer) load() {
 | 
			
		||||
	d.variable.loadValue(LoadConfig{false, 1, 0, 0, -1})
 | 
			
		||||
	d.variable.loadValue(LoadConfig{false, 1, 0, 0, -1, 0})
 | 
			
		||||
	if d.variable.Unreadable != nil {
 | 
			
		||||
		d.Unreadable = d.variable.Unreadable
 | 
			
		||||
		return
 | 
			
		||||
@@ -660,6 +646,7 @@ func (d *Defer) load() {
 | 
			
		||||
 | 
			
		||||
	d.DeferPC, _ = constant.Uint64Val(d.variable.fieldVariable("pc").Value)
 | 
			
		||||
	d.SP, _ = constant.Uint64Val(d.variable.fieldVariable("sp").Value)
 | 
			
		||||
	d.argSz, _ = constant.Int64Val(d.variable.fieldVariable("siz").Value)
 | 
			
		||||
 | 
			
		||||
	linkvar := d.variable.fieldVariable("link").maybeDereference()
 | 
			
		||||
	if linkvar.Addr != 0 {
 | 
			
		||||
@@ -685,3 +672,40 @@ func (d *Defer) Next() *Defer {
 | 
			
		||||
	}
 | 
			
		||||
	return d.link
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EvalScope returns an EvalScope relative to the argument frame of this deferred call.
 | 
			
		||||
// The argument frame of a deferred call is stored in memory immediately
 | 
			
		||||
// after the deferred header.
 | 
			
		||||
func (d *Defer) EvalScope(thread Thread) (*EvalScope, error) {
 | 
			
		||||
	scope, err := GoroutineScope(thread)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("could not get scope: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	bi := thread.BinInfo()
 | 
			
		||||
	scope.PC = d.DeferredPC
 | 
			
		||||
	scope.File, scope.Line, scope.Fn = bi.PCToLine(d.DeferredPC)
 | 
			
		||||
 | 
			
		||||
	if scope.Fn == nil {
 | 
			
		||||
		return nil, fmt.Errorf("could not find function at %#x", d.DeferredPC)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// The arguments are stored immediately after the defer header struct, i.e.
 | 
			
		||||
	// addr+sizeof(_defer). Since CFA in go is always the address of the first
 | 
			
		||||
	// argument, that's what we use for the value of CFA.
 | 
			
		||||
	// For SP we use CFA minus the size of one pointer because that would be
 | 
			
		||||
	// the space occupied by pushing the return address on the stack during the
 | 
			
		||||
	// CALL.
 | 
			
		||||
	scope.Regs.CFA = (int64(d.variable.Addr) + d.variable.RealType.Common().ByteSize)
 | 
			
		||||
	scope.Regs.Regs[scope.Regs.SPRegNum].Uint64Val = uint64(scope.Regs.CFA - int64(bi.Arch.PtrSize()))
 | 
			
		||||
 | 
			
		||||
	bi.dwarfReader.Seek(scope.Fn.offset)
 | 
			
		||||
	e, err := bi.dwarfReader.Next()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, fmt.Errorf("could not read DWARF function entry: %v", err)
 | 
			
		||||
	}
 | 
			
		||||
	scope.Regs.FrameBase, _, _, _ = bi.Location(e, dwarf.AttrFrameBase, scope.PC, scope.Regs)
 | 
			
		||||
	scope.Mem = cacheMemory(scope.Mem, uintptr(scope.Regs.CFA), int(d.argSz))
 | 
			
		||||
 | 
			
		||||
	return scope, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -10,8 +10,8 @@ import (
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strings"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Thread represents a thread.
 | 
			
		||||
@@ -225,15 +225,7 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	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)
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		deferreturns := findDeferReturnCalls(text)
 | 
			
		||||
 | 
			
		||||
		// Set breakpoint on the most recently deferred function (if any)
 | 
			
		||||
		var deferpc uint64
 | 
			
		||||
@@ -333,6 +325,20 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func findDeferReturnCalls(text []AsmInstruction) []uint64 {
 | 
			
		||||
	const deferreturn = "runtime.deferreturn"
 | 
			
		||||
	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 == deferreturn {
 | 
			
		||||
			deferreturns = append(deferreturns, instr.Loc.PC)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return deferreturns
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Removes instructions belonging to inlined calls of topframe from pcs.
 | 
			
		||||
// If includeCurrentFn is true it will also remove all instructions
 | 
			
		||||
// belonging to the current function.
 | 
			
		||||
@@ -373,21 +379,16 @@ func setStepIntoBreakpoint(dbp Process, text []AsmInstruction, cond ast.Expr) er
 | 
			
		||||
 | 
			
		||||
	instr := text[0]
 | 
			
		||||
 | 
			
		||||
	if instr.DestLoc == nil || instr.DestLoc.Fn == nil {
 | 
			
		||||
	if instr.DestLoc == nil {
 | 
			
		||||
		// Call destination couldn't be resolved because this was not the
 | 
			
		||||
		// current instruction, therefore the step-into breakpoint can not be set.
 | 
			
		||||
		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) {
 | 
			
		||||
	if fn != nil && strings.HasPrefix(fn.Name, "runtime.") && !isExportedRuntime(fn.Name) {
 | 
			
		||||
		return nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
@@ -395,8 +396,19 @@ func setStepIntoBreakpoint(dbp Process, text []AsmInstruction, cond ast.Expr) er
 | 
			
		||||
	// or entire packages from being stepped into with 'step'
 | 
			
		||||
	// those extra checks should be done here.
 | 
			
		||||
 | 
			
		||||
	pc := instr.DestLoc.PC
 | 
			
		||||
 | 
			
		||||
	// We want to skip the function prologue but we should only do it if the
 | 
			
		||||
	// destination address of the CALL instruction is the entry point of the
 | 
			
		||||
	// function.
 | 
			
		||||
	// Calls to runtime.duffzero and duffcopy inserted by the compiler can
 | 
			
		||||
	// sometimes point inside the body of those functions, well after the
 | 
			
		||||
	// prologue.
 | 
			
		||||
	if fn != nil && fn.Entry == instr.DestLoc.PC {
 | 
			
		||||
		pc, _ = FirstPCAfterPrologue(dbp, fn, false)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	// Set a breakpoint after the function's prologue
 | 
			
		||||
	pc, _ := FirstPCAfterPrologue(dbp, fn, false)
 | 
			
		||||
	if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, cond); err != nil {
 | 
			
		||||
		if _, ok := err.(BreakpointExistsError); !ok {
 | 
			
		||||
			return err
 | 
			
		||||
@@ -465,6 +477,10 @@ func newGVariable(thread Thread, gaddr uintptr, deref bool) (*Variable, error) {
 | 
			
		||||
// 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 GetG(thread Thread) (*G, error) {
 | 
			
		||||
	if loc, _ := thread.Location(); loc.Fn != nil && loc.Fn.Name == "runtime.clone" {
 | 
			
		||||
		// When threads are executing runtime.clone the value of TLS is unreliable.
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	gaddr, err := getGVariable(thread)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
@@ -8,7 +8,6 @@ import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/ast"
 | 
			
		||||
	"go/constant"
 | 
			
		||||
	"go/parser"
 | 
			
		||||
	"go/token"
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"reflect"
 | 
			
		||||
@@ -18,12 +17,12 @@ import (
 | 
			
		||||
	"sync"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/line"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/goversion"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/logflags"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/line"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/goversion"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/logflags"
 | 
			
		||||
	"github.com/sirupsen/logrus"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -180,7 +179,7 @@ func (v functionsDebugInfoByEntry) Swap(i, j int)      { v[i], v[j] = v[j], v[i]
 | 
			
		||||
type compileUnitsByLowpc []*compileUnit
 | 
			
		||||
 | 
			
		||||
func (v compileUnitsByLowpc) Len() int               { return len(v) }
 | 
			
		||||
func (v compileUnitsByLowpc) Less(i int, j int) bool { return v[i].LowPC < v[j].LowPC }
 | 
			
		||||
func (v compileUnitsByLowpc) Less(i int, j int) bool { return v[i].lowPC < v[j].lowPC }
 | 
			
		||||
func (v compileUnitsByLowpc) Swap(i int, j int)      { v[i], v[j] = v[j], v[i] }
 | 
			
		||||
 | 
			
		||||
type packageVarsByAddr []packageVar
 | 
			
		||||
@@ -230,18 +229,18 @@ outer:
 | 
			
		||||
			if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage {
 | 
			
		||||
				cu.isgo = true
 | 
			
		||||
			}
 | 
			
		||||
			cu.Name, _ = entry.Val(dwarf.AttrName).(string)
 | 
			
		||||
			cu.name, _ = entry.Val(dwarf.AttrName).(string)
 | 
			
		||||
			compdir, _ := entry.Val(dwarf.AttrCompDir).(string)
 | 
			
		||||
			if compdir != "" {
 | 
			
		||||
				cu.Name = filepath.Join(compdir, cu.Name)
 | 
			
		||||
				cu.name = filepath.Join(compdir, cu.name)
 | 
			
		||||
			}
 | 
			
		||||
			cu.Ranges, _ = bi.dwarf.Ranges(entry)
 | 
			
		||||
			for i := range cu.Ranges {
 | 
			
		||||
				cu.Ranges[i][0] += bi.staticBase
 | 
			
		||||
				cu.Ranges[i][1] += bi.staticBase
 | 
			
		||||
			cu.ranges, _ = bi.dwarf.Ranges(entry)
 | 
			
		||||
			for i := range cu.ranges {
 | 
			
		||||
				cu.ranges[i][0] += bi.staticBase
 | 
			
		||||
				cu.ranges[i][1] += bi.staticBase
 | 
			
		||||
			}
 | 
			
		||||
			if len(cu.Ranges) >= 1 {
 | 
			
		||||
				cu.LowPC = cu.Ranges[0][0]
 | 
			
		||||
			if len(cu.ranges) >= 1 {
 | 
			
		||||
				cu.lowPC = cu.ranges[0][0]
 | 
			
		||||
			}
 | 
			
		||||
			lineInfoOffset, _ := entry.Val(dwarf.AttrStmtList).(int64)
 | 
			
		||||
			if lineInfoOffset >= 0 && lineInfoOffset < int64(len(debugLineBytes)) {
 | 
			
		||||
@@ -398,6 +397,13 @@ outer:
 | 
			
		||||
				highpc = ranges[0][1] + bi.staticBase
 | 
			
		||||
			}
 | 
			
		||||
			name, ok2 := entry.Val(dwarf.AttrName).(string)
 | 
			
		||||
			if !ok2 {
 | 
			
		||||
				originOffset, hasAbstractOrigin := entry.Val(dwarf.AttrAbstractOrigin).(dwarf.Offset)
 | 
			
		||||
				if hasAbstractOrigin {
 | 
			
		||||
					name, ok2 = abstractOriginNameTable[originOffset]
 | 
			
		||||
				}
 | 
			
		||||
			}
 | 
			
		||||
 | 
			
		||||
			var fn Function
 | 
			
		||||
			if (ok1 == !inlined) && ok2 {
 | 
			
		||||
				if inlined {
 | 
			
		||||
@@ -559,45 +565,8 @@ func (bi *BinaryInfo) registerRuntimeTypeToDIE(entry *dwarf.Entry, ardr *reader.
 | 
			
		||||
// * After go1.11 the runtimeTypeToDIE map is used to look up the address of
 | 
			
		||||
//   the type and map it drectly to a DIE.
 | 
			
		||||
func runtimeTypeToDIE(_type *Variable, dataAddr uintptr) (typ godwarf.Type, kind int64, err error) {
 | 
			
		||||
	var go17 bool
 | 
			
		||||
	var typestring *Variable
 | 
			
		||||
 | 
			
		||||
	bi := _type.bi
 | 
			
		||||
 | 
			
		||||
	// Determine if we are in go1.7 or later
 | 
			
		||||
	typestring, err = _type.structMember("_string")
 | 
			
		||||
	if err == nil {
 | 
			
		||||
		typestring = typestring.maybeDereference()
 | 
			
		||||
	} else {
 | 
			
		||||
		err = nil
 | 
			
		||||
		go17 = true
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if !go17 {
 | 
			
		||||
		// pre-go1.7 compatibility
 | 
			
		||||
		if typestring == nil || typestring.Addr == 0 || typestring.Kind != reflect.String {
 | 
			
		||||
			return nil, 0, fmt.Errorf("invalid interface type")
 | 
			
		||||
		}
 | 
			
		||||
		typestring.loadValue(LoadConfig{false, 0, 512, 0, 0})
 | 
			
		||||
		if typestring.Unreadable != nil {
 | 
			
		||||
			return nil, 0, fmt.Errorf("invalid interface type: %v", typestring.Unreadable)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		typename := constant.StringVal(typestring.Value)
 | 
			
		||||
 | 
			
		||||
		t, err := parser.ParseExpr(typename)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, 0, fmt.Errorf("invalid interface type, unparsable data type: %v", err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		typ, err := bi.findTypeExpr(t)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return nil, 0, fmt.Errorf("interface type %q not found for %#x: %v", typename, dataAddr, err)
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		return typ, 0, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	_type = _type.maybeDereference()
 | 
			
		||||
 | 
			
		||||
	// go 1.11 implementation: use extended attribute in debug_info
 | 
			
		||||
@@ -880,7 +849,7 @@ func nameOfInterfaceRuntimeType(_type *Variable, kind, tflag int64) (string, err
 | 
			
		||||
	buf.WriteString("interface {")
 | 
			
		||||
 | 
			
		||||
	methods, _ := _type.structMember(interfacetypeFieldMhdr)
 | 
			
		||||
	methods.loadArrayValues(0, LoadConfig{false, 1, 0, 4096, -1})
 | 
			
		||||
	methods.loadArrayValues(0, LoadConfig{false, 1, 0, 4096, -1, 0})
 | 
			
		||||
	if methods.Unreadable != nil {
 | 
			
		||||
		return "", nil
 | 
			
		||||
	}
 | 
			
		||||
@@ -941,7 +910,7 @@ func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error)
 | 
			
		||||
	buf.WriteString("struct {")
 | 
			
		||||
 | 
			
		||||
	fields, _ := _type.structMember("fields")
 | 
			
		||||
	fields.loadArrayValues(0, LoadConfig{false, 2, 0, 4096, -1})
 | 
			
		||||
	fields.loadArrayValues(0, LoadConfig{false, 2, 0, 4096, -1, 0})
 | 
			
		||||
	if fields.Unreadable != nil {
 | 
			
		||||
		return "", fields.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
@@ -15,9 +15,9 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"unsafe"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/op"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -32,6 +32,8 @@ const (
 | 
			
		||||
	hashMinTopHash   = 4 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated
 | 
			
		||||
 | 
			
		||||
	maxFramePrefetchSize = 1 * 1024 * 1024 // Maximum prefetch size for a stack frame
 | 
			
		||||
 | 
			
		||||
	maxMapBucketsFactor = 100 // Maximum numbers of map buckets to read for every requested map entry when loading variables through (*EvalScope).LocalVariables and (*EvalScope).FunctionArguments.
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type floatSpecial uint8
 | 
			
		||||
@@ -122,10 +124,39 @@ type LoadConfig struct {
 | 
			
		||||
	MaxArrayValues int
 | 
			
		||||
	// MaxStructFields is the maximum number of fields read from a struct, -1 will read all fields.
 | 
			
		||||
	MaxStructFields int
 | 
			
		||||
 | 
			
		||||
	// MaxMapBuckets is the maximum number of map buckets to read before giving up.
 | 
			
		||||
	// A value of 0 will read as many buckets as necessary until the entire map
 | 
			
		||||
	// is read or MaxArrayValues is reached.
 | 
			
		||||
	//
 | 
			
		||||
	// Loading a map is an operation that issues O(num_buckets) operations.
 | 
			
		||||
	// Normally the number of buckets is proportional to the number of elements
 | 
			
		||||
	// in the map, since the runtime tries to keep the load factor of maps
 | 
			
		||||
	// between 40% and 80%.
 | 
			
		||||
	//
 | 
			
		||||
	// It is possible, however, to create very sparse maps either by:
 | 
			
		||||
	// a) adding lots of entries to a map and then deleting most of them, or
 | 
			
		||||
	// b) using the make(mapType, N) expression with a very large N
 | 
			
		||||
	//
 | 
			
		||||
	// When this happens delve will have to scan many empty buckets to find the
 | 
			
		||||
	// few entries in the map.
 | 
			
		||||
	// MaxMapBuckets can be set to avoid annoying slowdowns␣while reading
 | 
			
		||||
	// very sparse maps.
 | 
			
		||||
	//
 | 
			
		||||
	// Since there is no good way for a user of delve to specify the value of
 | 
			
		||||
	// MaxMapBuckets, this field is not actually exposed through the API.
 | 
			
		||||
	// Instead (*EvalScope).LocalVariables and  (*EvalScope).FunctionArguments
 | 
			
		||||
	// set this field automatically to MaxArrayValues * maxMapBucketsFactor.
 | 
			
		||||
	// Every other invocation uses the default value of 0, obtaining the old behavior.
 | 
			
		||||
	// In practice this means that debuggers using the ListLocalVars or
 | 
			
		||||
	// ListFunctionArgs API will not experience a massive slowdown when a very
 | 
			
		||||
	// sparse map is in scope, but evaluating a single variable will still work
 | 
			
		||||
	// correctly, even if the variable in question is a very sparse map.
 | 
			
		||||
	MaxMapBuckets int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var loadSingleValue = LoadConfig{false, 0, 64, 0, 0}
 | 
			
		||||
var loadFullValue = LoadConfig{true, 1, 64, 64, -1}
 | 
			
		||||
var loadSingleValue = LoadConfig{false, 0, 64, 0, 0, 0}
 | 
			
		||||
var loadFullValue = LoadConfig{true, 1, 64, 64, -1, 0}
 | 
			
		||||
 | 
			
		||||
// G status, from: src/runtime/runtime2.go
 | 
			
		||||
const (
 | 
			
		||||
@@ -169,6 +200,12 @@ type G struct {
 | 
			
		||||
	Unreadable error // could not read the G struct
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type Ancestor struct {
 | 
			
		||||
	ID         int64 // Goroutine ID
 | 
			
		||||
	Unreadable error
 | 
			
		||||
	pcsVar     *Variable
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// EvalScope is the scope for variable evaluation. Contains the thread,
 | 
			
		||||
// current location (PC), and canonical frame address.
 | 
			
		||||
type EvalScope struct {
 | 
			
		||||
@@ -405,13 +442,13 @@ func (scope *EvalScope) PtrSize() int {
 | 
			
		||||
	return scope.BinInfo.Arch.PtrSize()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// NoGError returned when a G could not be found
 | 
			
		||||
// ErrNoGoroutine returned when a G could not be found
 | 
			
		||||
// for a specific thread.
 | 
			
		||||
type NoGError struct {
 | 
			
		||||
type ErrNoGoroutine struct {
 | 
			
		||||
	tid int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (ng NoGError) Error() string {
 | 
			
		||||
func (ng ErrNoGoroutine) Error() string {
 | 
			
		||||
	return fmt.Sprintf("no G executing on thread %d", ng.tid)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -433,7 +470,7 @@ func (v *Variable) parseG() (*G, error) {
 | 
			
		||||
		if thread, ok := mem.(Thread); ok {
 | 
			
		||||
			id = thread.ThreadID()
 | 
			
		||||
		}
 | 
			
		||||
		return nil, NoGError{tid: id}
 | 
			
		||||
		return nil, ErrNoGoroutine{tid: id}
 | 
			
		||||
	}
 | 
			
		||||
	for {
 | 
			
		||||
		if _, isptr := v.RealType.(*godwarf.PtrType); !isptr {
 | 
			
		||||
@@ -441,7 +478,7 @@ func (v *Variable) parseG() (*G, error) {
 | 
			
		||||
		}
 | 
			
		||||
		v = v.maybeDereference()
 | 
			
		||||
	}
 | 
			
		||||
	v.loadValue(LoadConfig{false, 2, 64, 0, -1})
 | 
			
		||||
	v.loadValue(LoadConfig{false, 2, 64, 0, -1, 0})
 | 
			
		||||
	if v.Unreadable != nil {
 | 
			
		||||
		return nil, v.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
@@ -586,12 +623,102 @@ func (g *G) StartLoc() Location {
 | 
			
		||||
	return Location{PC: g.StartPC, File: f, Line: l, Fn: fn}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
var errTracebackAncestorsDisabled = errors.New("tracebackancestors is disabled")
 | 
			
		||||
 | 
			
		||||
// Ancestors returns the list of ancestors for g.
 | 
			
		||||
func (g *G) Ancestors(n int) ([]Ancestor, error) {
 | 
			
		||||
	scope := globalScope(g.Thread.BinInfo(), g.Thread)
 | 
			
		||||
	tbav, err := scope.EvalExpression("runtime.debug.tracebackancestors", loadSingleValue)
 | 
			
		||||
	if err == nil && tbav.Unreadable == nil && tbav.Kind == reflect.Int {
 | 
			
		||||
		tba, _ := constant.Int64Val(tbav.Value)
 | 
			
		||||
		if tba == 0 {
 | 
			
		||||
			return nil, errTracebackAncestorsDisabled
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	av, err := g.variable.structMember("ancestors")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	av = av.maybeDereference()
 | 
			
		||||
	av.loadValue(LoadConfig{MaxArrayValues: n, MaxVariableRecurse: 1, MaxStructFields: -1})
 | 
			
		||||
	if av.Unreadable != nil {
 | 
			
		||||
		return nil, err
 | 
			
		||||
	}
 | 
			
		||||
	if av.Addr == 0 {
 | 
			
		||||
		// no ancestors
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	r := make([]Ancestor, len(av.Children))
 | 
			
		||||
 | 
			
		||||
	for i := range av.Children {
 | 
			
		||||
		if av.Children[i].Unreadable != nil {
 | 
			
		||||
			r[i].Unreadable = av.Children[i].Unreadable
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		goidv := av.Children[i].fieldVariable("goid")
 | 
			
		||||
		if goidv.Unreadable != nil {
 | 
			
		||||
			r[i].Unreadable = goidv.Unreadable
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		r[i].ID, _ = constant.Int64Val(goidv.Value)
 | 
			
		||||
		pcsVar := av.Children[i].fieldVariable("pcs")
 | 
			
		||||
		if pcsVar.Unreadable != nil {
 | 
			
		||||
			r[i].Unreadable = pcsVar.Unreadable
 | 
			
		||||
		}
 | 
			
		||||
		pcsVar.loaded = false
 | 
			
		||||
		pcsVar.Children = pcsVar.Children[:0]
 | 
			
		||||
		r[i].pcsVar = pcsVar
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Stack returns the stack trace of ancestor 'a' as saved by the runtime.
 | 
			
		||||
func (a *Ancestor) Stack(n int) ([]Stackframe, error) {
 | 
			
		||||
	if a.Unreadable != nil {
 | 
			
		||||
		return nil, a.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
	pcsVar := a.pcsVar.clone()
 | 
			
		||||
	pcsVar.loadValue(LoadConfig{MaxArrayValues: n})
 | 
			
		||||
	if pcsVar.Unreadable != nil {
 | 
			
		||||
		return nil, pcsVar.Unreadable
 | 
			
		||||
	}
 | 
			
		||||
	r := make([]Stackframe, len(pcsVar.Children))
 | 
			
		||||
	for i := range pcsVar.Children {
 | 
			
		||||
		if pcsVar.Children[i].Unreadable != nil {
 | 
			
		||||
			r[i] = Stackframe{Err: pcsVar.Children[i].Unreadable}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		if pcsVar.Children[i].Kind != reflect.Uint {
 | 
			
		||||
			return nil, fmt.Errorf("wrong type for pcs item %d: %v", i, pcsVar.Children[i].Kind)
 | 
			
		||||
		}
 | 
			
		||||
		pc, _ := constant.Int64Val(pcsVar.Children[i].Value)
 | 
			
		||||
		fn := a.pcsVar.bi.PCToFunc(uint64(pc))
 | 
			
		||||
		if fn == nil {
 | 
			
		||||
			loc := Location{PC: uint64(pc)}
 | 
			
		||||
			r[i] = Stackframe{Current: loc, Call: loc}
 | 
			
		||||
			continue
 | 
			
		||||
		}
 | 
			
		||||
		pc2 := uint64(pc)
 | 
			
		||||
		if pc2-1 >= fn.Entry {
 | 
			
		||||
			pc2--
 | 
			
		||||
		}
 | 
			
		||||
		f, ln := fn.cu.lineInfo.PCToLine(fn.Entry, pc2)
 | 
			
		||||
		loc := Location{PC: uint64(pc), File: f, Line: ln, Fn: fn}
 | 
			
		||||
		r[i] = Stackframe{Current: loc, Call: loc}
 | 
			
		||||
	}
 | 
			
		||||
	r[len(r)-1].Bottom = pcsVar.Len == int64(len(pcsVar.Children))
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Returns the list of saved return addresses used by stack barriers
 | 
			
		||||
func (g *G) stkbar() ([]savedLR, error) {
 | 
			
		||||
	if g.stkbarVar == nil { // stack barriers were removed in Go 1.9
 | 
			
		||||
		return nil, nil
 | 
			
		||||
	}
 | 
			
		||||
	g.stkbarVar.loadValue(LoadConfig{false, 1, 0, int(g.stkbarVar.Len), 3})
 | 
			
		||||
	g.stkbarVar.loadValue(LoadConfig{false, 1, 0, int(g.stkbarVar.Len), 3, 0})
 | 
			
		||||
	if g.stkbarVar.Unreadable != nil {
 | 
			
		||||
		return nil, fmt.Errorf("unreadable stkbar: %v", g.stkbarVar.Unreadable)
 | 
			
		||||
	}
 | 
			
		||||
@@ -658,6 +785,7 @@ func (scope *EvalScope) LocalVariables(cfg LoadConfig) ([]*Variable, error) {
 | 
			
		||||
	vars = filterVariables(vars, func(v *Variable) bool {
 | 
			
		||||
		return (v.Flags & (VariableArgument | VariableReturnArgument)) == 0
 | 
			
		||||
	})
 | 
			
		||||
	cfg.MaxMapBuckets = maxMapBucketsFactor * cfg.MaxArrayValues
 | 
			
		||||
	loadValues(vars, cfg)
 | 
			
		||||
	return vars, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -671,6 +799,7 @@ func (scope *EvalScope) FunctionArguments(cfg LoadConfig) ([]*Variable, error) {
 | 
			
		||||
	vars = filterVariables(vars, func(v *Variable) bool {
 | 
			
		||||
		return (v.Flags & (VariableArgument | VariableReturnArgument)) != 0
 | 
			
		||||
	})
 | 
			
		||||
	cfg.MaxMapBuckets = maxMapBucketsFactor * cfg.MaxArrayValues
 | 
			
		||||
	loadValues(vars, cfg)
 | 
			
		||||
	return vars, nil
 | 
			
		||||
}
 | 
			
		||||
@@ -875,7 +1004,11 @@ func (scope *EvalScope) extractVarInfoFromEntry(varEntry *dwarf.Entry) (*Variabl
 | 
			
		||||
	mem := scope.Mem
 | 
			
		||||
	if pieces != nil {
 | 
			
		||||
		addr = fakeAddress
 | 
			
		||||
		mem = newCompositeMemory(scope.Mem, scope.Regs, pieces)
 | 
			
		||||
		var cmem *compositeMemory
 | 
			
		||||
		cmem, err = newCompositeMemory(scope.Mem, scope.Regs, pieces)
 | 
			
		||||
		if cmem != nil {
 | 
			
		||||
			mem = cmem
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	v := scope.newVariable(n, uintptr(addr), t, mem)
 | 
			
		||||
@@ -895,6 +1028,10 @@ func (v *Variable) maybeDereference() *Variable {
 | 
			
		||||
 | 
			
		||||
	switch t := v.RealType.(type) {
 | 
			
		||||
	case *godwarf.PtrType:
 | 
			
		||||
		if v.Addr == 0 && len(v.Children) == 1 && v.loaded {
 | 
			
		||||
			// fake pointer variable constructed by casting an integer to a pointer type
 | 
			
		||||
			return &v.Children[0]
 | 
			
		||||
		}
 | 
			
		||||
		ptrval, err := readUintRaw(v.mem, uintptr(v.Addr), t.ByteSize)
 | 
			
		||||
		r := v.newVariable("", uintptr(ptrval), t.Type, DereferenceMemory(v.mem))
 | 
			
		||||
		if err != nil {
 | 
			
		||||
@@ -1569,6 +1706,11 @@ func (v *Variable) loadMap(recurseLevel int, cfg LoadConfig) {
 | 
			
		||||
	if it == nil {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
	it.maxNumBuckets = uint64(cfg.MaxMapBuckets)
 | 
			
		||||
 | 
			
		||||
	if v.Len == 0 || int64(v.mapSkip) >= v.Len || cfg.MaxArrayValues == 0 {
 | 
			
		||||
		return
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	for skip := 0; skip < v.mapSkip; skip++ {
 | 
			
		||||
		if ok := it.next(); !ok {
 | 
			
		||||
@@ -1580,9 +1722,6 @@ func (v *Variable) loadMap(recurseLevel int, cfg LoadConfig) {
 | 
			
		||||
	count := 0
 | 
			
		||||
	errcount := 0
 | 
			
		||||
	for it.next() {
 | 
			
		||||
		if count >= cfg.MaxArrayValues {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		key := it.key()
 | 
			
		||||
		var val *Variable
 | 
			
		||||
		if it.values.fieldType.Size() > 0 {
 | 
			
		||||
@@ -1601,6 +1740,9 @@ func (v *Variable) loadMap(recurseLevel int, cfg LoadConfig) {
 | 
			
		||||
		if errcount > maxErrCount {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
		if count >= cfg.MaxArrayValues || int64(count) >= v.Len {
 | 
			
		||||
			break
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1618,6 +1760,8 @@ type mapIterator struct {
 | 
			
		||||
	values    *Variable
 | 
			
		||||
	overflow  *Variable
 | 
			
		||||
 | 
			
		||||
	maxNumBuckets uint64 // maximum number of buckets to scan
 | 
			
		||||
 | 
			
		||||
	idx int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1683,6 +1827,10 @@ func (it *mapIterator) nextBucket() bool {
 | 
			
		||||
	} else {
 | 
			
		||||
		it.b = nil
 | 
			
		||||
 | 
			
		||||
		if it.maxNumBuckets > 0 && it.bidx >= it.maxNumBuckets {
 | 
			
		||||
			return false
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for it.bidx < it.numbuckets {
 | 
			
		||||
			it.b = it.buckets.clone()
 | 
			
		||||
			it.b.Addr += uintptr(uint64(it.buckets.DwarfType.Size()) * it.bidx)
 | 
			
		||||
@@ -1,4 +1,4 @@
 | 
			
		||||
package native
 | 
			
		||||
package winutil
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"fmt"
 | 
			
		||||
@@ -6,11 +6,11 @@ import (
 | 
			
		||||
 | 
			
		||||
	"golang.org/x/arch/x86/x86asm"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// Regs represents CPU registers on an AMD64 processor.
 | 
			
		||||
type Regs struct {
 | 
			
		||||
// AMD64Registers represents CPU registers on an AMD64 processor.
 | 
			
		||||
type AMD64Registers struct {
 | 
			
		||||
	rax     uint64
 | 
			
		||||
	rbx     uint64
 | 
			
		||||
	rcx     uint64
 | 
			
		||||
@@ -33,11 +33,48 @@ type Regs struct {
 | 
			
		||||
	fs      uint64
 | 
			
		||||
	gs      uint64
 | 
			
		||||
	tls     uint64
 | 
			
		||||
	context *_CONTEXT
 | 
			
		||||
	fltSave *_XMM_SAVE_AREA32
 | 
			
		||||
	Context *CONTEXT
 | 
			
		||||
	fltSave *XMM_SAVE_AREA32
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) Slice() []proc.Register {
 | 
			
		||||
// NewAMD64Registers creates a new AMD64Registers struct from a CONTEXT
 | 
			
		||||
// struct and the TEB base address of the thread.
 | 
			
		||||
func NewAMD64Registers(context *CONTEXT, TebBaseAddress uint64, floatingPoint bool) *AMD64Registers {
 | 
			
		||||
	regs := &AMD64Registers{
 | 
			
		||||
		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:    TebBaseAddress,
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if floatingPoint {
 | 
			
		||||
		regs.fltSave = &context.FltSave
 | 
			
		||||
	}
 | 
			
		||||
	regs.Context = context
 | 
			
		||||
 | 
			
		||||
	return regs
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// Slice returns the registers as a list of (name, value) pairs.
 | 
			
		||||
func (r *AMD64Registers) Slice(floatingPoint bool) []proc.Register {
 | 
			
		||||
	var regs = []struct {
 | 
			
		||||
		k string
 | 
			
		||||
		v uint64
 | 
			
		||||
@@ -66,7 +103,7 @@ func (r *Regs) Slice() []proc.Register {
 | 
			
		||||
		{"TLS", r.tls},
 | 
			
		||||
	}
 | 
			
		||||
	outlen := len(regs)
 | 
			
		||||
	if r.fltSave != nil {
 | 
			
		||||
	if r.fltSave != nil && floatingPoint {
 | 
			
		||||
		outlen += 6 + 8 + 2 + 16
 | 
			
		||||
	}
 | 
			
		||||
	out := make([]proc.Register, 0, outlen)
 | 
			
		||||
@@ -77,7 +114,7 @@ func (r *Regs) Slice() []proc.Register {
 | 
			
		||||
			out = proc.AppendQwordReg(out, reg.k, reg.v)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if r.fltSave != nil {
 | 
			
		||||
	if r.fltSave != nil && floatingPoint {
 | 
			
		||||
		out = proc.AppendWordReg(out, "CW", r.fltSave.ControlWord)
 | 
			
		||||
		out = proc.AppendWordReg(out, "SW", r.fltSave.StatusWord)
 | 
			
		||||
		out = proc.AppendWordReg(out, "TW", uint16(r.fltSave.TagWord))
 | 
			
		||||
@@ -101,81 +138,40 @@ func (r *Regs) Slice() []proc.Register {
 | 
			
		||||
 | 
			
		||||
// PC returns the current program counter
 | 
			
		||||
// i.e. the RIP CPU register.
 | 
			
		||||
func (r *Regs) PC() uint64 {
 | 
			
		||||
func (r *AMD64Registers) PC() uint64 {
 | 
			
		||||
	return r.rip
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SP returns the stack pointer location,
 | 
			
		||||
// i.e. the RSP register.
 | 
			
		||||
func (r *Regs) SP() uint64 {
 | 
			
		||||
func (r *AMD64Registers) SP() uint64 {
 | 
			
		||||
	return r.rsp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) BP() uint64 {
 | 
			
		||||
func (r *AMD64Registers) BP() uint64 {
 | 
			
		||||
	return r.rbp
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CX returns the value of the RCX register.
 | 
			
		||||
func (r *Regs) CX() uint64 {
 | 
			
		||||
func (r *AMD64Registers) 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 {
 | 
			
		||||
func (r *AMD64Registers) TLS() uint64 {
 | 
			
		||||
	return r.tls
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) GAddr() (uint64, bool) {
 | 
			
		||||
// GAddr returns the address of the G variable if it is known, 0 and false
 | 
			
		||||
// otherwise.
 | 
			
		||||
func (r *AMD64Registers) GAddr() (uint64, bool) {
 | 
			
		||||
	return 0, false
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetPC sets the RIP register to the value specified by `pc`.
 | 
			
		||||
func (thread *Thread) SetPC(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)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// SetSP sets the RSP register to the value specified by `sp`.
 | 
			
		||||
func (thread *Thread) SetSP(sp uint64) error {
 | 
			
		||||
	context := newCONTEXT()
 | 
			
		||||
	context.ContextFlags = _CONTEXT_ALL
 | 
			
		||||
 | 
			
		||||
	err := _GetThreadContext(thread.os.hThread, context)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	context.Rsp = sp
 | 
			
		||||
 | 
			
		||||
	return _SetThreadContext(thread.os.hThread, context)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (thread *Thread) SetDX(dx uint64) error {
 | 
			
		||||
	context := newCONTEXT()
 | 
			
		||||
	context.ContextFlags = _CONTEXT_ALL
 | 
			
		||||
 | 
			
		||||
	err := _GetThreadContext(thread.os.hThread, context)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	context.Rdx = dx
 | 
			
		||||
 | 
			
		||||
	return _SetThreadContext(thread.os.hThread, context)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) Get(n int) (uint64, error) {
 | 
			
		||||
// Get returns the value of the n-th register (in x86asm order).
 | 
			
		||||
func (r *AMD64Registers) Get(n int) (uint64, error) {
 | 
			
		||||
	reg := x86asm.Reg(n)
 | 
			
		||||
	const (
 | 
			
		||||
		mask8  = 0x000f
 | 
			
		||||
@@ -332,59 +328,103 @@ func (r *Regs) Get(n int) (uint64, error) {
 | 
			
		||||
	return 0, proc.ErrUnknownRegister
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func registers(thread *Thread, floatingPoint bool) (proc.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
 | 
			
		||||
	}
 | 
			
		||||
	regs.context = context
 | 
			
		||||
 | 
			
		||||
	return regs, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (r *Regs) Copy() proc.Registers {
 | 
			
		||||
	var rr Regs
 | 
			
		||||
// Copy returns a copy of these registers that is guarenteed not to change.
 | 
			
		||||
func (r *AMD64Registers) Copy() proc.Registers {
 | 
			
		||||
	var rr AMD64Registers
 | 
			
		||||
	rr = *r
 | 
			
		||||
	rr.context = newCONTEXT()
 | 
			
		||||
	*(rr.context) = *(r.context)
 | 
			
		||||
	rr.fltSave = &rr.context.FltSave
 | 
			
		||||
	rr.Context = NewCONTEXT()
 | 
			
		||||
	*(rr.Context) = *(r.Context)
 | 
			
		||||
	rr.fltSave = &rr.Context.FltSave
 | 
			
		||||
	return &rr
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// M128A tracks the _M128A windows struct.
 | 
			
		||||
type M128A struct {
 | 
			
		||||
	Low  uint64
 | 
			
		||||
	High int64
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// XMM_SAVE_AREA32 tracks the _XMM_SAVE_AREA32 windows struct.
 | 
			
		||||
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
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
// CONTEXT tracks the _CONTEXT of windows.
 | 
			
		||||
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))
 | 
			
		||||
}
 | 
			
		||||
@@ -20,9 +20,9 @@ import (
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	"github.com/cosiner/argv"
 | 
			
		||||
	"github.com/derekparker/delve/service"
 | 
			
		||||
	"github.com/derekparker/delve/service/api"
 | 
			
		||||
	"github.com/derekparker/delve/service/debugger"
 | 
			
		||||
	"github.com/go-delve/delve/service"
 | 
			
		||||
	"github.com/go-delve/delve/service/api"
 | 
			
		||||
	"github.com/go-delve/delve/service/debugger"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const optimizedFunctionWarning = "Warning: debugging optimized function"
 | 
			
		||||
@@ -32,6 +32,7 @@ type cmdPrefix int
 | 
			
		||||
const (
 | 
			
		||||
	noPrefix = cmdPrefix(0)
 | 
			
		||||
	onPrefix = cmdPrefix(1 << iota)
 | 
			
		||||
	deferredPrefix
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type callContext struct {
 | 
			
		||||
@@ -113,24 +114,24 @@ Type "help" followed by the name of a command for more information about it.`},
 | 
			
		||||
 | 
			
		||||
	break [name] <linespec>
 | 
			
		||||
 | 
			
		||||
See $GOPATH/src/github.com/derekparker/delve/Documentation/cli/locspec.md for the syntax of linespec.
 | 
			
		||||
See $GOPATH/src/github.com/go-delve/delve/Documentation/cli/locspec.md for the syntax of linespec.
 | 
			
		||||
 | 
			
		||||
See also: "help on", "help cond" and "help clear"`},
 | 
			
		||||
		{aliases: []string{"trace", "t"}, cmdFn: tracepoint, helpMsg: `Set tracepoint.
 | 
			
		||||
 | 
			
		||||
	trace [name] <linespec>
 | 
			
		||||
 | 
			
		||||
A tracepoint is a breakpoint that does not stop the execution of the program, instead when the tracepoint is hit a notification is displayed. See $GOPATH/src/github.com/derekparker/delve/Documentation/cli/locspec.md for the syntax of linespec.
 | 
			
		||||
A tracepoint is a breakpoint that does not stop the execution of the program, instead when the tracepoint is hit a notification is displayed. See $GOPATH/src/github.com/go-delve/delve/Documentation/cli/locspec.md for the syntax of linespec.
 | 
			
		||||
 | 
			
		||||
See also: "help on", "help cond" and "help clear"`},
 | 
			
		||||
		{aliases: []string{"restart", "r"}, cmdFn: restart, helpMsg: `Restart process.
 | 
			
		||||
 | 
			
		||||
  restart [checkpoint]
 | 
			
		||||
  restart [-noargs] newargv...
 | 
			
		||||
	restart [checkpoint]
 | 
			
		||||
	restart [-noargs] newargv...
 | 
			
		||||
 | 
			
		||||
  For recorded processes restarts from the start or from the specified
 | 
			
		||||
  checkpoint.  For normal processes restarts the process, optionally changing
 | 
			
		||||
  the arguments.  With -noargs, the process starts with an empty commandline.
 | 
			
		||||
For recorded processes restarts from the start or from the specified
 | 
			
		||||
checkpoint.  For normal processes restarts the process, optionally changing
 | 
			
		||||
the arguments.  With -noargs, the process starts with an empty commandline.
 | 
			
		||||
`},
 | 
			
		||||
		{aliases: []string{"continue", "c"}, cmdFn: c.cont, helpMsg: "Run until breakpoint or program termination."},
 | 
			
		||||
		{aliases: []string{"step", "s"}, cmdFn: c.step, helpMsg: "Single step through program."},
 | 
			
		||||
@@ -139,6 +140,8 @@ See also: "help on", "help cond" and "help clear"`},
 | 
			
		||||
		{aliases: []string{"stepout"}, cmdFn: c.stepout, helpMsg: "Step out of the current function."},
 | 
			
		||||
		{aliases: []string{"call"}, cmdFn: c.call, helpMsg: `Resumes process, injecting a function call (EXPERIMENTAL!!!)
 | 
			
		||||
	
 | 
			
		||||
	call [-unsafe] <function call expression>
 | 
			
		||||
	
 | 
			
		||||
Current limitations:
 | 
			
		||||
- only pointers to stack-allocated objects can be passed as argument.
 | 
			
		||||
- only some automatic type conversions are supported.
 | 
			
		||||
@@ -186,11 +189,11 @@ Called without arguments it will show information about the current goroutine.
 | 
			
		||||
Called with a single argument it will switch to the specified goroutine.
 | 
			
		||||
Called with more arguments it will execute a command on the specified goroutine.`},
 | 
			
		||||
		{aliases: []string{"breakpoints", "bp"}, cmdFn: breakpoints, helpMsg: "Print out info for active breakpoints."},
 | 
			
		||||
		{aliases: []string{"print", "p"}, allowedPrefixes: onPrefix, cmdFn: printVar, helpMsg: `Evaluate an expression.
 | 
			
		||||
		{aliases: []string{"print", "p"}, allowedPrefixes: onPrefix | deferredPrefix, cmdFn: printVar, helpMsg: `Evaluate an expression.
 | 
			
		||||
 | 
			
		||||
	[goroutine <n>] [frame <m>] print <expression>
 | 
			
		||||
 | 
			
		||||
See $GOPATH/src/github.com/derekparker/delve/Documentation/cli/expr.md for a description of supported expressions.`},
 | 
			
		||||
See $GOPATH/src/github.com/go-delve/delve/Documentation/cli/expr.md for a description of supported expressions.`},
 | 
			
		||||
		{aliases: []string{"whatis"}, cmdFn: whatisCommand, helpMsg: `Prints type of an expression.
 | 
			
		||||
 | 
			
		||||
	whatis <expression>`},
 | 
			
		||||
@@ -198,7 +201,7 @@ See $GOPATH/src/github.com/derekparker/delve/Documentation/cli/expr.md for a des
 | 
			
		||||
 | 
			
		||||
	[goroutine <n>] [frame <m>] set <variable> = <value>
 | 
			
		||||
 | 
			
		||||
See $GOPATH/src/github.com/derekparker/delve/Documentation/cli/expr.md for a description of supported expressions. Only numerical variables and pointers can be changed.`},
 | 
			
		||||
See $GOPATH/src/github.com/go-delve/delve/Documentation/cli/expr.md for a description of supported expressions. Only numerical variables and pointers can be changed.`},
 | 
			
		||||
		{aliases: []string{"sources"}, cmdFn: sources, helpMsg: `Print list of source files.
 | 
			
		||||
 | 
			
		||||
	sources [<regex>]
 | 
			
		||||
@@ -214,12 +217,12 @@ If regex is specified only the functions matching it will be returned.`},
 | 
			
		||||
	types [<regex>]
 | 
			
		||||
 | 
			
		||||
If regex is specified only the types matching it will be returned.`},
 | 
			
		||||
		{aliases: []string{"args"}, allowedPrefixes: onPrefix, cmdFn: args, helpMsg: `Print function arguments.
 | 
			
		||||
		{aliases: []string{"args"}, allowedPrefixes: onPrefix | deferredPrefix, cmdFn: args, helpMsg: `Print function arguments.
 | 
			
		||||
 | 
			
		||||
	[goroutine <n>] [frame <m>] args [-v] [<regex>]
 | 
			
		||||
 | 
			
		||||
If regex is specified only function arguments with a name matching it will be returned. If -v is specified more information about each function argument will be shown.`},
 | 
			
		||||
		{aliases: []string{"locals"}, allowedPrefixes: onPrefix, cmdFn: locals, helpMsg: `Print local variables.
 | 
			
		||||
		{aliases: []string{"locals"}, allowedPrefixes: onPrefix | deferredPrefix, cmdFn: locals, helpMsg: `Print local variables.
 | 
			
		||||
 | 
			
		||||
	[goroutine <n>] [frame <m>] locals [-v] [<regex>]
 | 
			
		||||
 | 
			
		||||
@@ -248,10 +251,13 @@ When connected to a headless instance started with the --accept-multiclient, pas
 | 
			
		||||
Show source around current point or provided linespec.`},
 | 
			
		||||
		{aliases: []string{"stack", "bt"}, allowedPrefixes: onPrefix, cmdFn: stackCommand, helpMsg: `Print stack trace.
 | 
			
		||||
 | 
			
		||||
	[goroutine <n>] [frame <m>] stack [<depth>] [-full] [-g] [-s] [-offsets]
 | 
			
		||||
	[goroutine <n>] [frame <m>] stack [<depth>] [-full] [-offsets] [-defer] [-a <n>] [-adepth <depth>]
 | 
			
		||||
 | 
			
		||||
	-full		every stackframe is decorated with the value of its local variables and arguments.
 | 
			
		||||
	-offsets	prints frame offset of each frame
 | 
			
		||||
	-offsets	prints frame offset of each frame.
 | 
			
		||||
	-defer		prints deferred function call stack for each frame.
 | 
			
		||||
	-a <n>		prints stacktrace of n ancestors of the selected goroutine (target process must have tracebackancestors enabled)
 | 
			
		||||
	-adepth <depth>	configures depth of ancestor stacktrace
 | 
			
		||||
`},
 | 
			
		||||
		{aliases: []string{"frame"},
 | 
			
		||||
			cmdFn: func(t *Term, ctx callContext, arg string) error {
 | 
			
		||||
@@ -259,8 +265,8 @@ Show source around current point or provided linespec.`},
 | 
			
		||||
			},
 | 
			
		||||
			helpMsg: `Set the current frame, or execute command on a different frame.
 | 
			
		||||
 | 
			
		||||
  frame <m>
 | 
			
		||||
  frame <m> <command>
 | 
			
		||||
	frame <m>
 | 
			
		||||
	frame <m> <command>
 | 
			
		||||
 | 
			
		||||
The first form sets frame used by subsequent commands such as "print" or "set".
 | 
			
		||||
The second form runs the command on the given frame.`},
 | 
			
		||||
@@ -270,8 +276,8 @@ The second form runs the command on the given frame.`},
 | 
			
		||||
			},
 | 
			
		||||
			helpMsg: `Move the current frame up.
 | 
			
		||||
 | 
			
		||||
  up [<m>]
 | 
			
		||||
  up [<m>] <command>
 | 
			
		||||
	up [<m>]
 | 
			
		||||
	up [<m>] <command>
 | 
			
		||||
 | 
			
		||||
Move the current frame up by <m>. The second form runs the command on the given frame.`},
 | 
			
		||||
		{aliases: []string{"down"},
 | 
			
		||||
@@ -280,10 +286,15 @@ Move the current frame up by <m>. The second form runs the command on the given
 | 
			
		||||
			},
 | 
			
		||||
			helpMsg: `Move the current frame down.
 | 
			
		||||
 | 
			
		||||
  down [<m>]
 | 
			
		||||
  down [<m>] <command>
 | 
			
		||||
	down [<m>]
 | 
			
		||||
	down [<m>] <command>
 | 
			
		||||
 | 
			
		||||
Move the current frame down by <m>. The second form runs the command on the given frame.`},
 | 
			
		||||
		{aliases: []string{"deferred"}, cmdFn: c.deferredCommand, helpMsg: `Executes command in the context of a deferred call.
 | 
			
		||||
 | 
			
		||||
	deferred <n> <command>
 | 
			
		||||
 | 
			
		||||
Executes the specified command (print, args, locals) in the context of the n-th deferred call in the current frame.`},
 | 
			
		||||
		{aliases: []string{"source"}, cmdFn: c.sourceCommand, helpMsg: `Executes a file containing a list of delve commands
 | 
			
		||||
 | 
			
		||||
	source <path>`},
 | 
			
		||||
@@ -334,6 +345,7 @@ Defines <alias> as an alias to <command> or removes an alias.`},
 | 
			
		||||
	edit [locspec]
 | 
			
		||||
	
 | 
			
		||||
If locspec is omitted edit will open the current source file in the editor, otherwise it will open the specified location.`},
 | 
			
		||||
		{aliases: []string{"libraries"}, cmdFn: libraries, helpMsg: `List loaded dynamic libraries`},
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if client == nil || client.Recorded() {
 | 
			
		||||
@@ -347,7 +359,9 @@ If locspec is omitted edit will open the current source file in the editor, othe
 | 
			
		||||
			cmdFn:   checkpoint,
 | 
			
		||||
			helpMsg: `Creates a checkpoint at the current position.
 | 
			
		||||
 | 
			
		||||
	checkpoint [where]`,
 | 
			
		||||
	checkpoint [note]
 | 
			
		||||
 | 
			
		||||
The "note" is arbitrary text that can be used to identify the checkpoint, if it is not specified it defaults to the current filename:line position.`,
 | 
			
		||||
		})
 | 
			
		||||
		c.cmds = append(c.cmds, command{
 | 
			
		||||
			aliases: []string{"checkpoints"},
 | 
			
		||||
@@ -426,7 +440,7 @@ func (c *Commands) CallWithContext(cmdstr string, t *Term, ctx callContext) erro
 | 
			
		||||
 | 
			
		||||
// Call takes a command to execute.
 | 
			
		||||
func (c *Commands) Call(cmdstr string, t *Term) error {
 | 
			
		||||
	ctx := callContext{Prefix: noPrefix, Scope: api.EvalScope{GoroutineID: -1, Frame: c.frame}}
 | 
			
		||||
	ctx := callContext{Prefix: noPrefix, Scope: api.EvalScope{GoroutineID: -1, Frame: c.frame, DeferredCall: 0}}
 | 
			
		||||
	return c.CallWithContext(cmdstr, t, ctx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -559,6 +573,27 @@ func (a byGoroutineID) Len() int           { return len(a) }
 | 
			
		||||
func (a byGoroutineID) Swap(i, j int)      { a[i], a[j] = a[j], a[i] }
 | 
			
		||||
func (a byGoroutineID) Less(i, j int) bool { return a[i].ID < a[j].ID }
 | 
			
		||||
 | 
			
		||||
// The number of goroutines we're going to request on each RPC call
 | 
			
		||||
const goroutineBatchSize = 10000
 | 
			
		||||
 | 
			
		||||
func printGoroutines(t *Term, gs []*api.Goroutine, fgl formatGoroutineLoc, bPrintStack bool, state *api.DebuggerState) error {
 | 
			
		||||
	for _, g := range gs {
 | 
			
		||||
		prefix := "  "
 | 
			
		||||
		if state.SelectedGoroutine != nil && g.ID == state.SelectedGoroutine.ID {
 | 
			
		||||
			prefix = "* "
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Printf("%sGoroutine %s\n", prefix, formatGoroutine(g, fgl))
 | 
			
		||||
		if bPrintStack {
 | 
			
		||||
			stack, err := t.client.Stacktrace(g.ID, 10, false, nil)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			printStack(stack, "\t", false)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func goroutines(t *Term, ctx callContext, argstr string) error {
 | 
			
		||||
	args := strings.Split(argstr, " ")
 | 
			
		||||
	var fgl = fglUserCurrent
 | 
			
		||||
@@ -593,26 +628,24 @@ func goroutines(t *Term, ctx callContext, argstr string) error {
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	gs, err := t.client.ListGoroutines()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	sort.Sort(byGoroutineID(gs))
 | 
			
		||||
	fmt.Printf("[%d goroutines]\n", len(gs))
 | 
			
		||||
	for _, g := range gs {
 | 
			
		||||
		prefix := "  "
 | 
			
		||||
		if state.SelectedGoroutine != nil && g.ID == state.SelectedGoroutine.ID {
 | 
			
		||||
			prefix = "* "
 | 
			
		||||
	var (
 | 
			
		||||
		start = 0
 | 
			
		||||
		gslen = 0
 | 
			
		||||
		gs    []*api.Goroutine
 | 
			
		||||
	)
 | 
			
		||||
	for start >= 0 {
 | 
			
		||||
		gs, start, err = t.client.ListGoroutines(start, goroutineBatchSize)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		fmt.Printf("%sGoroutine %s\n", prefix, formatGoroutine(g, fgl))
 | 
			
		||||
		if bPrintStack {
 | 
			
		||||
			stack, err := t.client.Stacktrace(g.ID, 10, false, nil)
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			printStack(stack, "\t", false)
 | 
			
		||||
		sort.Sort(byGoroutineID(gs))
 | 
			
		||||
		err = printGoroutines(t, gs, fgl, bPrintStack, state)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		gslen += len(gs)
 | 
			
		||||
	}
 | 
			
		||||
	fmt.Printf("[%d goroutines]\n", gslen)
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -714,6 +747,22 @@ func (c *Commands) frameCommand(t *Term, ctx callContext, argstr string, directi
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (c *Commands) deferredCommand(t *Term, ctx callContext, argstr string) error {
 | 
			
		||||
	ctx.Prefix = deferredPrefix
 | 
			
		||||
 | 
			
		||||
	space := strings.Index(argstr, " ")
 | 
			
		||||
 | 
			
		||||
	var err error
 | 
			
		||||
	ctx.Scope.DeferredCall, err = strconv.Atoi(argstr[:space])
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	if ctx.Scope.DeferredCall <= 0 {
 | 
			
		||||
		return errors.New("argument of deferred must be a number greater than 0 (use 'stack -defer' to see the list of deferred calls)")
 | 
			
		||||
	}
 | 
			
		||||
	return c.CallWithContext(argstr[space:], t, ctx)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func printscope(t *Term) error {
 | 
			
		||||
	state, err := t.client.GetState()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
@@ -980,7 +1029,13 @@ func (c *Commands) call(t *Term, ctx callContext, args string) error {
 | 
			
		||||
	if err := scopePrefixSwitch(t, ctx); err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	state, err := exitedToError(t.client.Call(args))
 | 
			
		||||
	const unsafePrefix = "-unsafe "
 | 
			
		||||
	unsafe := false
 | 
			
		||||
	if strings.HasPrefix(args, unsafePrefix) {
 | 
			
		||||
		unsafe = true
 | 
			
		||||
		args = args[len(unsafePrefix):]
 | 
			
		||||
	}
 | 
			
		||||
	state, err := exitedToError(t.client.Call(args, unsafe))
 | 
			
		||||
	c.frame = 0
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		printcontextNoState(t)
 | 
			
		||||
@@ -1164,6 +1219,9 @@ func edit(t *Term, ctx callContext, args string) error {
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	cmd := exec.Command(editor, fmt.Sprintf("+%d", lineno), file)
 | 
			
		||||
	cmd.Stdin = os.Stdin
 | 
			
		||||
	cmd.Stdout = os.Stdout
 | 
			
		||||
	cmd.Stderr = os.Stderr
 | 
			
		||||
	return cmd.Run()
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1356,6 +1414,20 @@ func stackCommand(t *Term, ctx callContext, args string) error {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	printStack(stack, "", sa.offsets)
 | 
			
		||||
	if sa.ancestors > 0 {
 | 
			
		||||
		ancestors, err := t.client.Ancestors(ctx.Scope.GoroutineID, sa.ancestors, sa.ancestorDepth)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			return err
 | 
			
		||||
		}
 | 
			
		||||
		for _, ancestor := range ancestors {
 | 
			
		||||
			fmt.Printf("Created by Goroutine %d:\n", ancestor.ID)
 | 
			
		||||
			if ancestor.Unreadable != "" {
 | 
			
		||||
				fmt.Printf("\t%s\n", ancestor.Unreadable)
 | 
			
		||||
				continue
 | 
			
		||||
			}
 | 
			
		||||
			printStack(ancestor.Stack, "\t", false)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1364,6 +1436,9 @@ type stackArgs struct {
 | 
			
		||||
	full       bool
 | 
			
		||||
	offsets    bool
 | 
			
		||||
	readDefers bool
 | 
			
		||||
 | 
			
		||||
	ancestors     int
 | 
			
		||||
	ancestorDepth int
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func parseStackArgs(argstr string) (stackArgs, error) {
 | 
			
		||||
@@ -1373,7 +1448,18 @@ func parseStackArgs(argstr string) (stackArgs, error) {
 | 
			
		||||
	}
 | 
			
		||||
	if argstr != "" {
 | 
			
		||||
		args := strings.Split(argstr, " ")
 | 
			
		||||
		for i := range args {
 | 
			
		||||
		for i := 0; i < len(args); i++ {
 | 
			
		||||
			numarg := func(name string) (int, error) {
 | 
			
		||||
				if i >= len(args) {
 | 
			
		||||
					return 0, fmt.Errorf("expected number after %s", name)
 | 
			
		||||
				}
 | 
			
		||||
				n, err := strconv.Atoi(args[i])
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return 0, fmt.Errorf("expected number after %s: %v", name, err)
 | 
			
		||||
				}
 | 
			
		||||
				return n, nil
 | 
			
		||||
 | 
			
		||||
			}
 | 
			
		||||
			switch args[i] {
 | 
			
		||||
			case "-full":
 | 
			
		||||
				r.full = true
 | 
			
		||||
@@ -1381,6 +1467,20 @@ func parseStackArgs(argstr string) (stackArgs, error) {
 | 
			
		||||
				r.offsets = true
 | 
			
		||||
			case "-defer":
 | 
			
		||||
				r.readDefers = true
 | 
			
		||||
			case "-a":
 | 
			
		||||
				i++
 | 
			
		||||
				n, err := numarg("-a")
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return stackArgs{}, err
 | 
			
		||||
				}
 | 
			
		||||
				r.ancestors = n
 | 
			
		||||
			case "-adepth":
 | 
			
		||||
				i++
 | 
			
		||||
				n, err := numarg("-adepth")
 | 
			
		||||
				if err != nil {
 | 
			
		||||
					return stackArgs{}, err
 | 
			
		||||
				}
 | 
			
		||||
				r.ancestorDepth = n
 | 
			
		||||
			default:
 | 
			
		||||
				n, err := strconv.Atoi(args[i])
 | 
			
		||||
				if err != nil {
 | 
			
		||||
@@ -1390,6 +1490,9 @@ func parseStackArgs(argstr string) (stackArgs, error) {
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	if r.ancestors > 0 && r.ancestorDepth == 0 {
 | 
			
		||||
		r.ancestorDepth = r.depth
 | 
			
		||||
	}
 | 
			
		||||
	return r, nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -1526,6 +1629,18 @@ func disassCommand(t *Term, ctx callContext, args string) error {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func libraries(t *Term, ctx callContext, args string) error {
 | 
			
		||||
	libs, err := t.client.ListDynamicLibraries()
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		return err
 | 
			
		||||
	}
 | 
			
		||||
	d := digits(len(libs))
 | 
			
		||||
	for i := range libs {
 | 
			
		||||
		fmt.Printf("%"+strconv.Itoa(d)+"d. %s\n", i, libs[i].Path)
 | 
			
		||||
	}
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func digits(n int) int {
 | 
			
		||||
	if n <= 0 {
 | 
			
		||||
		return 1
 | 
			
		||||
@@ -1565,7 +1680,7 @@ func printStack(stack []api.Stackframe, ind string, offsets bool) {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		for j, d := range stack[i].Defers {
 | 
			
		||||
			deferHeader := fmt.Sprintf("%s    defer %d: ", s, j)
 | 
			
		||||
			deferHeader := fmt.Sprintf("%s    defer %d: ", s, j+1)
 | 
			
		||||
			s2 := strings.Repeat(" ", len(deferHeader))
 | 
			
		||||
			if d.Unreadable != "" {
 | 
			
		||||
				fmt.Printf("%s(unreadable defer: %s)\n", deferHeader, d.Unreadable)
 | 
			
		||||
@@ -1669,7 +1784,14 @@ func printcontextThread(t *Term, th *api.Thread) {
 | 
			
		||||
	if th.BreakpointInfo != nil && th.Breakpoint.LoadArgs != nil && *th.Breakpoint.LoadArgs == ShortLoadConfig {
 | 
			
		||||
		var arg []string
 | 
			
		||||
		for _, ar := range th.BreakpointInfo.Arguments {
 | 
			
		||||
			arg = append(arg, ar.SinglelineString())
 | 
			
		||||
			// For AI compatibility return values are included in the
 | 
			
		||||
			// argument list. This is a relic of the dark ages when the
 | 
			
		||||
			// Go debug information did not distinguish between the two.
 | 
			
		||||
			// Filter them out here instead, so during trace operations
 | 
			
		||||
			// they are not printed as an argument.
 | 
			
		||||
			if (ar.Flags & api.VariableArgument) != 0 {
 | 
			
		||||
				arg = append(arg, ar.SinglelineString())
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
		args = strings.Join(arg, ", ")
 | 
			
		||||
	}
 | 
			
		||||
@@ -1874,6 +1996,9 @@ func (c *Commands) executeFile(t *Term, name string) error {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		if err := c.Call(line, t); err != nil {
 | 
			
		||||
			if _, isExitRequest := err.(ExitRequestError); isExitRequest {
 | 
			
		||||
				return err
 | 
			
		||||
			}
 | 
			
		||||
			fmt.Printf("%s:%d: %v\n", name, lineno, err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -1923,7 +2048,7 @@ func checkpoints(t *Term, ctx callContext, args string) error {
 | 
			
		||||
	}
 | 
			
		||||
	w := new(tabwriter.Writer)
 | 
			
		||||
	w.Init(os.Stdout, 4, 4, 2, ' ', 0)
 | 
			
		||||
	fmt.Fprintln(w, "ID\tWhen\tWhere")
 | 
			
		||||
	fmt.Fprintln(w, "ID\tWhen\tNote")
 | 
			
		||||
	for _, cp := range cps {
 | 
			
		||||
		fmt.Fprintf(w, "c%d\t%s\t%s\n", cp.ID, cp.When, cp.Where)
 | 
			
		||||
	}
 | 
			
		||||
@@ -8,7 +8,7 @@ import (
 | 
			
		||||
	"strings"
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/config"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/config"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func configureCmd(t *Term, ctx callContext, args string) error {
 | 
			
		||||
@@ -7,7 +7,7 @@ import (
 | 
			
		||||
	"path/filepath"
 | 
			
		||||
	"text/tabwriter"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/service/api"
 | 
			
		||||
	"github.com/go-delve/delve/service/api"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func DisasmPrint(dv api.AsmInstructions, out io.Writer) {
 | 
			
		||||
@@ -7,7 +7,7 @@ import (
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
func replaceDocPath(s string) string {
 | 
			
		||||
	const docpath = "$GOPATH/src/github.com/derekparker/delve/"
 | 
			
		||||
	const docpath = "$GOPATH/src/github.com/go-delve/delve/"
 | 
			
		||||
 | 
			
		||||
	for {
 | 
			
		||||
		start := strings.Index(s, docpath)
 | 
			
		||||
@@ -22,7 +22,7 @@ func replaceDocPath(s string) string {
 | 
			
		||||
		}
 | 
			
		||||
 | 
			
		||||
		text := s[start+len(docpath) : end]
 | 
			
		||||
		s = s[:start] + fmt.Sprintf("[%s](//github.com/derekparker/delve/tree/master/%s)", text, text) + s[end:]
 | 
			
		||||
		s = s[:start] + fmt.Sprintf("[%s](//github.com/go-delve/delve/tree/master/%s)", text, text) + s[end:]
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -13,9 +13,9 @@ import (
 | 
			
		||||
 | 
			
		||||
	"github.com/peterh/liner"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/config"
 | 
			
		||||
	"github.com/derekparker/delve/service"
 | 
			
		||||
	"github.com/derekparker/delve/service/api"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/config"
 | 
			
		||||
	"github.com/go-delve/delve/service"
 | 
			
		||||
	"github.com/go-delve/delve/service/api"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
const (
 | 
			
		||||
@@ -64,20 +64,6 @@ type Term struct {
 | 
			
		||||
 | 
			
		||||
// New returns a new Term.
 | 
			
		||||
func New(client service.Client, conf *config.Config) *Term {
 | 
			
		||||
	if client != nil && client.IsMulticlient() {
 | 
			
		||||
		state, _ := client.GetStateNonBlocking()
 | 
			
		||||
		// The error return of GetState will usually be the ErrProcessExited,
 | 
			
		||||
		// which we don't care about. If there are other errors they will show up
 | 
			
		||||
		// later, here we are only concerned about stopping a running target so
 | 
			
		||||
		// that we can initialize our connection.
 | 
			
		||||
		if state != nil && state.Running {
 | 
			
		||||
			_, err := client.Halt()
 | 
			
		||||
			if err != nil {
 | 
			
		||||
				fmt.Fprintf(os.Stderr, "could not halt: %v", err)
 | 
			
		||||
				return nil
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
	cmds := DebugCommands(client)
 | 
			
		||||
	if conf != nil && conf.Aliases != nil {
 | 
			
		||||
		cmds.Merge(conf.Aliases)
 | 
			
		||||
@@ -204,6 +190,9 @@ func (t *Term) Run() (int, error) {
 | 
			
		||||
	if t.InitFile != "" {
 | 
			
		||||
		err := t.cmds.executeFile(t, t.InitFile)
 | 
			
		||||
		if err != nil {
 | 
			
		||||
			if _, ok := err.(ExitRequestError); ok {
 | 
			
		||||
				return t.handleExit()
 | 
			
		||||
			}
 | 
			
		||||
			fmt.Fprintf(os.Stderr, "Error executing init file: %s\n", err)
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
@@ -264,7 +253,14 @@ func (t *Term) substitutePath(path string) string {
 | 
			
		||||
	if t.conf == nil {
 | 
			
		||||
		return path
 | 
			
		||||
	}
 | 
			
		||||
	separator := string(os.PathSeparator)
 | 
			
		||||
 | 
			
		||||
	// On windows paths returned from headless server are as c:/dir/dir
 | 
			
		||||
	// though os.PathSeparator is '\\'
 | 
			
		||||
 | 
			
		||||
	separator := "/"                     //make it default
 | 
			
		||||
	if strings.Index(path, "\\") != -1 { //dependent on the path
 | 
			
		||||
		separator = "\\"
 | 
			
		||||
	}
 | 
			
		||||
	for _, r := range t.conf.SubstitutePath {
 | 
			
		||||
		from := crossPlatformPath(r.From)
 | 
			
		||||
		to := r.To
 | 
			
		||||
@@ -14,8 +14,8 @@ type Version struct {
 | 
			
		||||
var (
 | 
			
		||||
	// DelveVersion is the current version of Delve.
 | 
			
		||||
	DelveVersion = Version{
 | 
			
		||||
		Major: "1", Minor: "1", Patch: "0", Metadata: "",
 | 
			
		||||
		Build: "$Id: 1990ba12450cab9425a2ae62e6ab988725023d5c $",
 | 
			
		||||
		Major: "1", Minor: "2", Patch: "0", Metadata: "",
 | 
			
		||||
		Build: "$Id: 068e2451004e95d0b042e5257e34f0f08ce01466 $",
 | 
			
		||||
	}
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
@@ -2,14 +2,15 @@ package api
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"bytes"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"go/constant"
 | 
			
		||||
	"go/printer"
 | 
			
		||||
	"go/token"
 | 
			
		||||
	"reflect"
 | 
			
		||||
	"strconv"
 | 
			
		||||
 | 
			
		||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/derekparker/delve/pkg/proc"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
			
		||||
	"github.com/go-delve/delve/pkg/proc"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
// ConvertBreakpoint converts from a proc.Breakpoint to
 | 
			
		||||
@@ -23,6 +24,7 @@ func ConvertBreakpoint(bp *proc.Breakpoint) *Breakpoint {
 | 
			
		||||
		Line:          bp.Line,
 | 
			
		||||
		Addr:          bp.Addr,
 | 
			
		||||
		Tracepoint:    bp.Tracepoint,
 | 
			
		||||
		TraceReturn:   bp.TraceReturn,
 | 
			
		||||
		Stacktrace:    bp.Stacktrace,
 | 
			
		||||
		Goroutine:     bp.Goroutine,
 | 
			
		||||
		Variables:     bp.Variables,
 | 
			
		||||
@@ -142,8 +144,9 @@ func ConvertVar(v *proc.Variable) *Variable {
 | 
			
		||||
		case reflect.String, reflect.Func:
 | 
			
		||||
			r.Value = constant.StringVal(v.Value)
 | 
			
		||||
		default:
 | 
			
		||||
			r.Value = v.ConstDescr()
 | 
			
		||||
			if r.Value == "" {
 | 
			
		||||
			if cd := v.ConstDescr(); cd != "" {
 | 
			
		||||
				r.Value = fmt.Sprintf("%s (%s)", cd, v.Value.String())
 | 
			
		||||
			} else {
 | 
			
		||||
				r.Value = v.Value.String()
 | 
			
		||||
			}
 | 
			
		||||
		}
 | 
			
		||||
@@ -282,6 +285,7 @@ func LoadConfigToProc(cfg *LoadConfig) *proc.LoadConfig {
 | 
			
		||||
		cfg.MaxStringLen,
 | 
			
		||||
		cfg.MaxArrayValues,
 | 
			
		||||
		cfg.MaxStructFields,
 | 
			
		||||
		0, // MaxMapBuckets is set internally by pkg/proc, read its documentation for an explanation.
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
@@ -312,3 +316,7 @@ func ConvertRegisters(in []proc.Register) (out []Register) {
 | 
			
		||||
func ConvertCheckpoint(in proc.Checkpoint) (out Checkpoint) {
 | 
			
		||||
	return Checkpoint(in)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ConvertImage(image *proc.Image) Image {
 | 
			
		||||
	return Image{Path: image.Path}
 | 
			
		||||
}
 | 
			
		||||
Some files were not shown because too many files have changed in this diff Show More
		Reference in New Issue
	
	Block a user