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
 | 
										// Wait 1s before re-build until there is no file change
 | 
				
			||||||
					scheduleTime := time.Now().Add(1 * time.Second)
 | 
										scheduleTime := time.Now().Add(1 * time.Second)
 | 
				
			||||||
					time.Sleep(scheduleTime.Sub(time.Now()))
 | 
										time.Sleep(time.Until(scheduleTime))
 | 
				
			||||||
					_, err := buildDebug()
 | 
										_, err := buildDebug()
 | 
				
			||||||
					if err != nil {
 | 
										if err != nil {
 | 
				
			||||||
						utils.Notify("Build Failed: "+err.Error(), "bee")
 | 
											utils.Notify("Build Failed: "+err.Error(), "bee")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -85,7 +85,7 @@ func NewWatcher(paths []string, files []string, isgenerate bool) {
 | 
				
			|||||||
					go func() {
 | 
										go func() {
 | 
				
			||||||
						// Wait 1s before autobuild until there is no file change.
 | 
											// Wait 1s before autobuild until there is no file change.
 | 
				
			||||||
						scheduleTime = time.Now().Add(1 * time.Second)
 | 
											scheduleTime = time.Now().Add(1 * time.Second)
 | 
				
			||||||
						time.Sleep(scheduleTime.Sub(time.Now()))
 | 
											time.Sleep(time.Until(scheduleTime))
 | 
				
			||||||
						AutoBuild(files, isgenerate)
 | 
											AutoBuild(files, isgenerate)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
						if config.Conf.EnableReload {
 | 
											if config.Conf.EnableReload {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -116,7 +116,7 @@ func ParsePackagesFromDir(dirpath string) {
 | 
				
			|||||||
				err = parsePackageFromDir(fpath)
 | 
									err = parsePackageFromDir(fpath)
 | 
				
			||||||
				if err != nil {
 | 
									if err != nil {
 | 
				
			||||||
					// Send the error to through the channel and continue walking
 | 
										// 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
 | 
										return nil
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -328,7 +328,7 @@ func CheckEnv(appname string) (apppath, packpath string, err error) {
 | 
				
			|||||||
	apppath = filepath.Join(gosrcpath, appname)
 | 
						apppath = filepath.Join(gosrcpath, appname)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if _, e := os.Stat(apppath); !os.IsNotExist(e) {
 | 
						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)
 | 
							beeLogger.Log.Errorf("Path '%s' already exists", apppath)
 | 
				
			||||||
		return
 | 
							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 (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"io"
 | 
				
			||||||
	"io/ioutil"
 | 
						"io/ioutil"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/user"
 | 
						"os/user"
 | 
				
			||||||
	"path"
 | 
						"path"
 | 
				
			||||||
 | 
						"runtime"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"gopkg.in/yaml.v2"
 | 
						"gopkg.in/yaml.v2"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
	configDir  string = ".dlv"
 | 
						configDir       string = "dlv"
 | 
				
			||||||
	configFile string = "config.yml"
 | 
						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 {
 | 
					type SubstitutePathRule struct {
 | 
				
			||||||
	// Directory path will be substituted if it matches `From`.
 | 
						// Directory path will be substituted if it matches `From`.
 | 
				
			||||||
	From string
 | 
						From string
 | 
				
			||||||
@@ -23,7 +26,7 @@ type SubstitutePathRule struct {
 | 
				
			|||||||
	To string
 | 
						To string
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Slice of source code path substitution rules.
 | 
					// SubstitutePathRules is a slice of source code path substitution rules.
 | 
				
			||||||
type SubstitutePathRules []SubstitutePathRule
 | 
					type SubstitutePathRules []SubstitutePathRule
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Config defines all configuration options available to be set through the config file.
 | 
					// Config defines all configuration options available to be set through the config file.
 | 
				
			||||||
@@ -43,10 +46,14 @@ type Config struct {
 | 
				
			|||||||
	// If ShowLocationExpr is true whatis will print the DWARF location
 | 
						// If ShowLocationExpr is true whatis will print the DWARF location
 | 
				
			||||||
	// expression for its argument.
 | 
						// expression for its argument.
 | 
				
			||||||
	ShowLocationExpr bool `yaml:"show-location-expr"`
 | 
						ShowLocationExpr bool `yaml:"show-location-expr"`
 | 
				
			||||||
	
 | 
					
 | 
				
			||||||
	// Source list line-number color (3/4 bit color codes as defined
 | 
						// Source list line-number color (3/4 bit color codes as defined
 | 
				
			||||||
	// here: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors)
 | 
						// here: https://en.wikipedia.org/wiki/ANSI_escape_code#Colors)
 | 
				
			||||||
	SourceListLineColor int `yaml:"source-list-line-color"`
 | 
						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.
 | 
					// LoadConfig attempts to populate a Config object from the config.yml file.
 | 
				
			||||||
@@ -54,12 +61,32 @@ func LoadConfig() *Config {
 | 
				
			|||||||
	err := createConfigPath()
 | 
						err := createConfigPath()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Printf("Could not create config directory: %v.", err)
 | 
							fmt.Printf("Could not create config directory: %v.", err)
 | 
				
			||||||
		return nil
 | 
							return &Config{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	fullConfigFile, err := GetConfigFilePath(configFile)
 | 
						fullConfigFile, err := GetConfigFilePath(configFile)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Printf("Unable to get config file path: %v.", err)
 | 
							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)
 | 
						f, err := os.Open(fullConfigFile)
 | 
				
			||||||
@@ -67,7 +94,7 @@ func LoadConfig() *Config {
 | 
				
			|||||||
		f, err = createDefaultConfig(fullConfigFile)
 | 
							f, err = createDefaultConfig(fullConfigFile)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			fmt.Printf("Error creating default config file: %v", err)
 | 
								fmt.Printf("Error creating default config file: %v", err)
 | 
				
			||||||
			return nil
 | 
								return &Config{}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	defer func() {
 | 
						defer func() {
 | 
				
			||||||
@@ -80,19 +107,25 @@ func LoadConfig() *Config {
 | 
				
			|||||||
	data, err := ioutil.ReadAll(f)
 | 
						data, err := ioutil.ReadAll(f)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Printf("Unable to read config data: %v.", err)
 | 
							fmt.Printf("Unable to read config data: %v.", err)
 | 
				
			||||||
		return nil
 | 
							return &Config{}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var c Config
 | 
						var c Config
 | 
				
			||||||
	err = yaml.Unmarshal(data, &c)
 | 
						err = yaml.Unmarshal(data, &c)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		fmt.Printf("Unable to decode config file: %v.", err)
 | 
							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
 | 
						return &c
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// SaveConfig will marshal and save the config struct
 | 
				
			||||||
 | 
					// to disk.
 | 
				
			||||||
func SaveConfig(conf *Config) error {
 | 
					func SaveConfig(conf *Config) error {
 | 
				
			||||||
	fullConfigFile, err := GetConfigFilePath(configFile)
 | 
						fullConfigFile, err := GetConfigFilePath(configFile)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -114,15 +147,42 @@ func SaveConfig(conf *Config) error {
 | 
				
			|||||||
	return err
 | 
						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) {
 | 
					func createDefaultConfig(path string) (*os.File, error) {
 | 
				
			||||||
	f, err := os.Create(path)
 | 
						f, err := os.Create(path)
 | 
				
			||||||
	if err != nil {
 | 
						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)
 | 
						err = writeDefaultConfig(f)
 | 
				
			||||||
	if err != nil {
 | 
						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
 | 
						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.
 | 
					# Uncomment the following line to make the whatis command also print the DWARF location expression of its argument.
 | 
				
			||||||
# show-location-expr: true
 | 
					# 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
 | 
						return err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -173,9 +236,43 @@ func createConfigPath() error {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// GetConfigFilePath gets the full path to the given config file name.
 | 
					// GetConfigFilePath gets the full path to the given config file name.
 | 
				
			||||||
func GetConfigFilePath(file string) (string, error) {
 | 
					func GetConfigFilePath(file string) (string, error) {
 | 
				
			||||||
	usr, err := user.Current()
 | 
						if configPath := os.Getenv("XDG_CONFIG_HOME"); configPath != "" {
 | 
				
			||||||
	if err != nil {
 | 
							return path.Join(configPath, configDir, file), nil
 | 
				
			||||||
		return "", err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	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"
 | 
						"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.
 | 
					// by the specified quote character.
 | 
				
			||||||
// To specify a single quote use backslash to escape it: '\''
 | 
					// To specify a single quote use backslash to escape it: '\''
 | 
				
			||||||
func SplitQuotedFields(in string, quote rune) []string {
 | 
					func SplitQuotedFields(in string, quote rune) []string {
 | 
				
			||||||
@@ -7,7 +7,7 @@ import (
 | 
				
			|||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
	"encoding/binary"
 | 
						"encoding/binary"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/util"
 | 
						"github.com/go-delve/delve/pkg/dwarf/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type parsefunc func(*parseContext) parsefunc
 | 
					type parsefunc func(*parseContext) parsefunc
 | 
				
			||||||
@@ -5,7 +5,7 @@ import (
 | 
				
			|||||||
	"encoding/binary"
 | 
						"encoding/binary"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/util"
 | 
						"github.com/go-delve/delve/pkg/dwarf/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type DWRule struct {
 | 
					type DWRule struct {
 | 
				
			||||||
@@ -16,8 +16,8 @@ import (
 | 
				
			|||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
						"github.com/go-delve/delve/pkg/dwarf/op"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/util"
 | 
						"github.com/go-delve/delve/pkg/dwarf/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -41,6 +41,20 @@ const (
 | 
				
			|||||||
	encImaginaryFloat = 0x09
 | 
						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
 | 
					// A Type conventionally represents a pointer to any of the
 | 
				
			||||||
// specific Type structures (CharType, StructType, etc.).
 | 
					// specific Type structures (CharType, StructType, etc.).
 | 
				
			||||||
//TODO: remove this use dwarf.Type
 | 
					//TODO: remove this use dwarf.Type
 | 
				
			||||||
@@ -48,6 +62,9 @@ type Type interface {
 | 
				
			|||||||
	Common() *CommonType
 | 
						Common() *CommonType
 | 
				
			||||||
	String() string
 | 
						String() string
 | 
				
			||||||
	Size() int64
 | 
						Size() int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						stringIntl(recCheck) string
 | 
				
			||||||
 | 
						sizeIntl(recCheck) int64
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// A CommonType holds fields common to multiple types.
 | 
					// 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) 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
 | 
					// Basic types
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -75,7 +93,9 @@ type BasicType struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (b *BasicType) Basic() *BasicType { return b }
 | 
					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 != "" {
 | 
						if t.Name != "" {
 | 
				
			||||||
		return t.Name
 | 
							return t.Name
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -136,9 +156,27 @@ type QualType struct {
 | 
				
			|||||||
	Type Type
 | 
						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.
 | 
					// An ArrayType represents a fixed size array type.
 | 
				
			||||||
type ArrayType struct {
 | 
					type ArrayType struct {
 | 
				
			||||||
@@ -148,18 +186,36 @@ type ArrayType struct {
 | 
				
			|||||||
	Count         int64 // if == -1, an incomplete array, like char x[].
 | 
						Count         int64 // if == -1, an incomplete array, like char x[].
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t *ArrayType) String() string {
 | 
					func (t *ArrayType) String() string { return t.stringIntl(make(recCheck)) }
 | 
				
			||||||
	return "[" + strconv.FormatInt(t.Count, 10) + "]" + t.Type.String()
 | 
					
 | 
				
			||||||
 | 
					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.
 | 
					// A VoidType represents the C void type.
 | 
				
			||||||
type VoidType struct {
 | 
					type VoidType struct {
 | 
				
			||||||
	CommonType
 | 
						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.
 | 
					// A PtrType represents a pointer type.
 | 
				
			||||||
type PtrType struct {
 | 
					type PtrType struct {
 | 
				
			||||||
@@ -167,7 +223,16 @@ type PtrType struct {
 | 
				
			|||||||
	Type Type
 | 
						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.
 | 
					// A StructType represents a struct, union, or C++ class type.
 | 
				
			||||||
type StructType struct {
 | 
					type StructType struct {
 | 
				
			||||||
@@ -189,14 +254,21 @@ type StructField struct {
 | 
				
			|||||||
	Embedded   bool
 | 
						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 != "" {
 | 
						if t.StructName != "" {
 | 
				
			||||||
		return t.Kind + " " + 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
 | 
						s := t.Kind
 | 
				
			||||||
	if t.StructName != "" {
 | 
						if t.StructName != "" {
 | 
				
			||||||
		s += " " + t.StructName
 | 
							s += " " + t.StructName
 | 
				
			||||||
@@ -210,7 +282,7 @@ func (t *StructType) Defn() string {
 | 
				
			|||||||
		if i > 0 {
 | 
							if i > 0 {
 | 
				
			||||||
			s += "; "
 | 
								s += "; "
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		s += f.Name + " " + f.Type.String()
 | 
							s += f.Name + " " + f.Type.stringIntl(recCheck)
 | 
				
			||||||
		s += "@" + strconv.FormatInt(f.ByteOffset, 10)
 | 
							s += "@" + strconv.FormatInt(f.ByteOffset, 10)
 | 
				
			||||||
		if f.BitSize > 0 {
 | 
							if f.BitSize > 0 {
 | 
				
			||||||
			s += " : " + strconv.FormatInt(f.BitSize, 10)
 | 
								s += " : " + strconv.FormatInt(f.BitSize, 10)
 | 
				
			||||||
@@ -228,11 +300,18 @@ type SliceType struct {
 | 
				
			|||||||
	ElemType Type
 | 
						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 != "" {
 | 
						if t.Name != "" {
 | 
				
			||||||
		return 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
 | 
					// A StringType represents a Go string type. It looks like a StructType, describing
 | 
				
			||||||
@@ -241,7 +320,9 @@ type StringType struct {
 | 
				
			|||||||
	StructType
 | 
						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 != "" {
 | 
						if t.Name != "" {
 | 
				
			||||||
		return t.Name
 | 
							return t.Name
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -253,7 +334,9 @@ type InterfaceType struct {
 | 
				
			|||||||
	TypedefType
 | 
						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 != "" {
 | 
						if t.Name != "" {
 | 
				
			||||||
		return t.Name
 | 
							return t.Name
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -275,7 +358,9 @@ type EnumValue struct {
 | 
				
			|||||||
	Val  int64
 | 
						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"
 | 
						s := "enum"
 | 
				
			||||||
	if t.EnumName != "" {
 | 
						if t.EnumName != "" {
 | 
				
			||||||
		s += " " + t.EnumName
 | 
							s += " " + t.EnumName
 | 
				
			||||||
@@ -298,17 +383,24 @@ type FuncType struct {
 | 
				
			|||||||
	ParamType  []Type
 | 
						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("
 | 
						s := "func("
 | 
				
			||||||
	for i, t := range t.ParamType {
 | 
						for i, t := range t.ParamType {
 | 
				
			||||||
		if i > 0 {
 | 
							if i > 0 {
 | 
				
			||||||
			s += ", "
 | 
								s += ", "
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		s += t.String()
 | 
							s += t.stringIntl(recCheck)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	s += ")"
 | 
						s += ")"
 | 
				
			||||||
	if t.ReturnType != nil {
 | 
						if t.ReturnType != nil {
 | 
				
			||||||
		s += " " + t.ReturnType.String()
 | 
							s += " " + t.ReturnType.stringIntl(recCheck)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return s
 | 
						return s
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -318,7 +410,9 @@ type DotDotDotType struct {
 | 
				
			|||||||
	CommonType
 | 
						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.
 | 
					// A TypedefType represents a named type.
 | 
				
			||||||
type TypedefType struct {
 | 
					type TypedefType struct {
 | 
				
			||||||
@@ -326,9 +420,20 @@ type TypedefType struct {
 | 
				
			|||||||
	Type Type
 | 
						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
 | 
					// A MapType represents a Go map type. It looks like a TypedefType, describing
 | 
				
			||||||
// the runtime-internal structure, with extra fields.
 | 
					// the runtime-internal structure, with extra fields.
 | 
				
			||||||
@@ -338,7 +443,14 @@ type MapType struct {
 | 
				
			|||||||
	ElemType Type
 | 
						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 != "" {
 | 
						if t.Name != "" {
 | 
				
			||||||
		return t.Name
 | 
							return t.Name
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -351,7 +463,14 @@ type ChanType struct {
 | 
				
			|||||||
	ElemType Type
 | 
						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 != "" {
 | 
						if t.Name != "" {
 | 
				
			||||||
		return 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 {
 | 
							switch t.ReflectKind {
 | 
				
			||||||
		case reflect.Slice:
 | 
							case reflect.Slice:
 | 
				
			||||||
			slice := new(SliceType)
 | 
								slice := new(SliceType)
 | 
				
			||||||
 | 
								typ = slice
 | 
				
			||||||
 | 
								typeCache[off] = slice
 | 
				
			||||||
			slice.ElemType = typeOf(e, AttrGoElem)
 | 
								slice.ElemType = typeOf(e, AttrGoElem)
 | 
				
			||||||
			t = &slice.StructType
 | 
								t = &slice.StructType
 | 
				
			||||||
			typ = slice
 | 
					 | 
				
			||||||
		case reflect.String:
 | 
							case reflect.String:
 | 
				
			||||||
			str := new(StringType)
 | 
								str := new(StringType)
 | 
				
			||||||
			t = &str.StructType
 | 
								t = &str.StructType
 | 
				
			||||||
@@ -798,19 +918,22 @@ func readType(d *dwarf.Data, name string, r *dwarf.Reader, off dwarf.Offset, typ
 | 
				
			|||||||
		switch t.ReflectKind {
 | 
							switch t.ReflectKind {
 | 
				
			||||||
		case reflect.Map:
 | 
							case reflect.Map:
 | 
				
			||||||
			m := new(MapType)
 | 
								m := new(MapType)
 | 
				
			||||||
 | 
								typ = m
 | 
				
			||||||
 | 
								typeCache[off] = typ
 | 
				
			||||||
			m.KeyType = typeOf(e, AttrGoKey)
 | 
								m.KeyType = typeOf(e, AttrGoKey)
 | 
				
			||||||
			m.ElemType = typeOf(e, AttrGoElem)
 | 
								m.ElemType = typeOf(e, AttrGoElem)
 | 
				
			||||||
			t = &m.TypedefType
 | 
								t = &m.TypedefType
 | 
				
			||||||
			typ = m
 | 
					 | 
				
			||||||
		case reflect.Chan:
 | 
							case reflect.Chan:
 | 
				
			||||||
			c := new(ChanType)
 | 
								c := new(ChanType)
 | 
				
			||||||
 | 
								typ = c
 | 
				
			||||||
 | 
								typeCache[off] = typ
 | 
				
			||||||
			c.ElemType = typeOf(e, AttrGoElem)
 | 
								c.ElemType = typeOf(e, AttrGoElem)
 | 
				
			||||||
			t = &c.TypedefType
 | 
								t = &c.TypedefType
 | 
				
			||||||
			typ = c
 | 
					 | 
				
			||||||
		case reflect.Interface:
 | 
							case reflect.Interface:
 | 
				
			||||||
			it := new(InterfaceType)
 | 
								it := new(InterfaceType)
 | 
				
			||||||
			t = &it.TypedefType
 | 
					 | 
				
			||||||
			typ = it
 | 
								typ = it
 | 
				
			||||||
 | 
								typeCache[off] = it
 | 
				
			||||||
 | 
								t = &it.TypedefType
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			typ = t
 | 
								typ = t
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -876,3 +999,13 @@ func zeroArray(t Type) {
 | 
				
			|||||||
		t = at.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"
 | 
						"encoding/binary"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/util"
 | 
						"github.com/go-delve/delve/pkg/dwarf/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type DebugLinePrologue struct {
 | 
					type DebugLinePrologue struct {
 | 
				
			||||||
@@ -7,7 +7,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/util"
 | 
						"github.com/go-delve/delve/pkg/dwarf/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Location struct {
 | 
					type Location struct {
 | 
				
			||||||
@@ -7,7 +7,7 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/util"
 | 
						"github.com/go-delve/delve/pkg/dwarf/util"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Opcode byte
 | 
					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"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
						"github.com/go-delve/delve/pkg/dwarf/op"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type Reader struct {
 | 
					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 (
 | 
					import (
 | 
				
			||||||
	"encoding/binary"
 | 
						"encoding/binary"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/frame"
 | 
						"github.com/go-delve/delve/pkg/dwarf/frame"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
						"github.com/go-delve/delve/pkg/dwarf/op"
 | 
				
			||||||
	"golang.org/x/arch/x86/x86asm"
 | 
						"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 {
 | 
							for dwarfReg, regName := range amd64DwarfToName {
 | 
				
			||||||
			if regName == reg.Name {
 | 
								if regName == reg.Name {
 | 
				
			||||||
				dregs[dwarfReg] = op.DwarfRegisterFromBytes(reg.Bytes)
 | 
									dregs[dwarfReg] = op.DwarfRegisterFromBytes(reg.Bytes)
 | 
				
			||||||
@@ -12,40 +12,30 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"path/filepath"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/frame"
 | 
						"github.com/go-delve/delve/pkg/dwarf/frame"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
						"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/line"
 | 
						"github.com/go-delve/delve/pkg/dwarf/line"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
						"github.com/go-delve/delve/pkg/dwarf/op"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
						"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/goversion"
 | 
						"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 {
 | 
					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
 | 
						// GOOS operating system this binary is executing on.
 | 
				
			||||||
	closer         io.Closer
 | 
						GOOS string
 | 
				
			||||||
	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
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Functions is a list of all DW_TAG_subprogram entries in debug_info, sorted by entry point
 | 
						// Functions is a list of all DW_TAG_subprogram entries in debug_info, sorted by entry point
 | 
				
			||||||
	Functions []Function
 | 
						Functions []Function
 | 
				
			||||||
@@ -54,7 +44,32 @@ type BinaryInfo struct {
 | 
				
			|||||||
	// LookupFunc maps function names to a description of the function.
 | 
						// LookupFunc maps function names to a description of the function.
 | 
				
			||||||
	LookupFunc map[string]*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
 | 
						loadModuleDataOnce sync.Once
 | 
				
			||||||
	moduleData         []moduleData
 | 
						moduleData         []moduleData
 | 
				
			||||||
@@ -71,8 +86,6 @@ type BinaryInfo struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	loadErrMu sync.Mutex
 | 
						loadErrMu sync.Mutex
 | 
				
			||||||
	loadErr   error
 | 
						loadErr   error
 | 
				
			||||||
 | 
					 | 
				
			||||||
	dwarfReader *dwarf.Reader
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrUnsupportedLinuxArch is returned when attempting to debug a binary compiled for an unsupported architecture.
 | 
					// 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.
 | 
					// 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")
 | 
					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")
 | 
					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)
 | 
					const dwarfGoLanguage = 22 // DW_LANG_Go (from DWARF v5, section 7.12, page 231)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type compileUnit struct {
 | 
					type compileUnit struct {
 | 
				
			||||||
	Name   string // univocal name for non-go compile units
 | 
						name   string // univocal name for non-go compile units
 | 
				
			||||||
	LowPC  uint64
 | 
						lowPC  uint64
 | 
				
			||||||
	Ranges [][2]uint64
 | 
						ranges [][2]uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	entry              *dwarf.Entry        // debug_info entry describing this compile unit
 | 
						entry              *dwarf.Entry        // debug_info entry describing this compile unit
 | 
				
			||||||
	isgo               bool                // true if this is the go compile unit
 | 
						isgo               bool                // true if this is the go compile unit
 | 
				
			||||||
@@ -277,6 +296,12 @@ type buildIDHeader struct {
 | 
				
			|||||||
	Type   uint32
 | 
						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.
 | 
					// NewBinaryInfo returns an initialized but unloaded BinaryInfo struct.
 | 
				
			||||||
func NewBinaryInfo(goos, goarch string) *BinaryInfo {
 | 
					func NewBinaryInfo(goos, goarch string) *BinaryInfo {
 | 
				
			||||||
	r := &BinaryInfo{GOOS: goos, nameOfRuntimeType: make(map[uintptr]nameOfRuntimeTypeEntry), typeCache: make(map[dwarf.Offset]godwarf.Type)}
 | 
						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'.
 | 
					// 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
 | 
					// It is expected this will be called in parallel with other initialization steps
 | 
				
			||||||
// so a sync.WaitGroup must be provided.
 | 
					// 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)
 | 
						fi, err := os.Stat(path)
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		bi.lastModified = fi.ModTime()
 | 
							bi.lastModified = fi.ModTime()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						var wg sync.WaitGroup
 | 
				
			||||||
 | 
						defer wg.Wait()
 | 
				
			||||||
 | 
						bi.Path = path
 | 
				
			||||||
	switch bi.GOOS {
 | 
						switch bi.GOOS {
 | 
				
			||||||
	case "linux":
 | 
						case "linux":
 | 
				
			||||||
		return bi.LoadBinaryInfoElf(path, entryPoint, wg)
 | 
							return bi.LoadBinaryInfoElf(path, entryPoint, debugInfoDirs, &wg)
 | 
				
			||||||
	case "windows":
 | 
						case "windows":
 | 
				
			||||||
		return bi.LoadBinaryInfoPE(path, entryPoint, wg)
 | 
							return bi.LoadBinaryInfoPE(path, entryPoint, &wg)
 | 
				
			||||||
	case "darwin":
 | 
						case "darwin":
 | 
				
			||||||
		return bi.LoadBinaryInfoMacho(path, entryPoint, wg)
 | 
							return bi.LoadBinaryInfoMacho(path, entryPoint, &wg)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return errors.New("unsupported operating system")
 | 
						return errors.New("unsupported operating system")
 | 
				
			||||||
	return nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GStructOffset returns the offset of the G
 | 
					// GStructOffset returns the offset of the G
 | 
				
			||||||
@@ -398,12 +425,35 @@ func (bi *BinaryInfo) PCToFunc(pc uint64) *Function {
 | 
				
			|||||||
	return nil
 | 
						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.
 | 
					// Close closes all internal readers.
 | 
				
			||||||
func (bi *BinaryInfo) Close() error {
 | 
					func (bi *BinaryInfo) Close() error {
 | 
				
			||||||
	if bi.sepDebugCloser != nil {
 | 
						if bi.sepDebugCloser != nil {
 | 
				
			||||||
		bi.sepDebugCloser.Close()
 | 
							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{}) {
 | 
					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 {
 | 
					func (bi *BinaryInfo) loclistEntry(off int64, pc uint64) []byte {
 | 
				
			||||||
	var base uint64
 | 
						var base uint64
 | 
				
			||||||
	if cu := bi.findCompileUnit(pc); cu != nil {
 | 
						if cu := bi.findCompileUnit(pc); cu != nil {
 | 
				
			||||||
		base = cu.LowPC
 | 
							base = cu.lowPC
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bi.loclist.Seek(int(off))
 | 
						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.
 | 
					// findCompileUnit returns the compile unit containing address pc.
 | 
				
			||||||
func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit {
 | 
					func (bi *BinaryInfo) findCompileUnit(pc uint64) *compileUnit {
 | 
				
			||||||
	for _, cu := range bi.compileUnits {
 | 
						for _, cu := range bi.compileUnits {
 | 
				
			||||||
		for _, rng := range cu.Ranges {
 | 
							for _, rng := range cu.ranges {
 | 
				
			||||||
			if pc >= rng[0] && pc < rng[1] {
 | 
								if pc >= rng[0] && pc < rng[1] {
 | 
				
			||||||
				return cu
 | 
									return cu
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -558,35 +608,32 @@ func (e *ErrNoBuildIDNote) Error() string {
 | 
				
			|||||||
// in GDB's documentation [1], and if found returns two handles, one
 | 
					// in GDB's documentation [1], and if found returns two handles, one
 | 
				
			||||||
// for the bare file, and another for its corresponding elf.File.
 | 
					// for the bare file, and another for its corresponding elf.File.
 | 
				
			||||||
// [1] https://sourceware.org/gdb/onlinedocs/gdb/Separate-Debug-Files.html
 | 
					// [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")
 | 
					// Alternatively, if the debug file cannot be found be the build-id, Delve
 | 
				
			||||||
	if buildid == nil {
 | 
					// will look in directories specified by the debug-info-directories config value.
 | 
				
			||||||
		return nil, nil, &ErrNoBuildIDNote{}
 | 
					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
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						if debugFilePath == "" {
 | 
				
			||||||
	br := buildid.Open()
 | 
							return nil, nil, ErrNoDebugInfoFound
 | 
				
			||||||
	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())
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						sepFile, err := os.OpenFile(debugFilePath, 0, os.ModePerm)
 | 
				
			||||||
	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)
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, nil, errors.New("can't open separate debug file: " + err.Error())
 | 
							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)
 | 
						elfFile, err := elf.NewFile(sepFile)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		sepFile.Close()
 | 
							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 {
 | 
						if elfFile.Machine != elf.EM_X86_64 {
 | 
				
			||||||
		sepFile.Close()
 | 
							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
 | 
						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.
 | 
					// 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)
 | 
						exe, err := os.OpenFile(path, 0, os.ModePerm)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							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
 | 
						dwarfFile := elfFile
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bi.dwarf, err = elfFile.DWARF()
 | 
						bi.dwarf, err = elfFile.DWARF()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		var sepFile *os.File
 | 
							var sepFile *os.File
 | 
				
			||||||
		var serr error
 | 
							var serr error
 | 
				
			||||||
		sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(elfFile)
 | 
							sepFile, dwarfFile, serr = bi.openSeparateDebugInfo(elfFile, debugInfoDirectories)
 | 
				
			||||||
		if serr != nil {
 | 
							if serr != nil {
 | 
				
			||||||
			if _, ok := serr.(*ErrNoBuildIDNote); ok {
 | 
					 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			return serr
 | 
								return serr
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		bi.sepDebugCloser = sepFile
 | 
							bi.sepDebugCloser = sepFile
 | 
				
			||||||
@@ -714,9 +792,16 @@ func (bi *BinaryInfo) setGStructOffsetElf(exe *elf.File, wg *sync.WaitGroup) {
 | 
				
			|||||||
			break
 | 
								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
 | 
						// 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.
 | 
						// 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 ////////////////////////////////////////////////////////////////
 | 
					// PE ////////////////////////////////////////////////////////////////
 | 
				
			||||||
@@ -30,7 +30,8 @@ type Breakpoint struct {
 | 
				
			|||||||
	Kind BreakpointKind
 | 
						Kind BreakpointKind
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Breakpoint information
 | 
						// Breakpoint information
 | 
				
			||||||
	Tracepoint    bool     // Tracepoint flag
 | 
						Tracepoint    bool // Tracepoint flag
 | 
				
			||||||
 | 
						TraceReturn   bool
 | 
				
			||||||
	Goroutine     bool     // Retrieve goroutine information
 | 
						Goroutine     bool     // Retrieve goroutine information
 | 
				
			||||||
	Stacktrace    int      // Number of stack frames to retrieve
 | 
						Stacktrace    int      // Number of stack frames to retrieve
 | 
				
			||||||
	Variables     []string // Variables to evaluate
 | 
						Variables     []string // Variables to evaluate
 | 
				
			||||||
@@ -45,7 +46,7 @@ type Breakpoint struct {
 | 
				
			|||||||
	// Next uses NextDeferBreakpoints for the breakpoint it sets on the
 | 
						// Next uses NextDeferBreakpoints for the breakpoint it sets on the
 | 
				
			||||||
	// deferred function, DeferReturns is populated with the
 | 
						// deferred function, DeferReturns is populated with the
 | 
				
			||||||
	// addresses of calls to runtime.deferreturn in the current
 | 
						// 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
 | 
						// function only triggers on panic or on the defer call to
 | 
				
			||||||
	// the function, not when the function is called directly
 | 
						// the function, not when the function is called directly
 | 
				
			||||||
	DeferReturns []uint64
 | 
						DeferReturns []uint64
 | 
				
			||||||
@@ -220,13 +221,16 @@ func (bpmap *BreakpointMap) ResetBreakpointIDCounter() {
 | 
				
			|||||||
	bpmap.breakpointIDCounter = 0
 | 
						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
 | 
					type clearBreakpointFn func(*Breakpoint) error
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Set creates a breakpoint at addr calling writeBreakpoint. Do not call this
 | 
					// Set creates a breakpoint at addr calling writeBreakpoint. Do not call this
 | 
				
			||||||
// function, call proc.Process.SetBreakpoint instead, this function exists
 | 
					// function, call proc.Process.SetBreakpoint instead, this function exists
 | 
				
			||||||
// to implement proc.Process.SetBreakpoint.
 | 
					// 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 {
 | 
						if bp, ok := bpmap.M[addr]; ok {
 | 
				
			||||||
		// We can overlap one internal breakpoint with one user breakpoint, we
 | 
							// We can overlap one internal breakpoint with one user breakpoint, we
 | 
				
			||||||
		// need to support this otherwise a conditional breakpoint can mask a
 | 
							// 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
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						fnName := ""
 | 
				
			||||||
 | 
						if fn != nil {
 | 
				
			||||||
 | 
							fnName = fn.Name
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	newBreakpoint := &Breakpoint{
 | 
						newBreakpoint := &Breakpoint{
 | 
				
			||||||
		FunctionName: fn.Name,
 | 
							FunctionName: fnName,
 | 
				
			||||||
		File:         f,
 | 
							File:         f,
 | 
				
			||||||
		Line:         l,
 | 
							Line:         l,
 | 
				
			||||||
		Addr:         addr,
 | 
							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.
 | 
					// 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)
 | 
						bp, err := bpmap.Set(addr, UserBreakpoint, nil, writeBreakpoint)
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		bp.ID = id
 | 
							bp.ID = id
 | 
				
			||||||
@@ -421,12 +430,6 @@ func (rbpi *returnBreakpointInfo) Collect(thread Thread) []*Variable {
 | 
				
			|||||||
		return (v.Flags & VariableReturnArgument) != 0
 | 
							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
 | 
						return vars
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -5,9 +5,8 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"go/ast"
 | 
						"go/ast"
 | 
				
			||||||
	"io"
 | 
						"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,
 | 
					// 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.
 | 
					// Process represents a core file.
 | 
				
			||||||
type Process struct {
 | 
					type Process struct {
 | 
				
			||||||
 | 
						mem     proc.MemoryReader
 | 
				
			||||||
 | 
						Threads map[int]*Thread
 | 
				
			||||||
 | 
						pid     int
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						entryPoint uint64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	bi                *proc.BinaryInfo
 | 
						bi                *proc.BinaryInfo
 | 
				
			||||||
	core              *Core
 | 
					 | 
				
			||||||
	breakpoints       proc.BreakpointMap
 | 
						breakpoints       proc.BreakpointMap
 | 
				
			||||||
	currentThread     *Thread
 | 
						currentThread     *Thread
 | 
				
			||||||
	selectedGoroutine *proc.G
 | 
						selectedGoroutine *proc.G
 | 
				
			||||||
@@ -154,12 +158,16 @@ type Process struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Thread represents a thread in the core file being debugged.
 | 
					// Thread represents a thread in the core file being debugged.
 | 
				
			||||||
type Thread struct {
 | 
					type Thread struct {
 | 
				
			||||||
	th     *LinuxPrStatus
 | 
						th     osThread
 | 
				
			||||||
	fpregs []proc.Register
 | 
					 | 
				
			||||||
	p      *Process
 | 
						p      *Process
 | 
				
			||||||
	common proc.CommonThread
 | 
						common proc.CommonThread
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					type osThread interface {
 | 
				
			||||||
 | 
						registers(floatingPoint bool) (proc.Registers, error)
 | 
				
			||||||
 | 
						pid() int
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var (
 | 
					var (
 | 
				
			||||||
	// ErrWriteCore is returned when attempting to write to the core
 | 
						// ErrWriteCore is returned when attempting to write to the core
 | 
				
			||||||
	// process memory.
 | 
						// process memory.
 | 
				
			||||||
@@ -175,45 +183,66 @@ var (
 | 
				
			|||||||
	ErrChangeRegisterCore = errors.New("can not change register values of core process")
 | 
						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.
 | 
					// OpenCore will open the core file and return a Process struct.
 | 
				
			||||||
func OpenCore(corePath, exePath string) (*Process, error) {
 | 
					// If the DWARF information cannot be found in the binary, Delve will look
 | 
				
			||||||
	core, err := readCore(corePath, exePath)
 | 
					// for external debug files in the directories passed in.
 | 
				
			||||||
	if err != nil {
 | 
					func OpenCore(corePath, exePath string, debugInfoDirs []string) (*Process, error) {
 | 
				
			||||||
		return nil, err
 | 
						var p *Process
 | 
				
			||||||
	}
 | 
						var err error
 | 
				
			||||||
	p := &Process{
 | 
						for _, openFn := range openFns {
 | 
				
			||||||
		core:        core,
 | 
							p, err = openFn(corePath, exePath)
 | 
				
			||||||
		breakpoints: proc.NewBreakpointMap(),
 | 
							if err != ErrUnrecognizedFormat {
 | 
				
			||||||
		bi:          proc.NewBinaryInfo("linux", "amd64"),
 | 
								break
 | 
				
			||||||
	}
 | 
							}
 | 
				
			||||||
	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 err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, th := range p.core.Threads {
 | 
						if err := p.initialize(exePath, debugInfoDirs); err != nil {
 | 
				
			||||||
		p.currentThread = th
 | 
							return nil, err
 | 
				
			||||||
		break
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	return p, nil
 | 
						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.
 | 
					// BinInfo will return the binary info.
 | 
				
			||||||
func (p *Process) BinInfo() *proc.BinaryInfo {
 | 
					func (p *Process) BinInfo() *proc.BinaryInfo {
 | 
				
			||||||
	return p.bi
 | 
						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.
 | 
					// Recorded returns whether this is a live or recorded process. Always returns true for core files.
 | 
				
			||||||
func (p *Process) Recorded() (bool, string) { return true, "" }
 | 
					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
 | 
					// 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.
 | 
					// the length read is shorter than the length of the `data` buffer.
 | 
				
			||||||
func (t *Thread) ReadMemory(data []byte, addr uintptr) (n int, err error) {
 | 
					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) {
 | 
						if err == nil && n != len(data) {
 | 
				
			||||||
		err = ErrShortRead
 | 
							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
 | 
					// Location returns the location of this thread based on
 | 
				
			||||||
// the value of the instruction pointer register.
 | 
					// the value of the instruction pointer register.
 | 
				
			||||||
func (t *Thread) Location() (*proc.Location, error) {
 | 
					func (t *Thread) Location() (*proc.Location, error) {
 | 
				
			||||||
	f, l, fn := t.p.bi.PCToLine(t.th.Reg.Rip)
 | 
						regs, err := t.th.registers(false)
 | 
				
			||||||
	return &proc.Location{PC: t.th.Reg.Rip, File: f, Line: l, Fn: fn}, nil
 | 
						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.
 | 
					// 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.
 | 
					// ThreadID returns the ID for this thread.
 | 
				
			||||||
func (t *Thread) ThreadID() int {
 | 
					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.
 | 
					// Registers returns the current value of the registers for this thread.
 | 
				
			||||||
func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
 | 
					func (t *Thread) Registers(floatingPoint bool) (proc.Registers, error) {
 | 
				
			||||||
	r := &Registers{&t.th.Reg, nil}
 | 
						return t.th.registers(floatingPoint)
 | 
				
			||||||
	if floatingPoint {
 | 
					 | 
				
			||||||
		r.fpregs = t.fpregs
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	return r, nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RestoreRegisters will only return an error for core files,
 | 
					// 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.
 | 
					// Pid returns the process ID of this process.
 | 
				
			||||||
func (p *Process) Pid() int {
 | 
					func (p *Process) Pid() int {
 | 
				
			||||||
	return p.core.Pid
 | 
						return p.pid
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ResumeNotify is a no-op on core files as we cannot
 | 
					// 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.
 | 
					// SwitchThread will change the selected and active thread.
 | 
				
			||||||
func (p *Process) SwitchThread(tid int) error {
 | 
					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.currentThread = th
 | 
				
			||||||
		p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
 | 
							p.selectedGoroutine, _ = proc.GetG(p.CurrentThread())
 | 
				
			||||||
		return nil
 | 
							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.
 | 
					// ThreadList will return a list of all threads currently in the process.
 | 
				
			||||||
func (p *Process) ThreadList() []proc.Thread {
 | 
					func (p *Process) ThreadList() []proc.Thread {
 | 
				
			||||||
	r := make([]proc.Thread, 0, len(p.core.Threads))
 | 
						r := make([]proc.Thread, 0, len(p.Threads))
 | 
				
			||||||
	for _, v := range p.core.Threads {
 | 
						for _, v := range p.Threads {
 | 
				
			||||||
		r = append(r, v)
 | 
							r = append(r, v)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return r
 | 
						return r
 | 
				
			||||||
@@ -463,64 +493,6 @@ func (p *Process) ThreadList() []proc.Thread {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// FindThread will return the thread with the corresponding thread ID.
 | 
					// FindThread will return the thread with the corresponding thread ID.
 | 
				
			||||||
func (p *Process) FindThread(threadID int) (proc.Thread, bool) {
 | 
					func (p *Process) FindThread(threadID int) (proc.Thread, bool) {
 | 
				
			||||||
	t, ok := p.core.Threads[threadID]
 | 
						t, ok := p.Threads[threadID]
 | 
				
			||||||
	return t, ok
 | 
						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"
 | 
						"fmt"
 | 
				
			||||||
	"io"
 | 
						"io"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/arch/x86/x86asm"
 | 
						"github.com/go-delve/delve/pkg/proc"
 | 
				
			||||||
 | 
						"github.com/go-delve/delve/pkg/proc/linutil"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/proc"
 | 
					 | 
				
			||||||
	"github.com/derekparker/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
 | 
					// Copied from golang.org/x/sys/unix.Timeval since it's not available on all
 | 
				
			||||||
// systems.
 | 
					// systems.
 | 
				
			||||||
type LinuxCoreTimeval struct {
 | 
					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
 | 
					// NT_AUXV is the note type for notes containing a copy of the Auxv array
 | 
				
			||||||
const NT_AUXV elf.NType = 0x6
 | 
					const NT_AUXV elf.NType = 0x6
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// PC returns the value of RIP.
 | 
					const elfErrorBadMagicNumber = "bad magic number"
 | 
				
			||||||
func (r *LinuxCoreRegisters) PC() uint64 {
 | 
					 | 
				
			||||||
	return r.Rip
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SP returns the value of RSP.
 | 
					// readLinuxAMD64Core reads a core file from corePath corresponding to the executable at
 | 
				
			||||||
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
 | 
					 | 
				
			||||||
// exePath. For details on the Linux ELF core format, see:
 | 
					// exePath. For details on the Linux ELF core format, see:
 | 
				
			||||||
// http://www.gabriel.urdhr.fr/2015/05/29/core-file/,
 | 
					// http://www.gabriel.urdhr.fr/2015/05/29/core-file/,
 | 
				
			||||||
// http://uhlo.blogspot.fr/2012/05/brief-look-into-core-dumps.html,
 | 
					// 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,
 | 
					// elf_core_dump in http://lxr.free-electrons.com/source/fs/binfmt_elf.c,
 | 
				
			||||||
// and, if absolutely desperate, readelf.c from the binutils source.
 | 
					// 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)
 | 
						coreFile, err := elf.Open(corePath)
 | 
				
			||||||
	if err != nil {
 | 
						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
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	exe, err := os.Open(exePath)
 | 
						exe, err := os.Open(exePath)
 | 
				
			||||||
@@ -288,37 +69,51 @@ func readCore(corePath, exePath string) (*Core, error) {
 | 
				
			|||||||
	memory := buildMemory(coreFile, exeELF, exe, notes)
 | 
						memory := buildMemory(coreFile, exeELF, exe, notes)
 | 
				
			||||||
	entryPoint := findEntryPoint(notes)
 | 
						entryPoint := findEntryPoint(notes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	core := &Core{
 | 
						p := &Process{
 | 
				
			||||||
		MemoryReader: memory,
 | 
							mem:         memory,
 | 
				
			||||||
		Threads:      map[int]*Thread{},
 | 
							Threads:     map[int]*Thread{},
 | 
				
			||||||
		entryPoint:   entryPoint,
 | 
							entryPoint:  entryPoint,
 | 
				
			||||||
 | 
							bi:          proc.NewBinaryInfo("linux", "amd64"),
 | 
				
			||||||
 | 
							breakpoints: proc.NewBreakpointMap(),
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var lastThread *Thread
 | 
						var lastThread *linuxAMD64Thread
 | 
				
			||||||
	for _, note := range notes {
 | 
						for _, note := range notes {
 | 
				
			||||||
		switch note.Type {
 | 
							switch note.Type {
 | 
				
			||||||
		case elf.NT_PRSTATUS:
 | 
							case elf.NT_PRSTATUS:
 | 
				
			||||||
			t := note.Desc.(*LinuxPrStatus)
 | 
								t := note.Desc.(*LinuxPrStatus)
 | 
				
			||||||
			lastThread = &Thread{t, nil, nil, proc.CommonThread{}}
 | 
								lastThread = &linuxAMD64Thread{linutil.AMD64Registers{Regs: &t.Reg}, t}
 | 
				
			||||||
			core.Threads[int(t.Pid)] = lastThread
 | 
								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:
 | 
							case NT_X86_XSTATE:
 | 
				
			||||||
			if lastThread != nil {
 | 
								if lastThread != nil {
 | 
				
			||||||
				lastThread.fpregs = note.Desc.(*proc.LinuxX86Xstate).Decode()
 | 
									lastThread.regs.Fpregs = note.Desc.(*linutil.AMD64Xstate).Decode()
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		case elf.NT_PRPSINFO:
 | 
							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 linuxAMD64Thread struct {
 | 
				
			||||||
type Core struct {
 | 
						regs linutil.AMD64Registers
 | 
				
			||||||
	proc.MemoryReader
 | 
						t    *LinuxPrStatus
 | 
				
			||||||
	Threads map[int]*Thread
 | 
					}
 | 
				
			||||||
	Pid     int
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
	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.
 | 
					// Note is a note from the PT_NOTE prog.
 | 
				
			||||||
@@ -415,8 +210,8 @@ func readNote(r io.ReadSeeker) (*Note, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		note.Desc = data
 | 
							note.Desc = data
 | 
				
			||||||
	case NT_X86_XSTATE:
 | 
						case NT_X86_XSTATE:
 | 
				
			||||||
		var fpregs proc.LinuxX86Xstate
 | 
							var fpregs linutil.AMD64Xstate
 | 
				
			||||||
		if err := proc.LinuxX86XstateRead(desc, true, &fpregs); err != nil {
 | 
							if err := linutil.AMD64XstateRead(desc, true, &fpregs); err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		note.Desc = &fpregs
 | 
							note.Desc = &fpregs
 | 
				
			||||||
@@ -516,7 +311,7 @@ type LinuxPrStatus struct {
 | 
				
			|||||||
	Sighold                      uint64
 | 
						Sighold                      uint64
 | 
				
			||||||
	Pid, Ppid, Pgrp, Sid         int32
 | 
						Pid, Ppid, Pgrp, Sid         int32
 | 
				
			||||||
	Utime, Stime, CUtime, CStime LinuxCoreTimeval
 | 
						Utime, Stime, CUtime, CStime LinuxCoreTimeval
 | 
				
			||||||
	Reg                          LinuxCoreRegisters
 | 
						Reg                          linutil.AMD64PtraceRegs
 | 
				
			||||||
	Fpvalid                      int32
 | 
						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
 | 
						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 {
 | 
					func resolveCallArg(inst *archInst, currentGoroutine bool, regs Registers, mem MemoryReadWriter, bininfo *BinaryInfo) *Location {
 | 
				
			||||||
	if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
 | 
						if inst.Op != x86asm.CALL && inst.Op != x86asm.LCALL {
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -110,7 +118,7 @@ func resolveCallArg(inst *archInst, currentGoroutine bool, regs Registers, mem M
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	file, line, fn := bininfo.PCToLine(pc)
 | 
						file, line, fn := bininfo.PCToLine(pc)
 | 
				
			||||||
	if fn == nil {
 | 
						if fn == nil {
 | 
				
			||||||
		return nil
 | 
							return &Location{PC: pc}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return &Location{PC: pc, File: file, Line: line, Fn: fn}
 | 
						return &Location{PC: pc, File: file, Line: line, Fn: fn}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -14,9 +14,9 @@ import (
 | 
				
			|||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
						"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
						"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/goversion"
 | 
						"github.com/go-delve/delve/pkg/goversion"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var errOperationOnSpecialFloat = errors.New("operations on non-finite floats not implemented")
 | 
					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":
 | 
						case "string":
 | 
				
			||||||
		switch argv.Kind {
 | 
							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:
 | 
							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)
 | 
								b, _ := constant.Int64Val(argv.Value)
 | 
				
			||||||
			s := string(b)
 | 
								s := string(b)
 | 
				
			||||||
			v.Value = constant.MakeString(s)
 | 
								v.Value = constant.MakeString(s)
 | 
				
			||||||
			v.Len = int64(len(s))
 | 
								v.Len = int64(len(s))
 | 
				
			||||||
			return v, nil
 | 
								return v, nil
 | 
				
			||||||
 | 
							case reflect.Slice, reflect.Array:
 | 
				
			||||||
		case reflect.Slice:
 | 
								var elem godwarf.Type
 | 
				
			||||||
			switch elemType := argv.RealType.(*godwarf.SliceType).ElemType.(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:
 | 
								case *godwarf.UintType:
 | 
				
			||||||
				if elemType.Name != "uint8" && elemType.Name != "byte" {
 | 
									if elemType.Name != "uint8" && elemType.Name != "byte" {
 | 
				
			||||||
					return nil, nil
 | 
										return nil, nil
 | 
				
			||||||
@@ -1198,7 +1208,7 @@ func (err *typeConvErr) Error() string {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
func (v *Variable) isType(typ godwarf.Type, kind reflect.Kind) error {
 | 
					func (v *Variable) isType(typ godwarf.Type, kind reflect.Kind) error {
 | 
				
			||||||
	if v.DwarfType != nil {
 | 
						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 &typeConvErr{v.DwarfType, typ}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return nil
 | 
							return nil
 | 
				
			||||||
@@ -1255,6 +1265,34 @@ func (v *Variable) isType(typ godwarf.Type, kind reflect.Kind) error {
 | 
				
			|||||||
	return nil
 | 
						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) {
 | 
					func (v *Variable) sliceAccess(idx int) (*Variable, error) {
 | 
				
			||||||
	if idx < 0 || int64(idx) >= v.Len {
 | 
						if idx < 0 || int64(idx) >= v.Len {
 | 
				
			||||||
		return nil, fmt.Errorf("index out of bounds")
 | 
							return nil, fmt.Errorf("index out of bounds")
 | 
				
			||||||
@@ -11,11 +11,10 @@ import (
 | 
				
			|||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"sort"
 | 
						"sort"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
						"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
						"github.com/go-delve/delve/pkg/dwarf/op"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
						"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/logflags"
 | 
						"github.com/go-delve/delve/pkg/logflags"
 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
					 | 
				
			||||||
	"golang.org/x/arch/x86/x86asm"
 | 
						"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.
 | 
					// 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
 | 
					// See runtime.debugCallV1 in $GOROOT/src/runtime/asm_amd64.s for a
 | 
				
			||||||
// description of the protocol.
 | 
					// 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()
 | 
						bi := p.BinInfo()
 | 
				
			||||||
	if !p.Common().fncallEnabled {
 | 
						if !p.Common().fncallEnabled {
 | 
				
			||||||
		return errFuncCallUnsupportedBackend
 | 
							return errFuncCallUnsupportedBackend
 | 
				
			||||||
@@ -126,7 +125,7 @@ func CallFunction(p Process, expr string, retLoadCfg *LoadConfig) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	argmem, err := funcCallArgFrame(fn, argvars, g, bi)
 | 
						argmem, err := funcCallArgFrame(fn, argvars, g, bi, checkEscape)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -153,10 +152,7 @@ func CallFunction(p Process, expr string, retLoadCfg *LoadConfig) error {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func fncallLog(fmtstr string, args ...interface{}) {
 | 
					func fncallLog(fmtstr string, args ...interface{}) {
 | 
				
			||||||
	if !logflags.FnCall() {
 | 
						logflags.FnCallLogger().Infof(fmtstr, args...)
 | 
				
			||||||
		return
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	logrus.WithFields(logrus.Fields{"layer": "proc", "kind": "fncall"}).Infof(fmtstr, args...)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// writePointer writes val as an architecture pointer at addr in mem.
 | 
					// 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 {
 | 
						if fnvar.Kind != reflect.Func {
 | 
				
			||||||
		return nil, 0, nil, fmt.Errorf("expression %q is not a function", exprToString(callexpr.Fun))
 | 
							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 {
 | 
						if fnvar.Unreadable != nil {
 | 
				
			||||||
		return nil, 0, nil, fnvar.Unreadable
 | 
							return nil, 0, nil, fnvar.Unreadable
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -259,7 +255,7 @@ type funcCallArg struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// funcCallArgFrame checks type and pointer escaping for the arguments and
 | 
					// funcCallArgFrame checks type and pointer escaping for the arguments and
 | 
				
			||||||
// returns the argument frame.
 | 
					// 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)
 | 
						argFrameSize, formalArgs, err := funcCallArgs(fn, bi, false)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -278,9 +274,11 @@ func funcCallArgFrame(fn *Function, actualArgs []*Variable, g *G, bi *BinaryInfo
 | 
				
			|||||||
		formalArg := &formalArgs[i]
 | 
							formalArg := &formalArgs[i]
 | 
				
			||||||
		actualArg := actualArgs[i]
 | 
							actualArg := actualArgs[i]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		//TODO(aarzilli): only apply the escapeCheck to leaking parameters.
 | 
							if checkEscape {
 | 
				
			||||||
		if err := escapeCheck(actualArg, formalArg.name, g); err != nil {
 | 
								//TODO(aarzilli): only apply the escapeCheck to leaking parameters.
 | 
				
			||||||
			return nil, fmt.Errorf("can not pass %s to %s: %v", actualArg.Name, formalArg.name, err)
 | 
								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
 | 
							//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 {
 | 
					func escapeCheck(v *Variable, name string, g *G) error {
 | 
				
			||||||
	switch v.Kind {
 | 
						switch v.Kind {
 | 
				
			||||||
	case reflect.Ptr:
 | 
						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)
 | 
							return escapeCheckPointer(w.Addr, name, g)
 | 
				
			||||||
	case reflect.Chan, reflect.String, reflect.Slice:
 | 
						case reflect.Chan, reflect.String, reflect.Slice:
 | 
				
			||||||
		return escapeCheckPointer(v.Base, name, g)
 | 
							return escapeCheckPointer(v.Base, name, g)
 | 
				
			||||||
@@ -68,23 +68,20 @@ import (
 | 
				
			|||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"go/ast"
 | 
						"go/ast"
 | 
				
			||||||
	"io/ioutil"
 | 
					 | 
				
			||||||
	"net"
 | 
						"net"
 | 
				
			||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/arch/x86/x86asm"
 | 
						"golang.org/x/arch/x86/x86asm"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/logflags"
 | 
						"github.com/go-delve/delve/pkg/logflags"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/proc"
 | 
						"github.com/go-delve/delve/pkg/proc"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/proc/linutil"
 | 
						"github.com/go-delve/delve/pkg/proc/linutil"
 | 
				
			||||||
	"github.com/mattn/go-isatty"
 | 
						isatty "github.com/mattn/go-isatty"
 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
					 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -130,7 +127,7 @@ type Process struct {
 | 
				
			|||||||
	common proc.CommonProcess
 | 
						common proc.CommonProcess
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Thread is a thread.
 | 
					// Thread represents an operating system thread.
 | 
				
			||||||
type Thread struct {
 | 
					type Thread struct {
 | 
				
			||||||
	ID                int
 | 
						ID                int
 | 
				
			||||||
	strID             string
 | 
						strID             string
 | 
				
			||||||
@@ -142,8 +139,7 @@ type Thread struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ErrBackendUnavailable is returned when the stub program can not be found.
 | 
					// ErrBackendUnavailable is returned when the stub program can not be found.
 | 
				
			||||||
type ErrBackendUnavailable struct {
 | 
					type ErrBackendUnavailable struct{}
 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (err *ErrBackendUnavailable) Error() string {
 | 
					func (err *ErrBackendUnavailable) Error() string {
 | 
				
			||||||
	return "backend unavailable"
 | 
						return "backend unavailable"
 | 
				
			||||||
@@ -172,11 +168,7 @@ type gdbRegister struct {
 | 
				
			|||||||
// Detach.
 | 
					// Detach.
 | 
				
			||||||
// Use Listen, Dial or Connect to complete connection.
 | 
					// Use Listen, Dial or Connect to complete connection.
 | 
				
			||||||
func New(process *os.Process) *Process {
 | 
					func New(process *os.Process) *Process {
 | 
				
			||||||
	logger := logrus.New().WithFields(logrus.Fields{"layer": "gdbconn"})
 | 
						logger := logflags.GdbWireLogger()
 | 
				
			||||||
	logger.Logger.Level = logrus.DebugLevel
 | 
					 | 
				
			||||||
	if !logflags.GdbWire() {
 | 
					 | 
				
			||||||
		logger.Logger.Out = ioutil.Discard
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	p := &Process{
 | 
						p := &Process{
 | 
				
			||||||
		conn: gdbConn{
 | 
							conn: gdbConn{
 | 
				
			||||||
			maxTransmitAttempts: maxTransmitAttempts,
 | 
								maxTransmitAttempts: maxTransmitAttempts,
 | 
				
			||||||
@@ -205,7 +197,7 @@ func New(process *os.Process) *Process {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Listen waits for a connection from the stub.
 | 
					// 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)
 | 
						acceptChan := make(chan net.Conn)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	go func() {
 | 
						go func() {
 | 
				
			||||||
@@ -219,7 +211,7 @@ func (p *Process) Listen(listener net.Listener, path string, pid int) error {
 | 
				
			|||||||
		if conn == nil {
 | 
							if conn == nil {
 | 
				
			||||||
			return errors.New("could not connect")
 | 
								return errors.New("could not connect")
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return p.Connect(conn, path, pid)
 | 
							return p.Connect(conn, path, pid, debugInfoDirs)
 | 
				
			||||||
	case status := <-p.waitChan:
 | 
						case status := <-p.waitChan:
 | 
				
			||||||
		listener.Close()
 | 
							listener.Close()
 | 
				
			||||||
		return fmt.Errorf("stub exited while waiting for connection: %v", status)
 | 
							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.
 | 
					// 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 {
 | 
						for {
 | 
				
			||||||
		conn, err := net.Dial("tcp", addr)
 | 
							conn, err := net.Dial("tcp", addr)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			return p.Connect(conn, path, pid)
 | 
								return p.Connect(conn, path, pid, debugInfoDirs)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		select {
 | 
							select {
 | 
				
			||||||
		case status := <-p.waitChan:
 | 
							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
 | 
					// program and the PID of the target process, both are optional, however
 | 
				
			||||||
// some stubs do not provide ways to determine path and pid automatically
 | 
					// some stubs do not provide ways to determine path and pid automatically
 | 
				
			||||||
// and Connect will be unable to function without knowing them.
 | 
					// 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.conn = conn
 | 
				
			||||||
 | 
					 | 
				
			||||||
	p.conn.pid = pid
 | 
						p.conn.pid = pid
 | 
				
			||||||
	err := p.conn.handshake()
 | 
						err := p.conn.handshake()
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -270,55 +261,7 @@ func (p *Process) Connect(conn net.Conn, path string, pid int) error {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if path == "" {
 | 
						if err := p.initialize(path, debugInfoDirs); err != nil {
 | 
				
			||||||
		// 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()
 | 
					 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -335,36 +278,6 @@ func (p *Process) Connect(conn net.Conn, path string, pid int) error {
 | 
				
			|||||||
			p.loadGInstrAddr = addr
 | 
								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
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -405,7 +318,7 @@ func getLdEnvVars() []string {
 | 
				
			|||||||
// LLDBLaunch starts an instance of lldb-server and connects to it, asking
 | 
					// LLDBLaunch starts an instance of lldb-server and connects to it, asking
 | 
				
			||||||
// it to launch the specified target program with the specified arguments
 | 
					// it to launch the specified target program with the specified arguments
 | 
				
			||||||
// (cmd) on the specified directory wd.
 | 
					// (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 {
 | 
						switch runtime.GOOS {
 | 
				
			||||||
	case "windows":
 | 
						case "windows":
 | 
				
			||||||
		return nil, ErrUnsupportedOS
 | 
							return nil, ErrUnsupportedOS
 | 
				
			||||||
@@ -482,9 +395,9 @@ func LLDBLaunch(cmd []string, wd string, foreground bool) (*Process, error) {
 | 
				
			|||||||
	p.conn.isDebugserver = isDebugserver
 | 
						p.conn.isDebugserver = isDebugserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if listener != nil {
 | 
						if listener != nil {
 | 
				
			||||||
		err = p.Listen(listener, cmd[0], 0)
 | 
							err = p.Listen(listener, cmd[0], 0, debugInfoDirs)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		err = p.Dial(port, cmd[0], 0)
 | 
							err = p.Dial(port, cmd[0], 0, debugInfoDirs)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							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
 | 
					// 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 some stubs that do not provide an automated way of determining it
 | 
				
			||||||
// (for example debugserver).
 | 
					// (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" {
 | 
						if runtime.GOOS == "windows" {
 | 
				
			||||||
		return nil, ErrUnsupportedOS
 | 
							return nil, ErrUnsupportedOS
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	isDebugserver := false
 | 
						isDebugserver := false
 | 
				
			||||||
	var proc *exec.Cmd
 | 
						var process *exec.Cmd
 | 
				
			||||||
	var listener net.Listener
 | 
						var listener net.Listener
 | 
				
			||||||
	var port string
 | 
						var port string
 | 
				
			||||||
	if _, err := os.Stat(debugserverExecutable); err == nil {
 | 
						if _, err := os.Stat(debugserverExecutable); err == nil {
 | 
				
			||||||
@@ -512,32 +425,31 @@ func LLDBAttach(pid int, path string) (*Process, error) {
 | 
				
			|||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								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 {
 | 
						} else {
 | 
				
			||||||
		if _, err := exec.LookPath("lldb-server"); err != nil {
 | 
							if _, err := exec.LookPath("lldb-server"); err != nil {
 | 
				
			||||||
			return nil, &ErrBackendUnavailable{}
 | 
								return nil, &ErrBackendUnavailable{}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		port = unusedPort()
 | 
							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
 | 
						process.Stdout = os.Stdout
 | 
				
			||||||
	proc.Stderr = os.Stderr
 | 
						process.Stderr = os.Stderr
 | 
				
			||||||
 | 
						process.SysProcAttr = sysProcAttr(false)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	proc.SysProcAttr = sysProcAttr(false)
 | 
						err := process.Start()
 | 
				
			||||||
 | 
					 | 
				
			||||||
	err := proc.Start()
 | 
					 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	p := New(proc.Process)
 | 
						p := New(process.Process)
 | 
				
			||||||
	p.conn.isDebugserver = isDebugserver
 | 
						p.conn.isDebugserver = isDebugserver
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if listener != nil {
 | 
						if listener != nil {
 | 
				
			||||||
		err = p.Listen(listener, path, pid)
 | 
							err = p.Listen(listener, path, pid, debugInfoDirs)
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		err = p.Dial(port, path, pid)
 | 
							err = p.Dial(port, path, pid, debugInfoDirs)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -545,10 +457,80 @@ func LLDBAttach(pid int, path string) (*Process, error) {
 | 
				
			|||||||
	return p, nil
 | 
						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
 | 
					// executable path. This command is not supported by all stubs and not all
 | 
				
			||||||
// stubs will report both the PID and executable path.
 | 
					// 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)
 | 
						pi, err := p.conn.queryProcessInfo(pid)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, "", err
 | 
							return 0, "", err
 | 
				
			||||||
@@ -659,7 +641,7 @@ func (p *Process) ContinueOnce() (proc.Thread, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	// resume all threads
 | 
						// resume all threads
 | 
				
			||||||
	var threadID string
 | 
						var threadID string
 | 
				
			||||||
	var sig uint8 = 0
 | 
						var sig uint8
 | 
				
			||||||
	var tu = threadUpdater{p: p}
 | 
						var tu = threadUpdater{p: p}
 | 
				
			||||||
	var err error
 | 
						var err error
 | 
				
			||||||
continueLoop:
 | 
					continueLoop:
 | 
				
			||||||
@@ -697,6 +679,14 @@ continueLoop:
 | 
				
			|||||||
		// process so we must stop here.
 | 
							// process so we must stop here.
 | 
				
			||||||
		case 0x91, 0x92, 0x93, 0x94, 0x95, 0x96: /* TARGET_EXC_BAD_ACCESS */
 | 
							case 0x91, 0x92, 0x93, 0x94, 0x95, 0x96: /* TARGET_EXC_BAD_ACCESS */
 | 
				
			||||||
			break continueLoop
 | 
								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:
 | 
							default:
 | 
				
			||||||
			// any other signal is always propagated to inferior
 | 
								// any other signal is always propagated to inferior
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -706,13 +696,19 @@ continueLoop:
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						if p.BinInfo().GOOS == "linux" {
 | 
				
			||||||
 | 
							if err := linutil.ElfUpdateSharedObjects(p); err != nil {
 | 
				
			||||||
 | 
								return nil, err
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if err := p.setCurrentBreakpoints(); err != nil {
 | 
						if err := p.setCurrentBreakpoints(); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for _, thread := range p.threads {
 | 
						for _, thread := range p.threads {
 | 
				
			||||||
		if thread.strID == threadID {
 | 
							if thread.strID == threadID {
 | 
				
			||||||
			var err error = nil
 | 
								var err error
 | 
				
			||||||
			switch sig {
 | 
								switch sig {
 | 
				
			||||||
			case 0x91:
 | 
								case 0x91:
 | 
				
			||||||
				err = errors.New("bad access")
 | 
									err = errors.New("bad access")
 | 
				
			||||||
@@ -734,6 +730,13 @@ continueLoop:
 | 
				
			|||||||
	return nil, fmt.Errorf("could not find thread %s", threadID)
 | 
						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.
 | 
					// StepInstruction will step exactly one CPU instruction.
 | 
				
			||||||
func (p *Process) StepInstruction() error {
 | 
					func (p *Process) StepInstruction() error {
 | 
				
			||||||
	thread := p.currentThread
 | 
						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) {
 | 
					func (p *Process) writeBreakpoint(addr uint64) (string, int, *proc.Function, []byte, error) {
 | 
				
			||||||
	f, l, fn := p.bi.PCToLine(uint64(addr))
 | 
						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 {
 | 
						if err := p.conn.setBreakpoint(addr); err != nil {
 | 
				
			||||||
		return "", 0, nil, nil, err
 | 
							return "", 0, nil, nil, err
 | 
				
			||||||
@@ -1310,7 +1310,6 @@ func (t *Thread) Blocked() bool {
 | 
				
			|||||||
	default:
 | 
						default:
 | 
				
			||||||
		return strings.HasPrefix(fn.Name, "syscall.Syscall") || strings.HasPrefix(fn.Name, "syscall.RawSyscall")
 | 
							return strings.HasPrefix(fn.Name, "syscall.Syscall") || strings.HasPrefix(fn.Name, "syscall.RawSyscall")
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return false
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// loadGInstr returns the correct MOV instruction for the current
 | 
					// 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.
 | 
					// SetCurrentBreakpoint will find and set the threads current breakpoint.
 | 
				
			||||||
func (thread *Thread) SetCurrentBreakpoint() error {
 | 
					func (t *Thread) SetCurrentBreakpoint() error {
 | 
				
			||||||
	thread.clearBreakpointState()
 | 
						t.clearBreakpointState()
 | 
				
			||||||
	regs, err := thread.Registers(false)
 | 
						regs, err := t.Registers(false)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	pc := regs.PC()
 | 
						pc := regs.PC()
 | 
				
			||||||
	if bp, ok := thread.p.FindBreakpoint(pc); ok {
 | 
						if bp, ok := t.p.FindBreakpoint(pc); ok {
 | 
				
			||||||
		if thread.regs.PC() != bp.Addr {
 | 
							if t.regs.PC() != bp.Addr {
 | 
				
			||||||
			if err := thread.SetPC(bp.Addr); err != nil {
 | 
								if err := t.SetPC(bp.Addr); err != nil {
 | 
				
			||||||
				return err
 | 
									return err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		thread.CurrentBreakpoint = bp.CheckCondition(thread)
 | 
							t.CurrentBreakpoint = bp.CheckCondition(t)
 | 
				
			||||||
		if thread.CurrentBreakpoint.Breakpoint != nil && thread.CurrentBreakpoint.Active {
 | 
							if t.CurrentBreakpoint.Breakpoint != nil && t.CurrentBreakpoint.Active {
 | 
				
			||||||
			if g, err := proc.GetG(thread); err == nil {
 | 
								if g, err := proc.GetG(t); err == nil {
 | 
				
			||||||
				thread.CurrentBreakpoint.HitCount[g.ID]++
 | 
									t.CurrentBreakpoint.HitCount[g.ID]++
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			thread.CurrentBreakpoint.TotalHitCount++
 | 
								t.CurrentBreakpoint.TotalHitCount++
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
@@ -1827,9 +1826,12 @@ func (t *Thread) SetDX(dx uint64) error {
 | 
				
			|||||||
	return t.p.conn.writeRegister(t.strID, reg.regnum, reg.value)
 | 
						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))
 | 
						r := make([]proc.Register, 0, len(regs.regsInfo))
 | 
				
			||||||
	for _, reginfo := range regs.regsInfo {
 | 
						for _, reginfo := range regs.regsInfo {
 | 
				
			||||||
 | 
							if reginfo.Group == "float" && !floatingPoint {
 | 
				
			||||||
 | 
								continue
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
		switch {
 | 
							switch {
 | 
				
			||||||
		case reginfo.Name == "eflags":
 | 
							case reginfo.Name == "eflags":
 | 
				
			||||||
			r = proc.AppendEflagReg(r, reginfo.Name, uint64(binary.LittleEndian.Uint32(regs.regs[reginfo.Name].value)))
 | 
								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:
 | 
							case reginfo.Bitsize == 64:
 | 
				
			||||||
			r = proc.AppendQwordReg(r, reginfo.Name, binary.LittleEndian.Uint64(regs.regs[reginfo.Name].value))
 | 
								r = proc.AppendQwordReg(r, reginfo.Name, binary.LittleEndian.Uint64(regs.regs[reginfo.Name].value))
 | 
				
			||||||
		case reginfo.Bitsize == 80:
 | 
							case reginfo.Bitsize == 80:
 | 
				
			||||||
 | 
								if !floatingPoint {
 | 
				
			||||||
 | 
									continue
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			idx := 0
 | 
								idx := 0
 | 
				
			||||||
			for _, stprefix := range []string{"stmm", "st"} {
 | 
								for _, stprefix := range []string{"stmm", "st"} {
 | 
				
			||||||
				if strings.HasPrefix(reginfo.Name, stprefix) {
 | 
									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]))
 | 
								r = proc.AppendX87Reg(r, idx, binary.LittleEndian.Uint16(value[8:]), binary.LittleEndian.Uint64(value[:8]))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		case reginfo.Bitsize == 128:
 | 
							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:
 | 
							case reginfo.Bitsize == 256:
 | 
				
			||||||
			if !strings.HasPrefix(strings.ToLower(reginfo.Name), "ymm") {
 | 
								if !strings.HasPrefix(strings.ToLower(reginfo.Name), "ymm") || !floatingPoint {
 | 
				
			||||||
				continue
 | 
									continue
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -16,8 +16,8 @@ import (
 | 
				
			|||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/logflags"
 | 
						"github.com/go-delve/delve/pkg/logflags"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/proc"
 | 
						"github.com/go-delve/delve/pkg/proc"
 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
						"github.com/sirupsen/logrus"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -227,7 +227,8 @@ type gdbRegisterInfo struct {
 | 
				
			|||||||
	Name    string `xml:"name,attr"`
 | 
						Name    string `xml:"name,attr"`
 | 
				
			||||||
	Bitsize int    `xml:"bitsize,attr"`
 | 
						Bitsize int    `xml:"bitsize,attr"`
 | 
				
			||||||
	Offset  int
 | 
						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,
 | 
					// 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
 | 
					// Replay starts an instance of rr in replay mode, with the specified trace
 | 
				
			||||||
// directory, and connects to it.
 | 
					// 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 {
 | 
						if err := checkRRAvailabe(); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -82,7 +82,7 @@ func Replay(tracedir string, quiet bool) (*Process, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	p := New(rrcmd.Process)
 | 
						p := New(rrcmd.Process)
 | 
				
			||||||
	p.tracedir = tracedir
 | 
						p.tracedir = tracedir
 | 
				
			||||||
	err = p.Dial(init.port, init.exe, 0)
 | 
						err = p.Dial(init.port, init.exe, 0, debugInfoDirs)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		rrcmd.Process.Kill()
 | 
							rrcmd.Process.Kill()
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -257,11 +257,11 @@ func splitQuotedFields(in string) []string {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// RecordAndReplay acts like calling Record and then Replay.
 | 
					// 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)
 | 
						tracedir, err = Record(cmd, wd, quiet)
 | 
				
			||||||
	if tracedir == "" {
 | 
						if tracedir == "" {
 | 
				
			||||||
		return nil, "", err
 | 
							return nil, "", err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	p, err = Replay(tracedir, quiet)
 | 
						p, err = Replay(tracedir, quiet, debugInfoDirs)
 | 
				
			||||||
	return p, tracedir, err
 | 
						return p, tracedir, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -68,6 +68,7 @@ type Info interface {
 | 
				
			|||||||
	// ErrProcessExited or ProcessDetachedError).
 | 
						// ErrProcessExited or ProcessDetachedError).
 | 
				
			||||||
	Valid() (bool, error)
 | 
						Valid() (bool, error)
 | 
				
			||||||
	BinInfo() *BinaryInfo
 | 
						BinInfo() *BinaryInfo
 | 
				
			||||||
 | 
						EntryPoint() (uint64, error)
 | 
				
			||||||
	// Common returns a struct with fields common to all backends
 | 
						// Common returns a struct with fields common to all backends
 | 
				
			||||||
	Common() *CommonProcess
 | 
						Common() *CommonProcess
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -86,6 +87,7 @@ type ThreadInfo interface {
 | 
				
			|||||||
// GoroutineInfo is an interface for getting information on running goroutines.
 | 
					// GoroutineInfo is an interface for getting information on running goroutines.
 | 
				
			||||||
type GoroutineInfo interface {
 | 
					type GoroutineInfo interface {
 | 
				
			||||||
	SelectedGoroutine() *G
 | 
						SelectedGoroutine() *G
 | 
				
			||||||
 | 
						SetSelectedGoroutine(*G)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ProcessManipulation is an interface for changing the execution state of a process.
 | 
					// 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 (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
						"github.com/go-delve/delve/pkg/dwarf/op"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const cacheEnabled = true
 | 
					const cacheEnabled = true
 | 
				
			||||||
@@ -93,7 +94,7 @@ type compositeMemory struct {
 | 
				
			|||||||
	data    []byte
 | 
						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{}}
 | 
						cmem := &compositeMemory{realmem: mem, regs: regs, pieces: pieces, data: []byte{}}
 | 
				
			||||||
	for _, piece := range pieces {
 | 
						for _, piece := range pieces {
 | 
				
			||||||
		if piece.IsRegister {
 | 
							if piece.IsRegister {
 | 
				
			||||||
@@ -102,6 +103,9 @@ func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []o
 | 
				
			|||||||
			if sz == 0 && len(pieces) == 1 {
 | 
								if sz == 0 && len(pieces) == 1 {
 | 
				
			||||||
				sz = len(reg)
 | 
									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]...)
 | 
								cmem.data = append(cmem.data, reg[:sz]...)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			buf := make([]byte, piece.Size)
 | 
								buf := make([]byte, piece.Size)
 | 
				
			||||||
@@ -109,7 +113,7 @@ func newCompositeMemory(mem MemoryReadWriter, regs op.DwarfRegisters, pieces []o
 | 
				
			|||||||
			cmem.data = append(cmem.data, buf...)
 | 
								cmem.data = append(cmem.data, buf...)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return cmem
 | 
						return cmem, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (mem *compositeMemory) ReadMemory(data []byte, addr uintptr) (int, error) {
 | 
					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 {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								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)
 | 
							addr, _ := constant.Int64Val(v.Value)
 | 
				
			||||||
		return v.newVariable(v.Name, uintptr(addr), rtyp, mem), nil
 | 
							return v.newVariable(v.Name, uintptr(addr), rtyp, mem), nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -6,18 +6,18 @@ import (
 | 
				
			|||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/proc"
 | 
						"github.com/go-delve/delve/pkg/proc"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation")
 | 
					var ErrNativeBackendDisabled = errors.New("native backend disabled during compilation")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Launch returns ErrNativeBackendDisabled.
 | 
					// 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
 | 
						return nil, ErrNativeBackendDisabled
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Attach returns ErrNativeBackendDisabled.
 | 
					// Attach returns ErrNativeBackendDisabled.
 | 
				
			||||||
func Attach(pid int) (*Process, error) {
 | 
					func Attach(pid int, _ []string) (*Process, error) {
 | 
				
			||||||
	return nil, ErrNativeBackendDisabled
 | 
						return nil, ErrNativeBackendDisabled
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -75,7 +75,9 @@ func (dbp *Process) detach(kill bool) error {
 | 
				
			|||||||
	panic(ErrNativeBackendDisabled)
 | 
						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)
 | 
						panic(ErrNativeBackendDisabled)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -85,17 +87,17 @@ func (t *Thread) Blocked() bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetPC sets the value of the PC register.
 | 
					// SetPC sets the value of the PC register.
 | 
				
			||||||
func (thread *Thread) SetPC(pc uint64) error {
 | 
					func (t *Thread) SetPC(pc uint64) error {
 | 
				
			||||||
	panic(ErrNativeBackendDisabled)
 | 
						panic(ErrNativeBackendDisabled)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetSP sets the value of the SP register.
 | 
					// SetSP sets the value of the SP register.
 | 
				
			||||||
func (thread *Thread) SetSP(sp uint64) error {
 | 
					func (t *Thread) SetSP(sp uint64) error {
 | 
				
			||||||
	panic(ErrNativeBackendDisabled)
 | 
						panic(ErrNativeBackendDisabled)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetDX sets the value of the DX register.
 | 
					// SetDX sets the value of the DX register.
 | 
				
			||||||
func (thread *Thread) SetDX(dx uint64) error {
 | 
					func (t *Thread) SetDX(dx uint64) error {
 | 
				
			||||||
	panic(ErrNativeBackendDisabled)
 | 
						panic(ErrNativeBackendDisabled)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -126,3 +128,5 @@ func (t *Thread) restoreRegisters(sr proc.Registers) error {
 | 
				
			|||||||
func (t *Thread) Stopped() bool {
 | 
					func (t *Thread) Stopped() bool {
 | 
				
			||||||
	panic(ErrNativeBackendDisabled)
 | 
						panic(ErrNativeBackendDisabled)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func initialize(dbp *Process) error { return nil }
 | 
				
			||||||
@@ -6,13 +6,14 @@ import (
 | 
				
			|||||||
	"runtime"
 | 
						"runtime"
 | 
				
			||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/proc"
 | 
						"github.com/go-delve/delve/pkg/proc"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Process represents all of the information the debugger
 | 
					// Process represents all of the information the debugger
 | 
				
			||||||
// is holding onto regarding the process we are debugging.
 | 
					// is holding onto regarding the process we are debugging.
 | 
				
			||||||
type Process struct {
 | 
					type Process struct {
 | 
				
			||||||
	bi  *proc.BinaryInfo
 | 
						bi *proc.BinaryInfo
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	pid int // Process Pid
 | 
						pid int // Process Pid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Breakpoint table, holds information on breakpoints.
 | 
						// Breakpoint table, holds information on breakpoints.
 | 
				
			||||||
@@ -184,32 +185,6 @@ func (dbp *Process) Breakpoints() *proc.BreakpointMap {
 | 
				
			|||||||
	return &dbp.breakpoints
 | 
						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
 | 
					// RequestManualStop sets the `halt` flag and
 | 
				
			||||||
// sends SIGSTOP to all threads.
 | 
					// sends SIGSTOP to all threads.
 | 
				
			||||||
func (dbp *Process) RequestManualStop() error {
 | 
					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) {
 | 
					func (dbp *Process) writeBreakpoint(addr uint64) (string, int, *proc.Function, []byte, error) {
 | 
				
			||||||
	f, l, fn := dbp.bi.PCToLine(uint64(addr))
 | 
						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())
 | 
						originalData := make([]byte, dbp.bi.Arch.BreakpointSize())
 | 
				
			||||||
	_, err := dbp.currentThread.ReadMemory(originalData, uintptr(addr))
 | 
						_, err := dbp.currentThread.ReadMemory(originalData, uintptr(addr))
 | 
				
			||||||
@@ -379,26 +351,23 @@ func (dbp *Process) FindBreakpoint(pc uint64) (*proc.Breakpoint, bool) {
 | 
				
			|||||||
	return nil, false
 | 
						return nil, false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Returns a new Process struct.
 | 
					// initialize will ensure that all relevant information is loaded
 | 
				
			||||||
func initializeDebugProcess(dbp *Process, path string) (*Process, error) {
 | 
					// so the process is ready to be debugged.
 | 
				
			||||||
	err := dbp.LoadInformation(path)
 | 
					func (dbp *Process) initialize(path string, debugInfoDirs []string) error {
 | 
				
			||||||
	if err != nil {
 | 
						if err := initialize(dbp); err != nil {
 | 
				
			||||||
		return dbp, err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					 | 
				
			||||||
	if err := dbp.updateThreadList(); err != nil {
 | 
						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
 | 
					// SetSelectedGoroutine will set internally the goroutine that should be
 | 
				
			||||||
	// because without calling SetGStructOffset we can not read the G struct of currentThread
 | 
					// the default for any command executed, the goroutine being actively
 | 
				
			||||||
	// but without calling updateThreadList we can not examine memory to determine
 | 
					// followed.
 | 
				
			||||||
	// the offset of g struct inside TLS
 | 
					func (dbp *Process) SetSelectedGoroutine(g *proc.G) {
 | 
				
			||||||
	dbp.selectedGoroutine, _ = proc.GetG(dbp.currentThread)
 | 
						dbp.selectedGoroutine = g
 | 
				
			||||||
 | 
					 | 
				
			||||||
	proc.CreateUnrecoveredPanicBreakpoint(dbp, dbp.writeBreakpoint, &dbp.breakpoints)
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return dbp, nil
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ClearInternalBreakpoints will clear all non-user set breakpoints. These
 | 
					// ClearInternalBreakpoints will clear all non-user set breakpoints. These
 | 
				
			||||||
@@ -13,12 +13,11 @@ import (
 | 
				
			|||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"unsafe"
 | 
						"unsafe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sys "golang.org/x/sys/unix"
 | 
						sys "golang.org/x/sys/unix"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/proc"
 | 
						"github.com/go-delve/delve/pkg/proc"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// OSProcessDetails holds Darwin specific information.
 | 
					// OSProcessDetails holds Darwin specific information.
 | 
				
			||||||
@@ -38,7 +37,7 @@ type OSProcessDetails struct {
 | 
				
			|||||||
// custom fork/exec process in order to take advantage of
 | 
					// custom fork/exec process in order to take advantage of
 | 
				
			||||||
// PT_SIGEXC on Darwin which will turn Unix signals into
 | 
					// PT_SIGEXC on Darwin which will turn Unix signals into
 | 
				
			||||||
// Mach exceptions.
 | 
					// 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
 | 
						// check that the argument to Launch is an executable file
 | 
				
			||||||
	if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
 | 
						if fi, staterr := os.Stat(cmd[0]); staterr == nil && (fi.Mode()&0111) == 0 {
 | 
				
			||||||
		return nil, proc.ErrNotExecutable
 | 
							return nil, proc.ErrNotExecutable
 | 
				
			||||||
@@ -119,7 +118,7 @@ func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dbp.os.initialized = true
 | 
						dbp.os.initialized = true
 | 
				
			||||||
	dbp, err = initializeDebugProcess(dbp, argv0Go)
 | 
						err = dbp.initialize(argv0Go, []string{})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							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.
 | 
					// 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)
 | 
						dbp := New(pid)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	kret := C.acquire_mach_task(C.int(pid),
 | 
						kret := C.acquire_mach_task(C.int(pid),
 | 
				
			||||||
@@ -155,7 +154,7 @@ func Attach(pid int) (*Process, error) {
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dbp, err = initializeDebugProcess(dbp, "")
 | 
						err = dbp.initialize("", []string{})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		dbp.Detach(false)
 | 
							dbp.Detach(false)
 | 
				
			||||||
		return nil, err
 | 
							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) {
 | 
					func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
 | 
				
			||||||
	var status sys.WaitStatus
 | 
						var status sys.WaitStatus
 | 
				
			||||||
	wpid, err := sys.Wait4(pid, &status, options, nil)
 | 
						wpid, err := sys.Wait4(pid, &status, options, nil)
 | 
				
			||||||
@@ -464,7 +459,9 @@ func (dbp *Process) detach(kill bool) error {
 | 
				
			|||||||
	return PtraceDetach(dbp.pid, 0)
 | 
						return PtraceDetach(dbp.pid, 0)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (dbp *Process) entryPoint() (uint64, error) {
 | 
					func (dbp *Process) EntryPoint() (uint64, error) {
 | 
				
			||||||
	//TODO(aarzilli): implement this
 | 
						//TODO(aarzilli): implement this
 | 
				
			||||||
	return 0, nil
 | 
						return 0, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					func initialize(dbp *Process) error { return nil }
 | 
				
			||||||
@@ -12,15 +12,15 @@ import (
 | 
				
			|||||||
	"regexp"
 | 
						"regexp"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"syscall"
 | 
						"syscall"
 | 
				
			||||||
	"time"
 | 
						"time"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sys "golang.org/x/sys/unix"
 | 
						sys "golang.org/x/sys/unix"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/proc"
 | 
						"github.com/go-delve/delve/pkg/proc"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/proc/linutil"
 | 
						"github.com/go-delve/delve/pkg/proc/linutil"
 | 
				
			||||||
	"github.com/mattn/go-isatty"
 | 
					
 | 
				
			||||||
 | 
						isatty "github.com/mattn/go-isatty"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Process statuses
 | 
					// Process statuses
 | 
				
			||||||
@@ -46,7 +46,9 @@ type OSProcessDetails struct {
 | 
				
			|||||||
// Launch creates and begins debugging a new process. First entry in
 | 
					// Launch creates and begins debugging a new process. First entry in
 | 
				
			||||||
// `cmd` is the program to run, and then rest are the arguments
 | 
					// `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.
 | 
					// 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 (
 | 
						var (
 | 
				
			||||||
		process *exec.Cmd
 | 
							process *exec.Cmd
 | 
				
			||||||
		err     error
 | 
							err     error
 | 
				
			||||||
@@ -88,11 +90,16 @@ func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("waiting for target execve failed: %s", err)
 | 
							return nil, fmt.Errorf("waiting for target execve failed: %s", err)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	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.
 | 
					// Attach to an existing process with the given PID. Once attached, if
 | 
				
			||||||
func Attach(pid int) (*Process, error) {
 | 
					// 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 := New(pid)
 | 
				
			||||||
	dbp.common = proc.NewCommonProcess(true)
 | 
						dbp.common = proc.NewCommonProcess(true)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -106,7 +113,7 @@ func Attach(pid int) (*Process, error) {
 | 
				
			|||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dbp, err = initializeDebugProcess(dbp, "")
 | 
						err = dbp.initialize(findExecutable("", dbp.pid), debugInfoDirs)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		dbp.Detach(false)
 | 
							dbp.Detach(false)
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -114,6 +121,33 @@ func Attach(pid int) (*Process, error) {
 | 
				
			|||||||
	return dbp, nil
 | 
						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.
 | 
					// kill kills the target process.
 | 
				
			||||||
func (dbp *Process) kill() (err error) {
 | 
					func (dbp *Process) kill() (err error) {
 | 
				
			||||||
	if dbp.exited {
 | 
						if dbp.exited {
 | 
				
			||||||
@@ -199,7 +233,7 @@ func (dbp *Process) updateThreadList() error {
 | 
				
			|||||||
			return err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return nil
 | 
						return linutil.ElfUpdateSharedObjects(dbp)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func findExecutable(path string, pid int) string {
 | 
					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) {
 | 
					func (dbp *Process) loadProcessInformation() {
 | 
				
			||||||
	defer wg.Done()
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	comm, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/comm", dbp.pid))
 | 
					 | 
				
			||||||
	if err == nil {
 | 
					 | 
				
			||||||
		// removes newline character
 | 
					 | 
				
			||||||
		comm = bytes.TrimSuffix(comm, []byte("\n"))
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	if comm == nil || len(comm) <= 0 {
 | 
					 | 
				
			||||||
		stat, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/stat", dbp.pid))
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			fmt.Printf("Could not read proc stat: %v\n", err)
 | 
					 | 
				
			||||||
			os.Exit(1)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		expr := fmt.Sprintf("%d\\s*\\((.*)\\)", dbp.pid)
 | 
					 | 
				
			||||||
		rexp, err := regexp.Compile(expr)
 | 
					 | 
				
			||||||
		if err != nil {
 | 
					 | 
				
			||||||
			fmt.Printf("Regexp compile error: %v\n", err)
 | 
					 | 
				
			||||||
			os.Exit(1)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		match := rexp.FindSubmatch(stat)
 | 
					 | 
				
			||||||
		if match == nil {
 | 
					 | 
				
			||||||
			fmt.Printf("No match found using regexp '%s' in /proc/%d/stat\n", expr, dbp.pid)
 | 
					 | 
				
			||||||
			os.Exit(1)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		comm = match[1]
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
	dbp.os.comm = strings.Replace(string(comm), "%", "%%", -1)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func status(pid int, comm string) rune {
 | 
					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
 | 
						// set breakpoints on all threads
 | 
				
			||||||
	for _, th := range dbp.threads {
 | 
						for _, th := range dbp.threads {
 | 
				
			||||||
		if th.CurrentBreakpoint.Breakpoint == nil {
 | 
							if th.CurrentBreakpoint.Breakpoint == nil {
 | 
				
			||||||
@@ -479,7 +489,9 @@ func (dbp *Process) detach(kill bool) error {
 | 
				
			|||||||
	return nil
 | 
						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))
 | 
						auxvbuf, err := ioutil.ReadFile(fmt.Sprintf("/proc/%d/auxv", dbp.pid))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return 0, fmt.Errorf("could not read auxiliary vector: %v", err)
 | 
							return 0, fmt.Errorf("could not read auxiliary vector: %v", err)
 | 
				
			||||||
@@ -7,13 +7,12 @@ import (
 | 
				
			|||||||
	"os"
 | 
						"os"
 | 
				
			||||||
	"os/exec"
 | 
						"os/exec"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"sync"
 | 
					 | 
				
			||||||
	"syscall"
 | 
						"syscall"
 | 
				
			||||||
	"unsafe"
 | 
						"unsafe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sys "golang.org/x/sys/windows"
 | 
						sys "golang.org/x/sys/windows"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/proc"
 | 
						"github.com/go-delve/delve/pkg/proc"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// OSProcessDetails holds Windows specific information.
 | 
					// 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.
 | 
					// 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])
 | 
						argv0Go, err := filepath.Abs(cmd[0])
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -77,11 +76,14 @@ func Launch(cmd []string, wd string, foreground bool) (*Process, error) {
 | 
				
			|||||||
	dbp.pid = p.Pid
 | 
						dbp.pid = p.Pid
 | 
				
			||||||
	dbp.childProcess = true
 | 
						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 initialize(dbp *Process) error {
 | 
				
			||||||
func newDebugProcess(dbp *Process, exepath string) (*Process, error) {
 | 
					 | 
				
			||||||
	// It should not actually be possible for the
 | 
						// It should not actually be possible for the
 | 
				
			||||||
	// call to waitForDebugEvent to fail, since Windows
 | 
						// call to waitForDebugEvent to fail, since Windows
 | 
				
			||||||
	// will always fire a CREATE_PROCESS_DEBUG_EVENT event
 | 
						// 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)
 | 
							tid, exitCode, err = dbp.waitForDebugEvent(waitBlocking)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if tid == 0 {
 | 
						if tid == 0 {
 | 
				
			||||||
		dbp.postExit()
 | 
							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
 | 
						// Suspend all threads so that the call to _ContinueDebugEvent will
 | 
				
			||||||
	// not resume the target.
 | 
						// not resume the target.
 | 
				
			||||||
	for _, thread := range dbp.threads {
 | 
						for _, thread := range dbp.threads {
 | 
				
			||||||
		_, err := _SuspendThread(thread.os.hThread)
 | 
							_, err := _SuspendThread(thread.os.hThread)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	dbp.execPtraceFunc(func() {
 | 
						dbp.execPtraceFunc(func() {
 | 
				
			||||||
		err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
 | 
							err = _ContinueDebugEvent(uint32(dbp.pid), uint32(dbp.os.breakThread), _DBG_CONTINUE)
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	if err != nil {
 | 
						return err
 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	return initializeDebugProcess(dbp, exepath)
 | 
					 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// findExePath searches for process pid, and returns its executable path.
 | 
					// 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.
 | 
					// 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.
 | 
						// TODO: Probably should have SeDebugPrivilege before starting here.
 | 
				
			||||||
	err := _DebugActiveProcess(uint32(pid))
 | 
						err := _DebugActiveProcess(uint32(pid))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
@@ -163,11 +161,9 @@ func Attach(pid int) (*Process, error) {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	dbp, err := newDebugProcess(New(pid), exepath)
 | 
						dbp := New(pid)
 | 
				
			||||||
	if err != nil {
 | 
						if err = dbp.initialize(exepath, []string{}); err != nil {
 | 
				
			||||||
		if dbp != nil {
 | 
							dbp.Detach(true)
 | 
				
			||||||
			dbp.Detach(false)
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return dbp, nil
 | 
						return dbp, nil
 | 
				
			||||||
@@ -245,6 +241,8 @@ const (
 | 
				
			|||||||
	waitDontHandleExceptions
 | 
						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) {
 | 
					func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, exitCode int, err error) {
 | 
				
			||||||
	var debugEvent _DEBUG_EVENT
 | 
						var debugEvent _DEBUG_EVENT
 | 
				
			||||||
	shouldExit := false
 | 
						shouldExit := false
 | 
				
			||||||
@@ -346,6 +344,10 @@ func (dbp *Process) waitForDebugEvent(flags waitForDebugEventFlags) (threadID, e
 | 
				
			|||||||
			case _EXCEPTION_SINGLE_STEP:
 | 
								case _EXCEPTION_SINGLE_STEP:
 | 
				
			||||||
				dbp.os.breakThread = tid
 | 
									dbp.os.breakThread = tid
 | 
				
			||||||
				return tid, 0, nil
 | 
									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:
 | 
								default:
 | 
				
			||||||
				continueStatus = _DBG_EXCEPTION_NOT_HANDLED
 | 
									continueStatus = _DBG_EXCEPTION_NOT_HANDLED
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
@@ -386,10 +388,6 @@ func (dbp *Process) trapWait(pid int) (*Thread, error) {
 | 
				
			|||||||
	return th, nil
 | 
						return th, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (dbp *Process) loadProcessInformation(wg *sync.WaitGroup) {
 | 
					 | 
				
			||||||
	wg.Done()
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
 | 
					func (dbp *Process) wait(pid, options int) (int, *sys.WaitStatus, error) {
 | 
				
			||||||
	return 0, nil, fmt.Errorf("not implemented: wait")
 | 
						return 0, nil, fmt.Errorf("not implemented: wait")
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -482,7 +480,7 @@ func (dbp *Process) detach(kill bool) error {
 | 
				
			|||||||
	return _DebugActiveProcessStop(uint32(dbp.pid))
 | 
						return _DebugActiveProcessStop(uint32(dbp.pid))
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (dbp *Process) entryPoint() (uint64, error) {
 | 
					func (dbp *Process) EntryPoint() (uint64, error) {
 | 
				
			||||||
	return dbp.os.entryPoint, nil
 | 
						return dbp.os.entryPoint, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -6,7 +6,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	sys "golang.org/x/sys/unix"
 | 
						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.
 | 
					// 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
 | 
					// See amd64_linux_fetch_inferior_registers in gdb/amd64-linux-nat.c.html
 | 
				
			||||||
// and amd64_supply_xsave in gdb/amd64-tdep.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
 | 
					// 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) {
 | 
					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.PtraceFpRegs)), 0, 0)
 | 
						_, _, 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 {
 | 
						if err == syscall.Errno(0) || err == syscall.ENODEV {
 | 
				
			||||||
		// ignore ENODEV, it just means this CPU doesn't have X87 registers (??)
 | 
							// ignore ENODEV, it just means this CPU doesn't have X87 registers (??)
 | 
				
			||||||
		err = nil
 | 
							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}
 | 
						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)
 | 
						_, _, 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.Errno(0) {
 | 
				
			||||||
		if err == syscall.ENODEV {
 | 
							if err == syscall.ENODEV || err == syscall.EIO {
 | 
				
			||||||
			// ignore ENODEV, it just means this CPU or kernel doesn't support XSTATE, see https://github.com/derekparker/delve/issues/1022
 | 
								// 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
 | 
								err = nil
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -78,6 +79,6 @@ func PtraceGetRegset(tid int) (regset proc.LinuxX86Xstate, err error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	regset.Xsave = xstateargs[:iov.Len]
 | 
						regset.Xsave = xstateargs[:iov.Len]
 | 
				
			||||||
	err = proc.LinuxX86XstateRead(regset.Xsave, false, ®set)
 | 
						err = linutil.AMD64XstateRead(regset.Xsave, false, ®set)
 | 
				
			||||||
	return regset, err
 | 
						return regset, err
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -12,7 +12,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/arch/x86/x86asm"
 | 
						"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.
 | 
					// Regs represents CPU registers on an AMD64 processor.
 | 
				
			||||||
@@ -42,7 +42,7 @@ type Regs struct {
 | 
				
			|||||||
	fpregs []proc.Register
 | 
						fpregs []proc.Register
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *Regs) Slice() []proc.Register {
 | 
					func (r *Regs) Slice(floatingPoint bool) []proc.Register {
 | 
				
			||||||
	var regs = []struct {
 | 
						var regs = []struct {
 | 
				
			||||||
		k string
 | 
							k string
 | 
				
			||||||
		v uint64
 | 
							v uint64
 | 
				
			||||||
@@ -78,7 +78,9 @@ func (r *Regs) Slice() []proc.Register {
 | 
				
			|||||||
			out = proc.AppendQwordReg(out, reg.k, reg.v)
 | 
								out = proc.AppendQwordReg(out, reg.k, reg.v)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	out = append(out, r.fpregs...)
 | 
						if floatingPoint {
 | 
				
			||||||
 | 
							out = append(out, r.fpregs...)
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
	return out
 | 
						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 (
 | 
					import (
 | 
				
			||||||
	"syscall"
 | 
						"syscall"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						"github.com/go-delve/delve/pkg/proc/winutil"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type _NTSTATUS int32
 | 
					type _NTSTATUS int32
 | 
				
			||||||
@@ -97,6 +99,11 @@ func _NT_SUCCESS(x _NTSTATUS) bool {
 | 
				
			|||||||
	return x >= 0
 | 
						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	_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	_GetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) = kernel32.GetThreadContext
 | 
				
			||||||
//sys	_SetThreadContext(thread syscall.Handle, context *_CONTEXT) (err error) = kernel32.SetThreadContext
 | 
					//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 (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/proc"
 | 
						"github.com/go-delve/delve/pkg/proc"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Thread represents a single thread in the traced process
 | 
					// Thread represents a single thread in the traced process
 | 
				
			||||||
@@ -12,7 +12,7 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	sys "golang.org/x/sys/unix"
 | 
						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
 | 
					// WaitStatus is a synonym for the platform-specific WaitStatus
 | 
				
			||||||
@@ -7,7 +7,8 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	sys "golang.org/x/sys/unix"
 | 
						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
 | 
					type WaitStatus sys.WaitStatus
 | 
				
			||||||
@@ -83,21 +84,21 @@ func (t *Thread) Blocked() bool {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t *Thread) restoreRegisters(savedRegs proc.Registers) error {
 | 
					func (t *Thread) restoreRegisters(savedRegs proc.Registers) error {
 | 
				
			||||||
	sr := savedRegs.(*Regs)
 | 
						sr := savedRegs.(*linutil.AMD64Registers)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	var restoreRegistersErr error
 | 
						var restoreRegistersErr error
 | 
				
			||||||
	t.dbp.execPtraceFunc(func() {
 | 
						t.dbp.execPtraceFunc(func() {
 | 
				
			||||||
		restoreRegistersErr = sys.PtraceSetRegs(t.ID, sr.regs)
 | 
							restoreRegistersErr = sys.PtraceSetRegs(t.ID, (*sys.PtraceRegs)(sr.Regs))
 | 
				
			||||||
		if restoreRegistersErr != nil {
 | 
							if restoreRegistersErr != nil {
 | 
				
			||||||
			return
 | 
								return
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if sr.fpregset.Xsave != nil {
 | 
							if sr.Fpregset.Xsave != nil {
 | 
				
			||||||
			iov := sys.Iovec{Base: &sr.fpregset.Xsave[0], Len: uint64(len(sr.fpregset.Xsave))}
 | 
								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)
 | 
								_, _, restoreRegistersErr = syscall.Syscall6(syscall.SYS_PTRACE, sys.PTRACE_SETREGSET, uintptr(t.ID), _NT_X86_XSTATE, uintptr(unsafe.Pointer(&iov)), 0, 0)
 | 
				
			||||||
			return
 | 
								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
 | 
							return
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
	if restoreRegistersErr == syscall.Errno(0) {
 | 
						if restoreRegistersErr == syscall.Errno(0) {
 | 
				
			||||||
@@ -6,7 +6,8 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	sys "golang.org/x/sys/windows"
 | 
						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
 | 
					// WaitStatus is a synonym for the platform-specific WaitStatus
 | 
				
			||||||
@@ -19,7 +20,7 @@ type OSSpecificDetails struct {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (t *Thread) singleStep() error {
 | 
					func (t *Thread) singleStep() error {
 | 
				
			||||||
	context := newCONTEXT()
 | 
						context := winutil.NewCONTEXT()
 | 
				
			||||||
	context.ContextFlags = _CONTEXT_ALL
 | 
						context.ContextFlags = _CONTEXT_ALL
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// Set the processor TRAP flag
 | 
						// 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 {
 | 
					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.
 | 
					// only possible on recorded (traced) programs.
 | 
				
			||||||
var ErrNotRecorded = errors.New("not a recording")
 | 
					var ErrNotRecorded = errors.New("not a recording")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// UnrecoveredPanic is the name given to the unrecovered panic breakpoint.
 | 
					const (
 | 
				
			||||||
const UnrecoveredPanic = "unrecovered-panic"
 | 
						// 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
 | 
					// ErrProcessExited indicates that the process has exited and contains both
 | 
				
			||||||
// process id and exit status.
 | 
					// process id and exit status.
 | 
				
			||||||
@@ -41,6 +49,31 @@ func (pe ProcessDetachedError) Error() string {
 | 
				
			|||||||
	return "detached from the process"
 | 
						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.
 | 
					// FindFileLocation returns the PC for a given file:line.
 | 
				
			||||||
// Assumes that `file` is normalized to lower case and '/' on Windows.
 | 
					// Assumes that `file` is normalized to lower case and '/' on Windows.
 | 
				
			||||||
func FindFileLocation(p Process, fileName string, lineno int) (uint64, error) {
 | 
					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
 | 
					// 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
 | 
					// 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:
 | 
					// 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) {
 | 
					func FindFunctionLocation(p Process, funcName string, firstLine bool, lineOffset int) (uint64, error) {
 | 
				
			||||||
	bi := p.BinInfo()
 | 
						bi := p.BinInfo()
 | 
				
			||||||
	origfn := bi.LookupFunc[funcName]
 | 
						origfn := bi.LookupFunc[funcName]
 | 
				
			||||||
@@ -88,6 +121,33 @@ func FindFunctionLocation(p Process, funcName string, firstLine bool, lineOffset
 | 
				
			|||||||
	return origfn.Entry, nil
 | 
						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.
 | 
					// Next continues execution until the next source line.
 | 
				
			||||||
func Next(dbp Process) (err error) {
 | 
					func Next(dbp Process) (err error) {
 | 
				
			||||||
	if _, err := dbp.Valid(); err != nil {
 | 
						if _, err := dbp.Valid(); err != nil {
 | 
				
			||||||
@@ -184,7 +244,8 @@ func Continue(dbp Process) error {
 | 
				
			|||||||
				return conditionErrors(threads)
 | 
									return conditionErrors(threads)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		case curbp.Active && curbp.Internal:
 | 
							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
 | 
									// See description of proc.(*Process).next for the meaning of StepBreakpoints
 | 
				
			||||||
				if err := conditionErrors(threads); err != nil {
 | 
									if err := conditionErrors(threads); err != nil {
 | 
				
			||||||
					return err
 | 
										return err
 | 
				
			||||||
@@ -204,7 +265,7 @@ func Continue(dbp Process) error {
 | 
				
			|||||||
				if err = setStepIntoBreakpoint(dbp, text, SameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
 | 
									if err = setStepIntoBreakpoint(dbp, text, SameGoroutineCondition(dbp.SelectedGoroutine())); err != nil {
 | 
				
			||||||
					return err
 | 
										return err
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} else {
 | 
								default:
 | 
				
			||||||
				curthread.Common().returnValues = curbp.Breakpoint.returnInfo.Collect(curthread)
 | 
									curthread.Common().returnValues = curbp.Breakpoint.returnInfo.Collect(curthread)
 | 
				
			||||||
				if err := dbp.ClearInternalBreakpoints(); err != nil {
 | 
									if err := dbp.ClearInternalBreakpoints(); err != nil {
 | 
				
			||||||
					return err
 | 
										return err
 | 
				
			||||||
@@ -281,7 +342,7 @@ func stepInstructionOut(dbp Process, curthread Thread, fnname1, fnname2 string)
 | 
				
			|||||||
			if g := dbp.SelectedGoroutine(); g != nil {
 | 
								if g := dbp.SelectedGoroutine(); g != nil {
 | 
				
			||||||
				g.CurrentLoc = *loc
 | 
									g.CurrentLoc = *loc
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			return nil
 | 
								return curthread.SetCurrentBreakpoint()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -436,14 +497,22 @@ func StepOut(dbp Process) error {
 | 
				
			|||||||
	return Continue(dbp)
 | 
						return Continue(dbp)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// GoroutinesInfo returns an array of G structures representing the information
 | 
					// GoroutinesInfo searches for goroutines starting at index 'start', and
 | 
				
			||||||
// Delve cares about from the internal runtime G structure.
 | 
					// returns an array of up to 'count' (or all found elements, if 'count' is 0)
 | 
				
			||||||
func GoroutinesInfo(dbp Process) ([]*G, error) {
 | 
					// 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 {
 | 
						if _, err := dbp.Valid(); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, -1, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if dbp.Common().allGCache != nil {
 | 
						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 (
 | 
						var (
 | 
				
			||||||
@@ -465,12 +534,12 @@ func GoroutinesInfo(dbp Process) ([]*G, error) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	addr, err := rdr.AddrFor("runtime.allglen", dbp.BinInfo().staticBase)
 | 
						addr, err := rdr.AddrFor("runtime.allglen", dbp.BinInfo().staticBase)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, -1, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	allglenBytes := make([]byte, 8)
 | 
						allglenBytes := make([]byte, 8)
 | 
				
			||||||
	_, err = dbp.CurrentThread().ReadMemory(allglenBytes, uintptr(addr))
 | 
						_, err = dbp.CurrentThread().ReadMemory(allglenBytes, uintptr(addr))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, -1, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	allglen := binary.LittleEndian.Uint64(allglenBytes)
 | 
						allglen := binary.LittleEndian.Uint64(allglenBytes)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -480,17 +549,20 @@ func GoroutinesInfo(dbp Process) ([]*G, error) {
 | 
				
			|||||||
		// try old name (pre Go 1.6)
 | 
							// try old name (pre Go 1.6)
 | 
				
			||||||
		allgentryaddr, err = rdr.AddrFor("runtime.allg", dbp.BinInfo().staticBase)
 | 
							allgentryaddr, err = rdr.AddrFor("runtime.allg", dbp.BinInfo().staticBase)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			return nil, err
 | 
								return nil, -1, err
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	faddr := make([]byte, dbp.BinInfo().Arch.PtrSize())
 | 
						faddr := make([]byte, dbp.BinInfo().Arch.PtrSize())
 | 
				
			||||||
	_, err = dbp.CurrentThread().ReadMemory(faddr, uintptr(allgentryaddr))
 | 
						_, err = dbp.CurrentThread().ReadMemory(faddr, uintptr(allgentryaddr))
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, -1, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	allgptr := binary.LittleEndian.Uint64(faddr)
 | 
						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)
 | 
							gvar, err := newGVariable(dbp.CurrentThread(), uintptr(allgptr+(i*uint64(dbp.BinInfo().Arch.PtrSize()))), true)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
			allg = append(allg, &G{Unreadable: err})
 | 
								allg = append(allg, &G{Unreadable: err})
 | 
				
			||||||
@@ -504,7 +576,7 @@ func GoroutinesInfo(dbp Process) ([]*G, error) {
 | 
				
			|||||||
		if thg, allocated := threadg[g.ID]; allocated {
 | 
							if thg, allocated := threadg[g.ID]; allocated {
 | 
				
			||||||
			loc, err := thg.Thread.Location()
 | 
								loc, err := thg.Thread.Location()
 | 
				
			||||||
			if err != nil {
 | 
								if err != nil {
 | 
				
			||||||
				return nil, err
 | 
									return nil, -1, err
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			g.Thread = thg.Thread
 | 
								g.Thread = thg.Thread
 | 
				
			||||||
			// Prefer actual thread location information.
 | 
								// Prefer actual thread location information.
 | 
				
			||||||
@@ -515,36 +587,78 @@ func GoroutinesInfo(dbp Process) ([]*G, error) {
 | 
				
			|||||||
			allg = append(allg, g)
 | 
								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
 | 
					// FindGoroutine returns a G struct representing the goroutine
 | 
				
			||||||
// specified by `gid`.
 | 
					// specified by `gid`.
 | 
				
			||||||
func FindGoroutine(dbp Process, gid int) (*G, error) {
 | 
					func FindGoroutine(dbp Process, gid int) (*G, error) {
 | 
				
			||||||
	if gid == -1 {
 | 
						if selg := dbp.SelectedGoroutine(); (gid == -1) || (selg != nil && selg.ID == gid) || (selg == nil && gid == 0) {
 | 
				
			||||||
		return dbp.SelectedGoroutine(), nil
 | 
							// 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 gid == 0 {
 | 
				
			||||||
	if err != nil {
 | 
							return nil, fmt.Errorf("Unknown goroutine %d", gid)
 | 
				
			||||||
		return nil, err
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for i := range gs {
 | 
					
 | 
				
			||||||
		if gs[i].ID == gid {
 | 
						// Calling GoroutinesInfo could be slow if there are many goroutines
 | 
				
			||||||
			if gs[i].Unreadable != nil {
 | 
						// running, check if a running goroutine has been requested first.
 | 
				
			||||||
				return nil, gs[i].Unreadable
 | 
						for _, thread := range dbp.ThreadList() {
 | 
				
			||||||
			}
 | 
							g, _ := GetG(thread)
 | 
				
			||||||
			return gs[i], nil
 | 
							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)
 | 
						return nil, fmt.Errorf("Unknown goroutine %d", gid)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ConvertEvalScope returns a new EvalScope in the context of the
 | 
					// ConvertEvalScope returns a new EvalScope in the context of the
 | 
				
			||||||
// specified goroutine ID and stack frame.
 | 
					// 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 {
 | 
						if _, err := dbp.Valid(); err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -564,7 +678,7 @@ func ConvertEvalScope(dbp Process, gid, frame int) (*EvalScope, error) {
 | 
				
			|||||||
		thread = g.Thread
 | 
							thread = g.Thread
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	locs, err := g.Stacktrace(frame+1, false)
 | 
						locs, err := g.Stacktrace(frame+1, deferCall > 0)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							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)
 | 
							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
 | 
						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
 | 
						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.
 | 
					// 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)
 | 
						panicpc, err := FindFunctionLocation(p, "runtime.startpanic", true, 0)
 | 
				
			||||||
	if _, isFnNotFound := err.(*ErrFunctionNotFound); isFnNotFound {
 | 
						if _, isFnNotFound := err.(*ErrFunctionNotFound); isFnNotFound {
 | 
				
			||||||
		panicpc, err = FindFunctionLocation(p, "runtime.fatalpanic", true, 0)
 | 
							panicpc, err = FindFunctionLocation(p, "runtime.fatalpanic", true, 0)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if err == nil {
 | 
						if err == nil {
 | 
				
			||||||
		bp, err := breakpoints.SetWithID(-1, panicpc, writeBreakpoint)
 | 
							bp, err := p.Breakpoints().SetWithID(unrecoveredPanicID, panicpc, writeBreakpoint)
 | 
				
			||||||
		if err == nil {
 | 
							if err == nil {
 | 
				
			||||||
			bp.Name = UnrecoveredPanic
 | 
								bp.Name = UnrecoveredPanic
 | 
				
			||||||
			bp.Variables = []string{"runtime.curg._panic.arg"}
 | 
								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
 | 
					// 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 returns the address of the G variable if it is known, 0 and false otherwise
 | 
				
			||||||
	GAddr() (uint64, bool)
 | 
						GAddr() (uint64, bool)
 | 
				
			||||||
	Get(int) (uint64, error)
 | 
						Get(int) (uint64, error)
 | 
				
			||||||
	Slice() []Register
 | 
						Slice(floatingPoint bool) []Register
 | 
				
			||||||
	// Copy returns a copy of the registers that is guaranteed not to change
 | 
						// Copy returns a copy of the registers that is guaranteed not to change
 | 
				
			||||||
	// when the registers of the associated thread change.
 | 
						// when the registers of the associated thread change.
 | 
				
			||||||
	Copy() Registers
 | 
						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, " "))
 | 
						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()
 | 
							return buf.Bytes()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	if regname, ok := dwarfToName[i]; ok {
 | 
						if regname, ok := dwarfToName[i]; ok {
 | 
				
			||||||
		regslice := regs.Slice()
 | 
							regslice := regs.Slice(true)
 | 
				
			||||||
		for _, reg := range regslice {
 | 
							for _, reg := range regslice {
 | 
				
			||||||
			if reg.Name == regname {
 | 
								if reg.Name == regname {
 | 
				
			||||||
				return reg.Bytes
 | 
									return reg.Bytes
 | 
				
			||||||
@@ -7,9 +7,9 @@ import (
 | 
				
			|||||||
	"go/constant"
 | 
						"go/constant"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/frame"
 | 
						"github.com/go-delve/delve/pkg/dwarf/frame"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
						"github.com/go-delve/delve/pkg/dwarf/op"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
						"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This code is partly adapted from runtime.gentraceback in
 | 
					// 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)
 | 
							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}
 | 
						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 {
 | 
						r.Call = r.Current
 | 
				
			||||||
		fnname := ""
 | 
						if !it.top && r.Current.Fn != nil && it.pc != r.Current.Fn.Entry {
 | 
				
			||||||
		if r.Current.Fn != nil {
 | 
							// if the return address is the entry point of the function that
 | 
				
			||||||
			fnname = r.Current.Fn.Name
 | 
							// contains it then this is some kind of fake return frame (for example
 | 
				
			||||||
		}
 | 
							// runtime.sigreturn) that didn't actually call the current frame,
 | 
				
			||||||
		switch fnname {
 | 
							// 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":
 | 
							case "runtime.mstart", "runtime.systemstack_switch":
 | 
				
			||||||
			// these frames are inserted by runtime.systemstack and there is no CALL
 | 
								// these frames are inserted by runtime.systemstack and there is no CALL
 | 
				
			||||||
			// instruction to look for at pc - 1
 | 
								// instruction to look for at pc - 1
 | 
				
			||||||
			r.Call = r.Current
 | 
					 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			if r.Current.Fn != nil && it.pc == r.Current.Fn.Entry {
 | 
								r.lastpc = it.pc - 1
 | 
				
			||||||
				// if the return address is the entry point of the function that
 | 
								r.Call.File, r.Call.Line = r.Current.Fn.cu.lineInfo.PCToLine(r.Current.Fn.Entry, it.pc-1)
 | 
				
			||||||
				// 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
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
	} else {
 | 
					 | 
				
			||||||
		r.Call = r.Current
 | 
					 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	return r
 | 
						return r
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -594,6 +579,7 @@ type Defer struct {
 | 
				
			|||||||
	DeferPC    uint64 // PC address of instruction that added this defer
 | 
						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)
 | 
						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
 | 
						link       *Defer // Next deferred function
 | 
				
			||||||
 | 
						argSz      int64
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	variable   *Variable
 | 
						variable   *Variable
 | 
				
			||||||
	Unreadable error
 | 
						Unreadable error
 | 
				
			||||||
@@ -644,7 +630,7 @@ func (g *G) readDefers(frames []Stackframe) {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (d *Defer) load() {
 | 
					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 {
 | 
						if d.variable.Unreadable != nil {
 | 
				
			||||||
		d.Unreadable = d.variable.Unreadable
 | 
							d.Unreadable = d.variable.Unreadable
 | 
				
			||||||
		return
 | 
							return
 | 
				
			||||||
@@ -660,6 +646,7 @@ func (d *Defer) load() {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	d.DeferPC, _ = constant.Uint64Val(d.variable.fieldVariable("pc").Value)
 | 
						d.DeferPC, _ = constant.Uint64Val(d.variable.fieldVariable("pc").Value)
 | 
				
			||||||
	d.SP, _ = constant.Uint64Val(d.variable.fieldVariable("sp").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()
 | 
						linkvar := d.variable.fieldVariable("link").maybeDereference()
 | 
				
			||||||
	if linkvar.Addr != 0 {
 | 
						if linkvar.Addr != 0 {
 | 
				
			||||||
@@ -685,3 +672,40 @@ func (d *Defer) Next() *Defer {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	return d.link
 | 
						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"
 | 
						"reflect"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
						"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
						"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// Thread represents a thread.
 | 
					// Thread represents a thread.
 | 
				
			||||||
@@ -225,15 +225,7 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	if !csource {
 | 
						if !csource {
 | 
				
			||||||
		deferreturns := []uint64{}
 | 
							deferreturns := findDeferReturnCalls(text)
 | 
				
			||||||
 | 
					 | 
				
			||||||
		// Find all runtime.deferreturn locations in the function
 | 
					 | 
				
			||||||
		// See documentation of Breakpoint.DeferCond for why this is necessary
 | 
					 | 
				
			||||||
		for _, instr := range text {
 | 
					 | 
				
			||||||
			if instr.IsCall() && instr.DestLoc != nil && instr.DestLoc.Fn != nil && instr.DestLoc.Fn.Name == "runtime.deferreturn" {
 | 
					 | 
				
			||||||
				deferreturns = append(deferreturns, instr.Loc.PC)
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
		// Set breakpoint on the most recently deferred function (if any)
 | 
							// Set breakpoint on the most recently deferred function (if any)
 | 
				
			||||||
		var deferpc uint64
 | 
							var deferpc uint64
 | 
				
			||||||
@@ -333,6 +325,20 @@ func next(dbp Process, stepInto, inlinedStepOut bool) error {
 | 
				
			|||||||
	return nil
 | 
						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.
 | 
					// Removes instructions belonging to inlined calls of topframe from pcs.
 | 
				
			||||||
// If includeCurrentFn is true it will also remove all instructions
 | 
					// If includeCurrentFn is true it will also remove all instructions
 | 
				
			||||||
// belonging to the current function.
 | 
					// belonging to the current function.
 | 
				
			||||||
@@ -373,21 +379,16 @@ func setStepIntoBreakpoint(dbp Process, text []AsmInstruction, cond ast.Expr) er
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	instr := text[0]
 | 
						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
 | 
							return nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fn := instr.DestLoc.Fn
 | 
						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
 | 
						// 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
 | 
							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'
 | 
						// or entire packages from being stepped into with 'step'
 | 
				
			||||||
	// those extra checks should be done here.
 | 
						// 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
 | 
						// Set a breakpoint after the function's prologue
 | 
				
			||||||
	pc, _ := FirstPCAfterPrologue(dbp, fn, false)
 | 
					 | 
				
			||||||
	if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, cond); err != nil {
 | 
						if _, err := dbp.SetBreakpoint(pc, NextBreakpoint, cond); err != nil {
 | 
				
			||||||
		if _, ok := err.(BreakpointExistsError); !ok {
 | 
							if _, ok := err.(BreakpointExistsError); !ok {
 | 
				
			||||||
			return err
 | 
								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
 | 
					// 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.
 | 
					// the current thread from the thread local storage area.
 | 
				
			||||||
func GetG(thread Thread) (*G, error) {
 | 
					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)
 | 
						gaddr, err := getGVariable(thread)
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return nil, err
 | 
							return nil, err
 | 
				
			||||||
@@ -8,7 +8,6 @@ import (
 | 
				
			|||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
	"go/ast"
 | 
						"go/ast"
 | 
				
			||||||
	"go/constant"
 | 
						"go/constant"
 | 
				
			||||||
	"go/parser"
 | 
					 | 
				
			||||||
	"go/token"
 | 
						"go/token"
 | 
				
			||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
@@ -18,12 +17,12 @@ import (
 | 
				
			|||||||
	"sync"
 | 
						"sync"
 | 
				
			||||||
	"unsafe"
 | 
						"unsafe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
						"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/line"
 | 
						"github.com/go-delve/delve/pkg/dwarf/line"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
						"github.com/go-delve/delve/pkg/dwarf/op"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
						"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/goversion"
 | 
						"github.com/go-delve/delve/pkg/goversion"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/logflags"
 | 
						"github.com/go-delve/delve/pkg/logflags"
 | 
				
			||||||
	"github.com/sirupsen/logrus"
 | 
						"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
 | 
					type compileUnitsByLowpc []*compileUnit
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (v compileUnitsByLowpc) Len() int               { return len(v) }
 | 
					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] }
 | 
					func (v compileUnitsByLowpc) Swap(i int, j int)      { v[i], v[j] = v[j], v[i] }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type packageVarsByAddr []packageVar
 | 
					type packageVarsByAddr []packageVar
 | 
				
			||||||
@@ -230,18 +229,18 @@ outer:
 | 
				
			|||||||
			if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage {
 | 
								if lang, _ := entry.Val(dwarf.AttrLanguage).(int64); lang == dwarfGoLanguage {
 | 
				
			||||||
				cu.isgo = true
 | 
									cu.isgo = true
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			cu.Name, _ = entry.Val(dwarf.AttrName).(string)
 | 
								cu.name, _ = entry.Val(dwarf.AttrName).(string)
 | 
				
			||||||
			compdir, _ := entry.Val(dwarf.AttrCompDir).(string)
 | 
								compdir, _ := entry.Val(dwarf.AttrCompDir).(string)
 | 
				
			||||||
			if compdir != "" {
 | 
								if compdir != "" {
 | 
				
			||||||
				cu.Name = filepath.Join(compdir, cu.Name)
 | 
									cu.name = filepath.Join(compdir, cu.name)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			cu.Ranges, _ = bi.dwarf.Ranges(entry)
 | 
								cu.ranges, _ = bi.dwarf.Ranges(entry)
 | 
				
			||||||
			for i := range cu.Ranges {
 | 
								for i := range cu.ranges {
 | 
				
			||||||
				cu.Ranges[i][0] += bi.staticBase
 | 
									cu.ranges[i][0] += bi.staticBase
 | 
				
			||||||
				cu.Ranges[i][1] += bi.staticBase
 | 
									cu.ranges[i][1] += bi.staticBase
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			if len(cu.Ranges) >= 1 {
 | 
								if len(cu.ranges) >= 1 {
 | 
				
			||||||
				cu.LowPC = cu.Ranges[0][0]
 | 
									cu.lowPC = cu.ranges[0][0]
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			lineInfoOffset, _ := entry.Val(dwarf.AttrStmtList).(int64)
 | 
								lineInfoOffset, _ := entry.Val(dwarf.AttrStmtList).(int64)
 | 
				
			||||||
			if lineInfoOffset >= 0 && lineInfoOffset < int64(len(debugLineBytes)) {
 | 
								if lineInfoOffset >= 0 && lineInfoOffset < int64(len(debugLineBytes)) {
 | 
				
			||||||
@@ -398,6 +397,13 @@ outer:
 | 
				
			|||||||
				highpc = ranges[0][1] + bi.staticBase
 | 
									highpc = ranges[0][1] + bi.staticBase
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
			name, ok2 := entry.Val(dwarf.AttrName).(string)
 | 
								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
 | 
								var fn Function
 | 
				
			||||||
			if (ok1 == !inlined) && ok2 {
 | 
								if (ok1 == !inlined) && ok2 {
 | 
				
			||||||
				if inlined {
 | 
									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
 | 
					// * After go1.11 the runtimeTypeToDIE map is used to look up the address of
 | 
				
			||||||
//   the type and map it drectly to a DIE.
 | 
					//   the type and map it drectly to a DIE.
 | 
				
			||||||
func runtimeTypeToDIE(_type *Variable, dataAddr uintptr) (typ godwarf.Type, kind int64, err error) {
 | 
					func runtimeTypeToDIE(_type *Variable, dataAddr uintptr) (typ godwarf.Type, kind int64, err error) {
 | 
				
			||||||
	var go17 bool
 | 
					 | 
				
			||||||
	var typestring *Variable
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
	bi := _type.bi
 | 
						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()
 | 
						_type = _type.maybeDereference()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	// go 1.11 implementation: use extended attribute in debug_info
 | 
						// 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 {")
 | 
						buf.WriteString("interface {")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	methods, _ := _type.structMember(interfacetypeFieldMhdr)
 | 
						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 {
 | 
						if methods.Unreadable != nil {
 | 
				
			||||||
		return "", nil
 | 
							return "", nil
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -941,7 +910,7 @@ func nameOfStructRuntimeType(_type *Variable, kind, tflag int64) (string, error)
 | 
				
			|||||||
	buf.WriteString("struct {")
 | 
						buf.WriteString("struct {")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	fields, _ := _type.structMember("fields")
 | 
						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 {
 | 
						if fields.Unreadable != nil {
 | 
				
			||||||
		return "", fields.Unreadable
 | 
							return "", fields.Unreadable
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -15,9 +15,9 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"unsafe"
 | 
						"unsafe"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
						"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/op"
 | 
						"github.com/go-delve/delve/pkg/dwarf/op"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/reader"
 | 
						"github.com/go-delve/delve/pkg/dwarf/reader"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -32,6 +32,8 @@ const (
 | 
				
			|||||||
	hashMinTopHash   = 4 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated
 | 
						hashMinTopHash   = 4 // used by map reading code, indicates minimum value of tophash that isn't empty or evacuated
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	maxFramePrefetchSize = 1 * 1024 * 1024 // Maximum prefetch size for a stack frame
 | 
						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
 | 
					type floatSpecial uint8
 | 
				
			||||||
@@ -122,10 +124,39 @@ type LoadConfig struct {
 | 
				
			|||||||
	MaxArrayValues int
 | 
						MaxArrayValues int
 | 
				
			||||||
	// MaxStructFields is the maximum number of fields read from a struct, -1 will read all fields.
 | 
						// MaxStructFields is the maximum number of fields read from a struct, -1 will read all fields.
 | 
				
			||||||
	MaxStructFields int
 | 
						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 loadSingleValue = LoadConfig{false, 0, 64, 0, 0, 0}
 | 
				
			||||||
var loadFullValue = LoadConfig{true, 1, 64, 64, -1}
 | 
					var loadFullValue = LoadConfig{true, 1, 64, 64, -1, 0}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// G status, from: src/runtime/runtime2.go
 | 
					// G status, from: src/runtime/runtime2.go
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -169,6 +200,12 @@ type G struct {
 | 
				
			|||||||
	Unreadable error // could not read the 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,
 | 
					// EvalScope is the scope for variable evaluation. Contains the thread,
 | 
				
			||||||
// current location (PC), and canonical frame address.
 | 
					// current location (PC), and canonical frame address.
 | 
				
			||||||
type EvalScope struct {
 | 
					type EvalScope struct {
 | 
				
			||||||
@@ -405,13 +442,13 @@ func (scope *EvalScope) PtrSize() int {
 | 
				
			|||||||
	return scope.BinInfo.Arch.PtrSize()
 | 
						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.
 | 
					// for a specific thread.
 | 
				
			||||||
type NoGError struct {
 | 
					type ErrNoGoroutine struct {
 | 
				
			||||||
	tid int
 | 
						tid int
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (ng NoGError) Error() string {
 | 
					func (ng ErrNoGoroutine) Error() string {
 | 
				
			||||||
	return fmt.Sprintf("no G executing on thread %d", ng.tid)
 | 
						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 {
 | 
							if thread, ok := mem.(Thread); ok {
 | 
				
			||||||
			id = thread.ThreadID()
 | 
								id = thread.ThreadID()
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		return nil, NoGError{tid: id}
 | 
							return nil, ErrNoGoroutine{tid: id}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		if _, isptr := v.RealType.(*godwarf.PtrType); !isptr {
 | 
							if _, isptr := v.RealType.(*godwarf.PtrType); !isptr {
 | 
				
			||||||
@@ -441,7 +478,7 @@ func (v *Variable) parseG() (*G, error) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
		v = v.maybeDereference()
 | 
							v = v.maybeDereference()
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	v.loadValue(LoadConfig{false, 2, 64, 0, -1})
 | 
						v.loadValue(LoadConfig{false, 2, 64, 0, -1, 0})
 | 
				
			||||||
	if v.Unreadable != nil {
 | 
						if v.Unreadable != nil {
 | 
				
			||||||
		return nil, v.Unreadable
 | 
							return nil, v.Unreadable
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -586,12 +623,102 @@ func (g *G) StartLoc() Location {
 | 
				
			|||||||
	return Location{PC: g.StartPC, File: f, Line: l, Fn: fn}
 | 
						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
 | 
					// Returns the list of saved return addresses used by stack barriers
 | 
				
			||||||
func (g *G) stkbar() ([]savedLR, error) {
 | 
					func (g *G) stkbar() ([]savedLR, error) {
 | 
				
			||||||
	if g.stkbarVar == nil { // stack barriers were removed in Go 1.9
 | 
						if g.stkbarVar == nil { // stack barriers were removed in Go 1.9
 | 
				
			||||||
		return nil, nil
 | 
							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 {
 | 
						if g.stkbarVar.Unreadable != nil {
 | 
				
			||||||
		return nil, fmt.Errorf("unreadable stkbar: %v", g.stkbarVar.Unreadable)
 | 
							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 {
 | 
						vars = filterVariables(vars, func(v *Variable) bool {
 | 
				
			||||||
		return (v.Flags & (VariableArgument | VariableReturnArgument)) == 0
 | 
							return (v.Flags & (VariableArgument | VariableReturnArgument)) == 0
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
						cfg.MaxMapBuckets = maxMapBucketsFactor * cfg.MaxArrayValues
 | 
				
			||||||
	loadValues(vars, cfg)
 | 
						loadValues(vars, cfg)
 | 
				
			||||||
	return vars, nil
 | 
						return vars, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -671,6 +799,7 @@ func (scope *EvalScope) FunctionArguments(cfg LoadConfig) ([]*Variable, error) {
 | 
				
			|||||||
	vars = filterVariables(vars, func(v *Variable) bool {
 | 
						vars = filterVariables(vars, func(v *Variable) bool {
 | 
				
			||||||
		return (v.Flags & (VariableArgument | VariableReturnArgument)) != 0
 | 
							return (v.Flags & (VariableArgument | VariableReturnArgument)) != 0
 | 
				
			||||||
	})
 | 
						})
 | 
				
			||||||
 | 
						cfg.MaxMapBuckets = maxMapBucketsFactor * cfg.MaxArrayValues
 | 
				
			||||||
	loadValues(vars, cfg)
 | 
						loadValues(vars, cfg)
 | 
				
			||||||
	return vars, nil
 | 
						return vars, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
@@ -875,7 +1004,11 @@ func (scope *EvalScope) extractVarInfoFromEntry(varEntry *dwarf.Entry) (*Variabl
 | 
				
			|||||||
	mem := scope.Mem
 | 
						mem := scope.Mem
 | 
				
			||||||
	if pieces != nil {
 | 
						if pieces != nil {
 | 
				
			||||||
		addr = fakeAddress
 | 
							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)
 | 
						v := scope.newVariable(n, uintptr(addr), t, mem)
 | 
				
			||||||
@@ -895,6 +1028,10 @@ func (v *Variable) maybeDereference() *Variable {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	switch t := v.RealType.(type) {
 | 
						switch t := v.RealType.(type) {
 | 
				
			||||||
	case *godwarf.PtrType:
 | 
						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)
 | 
							ptrval, err := readUintRaw(v.mem, uintptr(v.Addr), t.ByteSize)
 | 
				
			||||||
		r := v.newVariable("", uintptr(ptrval), t.Type, DereferenceMemory(v.mem))
 | 
							r := v.newVariable("", uintptr(ptrval), t.Type, DereferenceMemory(v.mem))
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
@@ -1569,6 +1706,11 @@ func (v *Variable) loadMap(recurseLevel int, cfg LoadConfig) {
 | 
				
			|||||||
	if it == nil {
 | 
						if it == nil {
 | 
				
			||||||
		return
 | 
							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++ {
 | 
						for skip := 0; skip < v.mapSkip; skip++ {
 | 
				
			||||||
		if ok := it.next(); !ok {
 | 
							if ok := it.next(); !ok {
 | 
				
			||||||
@@ -1580,9 +1722,6 @@ func (v *Variable) loadMap(recurseLevel int, cfg LoadConfig) {
 | 
				
			|||||||
	count := 0
 | 
						count := 0
 | 
				
			||||||
	errcount := 0
 | 
						errcount := 0
 | 
				
			||||||
	for it.next() {
 | 
						for it.next() {
 | 
				
			||||||
		if count >= cfg.MaxArrayValues {
 | 
					 | 
				
			||||||
			break
 | 
					 | 
				
			||||||
		}
 | 
					 | 
				
			||||||
		key := it.key()
 | 
							key := it.key()
 | 
				
			||||||
		var val *Variable
 | 
							var val *Variable
 | 
				
			||||||
		if it.values.fieldType.Size() > 0 {
 | 
							if it.values.fieldType.Size() > 0 {
 | 
				
			||||||
@@ -1601,6 +1740,9 @@ func (v *Variable) loadMap(recurseLevel int, cfg LoadConfig) {
 | 
				
			|||||||
		if errcount > maxErrCount {
 | 
							if errcount > maxErrCount {
 | 
				
			||||||
			break
 | 
								break
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							if count >= cfg.MaxArrayValues || int64(count) >= v.Len {
 | 
				
			||||||
 | 
								break
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1618,6 +1760,8 @@ type mapIterator struct {
 | 
				
			|||||||
	values    *Variable
 | 
						values    *Variable
 | 
				
			||||||
	overflow  *Variable
 | 
						overflow  *Variable
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						maxNumBuckets uint64 // maximum number of buckets to scan
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	idx int64
 | 
						idx int64
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1683,6 +1827,10 @@ func (it *mapIterator) nextBucket() bool {
 | 
				
			|||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		it.b = nil
 | 
							it.b = nil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
							if it.maxNumBuckets > 0 && it.bidx >= it.maxNumBuckets {
 | 
				
			||||||
 | 
								return false
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for it.bidx < it.numbuckets {
 | 
							for it.bidx < it.numbuckets {
 | 
				
			||||||
			it.b = it.buckets.clone()
 | 
								it.b = it.buckets.clone()
 | 
				
			||||||
			it.b.Addr += uintptr(uint64(it.buckets.DwarfType.Size()) * it.bidx)
 | 
								it.b.Addr += uintptr(uint64(it.buckets.DwarfType.Size()) * it.bidx)
 | 
				
			||||||
@@ -1,4 +1,4 @@
 | 
				
			|||||||
package native
 | 
					package winutil
 | 
				
			||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
@@ -6,11 +6,11 @@ import (
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	"golang.org/x/arch/x86/x86asm"
 | 
						"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.
 | 
					// AMD64Registers represents CPU registers on an AMD64 processor.
 | 
				
			||||||
type Regs struct {
 | 
					type AMD64Registers struct {
 | 
				
			||||||
	rax     uint64
 | 
						rax     uint64
 | 
				
			||||||
	rbx     uint64
 | 
						rbx     uint64
 | 
				
			||||||
	rcx     uint64
 | 
						rcx     uint64
 | 
				
			||||||
@@ -33,11 +33,48 @@ type Regs struct {
 | 
				
			|||||||
	fs      uint64
 | 
						fs      uint64
 | 
				
			||||||
	gs      uint64
 | 
						gs      uint64
 | 
				
			||||||
	tls     uint64
 | 
						tls     uint64
 | 
				
			||||||
	context *_CONTEXT
 | 
						Context *CONTEXT
 | 
				
			||||||
	fltSave *_XMM_SAVE_AREA32
 | 
						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 {
 | 
						var regs = []struct {
 | 
				
			||||||
		k string
 | 
							k string
 | 
				
			||||||
		v uint64
 | 
							v uint64
 | 
				
			||||||
@@ -66,7 +103,7 @@ func (r *Regs) Slice() []proc.Register {
 | 
				
			|||||||
		{"TLS", r.tls},
 | 
							{"TLS", r.tls},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	outlen := len(regs)
 | 
						outlen := len(regs)
 | 
				
			||||||
	if r.fltSave != nil {
 | 
						if r.fltSave != nil && floatingPoint {
 | 
				
			||||||
		outlen += 6 + 8 + 2 + 16
 | 
							outlen += 6 + 8 + 2 + 16
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	out := make([]proc.Register, 0, outlen)
 | 
						out := make([]proc.Register, 0, outlen)
 | 
				
			||||||
@@ -77,7 +114,7 @@ func (r *Regs) Slice() []proc.Register {
 | 
				
			|||||||
			out = proc.AppendQwordReg(out, reg.k, reg.v)
 | 
								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, "CW", r.fltSave.ControlWord)
 | 
				
			||||||
		out = proc.AppendWordReg(out, "SW", r.fltSave.StatusWord)
 | 
							out = proc.AppendWordReg(out, "SW", r.fltSave.StatusWord)
 | 
				
			||||||
		out = proc.AppendWordReg(out, "TW", uint16(r.fltSave.TagWord))
 | 
							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
 | 
					// PC returns the current program counter
 | 
				
			||||||
// i.e. the RIP CPU register.
 | 
					// i.e. the RIP CPU register.
 | 
				
			||||||
func (r *Regs) PC() uint64 {
 | 
					func (r *AMD64Registers) PC() uint64 {
 | 
				
			||||||
	return r.rip
 | 
						return r.rip
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SP returns the stack pointer location,
 | 
					// SP returns the stack pointer location,
 | 
				
			||||||
// i.e. the RSP register.
 | 
					// i.e. the RSP register.
 | 
				
			||||||
func (r *Regs) SP() uint64 {
 | 
					func (r *AMD64Registers) SP() uint64 {
 | 
				
			||||||
	return r.rsp
 | 
						return r.rsp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func (r *Regs) BP() uint64 {
 | 
					func (r *AMD64Registers) BP() uint64 {
 | 
				
			||||||
	return r.rbp
 | 
						return r.rbp
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// CX returns the value of the RCX register.
 | 
					// CX returns the value of the RCX register.
 | 
				
			||||||
func (r *Regs) CX() uint64 {
 | 
					func (r *AMD64Registers) CX() uint64 {
 | 
				
			||||||
	return r.rcx
 | 
						return r.rcx
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// TLS returns the value of the register
 | 
					// TLS returns the value of the register
 | 
				
			||||||
// that contains the location of the thread
 | 
					// that contains the location of the thread
 | 
				
			||||||
// local storage segment.
 | 
					// local storage segment.
 | 
				
			||||||
func (r *Regs) TLS() uint64 {
 | 
					func (r *AMD64Registers) TLS() uint64 {
 | 
				
			||||||
	return r.tls
 | 
						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
 | 
						return 0, false
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// SetPC sets the RIP register to the value specified by `pc`.
 | 
					// Get returns the value of the n-th register (in x86asm order).
 | 
				
			||||||
func (thread *Thread) SetPC(pc uint64) error {
 | 
					func (r *AMD64Registers) Get(n int) (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) {
 | 
					 | 
				
			||||||
	reg := x86asm.Reg(n)
 | 
						reg := x86asm.Reg(n)
 | 
				
			||||||
	const (
 | 
						const (
 | 
				
			||||||
		mask8  = 0x000f
 | 
							mask8  = 0x000f
 | 
				
			||||||
@@ -332,59 +328,103 @@ func (r *Regs) Get(n int) (uint64, error) {
 | 
				
			|||||||
	return 0, proc.ErrUnknownRegister
 | 
						return 0, proc.ErrUnknownRegister
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func registers(thread *Thread, floatingPoint bool) (proc.Registers, error) {
 | 
					// Copy returns a copy of these registers that is guarenteed not to change.
 | 
				
			||||||
	context := newCONTEXT()
 | 
					func (r *AMD64Registers) Copy() proc.Registers {
 | 
				
			||||||
 | 
						var rr AMD64Registers
 | 
				
			||||||
	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
 | 
					 | 
				
			||||||
	rr = *r
 | 
						rr = *r
 | 
				
			||||||
	rr.context = newCONTEXT()
 | 
						rr.Context = NewCONTEXT()
 | 
				
			||||||
	*(rr.context) = *(r.context)
 | 
						*(rr.Context) = *(r.Context)
 | 
				
			||||||
	rr.fltSave = &rr.context.FltSave
 | 
						rr.fltSave = &rr.Context.FltSave
 | 
				
			||||||
	return &rr
 | 
						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"
 | 
						"text/tabwriter"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/cosiner/argv"
 | 
						"github.com/cosiner/argv"
 | 
				
			||||||
	"github.com/derekparker/delve/service"
 | 
						"github.com/go-delve/delve/service"
 | 
				
			||||||
	"github.com/derekparker/delve/service/api"
 | 
						"github.com/go-delve/delve/service/api"
 | 
				
			||||||
	"github.com/derekparker/delve/service/debugger"
 | 
						"github.com/go-delve/delve/service/debugger"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const optimizedFunctionWarning = "Warning: debugging optimized function"
 | 
					const optimizedFunctionWarning = "Warning: debugging optimized function"
 | 
				
			||||||
@@ -32,6 +32,7 @@ type cmdPrefix int
 | 
				
			|||||||
const (
 | 
					const (
 | 
				
			||||||
	noPrefix = cmdPrefix(0)
 | 
						noPrefix = cmdPrefix(0)
 | 
				
			||||||
	onPrefix = cmdPrefix(1 << iota)
 | 
						onPrefix = cmdPrefix(1 << iota)
 | 
				
			||||||
 | 
						deferredPrefix
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
type callContext struct {
 | 
					type callContext struct {
 | 
				
			||||||
@@ -113,24 +114,24 @@ Type "help" followed by the name of a command for more information about it.`},
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
	break [name] <linespec>
 | 
						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"`},
 | 
					See also: "help on", "help cond" and "help clear"`},
 | 
				
			||||||
		{aliases: []string{"trace", "t"}, cmdFn: tracepoint, helpMsg: `Set tracepoint.
 | 
							{aliases: []string{"trace", "t"}, cmdFn: tracepoint, helpMsg: `Set tracepoint.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	trace [name] <linespec>
 | 
						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"`},
 | 
					See also: "help on", "help cond" and "help clear"`},
 | 
				
			||||||
		{aliases: []string{"restart", "r"}, cmdFn: restart, helpMsg: `Restart process.
 | 
							{aliases: []string{"restart", "r"}, cmdFn: restart, helpMsg: `Restart process.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  restart [checkpoint]
 | 
						restart [checkpoint]
 | 
				
			||||||
  restart [-noargs] newargv...
 | 
						restart [-noargs] newargv...
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  For recorded processes restarts from the start or from the specified
 | 
					For recorded processes restarts from the start or from the specified
 | 
				
			||||||
  checkpoint.  For normal processes restarts the process, optionally changing
 | 
					checkpoint.  For normal processes restarts the process, optionally changing
 | 
				
			||||||
  the arguments.  With -noargs, the process starts with an empty commandline.
 | 
					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{"continue", "c"}, cmdFn: c.cont, helpMsg: "Run until breakpoint or program termination."},
 | 
				
			||||||
		{aliases: []string{"step", "s"}, cmdFn: c.step, helpMsg: "Single step through program."},
 | 
							{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{"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!!!)
 | 
							{aliases: []string{"call"}, cmdFn: c.call, helpMsg: `Resumes process, injecting a function call (EXPERIMENTAL!!!)
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
 | 
						call [-unsafe] <function call expression>
 | 
				
			||||||
 | 
						
 | 
				
			||||||
Current limitations:
 | 
					Current limitations:
 | 
				
			||||||
- only pointers to stack-allocated objects can be passed as argument.
 | 
					- only pointers to stack-allocated objects can be passed as argument.
 | 
				
			||||||
- only some automatic type conversions are supported.
 | 
					- 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 a single argument it will switch to the specified goroutine.
 | 
				
			||||||
Called with more arguments it will execute a command on 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{"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>
 | 
						[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.
 | 
							{aliases: []string{"whatis"}, cmdFn: whatisCommand, helpMsg: `Prints type of an expression.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	whatis <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>
 | 
						[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.
 | 
							{aliases: []string{"sources"}, cmdFn: sources, helpMsg: `Print list of source files.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	sources [<regex>]
 | 
						sources [<regex>]
 | 
				
			||||||
@@ -214,12 +217,12 @@ If regex is specified only the functions matching it will be returned.`},
 | 
				
			|||||||
	types [<regex>]
 | 
						types [<regex>]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
If regex is specified only the types matching it will be returned.`},
 | 
					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>]
 | 
						[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.`},
 | 
					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>]
 | 
						[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.`},
 | 
					Show source around current point or provided linespec.`},
 | 
				
			||||||
		{aliases: []string{"stack", "bt"}, allowedPrefixes: onPrefix, cmdFn: stackCommand, helpMsg: `Print stack trace.
 | 
							{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.
 | 
						-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"},
 | 
							{aliases: []string{"frame"},
 | 
				
			||||||
			cmdFn: func(t *Term, ctx callContext, arg string) error {
 | 
								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.
 | 
								helpMsg: `Set the current frame, or execute command on a different frame.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  frame <m>
 | 
						frame <m>
 | 
				
			||||||
  frame <m> <command>
 | 
						frame <m> <command>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
The first form sets frame used by subsequent commands such as "print" or "set".
 | 
					The first form sets frame used by subsequent commands such as "print" or "set".
 | 
				
			||||||
The second form runs the command on the given frame.`},
 | 
					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.
 | 
								helpMsg: `Move the current frame up.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  up [<m>]
 | 
						up [<m>]
 | 
				
			||||||
  up [<m>] <command>
 | 
						up [<m>] <command>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Move the current frame up by <m>. The second form runs the command on the given frame.`},
 | 
					Move the current frame up by <m>. The second form runs the command on the given frame.`},
 | 
				
			||||||
		{aliases: []string{"down"},
 | 
							{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.
 | 
								helpMsg: `Move the current frame down.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  down [<m>]
 | 
						down [<m>]
 | 
				
			||||||
  down [<m>] <command>
 | 
						down [<m>] <command>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Move the current frame down by <m>. The second form runs the command on the given frame.`},
 | 
					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
 | 
							{aliases: []string{"source"}, cmdFn: c.sourceCommand, helpMsg: `Executes a file containing a list of delve commands
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	source <path>`},
 | 
						source <path>`},
 | 
				
			||||||
@@ -334,6 +345,7 @@ Defines <alias> as an alias to <command> or removes an alias.`},
 | 
				
			|||||||
	edit [locspec]
 | 
						edit [locspec]
 | 
				
			||||||
	
 | 
						
 | 
				
			||||||
If locspec is omitted edit will open the current source file in the editor, otherwise it will open the specified location.`},
 | 
					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() {
 | 
						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,
 | 
								cmdFn:   checkpoint,
 | 
				
			||||||
			helpMsg: `Creates a checkpoint at the current position.
 | 
								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{
 | 
							c.cmds = append(c.cmds, command{
 | 
				
			||||||
			aliases: []string{"checkpoints"},
 | 
								aliases: []string{"checkpoints"},
 | 
				
			||||||
@@ -426,7 +440,7 @@ func (c *Commands) CallWithContext(cmdstr string, t *Term, ctx callContext) erro
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// Call takes a command to execute.
 | 
					// Call takes a command to execute.
 | 
				
			||||||
func (c *Commands) Call(cmdstr string, t *Term) error {
 | 
					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)
 | 
						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) 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 }
 | 
					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 {
 | 
					func goroutines(t *Term, ctx callContext, argstr string) error {
 | 
				
			||||||
	args := strings.Split(argstr, " ")
 | 
						args := strings.Split(argstr, " ")
 | 
				
			||||||
	var fgl = fglUserCurrent
 | 
						var fgl = fglUserCurrent
 | 
				
			||||||
@@ -593,26 +628,24 @@ func goroutines(t *Term, ctx callContext, argstr string) error {
 | 
				
			|||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	gs, err := t.client.ListGoroutines()
 | 
						var (
 | 
				
			||||||
	if err != nil {
 | 
							start = 0
 | 
				
			||||||
		return err
 | 
							gslen = 0
 | 
				
			||||||
	}
 | 
							gs    []*api.Goroutine
 | 
				
			||||||
	sort.Sort(byGoroutineID(gs))
 | 
						)
 | 
				
			||||||
	fmt.Printf("[%d goroutines]\n", len(gs))
 | 
						for start >= 0 {
 | 
				
			||||||
	for _, g := range gs {
 | 
							gs, start, err = t.client.ListGoroutines(start, goroutineBatchSize)
 | 
				
			||||||
		prefix := "  "
 | 
							if err != nil {
 | 
				
			||||||
		if state.SelectedGoroutine != nil && g.ID == state.SelectedGoroutine.ID {
 | 
								return err
 | 
				
			||||||
			prefix = "* "
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		fmt.Printf("%sGoroutine %s\n", prefix, formatGoroutine(g, fgl))
 | 
							sort.Sort(byGoroutineID(gs))
 | 
				
			||||||
		if bPrintStack {
 | 
							err = printGoroutines(t, gs, fgl, bPrintStack, state)
 | 
				
			||||||
			stack, err := t.client.Stacktrace(g.ID, 10, false, nil)
 | 
							if err != nil {
 | 
				
			||||||
			if err != nil {
 | 
								return err
 | 
				
			||||||
				return err
 | 
					 | 
				
			||||||
			}
 | 
					 | 
				
			||||||
			printStack(stack, "\t", false)
 | 
					 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
							gslen += len(gs)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
						fmt.Printf("[%d goroutines]\n", gslen)
 | 
				
			||||||
	return nil
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -714,6 +747,22 @@ func (c *Commands) frameCommand(t *Term, ctx callContext, argstr string, directi
 | 
				
			|||||||
	return nil
 | 
						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 {
 | 
					func printscope(t *Term) error {
 | 
				
			||||||
	state, err := t.client.GetState()
 | 
						state, err := t.client.GetState()
 | 
				
			||||||
	if err != nil {
 | 
						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 {
 | 
						if err := scopePrefixSwitch(t, ctx); err != nil {
 | 
				
			||||||
		return err
 | 
							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
 | 
						c.frame = 0
 | 
				
			||||||
	if err != nil {
 | 
						if err != nil {
 | 
				
			||||||
		printcontextNoState(t)
 | 
							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 := exec.Command(editor, fmt.Sprintf("+%d", lineno), file)
 | 
				
			||||||
 | 
						cmd.Stdin = os.Stdin
 | 
				
			||||||
 | 
						cmd.Stdout = os.Stdout
 | 
				
			||||||
 | 
						cmd.Stderr = os.Stderr
 | 
				
			||||||
	return cmd.Run()
 | 
						return cmd.Run()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1356,6 +1414,20 @@ func stackCommand(t *Term, ctx callContext, args string) error {
 | 
				
			|||||||
		return err
 | 
							return err
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
	printStack(stack, "", sa.offsets)
 | 
						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
 | 
						return nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1364,6 +1436,9 @@ type stackArgs struct {
 | 
				
			|||||||
	full       bool
 | 
						full       bool
 | 
				
			||||||
	offsets    bool
 | 
						offsets    bool
 | 
				
			||||||
	readDefers bool
 | 
						readDefers bool
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
						ancestors     int
 | 
				
			||||||
 | 
						ancestorDepth int
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func parseStackArgs(argstr string) (stackArgs, error) {
 | 
					func parseStackArgs(argstr string) (stackArgs, error) {
 | 
				
			||||||
@@ -1373,7 +1448,18 @@ func parseStackArgs(argstr string) (stackArgs, error) {
 | 
				
			|||||||
	}
 | 
						}
 | 
				
			||||||
	if argstr != "" {
 | 
						if argstr != "" {
 | 
				
			||||||
		args := strings.Split(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] {
 | 
								switch args[i] {
 | 
				
			||||||
			case "-full":
 | 
								case "-full":
 | 
				
			||||||
				r.full = true
 | 
									r.full = true
 | 
				
			||||||
@@ -1381,6 +1467,20 @@ func parseStackArgs(argstr string) (stackArgs, error) {
 | 
				
			|||||||
				r.offsets = true
 | 
									r.offsets = true
 | 
				
			||||||
			case "-defer":
 | 
								case "-defer":
 | 
				
			||||||
				r.readDefers = true
 | 
									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:
 | 
								default:
 | 
				
			||||||
				n, err := strconv.Atoi(args[i])
 | 
									n, err := strconv.Atoi(args[i])
 | 
				
			||||||
				if err != nil {
 | 
									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
 | 
						return r, nil
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -1526,6 +1629,18 @@ func disassCommand(t *Term, ctx callContext, args string) error {
 | 
				
			|||||||
	return nil
 | 
						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 {
 | 
					func digits(n int) int {
 | 
				
			||||||
	if n <= 0 {
 | 
						if n <= 0 {
 | 
				
			||||||
		return 1
 | 
							return 1
 | 
				
			||||||
@@ -1565,7 +1680,7 @@ func printStack(stack []api.Stackframe, ind string, offsets bool) {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		for j, d := range stack[i].Defers {
 | 
							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))
 | 
								s2 := strings.Repeat(" ", len(deferHeader))
 | 
				
			||||||
			if d.Unreadable != "" {
 | 
								if d.Unreadable != "" {
 | 
				
			||||||
				fmt.Printf("%s(unreadable defer: %s)\n", deferHeader, 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 {
 | 
						if th.BreakpointInfo != nil && th.Breakpoint.LoadArgs != nil && *th.Breakpoint.LoadArgs == ShortLoadConfig {
 | 
				
			||||||
		var arg []string
 | 
							var arg []string
 | 
				
			||||||
		for _, ar := range th.BreakpointInfo.Arguments {
 | 
							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, ", ")
 | 
							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 err := c.Call(line, t); err != nil {
 | 
				
			||||||
 | 
								if _, isExitRequest := err.(ExitRequestError); isExitRequest {
 | 
				
			||||||
 | 
									return err
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			fmt.Printf("%s:%d: %v\n", name, lineno, 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 := new(tabwriter.Writer)
 | 
				
			||||||
	w.Init(os.Stdout, 4, 4, 2, ' ', 0)
 | 
						w.Init(os.Stdout, 4, 4, 2, ' ', 0)
 | 
				
			||||||
	fmt.Fprintln(w, "ID\tWhen\tWhere")
 | 
						fmt.Fprintln(w, "ID\tWhen\tNote")
 | 
				
			||||||
	for _, cp := range cps {
 | 
						for _, cp := range cps {
 | 
				
			||||||
		fmt.Fprintf(w, "c%d\t%s\t%s\n", cp.ID, cp.When, cp.Where)
 | 
							fmt.Fprintf(w, "c%d\t%s\t%s\n", cp.ID, cp.When, cp.Where)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
@@ -8,7 +8,7 @@ import (
 | 
				
			|||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"text/tabwriter"
 | 
						"text/tabwriter"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/config"
 | 
						"github.com/go-delve/delve/pkg/config"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func configureCmd(t *Term, ctx callContext, args string) error {
 | 
					func configureCmd(t *Term, ctx callContext, args string) error {
 | 
				
			||||||
@@ -7,7 +7,7 @@ import (
 | 
				
			|||||||
	"path/filepath"
 | 
						"path/filepath"
 | 
				
			||||||
	"text/tabwriter"
 | 
						"text/tabwriter"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/service/api"
 | 
						"github.com/go-delve/delve/service/api"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func DisasmPrint(dv api.AsmInstructions, out io.Writer) {
 | 
					func DisasmPrint(dv api.AsmInstructions, out io.Writer) {
 | 
				
			||||||
@@ -7,7 +7,7 @@ import (
 | 
				
			|||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
func replaceDocPath(s string) string {
 | 
					func replaceDocPath(s string) string {
 | 
				
			||||||
	const docpath = "$GOPATH/src/github.com/derekparker/delve/"
 | 
						const docpath = "$GOPATH/src/github.com/go-delve/delve/"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for {
 | 
						for {
 | 
				
			||||||
		start := strings.Index(s, docpath)
 | 
							start := strings.Index(s, docpath)
 | 
				
			||||||
@@ -22,7 +22,7 @@ func replaceDocPath(s string) string {
 | 
				
			|||||||
		}
 | 
							}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
		text := s[start+len(docpath) : end]
 | 
							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/peterh/liner"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/config"
 | 
						"github.com/go-delve/delve/pkg/config"
 | 
				
			||||||
	"github.com/derekparker/delve/service"
 | 
						"github.com/go-delve/delve/service"
 | 
				
			||||||
	"github.com/derekparker/delve/service/api"
 | 
						"github.com/go-delve/delve/service/api"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
const (
 | 
					const (
 | 
				
			||||||
@@ -64,20 +64,6 @@ type Term struct {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
// New returns a new Term.
 | 
					// New returns a new Term.
 | 
				
			||||||
func New(client service.Client, conf *config.Config) *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)
 | 
						cmds := DebugCommands(client)
 | 
				
			||||||
	if conf != nil && conf.Aliases != nil {
 | 
						if conf != nil && conf.Aliases != nil {
 | 
				
			||||||
		cmds.Merge(conf.Aliases)
 | 
							cmds.Merge(conf.Aliases)
 | 
				
			||||||
@@ -204,6 +190,9 @@ func (t *Term) Run() (int, error) {
 | 
				
			|||||||
	if t.InitFile != "" {
 | 
						if t.InitFile != "" {
 | 
				
			||||||
		err := t.cmds.executeFile(t, t.InitFile)
 | 
							err := t.cmds.executeFile(t, t.InitFile)
 | 
				
			||||||
		if err != nil {
 | 
							if err != nil {
 | 
				
			||||||
 | 
								if _, ok := err.(ExitRequestError); ok {
 | 
				
			||||||
 | 
									return t.handleExit()
 | 
				
			||||||
 | 
								}
 | 
				
			||||||
			fmt.Fprintf(os.Stderr, "Error executing init file: %s\n", err)
 | 
								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 {
 | 
						if t.conf == nil {
 | 
				
			||||||
		return path
 | 
							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 {
 | 
						for _, r := range t.conf.SubstitutePath {
 | 
				
			||||||
		from := crossPlatformPath(r.From)
 | 
							from := crossPlatformPath(r.From)
 | 
				
			||||||
		to := r.To
 | 
							to := r.To
 | 
				
			||||||
@@ -14,8 +14,8 @@ type Version struct {
 | 
				
			|||||||
var (
 | 
					var (
 | 
				
			||||||
	// DelveVersion is the current version of Delve.
 | 
						// DelveVersion is the current version of Delve.
 | 
				
			||||||
	DelveVersion = Version{
 | 
						DelveVersion = Version{
 | 
				
			||||||
		Major: "1", Minor: "1", Patch: "0", Metadata: "",
 | 
							Major: "1", Minor: "2", Patch: "0", Metadata: "",
 | 
				
			||||||
		Build: "$Id: 1990ba12450cab9425a2ae62e6ab988725023d5c $",
 | 
							Build: "$Id: 068e2451004e95d0b042e5257e34f0f08ce01466 $",
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -2,14 +2,15 @@ package api
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
import (
 | 
					import (
 | 
				
			||||||
	"bytes"
 | 
						"bytes"
 | 
				
			||||||
 | 
						"fmt"
 | 
				
			||||||
	"go/constant"
 | 
						"go/constant"
 | 
				
			||||||
	"go/printer"
 | 
						"go/printer"
 | 
				
			||||||
	"go/token"
 | 
						"go/token"
 | 
				
			||||||
	"reflect"
 | 
						"reflect"
 | 
				
			||||||
	"strconv"
 | 
						"strconv"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/dwarf/godwarf"
 | 
						"github.com/go-delve/delve/pkg/dwarf/godwarf"
 | 
				
			||||||
	"github.com/derekparker/delve/pkg/proc"
 | 
						"github.com/go-delve/delve/pkg/proc"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// ConvertBreakpoint converts from a proc.Breakpoint to
 | 
					// ConvertBreakpoint converts from a proc.Breakpoint to
 | 
				
			||||||
@@ -23,6 +24,7 @@ func ConvertBreakpoint(bp *proc.Breakpoint) *Breakpoint {
 | 
				
			|||||||
		Line:          bp.Line,
 | 
							Line:          bp.Line,
 | 
				
			||||||
		Addr:          bp.Addr,
 | 
							Addr:          bp.Addr,
 | 
				
			||||||
		Tracepoint:    bp.Tracepoint,
 | 
							Tracepoint:    bp.Tracepoint,
 | 
				
			||||||
 | 
							TraceReturn:   bp.TraceReturn,
 | 
				
			||||||
		Stacktrace:    bp.Stacktrace,
 | 
							Stacktrace:    bp.Stacktrace,
 | 
				
			||||||
		Goroutine:     bp.Goroutine,
 | 
							Goroutine:     bp.Goroutine,
 | 
				
			||||||
		Variables:     bp.Variables,
 | 
							Variables:     bp.Variables,
 | 
				
			||||||
@@ -142,8 +144,9 @@ func ConvertVar(v *proc.Variable) *Variable {
 | 
				
			|||||||
		case reflect.String, reflect.Func:
 | 
							case reflect.String, reflect.Func:
 | 
				
			||||||
			r.Value = constant.StringVal(v.Value)
 | 
								r.Value = constant.StringVal(v.Value)
 | 
				
			||||||
		default:
 | 
							default:
 | 
				
			||||||
			r.Value = v.ConstDescr()
 | 
								if cd := v.ConstDescr(); cd != "" {
 | 
				
			||||||
			if r.Value == "" {
 | 
									r.Value = fmt.Sprintf("%s (%s)", cd, v.Value.String())
 | 
				
			||||||
 | 
								} else {
 | 
				
			||||||
				r.Value = v.Value.String()
 | 
									r.Value = v.Value.String()
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@@ -282,6 +285,7 @@ func LoadConfigToProc(cfg *LoadConfig) *proc.LoadConfig {
 | 
				
			|||||||
		cfg.MaxStringLen,
 | 
							cfg.MaxStringLen,
 | 
				
			||||||
		cfg.MaxArrayValues,
 | 
							cfg.MaxArrayValues,
 | 
				
			||||||
		cfg.MaxStructFields,
 | 
							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) {
 | 
					func ConvertCheckpoint(in proc.Checkpoint) (out Checkpoint) {
 | 
				
			||||||
	return Checkpoint(in)
 | 
						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