summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorStefan Haller <stefan@haller-berlin.de>2023-09-11 08:27:03 +0200
committerGitHub <noreply@github.com>2023-09-11 08:27:03 +0200
commit0aad599a9b17d611c9d7de940de7ec875896cf09 (patch)
tree0899f8c321f14006a3b888ede6d4a7d66476301a
parent67ac2c5d9b32f2916decc4d4afcf9ab7579dc364 (diff)
parentb6c892a08a29527211304b1558c2e584aa2e0cea (diff)
Various debugging improvements (#3000)
-rw-r--r--.vscode/launch.json39
-rw-r--r--Makefile4
-rw-r--r--cmd/integration_test/main.go6
-rw-r--r--go.mod1
-rw-r--r--go.sum2
-rw-r--r--pkg/integration/README.md9
-rw-r--r--pkg/integration/clients/cli.go3
-rw-r--r--pkg/integration/clients/go_test.go1
-rw-r--r--pkg/integration/clients/injector/main.go29
-rw-r--r--pkg/integration/clients/tui.go28
-rw-r--r--pkg/integration/components/runner.go11
-rw-r--r--vendor/github.com/mitchellh/go-ps/.gitignore1
-rw-r--r--vendor/github.com/mitchellh/go-ps/LICENSE.md21
-rw-r--r--vendor/github.com/mitchellh/go-ps/README.md34
-rw-r--r--vendor/github.com/mitchellh/go-ps/Vagrantfile43
-rw-r--r--vendor/github.com/mitchellh/go-ps/process.go40
-rw-r--r--vendor/github.com/mitchellh/go-ps/process_darwin.go138
-rw-r--r--vendor/github.com/mitchellh/go-ps/process_freebsd.go260
-rw-r--r--vendor/github.com/mitchellh/go-ps/process_linux.go35
-rw-r--r--vendor/github.com/mitchellh/go-ps/process_solaris.go96
-rw-r--r--vendor/github.com/mitchellh/go-ps/process_unix.go95
-rw-r--r--vendor/github.com/mitchellh/go-ps/process_windows.go119
-rw-r--r--vendor/modules.txt3
23 files changed, 1000 insertions, 18 deletions
diff --git a/.vscode/launch.json b/.vscode/launch.json
index 6bddacc40..4e05edffb 100644
--- a/.vscode/launch.json
+++ b/.vscode/launch.json
@@ -7,7 +7,11 @@
"request": "launch",
"mode": "auto",
"program": "main.go",
- "args": ["--debug", "--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"],
+ "args": [
+ "--debug",
+ "--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"
+ ],
+ "hideSystemGoroutines": true,
"console": "integratedTerminal",
"presentation": {
"hidden": true
@@ -19,17 +23,44 @@
"request": "launch",
"mode": "auto",
"program": "main.go",
- "args": ["--logs", "--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"],
+ "args": [
+ "--logs",
+ "--use-config-file=${workspaceFolder}/.vscode/debugger_config.yml"
+ ],
"console": "integratedTerminal",
"presentation": {
"hidden": true
}
- }
+ },
+ {
+ "name": "Attach to a running Lazygit",
+ "type": "go",
+ "request": "attach",
+ "mode": "local",
+ "processId": "lazygit",
+ "hideSystemGoroutines": true,
+ "console": "integratedTerminal",
+ },
+ {
+ // To use this, first start an integration test with the "cli" runner and
+ // use the -debug option; e.g.
+ // $ make integration-test-cli -- -debug tag/reset.go
+ "name": "Attach to integration test runner",
+ "type": "go",
+ "request": "attach",
+ "mode": "local",
+ "processId": "test_lazygit",
+ "hideSystemGoroutines": true,
+ "console": "integratedTerminal",
+ },
],
"compounds": [
{
"name": "Run with logs",
- "configurations": ["Tail Lazygit logs", "Debug Lazygit"],
+ "configurations": [
+ "Tail Lazygit logs",
+ "Debug Lazygit"
+ ],
"stopAll": true
}
]
diff --git a/Makefile b/Makefile
index 158ab498d..f10109c19 100644
--- a/Makefile
+++ b/Makefile
@@ -10,8 +10,8 @@ install:
go install
.PHONY: run
-run:
- go run main.go
+run: build
+ ./lazygit
# Run `make run-debug` in one terminal tab and `make print-log` in another to view the program and its log output side by side
.PHONY: run-debug
diff --git a/cmd/integration_test/main.go b/cmd/integration_test/main.go
index 32df7dd0a..7153691da 100644
--- a/cmd/integration_test/main.go
+++ b/cmd/integration_test/main.go
@@ -38,6 +38,7 @@ func main() {
testNames := os.Args[2:]
slow := false
sandbox := false
+ waitForDebugger := false
// get the next arg if it's --slow
if len(os.Args) > 2 {
if os.Args[2] == "--slow" || os.Args[2] == "-slow" {
@@ -46,10 +47,13 @@ func main() {
} else if os.Args[2] == "--sandbox" || os.Args[2] == "-sandbox" {
testNames = os.Args[3:]
sandbox = true
+ } else if os.Args[2] == "--debug" || os.Args[2] == "-debug" {
+ testNames = os.Args[3:]
+ waitForDebugger = true
}
}
- clients.RunCLI(testNames, slow, sandbox)
+ clients.RunCLI(testNames, slow, sandbox, waitForDebugger)
case "tui":
clients.RunTUI()
default:
diff --git a/go.mod b/go.mod
index a2c8c36de..2c609288d 100644
--- a/go.mod
+++ b/go.mod
@@ -25,6 +25,7 @@ require (
github.com/lucasb-eyer/go-colorful v1.2.0
github.com/mattn/go-runewidth v0.0.15
github.com/mgutz/str v1.2.0
+ github.com/mitchellh/go-ps v1.0.0
github.com/pmezard/go-difflib v1.0.0
github.com/sahilm/fuzzy v0.1.0
github.com/samber/lo v1.31.0
diff --git a/go.sum b/go.sum
index 0dcfeb428..5b4853b2c 100644
--- a/go.sum
+++ b/go.sum
@@ -230,6 +230,8 @@ github.com/mgutz/str v1.2.0 h1:4IzWSdIz9qPQWLfKZ0rJcV0jcUDpxvP4JVZ4GXQyvSw=
github.com/mgutz/str v1.2.0/go.mod h1:w1v0ofgLaJdoD0HpQ3fycxKD1WtxpjSo151pK/31q6w=
github.com/mitchellh/go-homedir v1.1.0 h1:lukF9ziXFxDFPkA1vsr5zpc1XuPDn/wFntq5mG+4E0Y=
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=
+github.com/mitchellh/go-ps v1.0.0 h1:i6ampVEEF4wQFF+bkYfwYgY+F/uYJDktmvLPf7qIgjc=
+github.com/mitchellh/go-ps v1.0.0/go.mod h1:J4lOc8z8yJs6vUwklHw2XEIiT4z4C40KtWVN3nvg8Pg=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e h1:fD57ERR4JtEqsWbfPhv4DMiApHyliiK5xCTNVSPiaAs=
github.com/niemeyer/pretty v0.0.0-20200227124842-a10e7caefd8e/go.mod h1:zD1mROLANZcx1PVRCS0qkT7pwLkGfwJo4zjcN/Tysno=
github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE=
diff --git a/pkg/integration/README.md b/pkg/integration/README.md
index e0d32665a..1e0562509 100644
--- a/pkg/integration/README.md
+++ b/pkg/integration/README.md
@@ -61,6 +61,15 @@ If you've opened an integration test file in your editor you can run that file b
The test will run in a VSCode terminal:
![image](https://user-images.githubusercontent.com/8456633/201500446-b87abf11-9653-438f-8a9a-e0bf8abdb7ee.png)
+### Debugging tests
+
+Debugging an integration test is possible in two ways:
+
+1. Use the -debug option of the integration test runner's "cli" command, e.g. `go run cmd/integration_test/main.go cli -debug tag/reset.go`
+2. Select a test in the "tui" runner and hit "d" to debug it.
+
+In both cases the test runner will print to the console that it is waiting for a debugger to attach, so now you need to tell your debugger to attach to a running process with the name "test_lazygit". If you are using Visual Studio Code, an easy way to do that is to use the "Attach to integration test runner" debug configuration. The test runner will resume automatically when it detects that a debugger was attached. Don't forget to set a breakpoint in the code that you want to step through, otherwise the test will just finish (i.e. it doesn't stop in the debugger automatically).
+
### Sandbox mode
Say you want to do a manual test of how lazygit handles merge-conflicts, but you can't be bothered actually finding a way to create merge conflicts in a repo. To make your life easier, you can simply run a merge-conflicts test in sandbox mode, meaning the setup step is run for you, and then instead of the test driving the lazygit session, you're allowed to drive it yourself.
diff --git a/pkg/integration/clients/cli.go b/pkg/integration/clients/cli.go
index 08cff3f00..571dea491 100644
--- a/pkg/integration/clients/cli.go
+++ b/pkg/integration/clients/cli.go
@@ -23,7 +23,7 @@ import (
// If invoked directly, you can specify tests to run by passing their names as positional arguments
-func RunCLI(testNames []string, slow bool, sandbox bool) {
+func RunCLI(testNames []string, slow bool, sandbox bool, waitForDebugger bool) {
inputDelay := tryConvert(os.Getenv("INPUT_DELAY"), 0)
if slow {
inputDelay = SLOW_INPUT_DELAY
@@ -35,6 +35,7 @@ func RunCLI(testNames []string, slow bool, sandbox bool) {
runCmdInTerminal,
runAndPrintFatalError,
sandbox,
+ waitForDebugger,
inputDelay,
1,
)
diff --git a/pkg/integration/clients/go_test.go b/pkg/integration/clients/go_test.go
index 18d8569bd..01f174211 100644
--- a/pkg/integration/clients/go_test.go
+++ b/pkg/integration/clients/go_test.go
@@ -52,6 +52,7 @@ func TestIntegration(t *testing.T) {
})
},
false,
+ false,
0,
// Allow two attempts at each test to get around flakiness
2,
diff --git a/pkg/integration/clients/injector/main.go b/pkg/integration/clients/injector/main.go
index 0754503e8..63c3e2a07 100644
--- a/pkg/integration/clients/injector/main.go
+++ b/pkg/integration/clients/injector/main.go
@@ -3,12 +3,14 @@ package main
import (
"fmt"
"os"
+ "time"
"github.com/jesseduffield/lazygit/pkg/app"
"github.com/jesseduffield/lazygit/pkg/app/daemon"
"github.com/jesseduffield/lazygit/pkg/integration/components"
"github.com/jesseduffield/lazygit/pkg/integration/tests"
integrationTypes "github.com/jesseduffield/lazygit/pkg/integration/types"
+ "github.com/mitchellh/go-ps"
)
// The purpose of this program is to run lazygit with an integration test passed in.
@@ -29,6 +31,15 @@ func main() {
integrationTest := getIntegrationTest()
+ if os.Getenv("WAIT_FOR_DEBUGGER") != "" {
+ println("Waiting for debugger to attach...")
+ for !isDebuggerAttached() {
+ time.Sleep(time.Millisecond * 100)
+ }
+
+ println("Debugger attached, continuing")
+ }
+
app.Start(dummyBuildInfo, integrationTest)
}
@@ -56,3 +67,21 @@ func getIntegrationTest() integrationTypes.IntegrationTest {
panic("Could not find integration test with name: " + integrationTestName)
}
+
+// Returns whether we are running under a debugger. It uses a heuristic to find
+// out: when using dlv, it starts a debugserver executable (which is part of
+// lldb), and the debuggee becomes a child process of that. So if the name of
+// our parent process is "debugserver", we run under a debugger. This works even
+// if the parent process used to be the shell and you then attach to the running
+// executable.
+//
+// On Mac this works with VS Code, with the Jetbrains Goland IDE, and when using
+// dlv attach in a terminal. I have not been able to verify that it works on
+// other platforms, it may have to be adapted there.
+func isDebuggerAttached() bool {
+ process, err := ps.FindProcess(os.Getppid())
+ if err != nil {
+ return false
+ }
+ return process.Executable() == "debugserver"
+}
diff --git a/pkg/integration/clients/tui.go b/pkg/integration/clients/tui.go
index 9b873caab..8036b118b 100644
--- a/pkg/integration/clients/tui.go
+++ b/pkg/integration/clients/tui.go
@@ -85,7 +85,7 @@ func RunTUI() {
return nil
}
- suspendAndRunTest(currentTest, true, 0)
+ suspendAndRunTest(currentTest, true, false, 0)
return nil
}); err != nil {
@@ -98,7 +98,7 @@ func RunTUI() {
return nil
}
- suspendAndRunTest(currentTest, false, 0)
+ suspendAndRunTest(currentTest, false, false, 0)
return nil
}); err != nil {
@@ -111,7 +111,20 @@ func RunTUI() {
return nil
}
- suspendAndRunTest(currentTest, false, SLOW_INPUT_DELAY)
+ suspendAndRunTest(currentTest, false, false, SLOW_INPUT_DELAY)
+
+ return nil
+ }); err != nil {
+ log.Panicln(err)
+ }
+
+ if err := g.SetKeybinding("list", 'd', gocui.ModNone, func(*gocui.Gui, *gocui.View) error {
+ currentTest := app.getCurrentTest()
+ if currentTest == nil {
+ return nil
+ }
+
+ suspendAndRunTest(currentTest, false, true, 0)
return nil
}); err != nil {
@@ -271,12 +284,12 @@ func (self *app) wrapEditor(f func(v *gocui.View, key gocui.Key, ch rune, mod go
}
}
-func suspendAndRunTest(test *components.IntegrationTest, sandbox bool, inputDelay int) {
+func suspendAndRunTest(test *components.IntegrationTest, sandbox bool, waitForDebugger bool, inputDelay int) {
if err := gocui.Screen.Suspend(); err != nil {
panic(err)
}
- runTuiTest(test, sandbox, inputDelay)
+ runTuiTest(test, sandbox, waitForDebugger, inputDelay)
fmt.Fprintf(os.Stdout, "\n%s", style.FgGreen.Sprint("press enter to return"))
fmt.Scanln() // wait for enter press
@@ -337,7 +350,7 @@ func (self *app) layout(g *gocui.Gui) error {
keybindingsView.Title = "Keybindings"
keybindingsView.Wrap = true
keybindingsView.FgColor = gocui.ColorDefault
- fmt.Fprintln(keybindingsView, "up/down: navigate, enter: run test, t: run test slow, s: sandbox, o: open test file, shift+o: open test snapshot directory, forward-slash: filter")
+ fmt.Fprintln(keybindingsView, "up/down: navigate, enter: run test, t: run test slow, s: sandbox, d: debug test, o: open test file, shift+o: open test snapshot directory, forward-slash: filter")
}
editorView, err := g.SetViewBeneath("editor", "keybindings", editorViewHeight)
@@ -371,13 +384,14 @@ func quit(g *gocui.Gui, v *gocui.View) error {
return gocui.ErrQuit
}
-func runTuiTest(test *components.IntegrationTest, sandbox bool, inputDelay int) {
+func runTuiTest(test *components.IntegrationTest, sandbox bool, waitForDebugger bool, inputDelay int) {
err := components.RunTests(
[]*components.IntegrationTest{test},
log.Printf,
runCmdInTerminal,
runAndPrintError,
sandbox,
+ waitForDebugger,
inputDelay,
1,
)
diff --git a/pkg/integration/components/runner.go b/pkg/integration/components/runner.go
index 9d380044a..339a7114e 100644
--- a/pkg/integration/components/runner.go
+++ b/pkg/integration/components/runner.go
@@ -29,6 +29,7 @@ func RunTests(
runCmd func(cmd *exec.Cmd) error,
testWrapper func(test *IntegrationTest, f func() error),
sandbox bool,
+ waitForDebugger bool,
inputDelay int,
maxAttempts int,
) error {
@@ -58,7 +59,7 @@ func RunTests(
)
for i := 0; i < maxAttempts; i++ {
- err := runTest(test, paths, projectRootDir, logf, runCmd, sandbox, inputDelay, gitVersion)
+ err := runTest(test, paths, projectRootDir, logf, runCmd, sandbox, waitForDebugger, inputDelay, gitVersion)
if err != nil {
if i == maxAttempts-1 {
return err
@@ -83,6 +84,7 @@ func runTest(
logf func(format string, formatArgs ...interface{}),
runCmd func(cmd *exec.Cmd) error,
sandbox bool,
+ waitForDebugger bool,
inputDelay int,
gitVersion *git_commands.GitVersion,
) error {
@@ -100,7 +102,7 @@ func runTest(
return err
}
- cmd, err := getLazygitCommand(test, paths, projectRootDir, sandbox, inputDelay)
+ cmd, err := getLazygitCommand(test, paths, projectRootDir, sandbox, waitForDebugger, inputDelay)
if err != nil {
return err
}
@@ -165,7 +167,7 @@ func getGitVersion() (*git_commands.GitVersion, error) {
return git_commands.ParseGitVersion(versionStr)
}
-func getLazygitCommand(test *IntegrationTest, paths Paths, rootDir string, sandbox bool, inputDelay int) (*exec.Cmd, error) {
+func getLazygitCommand(test *IntegrationTest, paths Paths, rootDir string, sandbox bool, waitForDebugger bool, inputDelay int) (*exec.Cmd, error) {
osCommand := oscommands.NewDummyOSCommand()
err := os.RemoveAll(paths.Config())
@@ -197,6 +199,9 @@ func getLazygitCommand(test *IntegrationTest, paths Paths, rootDir string, sandb
if sandbox {
cmdObj.AddEnvVars(fmt.Sprintf("%s=%s", SANDBOX_ENV_VAR, "true"))
}
+ if waitForDebugger {
+ cmdObj.AddEnvVars("WAIT_FOR_DEBUGGER=true")
+ }
if test.ExtraEnvVars() != nil {
for key, value := range test.ExtraEnvVars() {
cmdObj.AddEnvVars(fmt.Sprintf("%s=%s", key, value))
diff --git a/vendor/github.com/mitchellh/go-ps/.gitignore b/vendor/github.com/mitchellh/go-ps/.gitignore
new file mode 100644
index 000000000..a977916f6
--- /dev/null
+++ b/vendor/github.com/mitchellh/go-ps/.gitignore
@@ -0,0 +1 @@
+.vagrant/
diff --git a/vendor/github.com/mitchellh/go-ps/LICENSE.md b/vendor/github.com/mitchellh/go-ps/LICENSE.md
new file mode 100644
index 000000000..229851590
--- /dev/null
+++ b/vendor/github.com/mitchellh/go-ps/LICENSE.md
@@ -0,0 +1,21 @@
+The MIT License (MIT)
+
+Copyright (c) 2014 Mitchell Hashimoto
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
diff --git a/vendor/github.com/mitchellh/go-ps/README.md b/vendor/github.com/mitchellh/go-ps/README.md
new file mode 100644
index 000000000..4e3d0e146
--- /dev/null
+++ b/vendor/github.com/mitchellh/go-ps/README.md
@@ -0,0 +1,34 @@
+# Process List Library for Go [![GoDoc](https://godoc.org/github.com/mitchellh/go-ps?status.png)](https://godoc.org/github.com/mitchellh/go-ps)
+
+go-ps is a library for Go that implements OS-specific APIs to list and
+manipulate processes in a platform-safe way. The library can find and
+list processes on Linux, Mac OS X, Solaris, and Windows.
+
+If you're new to Go, this library has a good amount of advanced Go educational
+value as well. It uses some advanced features of Go: build tags, accessing
+DLL methods for Windows, cgo for Darwin, etc.
+
+How it works:
+
+ * **Darwin** uses the `sysctl` syscall to retrieve the process table.
+ * **Unix** uses the procfs at `/proc` to inspect the process tree.
+ * **Windows** uses the Windows API, and methods such as
+ `CreateToolhelp32Snapshot` to get a point-in-time snapshot of
+ the process table.
+
+## Installation
+
+Install using standard `go get`:
+
+```
+$ go get github.com/mitchellh/go-ps
+...
+```
+
+## TODO
+
+Want to contribute? Here is a short TODO list of things that aren't
+implemented for this library that would be nice:
+
+ * FreeBSD support
+ * Plan9 support
diff --git a/vendor/github.com/mitchellh/go-ps/Vagrantfile b/vendor/github.com/mitchellh/go-ps/Vagrantfile
new file mode 100644
index 000000000..61662ab1e
--- /dev/null
+++ b/vendor/github.com/mitchellh/go-ps/Vagrantfile
@@ -0,0 +1,43 @@
+# -*- mode: ruby -*-
+# vi: set ft=ruby :
+
+# Vagrantfile API/syntax version. Don't touch unless you know what you're doing!
+VAGRANTFILE_API_VERSION = "2"
+
+Vagrant.configure(VAGRANTFILE_API_VERSION) do |config|
+ config.vm.box = "chef/ubuntu-12.04"
+
+ config.vm.provision "shell", inline: $script
+
+ ["vmware_fusion", "vmware_workstation"].each do |p|
+ config.vm.provider "p" do |v|
+ v.vmx["memsize"] = "1024"
+ v.vmx["numvcpus"] = "2"
+ v.vmx["cpuid.coresPerSocket"] = "1"
+ end
+ end
+end
+
+$script = <<SCRIPT
+SRCROOT="/opt/go"
+
+# Install Go
+sudo apt-get update
+sudo apt-get install -y build-essential mercurial
+sudo hg clone -u release https://code.google.com/p/go ${SRCROOT}
+cd ${SRCROOT}/src
+sudo ./all.bash
+
+# Setup the GOPATH
+sudo mkdir -p /opt/gopath
+cat <<EOF >/tmp/gopath.sh
+export GOPATH="/opt/gopath"
+export PATH="/opt/go/bin:\$GOPATH/bin:\$PATH"
+EOF
+sudo mv /tmp/gopath.sh /etc/profile.d/gopath.sh
+sudo chmod 0755 /etc/profile.d/gopath.sh
+
+# Make sure the gopath is usable by bamboo
+sudo chown -R vagrant:vagrant $SRCROOT
+sudo chown -R vagrant:vagrant /opt/gopath
+SCRIPT
diff --git a/vendor/github.com/mitchellh/go-ps/process.go b/vendor/github.com/mitchellh/go-ps/process.go
new file mode 100644
index 000000000..2b5e8ed23
--- /dev/null
+++ b/vendor/github.com/mitchellh/go-ps/process.go
@@ -0,0 +1,40 @@
+// ps provides an API for finding and listing processes in a platform-agnostic
+// way.
+//
+// NOTE: If you're reading these docs online via GoDocs or some other system,
+// you might only see the Unix docs. This project makes heavy use of
+// platform-specific implementations. We recommend reading the source if you
+// are interested.
+package ps
+
+// Process is the generic interface that is implemented on every platform
+// and provides common operations for processes.
+type Process interface {
+ // Pid is the process ID for this process.
+ Pid() int
+
+ // PPid is the parent process ID for this process.
+ PPid() int
+
+ // Executable name running this process. This is not a path to the
+ // executable.
+ Executable() string
+}
+
+// Processes returns all processes.
+//
+// This of course will be a point-in-time snapshot of when this method was
+// called. Some operating systems don't provide snapshot capability of the
+// process table, in which case the process table returned might contain
+// ephemeral entities that happened to be running when this was called.
+func Processes() ([]Process, error) {
+ return processes()
+}
+
+// FindProcess looks up a single process by pid.
+//
+// Process will be nil and error will be nil if a matching process is
+// not found.
+func FindProcess(pid int) (Process, error) {
+ return findProcess(pid)
+}
diff --git a/vendor/github.com/mitchellh/go-ps/process_darwin.go b/vendor/github.com/mitchellh/go-ps/process_darwin.go
new file mode 100644
index 000000000..5ee87fb68
--- /dev/null
+++ b/vendor/github.com/mitchellh/go-ps/process_darwin.go
@@ -0,0 +1,138 @@
+// +build darwin
+
+package ps
+
+import (
+ "bytes"
+ "encoding/binary"
+ "syscall"
+ "unsafe"
+)
+
+type DarwinProcess struct {
+ pid int
+ ppid int
+ binary string
+}
+
+func (p *DarwinProcess) Pid() int {
+ return p.pid
+}
+
+func (p *DarwinProcess) PPid() int {
+ return p.ppid
+}
+
+func (p *DarwinProcess) Executable() string {
+ return p.binary
+}
+
+func findProcess(pid int) (Process, error) {
+ ps, err := processes()
+ if err != nil {
+ return nil, err
+ }
+
+ for _, p := range ps {
+ if p.Pid() == pid {
+ return p, nil
+ }
+ }
+
+ return nil, nil
+}
+
+func processes() ([]Process, error) {
+ buf, err := darwinSyscall()
+ if err != nil {
+ return nil, err
+ }
+
+ procs := make([]*kinfoProc, 0, 50)
+ k := 0
+ for i := _KINFO_STRUCT_SIZE; i < buf.Len(); i += _KINFO_STRUCT_SIZE {
+ proc := &kinfoProc{}
+ err = binary.Read(bytes.NewBuffer(buf.Bytes()[k:i]), binary.LittleEndian, proc)
+ if err != nil {
+ return nil, err
+ }
+
+ k = i
+ procs = append(procs, proc)
+ }
+
+ darwinProcs := make([]Process, len(procs))
+ for i, p := range procs {
+ darwinProcs[i] = &DarwinProcess{
+ pid: int(p.Pid),
+ ppid: int(p.PPid),
+ binary: darwinCstring(p.Comm),
+ }
+ }
+
+ return darwinProcs, nil
+}
+
+func darwinCstring(s [16]byte) string {
+ i := 0
+ for _, b := range s {
+ if b != 0 {
+ i++
+ } else {
+ break
+ }
+ }
+
+ return string(s[:i])
+}
+
+func darwinSyscall() (*bytes.Buffer, error) {
+ mib := [4]int32{_CTRL_KERN, _KERN_PROC, _KERN_PROC_ALL, 0}
+ size := uintptr(0)
+
+ _, _, errno := syscall.Syscall6(
+ syscall.SYS___SYSCTL,
+ uintptr(unsafe.Pointer(&mib[0])),
+ 4,
+ 0,
+ uintptr(unsafe.Pointer(&size)),
+ 0,
+ 0)
+
+ if errno != 0 {
+ return nil, errno
+ }
+
+ bs := make([]byte, size)
+ _, _, errno = syscall.Syscall6(
+ syscall.SYS___SYSCTL,
+ uintptr(unsafe.Pointer(&mib[0])),
+ 4,
+ uintptr(unsafe.Pointer(&bs[0])),
+ uintptr(unsafe.Pointer(&size)),
+ 0,
+ 0)
+
+ if errno != 0 {
+ return nil, errno
+ }
+
+ return bytes.NewBuffer(bs[0:size]), nil
+}
+
+const (
+ _CTRL_KERN = 1
+ _KERN_PROC = 14
+ _KERN_PROC_ALL = 0
+ _KINFO_STRUCT_SIZE = 648
+)
+
+type kinfoProc struct {
+ _ [40]byte
+ Pid int32
+ _ [199]byte
+ Comm [16]byte
+ _ [301]byte
+ PPid int32
+ _ [84]byte
+}
diff --git a/vendor/github.com/mitchellh/go-ps/process_freebsd.go b/vendor/github.com/mitchellh/go-ps/process_freebsd.go
new file mode 100644
index 000000000..130acbe6c
--- /dev/null
+++ b/vendor/github.com/mitchellh/go-ps/process_freebsd.go
@@ -0,0 +1,260 @@
+// +build freebsd
+
+package ps
+
+import (
+ "bytes"
+ "encoding/binary"
+ "syscall"
+ "unsafe"
+)
+
+// copied from sys/sysctl.h
+const (
+ CTL_KERN = 1 // "high kernel": proc, limits
+ KERN_PROC = 14 // struct: process entries
+ KERN_PROC_PID = 1 // by process id
+ KERN_PROC_PROC = 8 // only return procs
+ KERN_PROC_PATHNAME = 12 // path to executable
+)
+
+// copied from sys/user.h
+type Kinfo_proc struct {
+ Ki_structsize int32
+ Ki_layout int32
+ Ki_args int64
+ Ki_paddr int64
+ Ki_addr int64
+ Ki_tracep int64
+ Ki_textvp int64
+ Ki_fd int64
+ Ki_vmspace int64
+ Ki_wchan int64
+ Ki_pid int32
+ Ki_ppid int32
+ Ki_pgid int32
+ Ki_tpgid int32
+ Ki_sid int32
+ Ki_tsid int32
+ Ki_jobc [2]byte
+ Ki_spare_short1 [2]byte
+ Ki_tdev int32
+ Ki_siglist [16]byte
+ Ki_sigmask [16]byte
+ Ki_sigignore [16]byte
+ Ki_sigcatch [16]byte
+ Ki_uid int32
+ Ki_ruid int32
+ Ki_svuid int32
+ Ki_rgid int32
+ Ki_svgid int32
+ Ki_ngroups [2]byte
+ Ki_spare_short2 [2]byte
+ Ki_groups [64]byte
+ Ki_size int64
+ Ki_rssize int64
+ Ki_swrss int64
+ Ki_tsize int64
+ Ki_dsize int64
+ Ki_ssize int64
+ Ki_xstat [2]byte
+ Ki_acflag [2]byte
+ Ki_pctcpu int32
+ Ki_estcpu int32
+ Ki_slptime int32
+ Ki_swtime int32
+ Ki_cow int32
+ Ki_runtime int64
+ Ki_start [16]byte
+ Ki_childtime [16]byte