summaryrefslogtreecommitdiff
path: root/misc
diff options
context:
space:
mode:
authorTianon Gravi <admwiggin@gmail.com>2015-01-15 11:54:00 -0700
committerTianon Gravi <admwiggin@gmail.com>2015-01-15 11:54:00 -0700
commitf154da9e12608589e8d5f0508f908a0c3e88a1bb (patch)
treef8255d51e10c6f1e0ed69702200b966c9556a431 /misc
parent8d8329ed5dfb9622c82a9fbec6fd99a580f9c9f6 (diff)
downloadgolang-upstream/1.4.tar.gz
Imported Upstream version 1.4upstream/1.4
Diffstat (limited to 'misc')
-rw-r--r--misc/IntelliJIDEA/Go.xml99
-rw-r--r--misc/android/README11
-rw-r--r--misc/android/go_android_exec.go96
-rw-r--r--misc/bash/go237
-rw-r--r--misc/bbedit/Go.plist104
-rwxr-xr-xmisc/benchcmp2
-rw-r--r--misc/cgo/errors/issue7757.go (renamed from misc/cgo/test/backdoor/backdoor_gccgo.go)13
-rw-r--r--misc/cgo/errors/issue8442.go17
-rwxr-xr-xmisc/cgo/errors/test.bash2
-rw-r--r--misc/cgo/test/backdoor/backdoor.go3
-rw-r--r--misc/cgo/test/backdoor/runtime.c32
-rw-r--r--misc/cgo/test/backdoor/thunk.s16
-rw-r--r--misc/cgo/test/basic.go5
-rw-r--r--misc/cgo/test/buildid_linux.go77
-rw-r--r--misc/cgo/test/callback.go67
-rw-r--r--misc/cgo/test/callback_c.c16
-rw-r--r--misc/cgo/test/callback_c_gc.c59
-rw-r--r--misc/cgo/test/callback_c_gccgo.c53
-rw-r--r--misc/cgo/test/cgo_linux_test.go5
-rw-r--r--misc/cgo/test/cgo_test.go97
-rw-r--r--misc/cgo/test/exports.go6
-rw-r--r--misc/cgo/test/issue5242.go31
-rw-r--r--misc/cgo/test/issue5548.go9
-rw-r--r--misc/cgo/test/issue6997_linux.go2
-rw-r--r--misc/cgo/test/issue7695_test.go27
-rw-r--r--misc/cgo/test/issue7978.go10
-rw-r--r--misc/cgo/test/issue8092.go36
-rw-r--r--misc/cgo/test/issue8428.go52
-rw-r--r--misc/cgo/test/issue8517.go13
-rw-r--r--misc/cgo/test/issue8517_windows.c24
-rw-r--r--misc/cgo/test/issue8517_windows.go45
-rw-r--r--misc/cgo/test/issue8694.go38
-rw-r--r--misc/cgo/test/issue8811.c8
-rw-r--r--misc/cgo/test/issue8811.go22
-rw-r--r--misc/cgo/test/issue8828.go16
-rw-r--r--misc/cgo/test/issue8828/issue8828.c7
-rw-r--r--misc/cgo/test/issue8828/trivial.go8
-rw-r--r--misc/cgo/test/issue9026.go9
-rw-r--r--misc/cgo/test/issue9026/issue9026.go36
-rw-r--r--misc/cgo/testcdefs/cdefstest.c1
-rw-r--r--misc/cgo/testcdefs/cdefstest.go19
-rw-r--r--misc/cgo/testcdefs/main.c28
-rw-r--r--misc/cgo/testgodefs/anonunion.go26
-rw-r--r--misc/cgo/testgodefs/issue8478.go20
-rw-r--r--misc/cgo/testgodefs/main.go15
-rwxr-xr-xmisc/cgo/testgodefs/test.bash20
-rw-r--r--misc/editors5
-rw-r--r--misc/emacs/go-mode-load.el96
-rw-r--r--misc/emacs/go-mode.el1196
-rw-r--r--misc/fraise/go.plist92
-rw-r--r--misc/fraise/readme.txt16
-rw-r--r--misc/kate/go.xml150
-rw-r--r--misc/makerelease/makerelease.go83
-rw-r--r--misc/makerelease/windows/installer.wxs11
-rw-r--r--misc/nacl/README97
-rwxr-xr-xmisc/nacl/go_nacl_arm_exec10
-rw-r--r--misc/nacl/mkzip.go10
-rw-r--r--misc/nacl/testzip.proto164
-rw-r--r--misc/notepadplus/README59
-rw-r--r--misc/notepadplus/functionList.xml50
-rw-r--r--misc/notepadplus/go.xml133
-rw-r--r--misc/notepadplus/userDefineLang.xml64
-rwxr-xr-xmisc/pprof5094
-rw-r--r--misc/vim/autoload/go/complete.vim103
-rw-r--r--misc/vim/compiler/go.vim30
-rw-r--r--misc/vim/ftdetect/gofiletype.vim23
-rw-r--r--misc/vim/ftplugin/go.vim19
-rw-r--r--misc/vim/ftplugin/go/fmt.vim69
-rw-r--r--misc/vim/ftplugin/go/import.vim250
-rwxr-xr-xmisc/vim/ftplugin/go/test.sh78
-rw-r--r--misc/vim/indent/go.vim77
-rw-r--r--misc/vim/plugin/godoc.vim130
-rw-r--r--misc/vim/readme.txt103
-rw-r--r--misc/vim/syntax/go.vim207
-rw-r--r--misc/vim/syntax/godoc.vim20
-rw-r--r--misc/xcode/3/README3
-rw-r--r--misc/xcode/3/go.pbfilespec31
-rw-r--r--misc/xcode/3/go.xclangspec293
-rw-r--r--misc/xcode/4/README2
-rw-r--r--misc/xcode/4/go.xclangspec290
-rwxr-xr-xmisc/xcode/4/go4xcode.sh112
-rw-r--r--misc/zsh/go161
82 files changed, 1225 insertions, 9645 deletions
diff --git a/misc/IntelliJIDEA/Go.xml b/misc/IntelliJIDEA/Go.xml
deleted file mode 100644
index 51abdf412..000000000
--- a/misc/IntelliJIDEA/Go.xml
+++ /dev/null
@@ -1,99 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!--
- Copyright 2011 The Go Authors. All rights reserved.
- Use of this source code is governed by a BSD-style
- license that can be found in the LICENSE file.
-
-Copy this custom language definition & configuration file to
- * Mac : ~/Library/Preferences/IntelliJIdea10/filetypes/
- * Linux & Windows : ~/.IntelliJIdea10/config/filetypes/
--->
-
-<filetype binary="false" default_extension="" description="Go" name="Go">
- <highlighting>
- <options>
- <option name="LINE_COMMENT" value="//"/>
- <option name="COMMENT_START" value="/*"/>
- <option name="COMMENT_END" value="*/"/>
- <option name="HEX_PREFIX" value="0x"/>
- <option name="NUM_POSTFIXES" value=""/>
- <option name="HAS_BRACKETS" value="true"/>
- <option name="HAS_BRACES" value="true"/>
- <option name="HAS_PARENS" value="true"/>
- <option name="HAS_STRING_ESCAPES" value="true"/>
- </options>
- <keywords ignore_case="false">
- <keyword name="break"/>
- <keyword name="case"/>
- <keyword name="chan"/>
- <keyword name="const"/>
- <keyword name="continue"/>
- <keyword name="default"/>
- <keyword name="defer"/>
- <keyword name="else"/>
- <keyword name="fallthrough"/>
- <keyword name="for"/>
- <keyword name="func"/>
- <keyword name="go"/>
- <keyword name="goto"/>
- <keyword name="if"/>
- <keyword name="import"/>
- <keyword name="interface"/>
- <keyword name="map"/>
- <keyword name="package"/>
- <keyword name="range"/>
- <keyword name="return"/>
- <keyword name="select"/>
- <keyword name="struct"/>
- <keyword name="switch"/>
- <keyword name="type"/>
- <keyword name="var"/>
- </keywords>
- <keywords2>
- <keyword name="bool"/>
- <keyword name="byte"/>
- <keyword name="complex64"/>
- <keyword name="complex128"/>
- <keyword name="float32"/>
- <keyword name="float64"/>
- <keyword name="int"/>
- <keyword name="int8"/>
- <keyword name="int16"/>
- <keyword name="int32"/>
- <keyword name="int64"/>
- <keyword name="string"/>
- <keyword name="uint"/>
- <keyword name="uint8"/>
- <keyword name="uint16"/>
- <keyword name="uint32"/>
- <keyword name="uint64"/>
- <keyword name="uintptr"/>
- </keywords2>
- <keywords3>
- <keyword name="append"/>
- <keyword name="cap"/>
- <keyword name="close"/>
- <keyword name="complex"/>
- <keyword name="copy"/>
- <keyword name="delete"/>
- <keyword name="imag"/>
- <keyword name="len"/>
- <keyword name="make"/>
- <keyword name="new"/>
- <keyword name="panic"/>
- <keyword name="print"/>
- <keyword name="println"/>
- <keyword name="real"/>
- <keyword name="recover"/>
- </keywords3>
- <keywords4>
- <keyword name="false"/>
- <keyword name="iota"/>
- <keyword name="nil"/>
- <keyword name="true"/>
- </keywords4>
- </highlighting>
- <extensionMap>
- <mapping ext="go"/>
- </extensionMap>
-</filetype>
diff --git a/misc/android/README b/misc/android/README
new file mode 100644
index 000000000..5f24fafc7
--- /dev/null
+++ b/misc/android/README
@@ -0,0 +1,11 @@
+Android
+=======
+
+For details on developing Go for Android, see the documentation in the
+go.mobile subrepository:
+
+ https://code.google.com/p/go/source/browse/README?repo=mobile
+
+To run the standard library tests, see androidtest.bash. Run it as
+
+ CC_FOR_TARGET=.../ndk-gcc GOARCH=arm GOARM=7 ./androidtest.bash
diff --git a/misc/android/go_android_exec.go b/misc/android/go_android_exec.go
new file mode 100644
index 000000000..e32a805a8
--- /dev/null
+++ b/misc/android/go_android_exec.go
@@ -0,0 +1,96 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This program can be used as go_android_GOARCH_exec by the Go tool.
+// It executes binaries on an android device using adb.
+package main
+
+import (
+ "bytes"
+ "fmt"
+ "io"
+ "log"
+ "os"
+ "os/exec"
+ "path/filepath"
+ "runtime"
+ "strconv"
+ "strings"
+)
+
+func run(args ...string) string {
+ buf := new(bytes.Buffer)
+ cmd := exec.Command("adb", args...)
+ cmd.Stdout = io.MultiWriter(os.Stdout, buf)
+ cmd.Stderr = os.Stderr
+ log.Printf("adb %s", strings.Join(args, " "))
+ err := cmd.Run()
+ if err != nil {
+ log.Fatalf("adb %s: %v", strings.Join(args, " "), err)
+ }
+ return buf.String()
+}
+
+func main() {
+ log.SetFlags(0)
+ log.SetPrefix("go_android_exec: ")
+
+ // Determine thepackage by examining the current working
+ // directory, which will look something like
+ // "$GOROOT/src/mime/multipart". We extract everything
+ // after the $GOROOT to run on the same relative directory
+ // on the target device.
+ //
+ // TODO(crawshaw): Pick useful subdir when we are not
+ // inside a GOROOT, e.g. we are in a GOPATH.
+ cwd, err := os.Getwd()
+ if err != nil {
+ log.Fatal(err)
+ }
+ subdir, err := filepath.Rel(runtime.GOROOT(), cwd)
+ if err != nil {
+ log.Fatal(err)
+ }
+ subdir = filepath.ToSlash(subdir)
+
+ // Binary names can conflict.
+ // E.g. template.test from the {html,text}/template packages.
+ binName := filepath.Base(os.Args[1])
+ deviceGoroot := "/data/local/tmp/goroot"
+ deviceBin := fmt.Sprintf("%s/%s-%d", deviceGoroot, binName, os.Getpid())
+
+ // The push of the binary happens in parallel with other tests.
+ // Unfortunately, a simultaneous call to adb shell hold open
+ // file descriptors, so it is necessary to push then move to
+ // avoid a "text file busy" error on execution.
+ // https://code.google.com/p/android/issues/detail?id=65857
+ run("push", os.Args[1], deviceBin+"-tmp")
+ run("shell", "cp '"+deviceBin+"-tmp' '"+deviceBin+"'")
+ run("shell", "rm '"+deviceBin+"-tmp'")
+
+ // The adb shell command will return an exit code of 0 regardless
+ // of the command run. E.g.
+ // $ adb shell false
+ // $ echo $?
+ // 0
+ // https://code.google.com/p/android/issues/detail?id=3254
+ // So we append the exitcode to the output and parse it from there.
+ const exitstr = "exitcode="
+ cmd := `export TMPDIR="/data/local/tmp"` +
+ `; export GOROOT="` + deviceGoroot + `"` +
+ `; cd "$GOROOT/` + subdir + `"` +
+ "; '" + deviceBin + "' " + strings.Join(os.Args[2:], " ") +
+ "; echo -n " + exitstr + "$?"
+ output := run("shell", cmd)
+ run("shell", "rm '"+deviceBin+"'") // cleanup
+ output = output[strings.LastIndex(output, "\n")+1:]
+ if !strings.HasPrefix(output, exitstr) {
+ log.Fatalf("no exit code: %q", output)
+ }
+ code, err := strconv.Atoi(output[len(exitstr):])
+ if err != nil {
+ log.Fatalf("bad exit code: %v", err)
+ }
+ os.Exit(code)
+}
diff --git a/misc/bash/go b/misc/bash/go
deleted file mode 100644
index 50f4f720b..000000000
--- a/misc/bash/go
+++ /dev/null
@@ -1,237 +0,0 @@
-# install in /etc/bash_completion.d/ or your personal directory
-
-complete -f -X '!*.8' 8l
-complete -f -X '!*.6' 6l
-complete -f -X '!*.5' 5l
-complete -f -X '!*.go' 8g 6g 5g gofmt gccgo
-
-_go_importpath()
-{
- echo "$(compgen -W "$(go list all) all std" -- "$1")"
-}
-
-_go()
-{
- # TODO: Only allow flags before other arguments. run already does
- # this.
-
- local cur=`_get_cword`
- local prev="${COMP_WORDS[COMP_CWORD-1]}"
-
- local cmd="${COMP_WORDS[1]}"
-
- local cmds="build clean env fix fmt get
- install list run test tool version vet"
- local addhelp="c gopath importpath packages testflag testfunc"
- local other="help"
-
- if [ "$COMP_CWORD" == 1 ]; then
- for opt in $cmds; do
- if [[ "$opt" == "$cmd" ]]; then
- COMPREPLY=("$opt")
- return
- fi
- done
- fi
-
- case "$cmd" in
- 'build')
- case "$prev" in
- '-o')
- _filedir
- ;;
- '-p')
- ;;
- *)
- if [[ "$cur" == -* ]]; then
- COMPREPLY=($(compgen -W "-a -n -o -p -v -x" -- "$cur"))
- else
- local found=0
- for ((i=0; i < ${#COMP_WORDS[@]}; i++)); do
- case "$i" in
- 0|1|"$COMP_CWORD")
- continue
- ;;
- esac
- local opt="${COMP_WORDS[i]}"
- if [[ "$opt" != -* ]]; then
- if [[ "$opt" == *.go && -f "$opt" ]]; then
- found=1
- break
- else
- found=2
- break
- fi
- fi
- done
- case "$found" in
- 0)
- _filedir go
- COMPREPLY+=(`_go_importpath "$cur"`)
- ;;
- 1)
- _filedir go
- ;;
- 2)
- COMPREPLY=(`_go_importpath "$cur"`)
- ;;
- esac
- fi
- ;;
- esac
- ;;
- 'clean')
- if [[ "$cur" == -* ]]; then
- COMPREPLY=($(compgen -W "-i -r -n -x" -- "$cur"))
- else
- COMPREPLY=(`_go_importpath "$cur"`)
- fi
- ;;
- 'fix')
- COMPREPLY=(`_go_importpath "$cur"`)
- ;;
- 'fmt')
- COMPREPLY=(`_go_importpath "$cur"`)
- ;;
- 'get')
- case "$prev" in
- '-p')
- ;;
- *)
- if [[ "$cur" == -* ]]; then
- COMPREPLY=($(compgen -W "-a -d -fix -n -p -u -v -x" -- "$cur"))
- else
- COMPREPLY=(`_go_importpath "$cur"`)
- fi
- ;;
- esac
- ;;
- 'install')
- case "$prev" in
- '-p')
- ;;
- *)
- if [[ "$cur" == -* ]]; then
- COMPREPLY=($(compgen -W "-a -n -p -v -x" -- "$cur"))
- else
- COMPREPLY=(`_go_importpath "$cur"`)
- fi
- ;;
- esac
- ;;
- 'list')
- case "$prev" in
- '-f')
- ;;
- *)
- if [[ "$cur" == -* ]]; then
- COMPREPLY=($(compgen -W "-e -f -json" -- "$cur"))
- else
- COMPREPLY=(`_go_importpath "$cur"`)
- fi
- ;;
- esac
- ;;
- 'run')
- if [[ "$cur" == -* && "$prev" != *.go ]]; then
- COMPREPLY=($(compgen -W "-a -n -x" -- "$cur"))
- else
- _filedir
- fi
- ;;
- 'test') # TODO: Support for testflags.
- case "$prev" in
- '-file')
- _filedir go
- ;;
- '-p')
- ;;
- *)
- if [[ "$cur" == -* ]]; then
- COMPREPLY=($(compgen -W "-c -file -i -p -x" -- "$cur"))
- else
- COMPREPLY=(`_go_importpath "$cur"`)
- fi
- ;;
- esac
- ;;
- 'tool')
- if [ "$COMP_CWORD" == 2 ]; then
- COMPREPLY=($(compgen -W "$(go tool)" -- "$cur"))
- else
- case "${COMP_WORDS[2]}" in
- [568]a) # TODO: Implement something.
- #_go_tool_568a
- ;;
- [568]c) # TODO: Implement something.
- #_go_tool_568c
- ;;
- [568]g) # TODO: Implement something.
- #_go_tool_568g
- ;;
- [568]l) # TODO: Implement something.
- #_go_tool_568l
- ;;
- 'api') # TODO: Implement something.
- #_go_tool_api
- ;;
- 'cgo') # TODO: Implement something.
- #_go_tool_cgo
- ;;
- 'cov') # TODO: Implement something.
- #_go_tool_cov
- ;;
- 'dist') # TODO: Implement something.
- #_go_tool_dist
- ;;
- 'fix') # TODO: Implement something.
- #_go_tool_fix
- ;;
- 'nm') # TODO: Implement something.
- #_go_tool_nm
- ;;
- 'pack') # TODO: Implement something.
- #_go_tool_pack
- ;;
- 'pprof') # TODO: Implement something.
- #_go_tool_pprof
- ;;
- 'vet') # TODO: Implement something.
- #_go_tool_vet
- ;;
- 'yacc') # TODO: Implement something.
- #_go_tool_yacc
- ;;
- esac
- if [[ "$cur" == -* ]]; then
- COMPREPLY=($(compgen -W "${COMPREPLY[*]} -h" -- "$cur"))
- fi
- fi
- ;;
- 'version')
- ;;
- 'vet')
- if [[ "$cur" == -* ]]; then
- :
- else
- COMPREPLY=(`_go_importpath "$cur"`)
- fi
- ;;
- 'help')
- if [ "$COMP_CWORD" == 2 ]; then
- COMPREPLY=($(compgen -W "$cmds $addhelp" -- "$cur"))
- fi
- ;;
- *)
- if [ "$COMP_CWORD" == 1 ]; then
- COMPREPLY=($(compgen -W "$cmds $other" -- "$cur"))
- else
- _filedir
- fi
- ;;
- esac
-}
-
-complete $filenames -F _go go
-
-# vim:ts=2 sw=2 et syn=sh
diff --git a/misc/bbedit/Go.plist b/misc/bbedit/Go.plist
deleted file mode 100644
index 1220d6efb..000000000
--- a/misc/bbedit/Go.plist
+++ /dev/null
@@ -1,104 +0,0 @@
-// Copyright 2009 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-{
- BBEditDocumentType = "CodelessLanguageModule";
- BBLMColorsSyntax = YES;
- BBLMIsCaseSensitive = YES;
- BBLMKeywordList = (
- append,
- bool,
- break,
- byte,
- cap,
- case,
- chan,
- close,
- complex,
- complex128,
- complex64,
- const,
- continue,
- copy,
- default,
- defer,
- delete,
- else,
- error,
- fallthrough,
- false,
- float32,
- float64,
- for,
- func,
- go,
- goto,
- if,
- iota,
- imag,
- import,
- int,
- int16,
- int32,
- int64,
- int8,
- interface,
- len,
- make,
- map,
- new,
- nil,
- package,
- panic,
- print,
- println,
- range,
- real,
- recover,
- return,
- rune,
- select,
- string,
- struct,
- switch,
- true,
- type,
- uint,
- uint16,
- uint32,
- uint64,
- uint8,
- uintptr,
- var,
- );
- BBLMLanguageCode = go;
- "BBLMLanguageDisplayName" = "Go";
- BBLMScansFunctions = YES;
- BBLMSuffixMap = (
- {
- BBLMLanguageSuffix = ".go";
- },
- );
- "Language Features" = {
- "Close Block Comments" = "*/";
- "Close Parameter Lists" = ")";
- "Close Statement Blocks" = "}";
- "Close Strings 1" = "`";
- "Close Strings 2" = "\"";
- "End-of-line Ends Strings 1" = YES;
- "End-of-line Ends Strings 2" = YES;
- "Escape Char in Strings 1" = "\\";
- "Escape Char in Strings 2" = "\\";
- "Identifier and Keyword Characters" = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_abcdefghijklmnopqrstuvwxyz";
- "Open Block Comments" = "/*";
- "Open Line Comments" = "//";
- "Open Parameter Lists" = "(";
- "Open Statement Blocks" = "{";
- "Open Strings 1" = "`";
- "Open Strings 2" = "\"";
- "Prefix for Functions" = "func";
- "Prefix for Procedures" = "func";
- "Terminator for Prototypes 1" = ";";
- "Terminator for Prototypes 2" = "";
- };
-}
diff --git a/misc/benchcmp b/misc/benchcmp
index 28a37392d..84d92eefd 100755
--- a/misc/benchcmp
+++ b/misc/benchcmp
@@ -1,5 +1,5 @@
#!/bin/bash
echo 'misc/benchcmp has moved:' >&2
-echo ' go get -u code.google.com/p/go.tools/cmd/benchcmp' >&2
+echo ' go get -u golang.org/x/tools/cmd/benchcmp' >&2
exit 2
diff --git a/misc/cgo/test/backdoor/backdoor_gccgo.go b/misc/cgo/errors/issue7757.go
index 514f76ec5..5eafd22e8 100644
--- a/misc/cgo/test/backdoor/backdoor_gccgo.go
+++ b/misc/cgo/errors/issue7757.go
@@ -2,10 +2,13 @@
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
-// This is the gccgo version of the stub in runtime.c.
+package main
-// +build gccgo
+/*
+void foo() {}
+*/
+import "C"
-package backdoor
-
-func Issue7695(x1, x2, x3, x4, x5, x6, x7, x8 uintptr) {}
+func main() {
+ C.foo = C.foo // ERROR HERE
+}
diff --git a/misc/cgo/errors/issue8442.go b/misc/cgo/errors/issue8442.go
new file mode 100644
index 000000000..45daf8e59
--- /dev/null
+++ b/misc/cgo/errors/issue8442.go
@@ -0,0 +1,17 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// Issue 8442. Cgo output unhelpful error messages for
+// invalid C preambles.
+
+/*
+void issue8442foo(UNDEF*); // ERROR HERE
+*/
+import "C"
+
+func main() {
+ C.issue8442foo(nil)
+}
diff --git a/misc/cgo/errors/test.bash b/misc/cgo/errors/test.bash
index f0f60c844..c96264389 100755
--- a/misc/cgo/errors/test.bash
+++ b/misc/cgo/errors/test.bash
@@ -27,6 +27,8 @@ check() {
check err1.go
check err2.go
check err3.go
+check issue7757.go
+check issue8442.go
rm -rf errs _obj
exit 0
diff --git a/misc/cgo/test/backdoor/backdoor.go b/misc/cgo/test/backdoor/backdoor.go
index 7398772bd..3a973494b 100644
--- a/misc/cgo/test/backdoor/backdoor.go
+++ b/misc/cgo/test/backdoor/backdoor.go
@@ -4,5 +4,4 @@
package backdoor
-func LockedOSThread() bool // in runtime.c
-func Issue7695(x1, x2, x3, x4, x5, x6, x7, x8 uintptr)
+func LockedOSThread() bool // in thunk.s
diff --git a/misc/cgo/test/backdoor/runtime.c b/misc/cgo/test/backdoor/runtime.c
deleted file mode 100644
index 7e6b44872..000000000
--- a/misc/cgo/test/backdoor/runtime.c
+++ /dev/null
@@ -1,32 +0,0 @@
-// Copyright 2011 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Expose some runtime functions for testing.
-// Must be in a non-cgo-using package so that
-// the go command compiles this file with 6c, not gcc.
-
-// +build gc
-
-typedef char bool;
-
-bool runtime·lockedOSThread(void);
-
-static void
-FLUSH(void*)
-{
-}
-
-void
-·LockedOSThread(bool b)
-{
- b = runtime·lockedOSThread();
- FLUSH(&b);
-}
-
-// This is what a cgo-compiled stub declaration looks like.
-void
-·Issue7695(struct{void *y[8*sizeof(void*)];}p)
-{
- USED(p);
-}
diff --git a/misc/cgo/test/backdoor/thunk.s b/misc/cgo/test/backdoor/thunk.s
new file mode 100644
index 000000000..ae735c8a3
--- /dev/null
+++ b/misc/cgo/test/backdoor/thunk.s
@@ -0,0 +1,16 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Assembly to get into package runtime without using exported symbols.
+
+// +build amd64 amd64p32 arm 386
+
+#include "textflag.h"
+
+#ifdef GOARCH_arm
+#define JMP B
+#endif
+
+TEXT ·LockedOSThread(SB),NOSPLIT,$0-0
+ JMP runtime·lockedOSThread(SB)
diff --git a/misc/cgo/test/basic.go b/misc/cgo/test/basic.go
index 79cbf2b9c..019139d01 100644
--- a/misc/cgo/test/basic.go
+++ b/misc/cgo/test/basic.go
@@ -157,3 +157,8 @@ func testUnsignedInt(t *testing.T) {
t.Errorf("Incorrect unsigned int - got %x, want %x", a, b)
}
}
+
+// Static (build-time) test that syntax traversal visits all operands of s[i:j:k].
+func sliceOperands(array [2000]int) {
+ _ = array[C.KILO:C.KILO:C.KILO] // no type error
+}
diff --git a/misc/cgo/test/buildid_linux.go b/misc/cgo/test/buildid_linux.go
new file mode 100644
index 000000000..a3a86edfc
--- /dev/null
+++ b/misc/cgo/test/buildid_linux.go
@@ -0,0 +1,77 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+// Test that we have no more than one build ID. In the past we used
+// to generate a separate build ID for each package using cgo, and the
+// linker concatenated them all. We don't want that--we only want
+// one.
+
+import (
+ "bytes"
+ "debug/elf"
+ "os"
+ "testing"
+)
+
+func testBuildID(t *testing.T) {
+ f, err := elf.Open("/proc/self/exe")
+ if err != nil {
+ if os.IsNotExist(err) {
+ t.Skip("no /proc/self/exe")
+ }
+ t.Fatalf("opening /proc/self/exe: ", err)
+ }
+ defer f.Close()
+
+ c := 0
+ for i, s := range f.Sections {
+ if s.Type != elf.SHT_NOTE {
+ continue
+ }
+
+ d, err := s.Data()
+ if err != nil {
+ t.Logf("reading data of note section %d: %v", i, err)
+ continue
+ }
+
+ for len(d) > 0 {
+
+ // ELF standards differ as to the sizes in
+ // note sections. Both the GNU linker and
+ // gold always generate 32-bit sizes, so that
+ // is what we assume here.
+
+ if len(d) < 12 {
+ t.Logf("note section %d too short (%d < 12)", i, len(d))
+ continue
+ }
+
+ namesz := f.ByteOrder.Uint32(d)
+ descsz := f.ByteOrder.Uint32(d[4:])
+ typ := f.ByteOrder.Uint32(d[8:])
+
+ an := (namesz + 3) &^ 3
+ ad := (descsz + 3) &^ 3
+
+ if int(12+an+ad) > len(d) {
+ t.Logf("note section %d too short for header (%d < 12 + align(%d,4) + align(%d,4))", i, len(d), namesz, descsz)
+ continue
+ }
+
+ // 3 == NT_GNU_BUILD_ID
+ if typ == 3 && namesz == 4 && bytes.Equal(d[12:16], []byte("GNU\000")) {
+ c++
+ }
+
+ d = d[12+an+ad:]
+ }
+ }
+
+ if c > 1 {
+ t.Errorf("found %d build ID notes", c)
+ }
+}
diff --git a/misc/cgo/test/callback.go b/misc/cgo/test/callback.go
index 82ed015bd..44167e6e9 100644
--- a/misc/cgo/test/callback.go
+++ b/misc/cgo/test/callback.go
@@ -9,16 +9,21 @@ void callback(void *f);
void callGoFoo(void);
void callGoStackCheck(void);
void callPanic(void);
+void callCgoAllocate(void);
+int callGoReturnVal(void);
+int returnAfterGrow(void);
+int returnAfterGrowFromGo(void);
*/
import "C"
import (
- "./backdoor"
"path"
"runtime"
"strings"
"testing"
"unsafe"
+
+ "./backdoor"
)
// nestedCall calls into C, back into Go, and finally to f.
@@ -152,11 +157,13 @@ func testCallbackCallers(t *testing.T) {
n := 0
name := []string{
"test.goCallback",
+ "runtime.call16",
"runtime.cgocallbackg1",
"runtime.cgocallbackg",
"runtime.cgocallback_gofunc",
- "runtime.asmcgocall",
- "runtime.cgocall",
+ "asmcgocall",
+ "runtime.asmcgocall_errno",
+ "runtime.cgocall_errno",
"test._Cfunc_callback",
"test.nestedCall",
"test.testCallbackCallers",
@@ -181,8 +188,12 @@ func testCallbackCallers(t *testing.T) {
if strings.HasPrefix(fname, "_") {
fname = path.Base(f.Name()[1:])
}
- if fname != name[i] {
- t.Errorf("expected function name %s, got %s", name[i], fname)
+ namei := ""
+ if i < len(name) {
+ namei = name[i]
+ }
+ if fname != namei {
+ t.Errorf("stk[%d] = %q, want %q", i, fname, namei)
}
}
}
@@ -200,6 +211,52 @@ func testPanicFromC(t *testing.T) {
C.callPanic()
}
+func testAllocateFromC(t *testing.T) {
+ C.callCgoAllocate() // crashes or exits on failure
+}
+
+// Test that C code can return a value if it calls a Go function that
+// causes a stack copy.
+func testReturnAfterGrow(t *testing.T) {
+ // Use a new goroutine so that we get a small stack.
+ c := make(chan int)
+ go func() {
+ c <- int(C.returnAfterGrow())
+ }()
+ if got, want := <-c, 123456; got != want {
+ t.Errorf("got %d want %d", got, want)
+ }
+}
+
+// Test that we can return a value from Go->C->Go if the Go code
+// causes a stack copy.
+func testReturnAfterGrowFromGo(t *testing.T) {
+ // Use a new goroutine so that we get a small stack.
+ c := make(chan int)
+ go func() {
+ c <- int(C.returnAfterGrowFromGo())
+ }()
+ if got, want := <-c, 129*128/2; got != want {
+ t.Errorf("got %d want %d", got, want)
+ }
+}
+
+//export goReturnVal
+func goReturnVal() (r C.int) {
+ // Force a stack copy.
+ var f func(int) int
+ f = func(i int) int {
+ var buf [256]byte
+ use(buf[:])
+ if i == 0 {
+ return 0
+ }
+ return i + f(i-1)
+ }
+ r = C.int(f(128))
+ return
+}
+
func testCallbackStack(t *testing.T) {
// Make cgo call and callback with different amount of stack stack available.
// We do not do any explicit checks, just ensure that it does not crash.
diff --git a/misc/cgo/test/callback_c.c b/misc/cgo/test/callback_c.c
index dcd4ddd4e..5bb642534 100644
--- a/misc/cgo/test/callback_c.c
+++ b/misc/cgo/test/callback_c.c
@@ -64,3 +64,19 @@ callGoStackCheck(void)
extern void goStackCheck(void);
goStackCheck();
}
+
+int
+returnAfterGrow(void)
+{
+ extern int goReturnVal(void);
+ goReturnVal();
+ return 123456;
+}
+
+int
+returnAfterGrowFromGo(void)
+{
+ extern int goReturnVal(void);
+ return goReturnVal();
+}
+
diff --git a/misc/cgo/test/callback_c_gc.c b/misc/cgo/test/callback_c_gc.c
index 8953b74a6..28a62c6db 100644
--- a/misc/cgo/test/callback_c_gc.c
+++ b/misc/cgo/test/callback_c_gc.c
@@ -5,11 +5,15 @@
// +build gc
#include "_cgo_export.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
/* Test calling panic from C. This is what SWIG does. */
extern void crosscall2(void (*fn)(void *, int), void *, int);
extern void _cgo_panic(void *, int);
+extern void _cgo_allocate(void *, int);
void
callPanic(void)
@@ -19,3 +23,58 @@ callPanic(void)
crosscall2(_cgo_panic, &a, sizeof a);
*(int*)1 = 1;
}
+
+/* Test calling cgo_allocate from C. This is what SWIG does. */
+
+typedef struct List List;
+struct List
+{
+ List *next;
+ int x;
+};
+
+void
+callCgoAllocate(void)
+{
+ int i;
+ struct { size_t n; void *ret; } a;
+ List *l, *head, **tail;
+
+ // Make sure this doesn't crash.
+ // And make sure it returns non-nil.
+ a.n = 0;
+ a.ret = 0;
+ crosscall2(_cgo_allocate, &a, sizeof a);
+ if(a.ret == 0) {
+ fprintf(stderr, "callCgoAllocate: alloc 0 returned nil\n");
+ exit(2);
+ }
+
+ head = 0;
+ tail = &head;
+ for(i=0; i<100; i++) {
+ a.n = sizeof *l;
+ crosscall2(_cgo_allocate, &a, sizeof a);
+ l = a.ret;
+ l->x = i;
+ l->next = 0;
+ *tail = l;
+ tail = &l->next;
+ }
+
+ gc();
+
+ l = head;
+ for(i=0; i<100; i++) {
+ if(l->x != i) {
+ fprintf(stderr, "callCgoAllocate: lost memory\n");
+ exit(2);
+ }
+ l = l->next;
+ }
+ if(l != 0) {
+ fprintf(stderr, "callCgoAllocate: lost memory\n");
+ exit(2);
+ }
+}
+
diff --git a/misc/cgo/test/callback_c_gccgo.c b/misc/cgo/test/callback_c_gccgo.c
index 0ea7296c6..d367b7b68 100644
--- a/misc/cgo/test/callback_c_gccgo.c
+++ b/misc/cgo/test/callback_c_gccgo.c
@@ -5,13 +5,66 @@
// +build gccgo
#include "_cgo_export.h"
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
/* Test calling panic from C. This is what SWIG does. */
extern void _cgo_panic(const char *);
+extern void *_cgo_allocate(size_t);
void
callPanic(void)
{
_cgo_panic("panic from C");
}
+
+/* Test calling cgo_allocate from C. This is what SWIG does. */
+
+typedef struct List List;
+struct List
+{
+ List *next;
+ int x;
+};
+
+void
+callCgoAllocate(void)
+{
+ int i;
+ List *l, *head, **tail;
+
+ // Make sure this doesn't crash.
+ // And make sure it returns non-nil.
+ if(_cgo_allocate(0) == 0) {
+ fprintf(stderr, "callCgoAllocate: alloc 0 returned nil\n");
+ exit(2);
+ }
+
+ head = 0;
+ tail = &head;
+ for(i=0; i<100; i++) {
+ l = _cgo_allocate(sizeof *l);
+ l->x = i;
+ l->next = 0;
+ *tail = l;
+ tail = &l->next;
+ }
+
+ gc();
+
+ l = head;
+ for(i=0; i<100; i++) {
+ if(l->x != i) {
+ fprintf(stderr, "callCgoAllocate: lost memory\n");
+ exit(2);
+ }
+ l = l->next;
+ }
+ if(l != 0) {
+ fprintf(stderr, "callCgoAllocate: lost memory\n");
+ exit(2);
+ }
+}
+
diff --git a/misc/cgo/test/cgo_linux_test.go b/misc/cgo/test/cgo_linux_test.go
index 0a405c7a3..4fe0db1b2 100644
--- a/misc/cgo/test/cgo_linux_test.go
+++ b/misc/cgo/test/cgo_linux_test.go
@@ -6,5 +6,6 @@ package cgotest
import "testing"
-func TestSetgid(t *testing.T) { testSetgid(t) }
-func Test6997(t *testing.T) { test6997(t) }
+func TestSetgid(t *testing.T) { testSetgid(t) }
+func Test6997(t *testing.T) { test6997(t) }
+func TestBuildID(t *testing.T) { testBuildID(t) }
diff --git a/misc/cgo/test/cgo_test.go b/misc/cgo/test/cgo_test.go
index e2e5a2bc1..fbdfac87a 100644
--- a/misc/cgo/test/cgo_test.go
+++ b/misc/cgo/test/cgo_test.go
@@ -10,49 +10,58 @@ import "testing"
// so that they can use cgo (import "C").
// These wrappers are here for gotest to find.
-func TestAlign(t *testing.T) { testAlign(t) }
-func TestConst(t *testing.T) { testConst(t) }
-func TestEnum(t *testing.T) { testEnum(t) }
-func TestAtol(t *testing.T) { testAtol(t) }
-func TestErrno(t *testing.T) { testErrno(t) }
-func TestMultipleAssign(t *testing.T) { testMultipleAssign(t) }
-func TestUnsignedInt(t *testing.T) { testUnsignedInt(t) }
-func TestCallback(t *testing.T) { testCallback(t) }
-func TestCallbackGC(t *testing.T) { testCallbackGC(t) }
-func TestCallbackPanic(t *testing.T) { testCallbackPanic(t) }
-func TestCallbackPanicLoop(t *testing.T) { testCallbackPanicLoop(t) }
-func TestCallbackPanicLocked(t *testing.T) { testCallbackPanicLocked(t) }
-func TestPanicFromC(t *testing.T) { testPanicFromC(t) }
-func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) }
-func TestBlocking(t *testing.T) { testBlocking(t) }
-func Test1328(t *testing.T) { test1328(t) }
-func TestParallelSleep(t *testing.T) { testParallelSleep(t) }
-func TestSetEnv(t *testing.T) { testSetEnv(t) }
-func TestHelpers(t *testing.T) { testHelpers(t) }
-func TestLibgcc(t *testing.T) { testLibgcc(t) }
-func Test1635(t *testing.T) { test1635(t) }
-func TestPrintf(t *testing.T) { testPrintf(t) }
-func Test4029(t *testing.T) { test4029(t) }
-func TestBoolAlign(t *testing.T) { testBoolAlign(t) }
-func Test3729(t *testing.T) { test3729(t) }
-func Test3775(t *testing.T) { test3775(t) }
-func TestCthread(t *testing.T) { testCthread(t) }
-func TestCallbackCallers(t *testing.T) { testCallbackCallers(t) }
-func Test5227(t *testing.T) { test5227(t) }
-func TestCflags(t *testing.T) { testCflags(t) }
-func Test5337(t *testing.T) { test5337(t) }
-func Test5548(t *testing.T) { test5548(t) }
-func Test5603(t *testing.T) { test5603(t) }
-func Test6833(t *testing.T) { test6833(t) }
-func Test3250(t *testing.T) { test3250(t) }
-func TestCallbackStack(t *testing.T) { testCallbackStack(t) }
-func TestFpVar(t *testing.T) { testFpVar(t) }
-func Test4339(t *testing.T) { test4339(t) }
-func Test6390(t *testing.T) { test6390(t) }
-func Test5986(t *testing.T) { test5986(t) }
-func Test7665(t *testing.T) { test7665(t) }
-func TestNaming(t *testing.T) { testNaming(t) }
-func Test7560(t *testing.T) { test7560(t) }
-func Test7978(t *testing.T) { test7978(t) }
+func TestAlign(t *testing.T) { testAlign(t) }
+func TestConst(t *testing.T) { testConst(t) }
+func TestEnum(t *testing.T) { testEnum(t) }
+func TestAtol(t *testing.T) { testAtol(t) }
+func TestErrno(t *testing.T) { testErrno(t) }
+func TestMultipleAssign(t *testing.T) { testMultipleAssign(t) }
+func TestUnsignedInt(t *testing.T) { testUnsignedInt(t) }
+func TestCallback(t *testing.T) { testCallback(t) }
+func TestCallbackGC(t *testing.T) { testCallbackGC(t) }
+func TestCallbackPanic(t *testing.T) { testCallbackPanic(t) }
+func TestCallbackPanicLoop(t *testing.T) { testCallbackPanicLoop(t) }
+func TestCallbackPanicLocked(t *testing.T) { testCallbackPanicLocked(t) }
+func TestPanicFromC(t *testing.T) { testPanicFromC(t) }
+func TestAllocateFromC(t *testing.T) { testAllocateFromC(t) }
+func TestZeroArgCallback(t *testing.T) { testZeroArgCallback(t) }
+func TestBlocking(t *testing.T) { testBlocking(t) }
+func Test1328(t *testing.T) { test1328(t) }
+func TestParallelSleep(t *testing.T) { testParallelSleep(t) }
+func TestSetEnv(t *testing.T) { testSetEnv(t) }
+func TestHelpers(t *testing.T) { testHelpers(t) }
+func TestLibgcc(t *testing.T) { testLibgcc(t) }
+func Test1635(t *testing.T) { test1635(t) }
+func TestPrintf(t *testing.T) { testPrintf(t) }
+func Test4029(t *testing.T) { test4029(t) }
+func TestBoolAlign(t *testing.T) { testBoolAlign(t) }
+func Test3729(t *testing.T) { test3729(t) }
+func Test3775(t *testing.T) { test3775(t) }
+func TestCthread(t *testing.T) { testCthread(t) }
+func TestCallbackCallers(t *testing.T) { testCallbackCallers(t) }
+func Test5227(t *testing.T) { test5227(t) }
+func TestCflags(t *testing.T) { testCflags(t) }
+func Test5337(t *testing.T) { test5337(t) }
+func Test5548(t *testing.T) { test5548(t) }
+func Test5603(t *testing.T) { test5603(t) }
+func Test6833(t *testing.T) { test6833(t) }
+func Test3250(t *testing.T) { test3250(t) }
+func TestCallbackStack(t *testing.T) { testCallbackStack(t) }
+func TestFpVar(t *testing.T) { testFpVar(t) }
+func Test4339(t *testing.T) { test4339(t) }
+func Test6390(t *testing.T) { test6390(t) }
+func Test5986(t *testing.T) { test5986(t) }
+func Test7665(t *testing.T) { test7665(t) }
+func TestNaming(t *testing.T) { testNaming(t) }
+func Test7560(t *testing.T) { test7560(t) }
+func Test5242(t *testing.T) { test5242(t) }
+func Test8092(t *testing.T) { test8092(t) }
+func Test7978(t *testing.T) { test7978(t) }
+func Test8694(t *testing.T) { test8694(t) }
+func Test8517(t *testing.T) { test8517(t) }
+func Test8811(t *testing.T) { test8811(t) }
+func TestReturnAfterGrow(t *testing.T) { testReturnAfterGrow(t) }
+func TestReturnAfterGrowFromGo(t *testing.T) { testReturnAfterGrowFromGo(t) }
+func Test9026(t *testing.T) { test9026(t) }
func BenchmarkCgoCall(b *testing.B) { benchCgoCall(b) }
diff --git a/misc/cgo/test/exports.go b/misc/cgo/test/exports.go
index f96c60b00..4fe1703a6 100644
--- a/misc/cgo/test/exports.go
+++ b/misc/cgo/test/exports.go
@@ -5,8 +5,14 @@
package cgotest
import "C"
+import "runtime"
//export ReturnIntLong
func ReturnIntLong() (int, C.long) {
return 1, 2
}
+
+//export gc
+func gc() {
+ runtime.GC()
+}
diff --git a/misc/cgo/test/issue5242.go b/misc/cgo/test/issue5242.go
new file mode 100644
index 000000000..fe0a6321c
--- /dev/null
+++ b/misc/cgo/test/issue5242.go
@@ -0,0 +1,31 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 5242. Cgo incorrectly computed the alignment of structs
+// with no Go accessible fields as 0, and then panicked on
+// modulo-by-zero computations.
+
+package cgotest
+
+/*
+typedef struct {
+} foo;
+
+typedef struct {
+ int x : 1;
+} bar;
+
+int issue5242(foo f, bar b) {
+ return 5242;
+}
+*/
+import "C"
+
+import "testing"
+
+func test5242(t *testing.T) {
+ if got := C.issue5242(C.foo{}, C.bar{}); got != 5242 {
+ t.Errorf("got %v", got)
+ }
+}
diff --git a/misc/cgo/test/issue5548.go b/misc/cgo/test/issue5548.go
index b41f46562..c879f2ae9 100644
--- a/misc/cgo/test/issue5548.go
+++ b/misc/cgo/test/issue5548.go
@@ -14,13 +14,14 @@ import "C"
//export issue5548FromC
func issue5548FromC(s string, i int) int {
if len(s) == 4 && s == "test" && i == 42 {
- return 1
+ return 12345
}
- return 0
+ println("got", len(s), i)
+ return 9876
}
func test5548(t *testing.T) {
- if C.issue5548_in_c() == 0 {
- t.Fail()
+ if x := C.issue5548_in_c(); x != 12345 {
+ t.Errorf("issue5548_in_c = %d, want %d", x, 12345)
}
}
diff --git a/misc/cgo/test/issue6997_linux.go b/misc/cgo/test/issue6997_linux.go
index 871bd517a..5455f0c53 100644
--- a/misc/cgo/test/issue6997_linux.go
+++ b/misc/cgo/test/issue6997_linux.go
@@ -34,7 +34,7 @@ func test6997(t *testing.T) {
if r == 0 {
t.Error("pthread finished but wasn't cancelled??")
}
- case <-time.After(5 * time.Second):
+ case <-time.After(30 * time.Second):
t.Error("hung in pthread_cancel/pthread_join")
}
}
diff --git a/misc/cgo/test/issue7695_test.go b/misc/cgo/test/issue7695_test.go
deleted file mode 100644
index 4bd6f8e73..000000000
--- a/misc/cgo/test/issue7695_test.go
+++ /dev/null
@@ -1,27 +0,0 @@
-// Copyright 2014 The Go Authors. All rights reserved.
-// Use of this source code is governed by a BSD-style
-// license that can be found in the LICENSE file.
-
-// Demo of deferred C function with untrue prototype
-// breaking stack copying. See golang.org/issue/7695.
-
-package cgotest
-
-import (
- "testing"
-
- "./backdoor"
-)
-
-func TestIssue7695(t *testing.T) {
- defer backdoor.Issue7695(1, 0, 2, 0, 0, 3, 0, 4)
- recurse(100)
-}
-
-func recurse(n int) {
- var x [128]int
- n += x[0]
- if n > 0 {
- recurse(n - 1)
- }
-}
diff --git a/misc/cgo/test/issue7978.go b/misc/cgo/test/issue7978.go
index 432e006eb..5feed07b9 100644
--- a/misc/cgo/test/issue7978.go
+++ b/misc/cgo/test/issue7978.go
@@ -83,21 +83,21 @@ func issue7978go() {
func test7978(t *testing.T) {
if os.Getenv("GOTRACEBACK") != "2" {
- t.Fatal("GOTRACEBACK must be 2")
+ t.Fatalf("GOTRACEBACK must be 2")
}
issue7978sync = 0
go issue7978go()
// test in c code, before callback
issue7978wait(0, 1)
- issue7978check(t, "runtime.cgocall(", "", 1)
+ issue7978check(t, "runtime.cgocall_errno(", "", 1)
// test in go code, during callback
issue7978wait(2, 3)
- issue7978check(t, "test.issue7978cb(", "test.issue7978go", 4)
+ issue7978check(t, "test.issue7978cb(", "test.issue7978go", 3)
// test in c code, after callback
issue7978wait(4, 5)
- issue7978check(t, "runtime.cgocall(", "runtime.cgocallback", 1)
+ issue7978check(t, "runtime.cgocall_errno(", "runtime.cgocallback", 1)
// test in go code, after return from cgo
issue7978wait(6, 7)
- issue7978check(t, "test.issue7978go(", "", 4)
+ issue7978check(t, "test.issue7978go(", "", 3)
atomic.StoreUint32(&issue7978sync, 8)
}
diff --git a/misc/cgo/test/issue8092.go b/misc/cgo/test/issue8092.go
new file mode 100644
index 000000000..8a14ce6d7
--- /dev/null
+++ b/misc/cgo/test/issue8092.go
@@ -0,0 +1,36 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 8092. Test that linker defined symbols (e.g., text, data) don't
+// conflict with C symbols.
+
+package cgotest
+
+/*
+char text[] = "text";
+char data[] = "data";
+char *ctext(void) { return text; }
+char *cdata(void) { return data; }
+*/
+import "C"
+
+import "testing"
+
+func test8092(t *testing.T) {
+ tests := []struct {
+ s string
+ a, b *C.char
+ }{
+ {"text", &C.text[0], C.ctext()},
+ {"data", &C.data[0], C.cdata()},
+ }
+ for _, test := range tests {
+ if test.a != test.b {
+ t.Errorf("%s: pointer mismatch: %v != %v", test.s, test.a, test.b)
+ }
+ if got := C.GoString(test.a); got != test.s {
+ t.Errorf("%s: points at %#v, want %#v", test.s, got, test.s)
+ }
+ }
+}
diff --git a/misc/cgo/test/issue8428.go b/misc/cgo/test/issue8428.go
new file mode 100644
index 000000000..a3dc5755c
--- /dev/null
+++ b/misc/cgo/test/issue8428.go
@@ -0,0 +1,52 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// This test fails on older versions of OS X because they use older buggy
+// versions of Clang that emit ambiguous DWARF info. See issue 8611.
+// +build !darwin
+
+package cgotest
+
+// Issue 8428. Cgo inconsistently translated zero size arrays.
+
+/*
+struct issue8428one {
+ char b;
+ char rest[];
+};
+
+struct issue8428two {
+ void *p;
+ char b;
+ char rest[0];
+};
+
+struct issue8428three {
+ char w[1][2][3][0];
+ char x[2][3][0][1];
+ char y[3][0][1][2];
+ char z[0][1][2][3];
+};
+*/
+import "C"
+
+import "unsafe"
+
+var _ = C.struct_issue8428one{
+ b: C.char(0),
+ rest: [0]C.char{},
+}
+
+var _ = C.struct_issue8428two{
+ p: unsafe.Pointer(nil),
+ b: C.char(0),
+ rest: [0]C.char{},
+}
+
+var _ = C.struct_issue8428three{
+ w: [1][2][3][0]C.char{},
+ x: [2][3][0][1]C.char{},
+ y: [3][0][1][2]C.char{},
+ z: [0][1][2][3]C.char{},
+}
diff --git a/misc/cgo/test/issue8517.go b/misc/cgo/test/issue8517.go
new file mode 100644
index 000000000..4e431df92
--- /dev/null
+++ b/misc/cgo/test/issue8517.go
@@ -0,0 +1,13 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// +build !windows
+
+package cgotest
+
+import "testing"
+
+func test8517(t *testing.T) {
+ t.Skip("skipping windows only test")
+}
diff --git a/misc/cgo/test/issue8517_windows.c b/misc/cgo/test/issue8517_windows.c
new file mode 100644
index 000000000..a0b94c126
--- /dev/null
+++ b/misc/cgo/test/issue8517_windows.c
@@ -0,0 +1,24 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+#include "windows.h"
+
+extern void testHandleLeaksCallback();
+
+DWORD WINAPI testHandleLeaksFunc(LPVOID lpThreadParameter)
+{
+ int i;
+ for(i = 0; i < 100; i++) {
+ testHandleLeaksCallback();
+ }
+ return 0;
+}
+
+void testHandleLeaks()
+{
+ HANDLE h;
+ h = CreateThread(NULL, 0, &testHandleLeaksFunc, 0, 0, NULL);
+ WaitForSingleObject(h, INFINITE);
+ CloseHandle(h);
+}
diff --git a/misc/cgo/test/issue8517_windows.go b/misc/cgo/test/issue8517_windows.go
new file mode 100644
index 000000000..3782631e9
--- /dev/null
+++ b/misc/cgo/test/issue8517_windows.go
@@ -0,0 +1,45 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+//void testHandleLeaks();
+import "C"
+
+import (
+ "syscall"
+ "testing"
+ "unsafe"
+)
+
+var issue8517counter int
+
+var (
+ kernel32 = syscall.MustLoadDLL("kernel32.dll")
+ getProcessHandleCount = kernel32.MustFindProc("GetProcessHandleCount")
+)
+
+func processHandleCount(t *testing.T) int {
+ const current_process = ^uintptr(0)
+ var c uint32
+ r, _, err := getProcessHandleCount.Call(current_process, uintptr(unsafe.Pointer(&c)))
+ if r == 0 {
+ t.Fatal(err)
+ }
+ return int(c)
+}
+
+func test8517(t *testing.T) {
+ c1 := processHandleCount(t)
+ C.testHandleLeaks()
+ c2 := processHandleCount(t)
+ if c1+issue8517counter <= c2 {
+ t.Fatalf("too many handles leaked: issue8517counter=%v c1=%v c2=%v", issue8517counter, c1, c2)
+ }
+}
+
+//export testHandleLeaksCallback
+func testHandleLeaksCallback() {
+ issue8517counter++
+}
diff --git a/misc/cgo/test/issue8694.go b/misc/cgo/test/issue8694.go
new file mode 100644
index 000000000..1876f782d
--- /dev/null
+++ b/misc/cgo/test/issue8694.go
@@ -0,0 +1,38 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+/*
+#include <complex.h>
+
+complex float complexFloatSquared(complex float a) { return a*a; }
+complex double complexDoubleSquared(complex double a) { return a*a; }
+*/
+import "C"
+
+import (
+ "runtime"
+ "testing"
+)
+
+func test8694(t *testing.T) {
+ if runtime.GOARCH == "arm" {
+ t.Skip("test8694 is disabled on ARM because 5l cannot handle thumb library.")
+ }
+ // Really just testing that this compiles, but check answer anyway.
+ x := complex64(2 + 3i)
+ x2 := x * x
+ cx2 := C.complexFloatSquared(x)
+ if cx2 != x2 {
+ t.Errorf("C.complexFloatSquared(%v) = %v, want %v", x, cx2, x2)
+ }
+
+ y := complex128(2 + 3i)
+ y2 := y * y
+ cy2 := C.complexDoubleSquared(y)
+ if cy2 != y2 {
+ t.Errorf("C.complexDoubleSquared(%v) = %v, want %v", y, cy2, y2)
+ }
+}
diff --git a/misc/cgo/test/issue8811.c b/misc/cgo/test/issue8811.c
new file mode 100644
index 000000000..584bb3934
--- /dev/null
+++ b/misc/cgo/test/issue8811.c
@@ -0,0 +1,8 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+int issue8811Initialized = 0;
+
+void issue8811Init() {
+}
diff --git a/misc/cgo/test/issue8811.go b/misc/cgo/test/issue8811.go
new file mode 100644
index 000000000..2e217d935
--- /dev/null
+++ b/misc/cgo/test/issue8811.go
@@ -0,0 +1,22 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package cgotest
+
+/*
+extern int issue8811Initialized;
+extern void issue8811Init();
+
+void issue8811Execute() {
+ if(!issue8811Initialized)
+ issue8811Init();
+}
+*/
+import "C"
+
+import "testing"
+
+func test8811(t *testing.T) {
+ C.issue8811Execute()
+}
diff --git a/misc/cgo/test/issue8828.go b/misc/cgo/test/issue8828.go
new file mode 100644
index 000000000..304797c92
--- /dev/null
+++ b/misc/cgo/test/issue8828.go
@@ -0,0 +1,16 @@
+// compile
+
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+// Issue 8828: compiling a file with -compiler=gccgo fails if a .c file
+// has the same name as compiled directory.
+
+package cgotest
+
+import "./issue8828"
+
+func p() {
+ issue8828.Bar()
+}
diff --git a/misc/cgo/test/issue8828/issue8828.c b/misc/cgo/test/issue8828/issue8828.c
new file mode 100644
index 000000000..2950f87cf
--- /dev/null
+++ b/misc/cgo/test/issue8828/issue8828.c
@@ -0,0 +1,7 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+void foo()
+{
+}
diff --git a/misc/cgo/test/issue8828/trivial.go b/misc/cgo/test/issue8828/trivial.go
new file mode 100644
index 000000000..e7b9a4e57
--- /dev/null
+++ b/misc/cgo/test/issue8828/trivial.go
@@ -0,0 +1,8 @@
+package issue8828
+
+//void foo();
+import "C"
+
+func Bar() {
+ C.foo()
+}
diff --git a/misc/cgo/test/issue9026.go b/misc/cgo/test/issue9026.go
new file mode 100644
index 000000000..8848d0e81
--- /dev/null
+++ b/misc/cgo/test/issue9026.go
@@ -0,0 +1,9 @@
+package cgotest
+
+import (
+ "testing"
+
+ "./issue9026"
+)
+
+func test9026(t *testing.T) { issue9026.Test(t) }
diff --git a/misc/cgo/test/issue9026/issue9026.go b/misc/cgo/test/issue9026/issue9026.go
new file mode 100644
index 000000000..0af86e64d
--- /dev/null
+++ b/misc/cgo/test/issue9026/issue9026.go
@@ -0,0 +1,36 @@
+package issue9026
+
+// This file appears in its own package since the assertion tests the
+// per-package counter used to create fresh identifiers.
+
+/*
+typedef struct {} git_merge_file_input;
+
+typedef struct {} git_merge_file_options;
+
+void git_merge_file(
+ git_merge_file_input *in,
+ git_merge_file_options *opts) {}
+*/
+import "C"
+import (
+ "fmt"
+ "testing"
+)
+
+func Test(t *testing.T) {
+ var in C.git_merge_file_input
+ var opts *C.git_merge_file_options
+ C.git_merge_file(&in, opts)
+
+ // Test that the generated type names are deterministic.
+ // (Previously this would fail about 10% of the time.)
+ //
+ // Brittle: the assertion may fail spuriously when the algorithm
+ // changes, but should remain stable otherwise.
+ got := fmt.Sprintf("%T %T", in, opts)
+ want := "issue9026._Ctype_struct___0 *issue9026._Ctype_struct___1"
+ if got != want {
+ t.Errorf("Non-deterministic type names: got %s, want %s", got, want)
+ }
+}
diff --git a/misc/cgo/testcdefs/cdefstest.c b/misc/cgo/testcdefs/cdefstest.c
index 10cdd66b6..ce670e729 100644
--- a/misc/cgo/testcdefs/cdefstest.c
+++ b/misc/cgo/testcdefs/cdefstest.c
@@ -6,3 +6,4 @@
#include "cdefstest.h"
struct CdefsTest test;
+struct PackedTest packed;
diff --git a/misc/cgo/testcdefs/cdefstest.go b/misc/cgo/testcdefs/cdefstest.go
index e6305b77d..5e613c79e 100644
--- a/misc/cgo/testcdefs/cdefstest.go
+++ b/misc/cgo/testcdefs/cdefstest.go
@@ -35,7 +35,26 @@ struct cdefsTest {
// Correct: -> Array [20][20]**int8 -> int8 **array[20][20]
char **array5[20][20];
};
+
+// Test that packed structures can be translated to C correctly too.
+// See issue 8477.
+
+struct packedTest {
+ char first;
+ int second;
+ long long third;
+} __attribute__((packed));
+
+// Test that conflicting type definitions don't cause problems with cgo.
+// See issue 8477.
+
+typedef struct timespec {
+ double bogus;
+} pid_t;
+
*/
import "C"
type CdefsTest C.struct_cdefsTest
+
+//type PackedTest C.struct_packedTest
diff --git a/misc/cgo/testcdefs/main.c b/misc/cgo/testcdefs/main.c
index 2d3ee4dbe..594a43167 100644
--- a/misc/cgo/testcdefs/main.c
+++ b/misc/cgo/testcdefs/main.c
@@ -17,11 +17,25 @@ struct CdefsOrig {
int8 **array5[20][20];
};
+// Packed structs are no longer supported for -cdefs.
+/*
+typedef struct PackedOrig PackedOrig;
+#pragma pack on
+struct PackedOrig {
+ int8 first;
+ int32 second;
+ int64 third;
+};
+#pragma pack off
+*/
+
void
main·test(int32 ret)
{
CdefsOrig o;
CdefsTest t;
+ // PackedOrig po;
+ // PackedTest pt;
ret = 0;
if(sizeof(t.array1) != sizeof(o.array1) || offsetof(CdefsTest, array1[0]) != offsetof(CdefsOrig, array1[0])) {
@@ -44,5 +58,19 @@ main·test(int32 ret)
runtime·printf("array5: size, offset = %d, %d, want %d, %d\n", sizeof(t.array5), offsetof(CdefsTest, array5[0][0]), sizeof(o.array5), offsetof(CdefsOrig, array5[0][0]));
ret = 1;
}
+/*
+ if(sizeof(pt.first) != sizeof(po.first) || offsetof(PackedTest, first) != offsetof(PackedOrig, first)) {
+ runtime·printf("first: size, offset = %d, %d, want %d, %d\n", sizeof(pt.first), offsetof(PackedTest, first), sizeof(po.first), offsetof(PackedOrig, first));
+ ret = 1;
+ }
+ if(sizeof(pt.second) != sizeof(po.second) || offsetof(PackedTest, second) != offsetof(PackedOrig, second)) {
+ runtime·printf("second: size, offset = %d, %d, want %d, %d\n", sizeof(pt.second), offsetof(PackedTest, second), sizeof(po.second), offsetof(PackedOrig, second));
+ ret = 1;
+ }
+ if(sizeof(pt.third) != sizeof(po.third) || offsetof(PackedTest, third) != offsetof(PackedOrig, third)) {
+ runtime·printf("third: size, offset = %d, %d, want %d, %d\n", sizeof(pt.third), offsetof(PackedTest, third), sizeof(po.third), offsetof(PackedOrig, third));
+ ret = 1;
+ }
+*/
FLUSH(&ret); // flush return value
}
diff --git a/misc/cgo/testgodefs/anonunion.go b/misc/cgo/testgodefs/anonunion.go
new file mode 100644
index 000000000..7bc736b9d
--- /dev/null
+++ b/misc/cgo/testgodefs/anonunion.go
@@ -0,0 +1,26 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// +build ignore
+
+package main
+
+// This file tests that when cgo -godefs sees a struct with a field
+// that is an anonymous union, the first field in the union is
+// promoted to become a field of the struct. See issue 6677 for
+// background.
+
+/*
+typedef struct {
+ union {
+ long l;
+ int c;
+ };
+} t;
+*/
+import "C"
+
+// Input for cgo -godefs.
+
+type T C.t
diff --git a/misc/cgo/testgodefs/issue8478.go b/misc/cgo/testgodefs/issue8478.go
new file mode 100644
index 000000000..92258fde7
--- /dev/null
+++ b/misc/cgo/testgodefs/issue8478.go
@@ -0,0 +1,20 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+//
+// +build ignore
+
+package main
+
+// Issue 8478. Test that void* is consistently mapped to *byte.
+
+/*
+typedef struct {
+ void *p;
+ void **q;
+ void ***r;
+} s;
+*/
+import "C"
+
+type Issue8478 C.s
diff --git a/misc/cgo/testgodefs/main.go b/misc/cgo/testgodefs/main.go
new file mode 100644
index 000000000..7faccf265
--- /dev/null
+++ b/misc/cgo/testgodefs/main.go
@@ -0,0 +1,15 @@
+// Copyright 2014 The Go Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package main
+
+// Test that the struct field in anonunion.go was promoted.
+var v1 T
+var v2 = v1.L
+
+// Test that P, Q, and R all point to byte.
+var v3 = Issue8478{P: (*byte)(nil), Q: (**byte)(nil), R: (***byte)(nil)}
+
+func main() {
+}
diff --git a/misc/cgo/testgodefs/test.bash b/misc/cgo/testgodefs/test.bash
new file mode 100755
index 000000000..5281b1056
--- /dev/null
+++ b/misc/cgo/testgodefs/test.bash
@@ -0,0 +1,20 @@
+# Copyright 2014 The Go Authors. All rights reserved.
+# Use of this source code is governed by a BSD-style
+# license that can be found in the LICENSE file.
+
+# We are testing cgo -godefs, which translates Go files that use
+# import "C" into Go files with Go definitions of types defined in the
+# import "C" block. Add more tests here.
+FILE_PREFIXES="anonunion issue8478"
+
+RM=
+for FP in $FILE_PREFIXES
+do
+ go tool cgo -godefs ${FP}.go > ${FP}_defs.go
+ RM="${RM} ${FP}_defs.go"
+done
+
+go build . && ./testgodefs
+EXIT=$?
+rm -rf _obj testgodefs ${RM}
+exit $EXIT
diff --git a/misc/editors b/misc/editors
new file mode 100644
index 000000000..850ec3401
--- /dev/null
+++ b/misc/editors
@@ -0,0 +1,5 @@
+For information about plugins and other support for Go in editors and shells,
+see this page on the Go Wiki:
+
+https://code.google.com/p/go-wiki/wiki/IDEsAndTextEditorPlugins
+
diff --git a/misc/emacs/go-mode-load.el b/misc/emacs/go-mode-load.el
deleted file mode 100644
index 3fc35c116..000000000
--- a/misc/emacs/go-mode-load.el
+++ /dev/null
@@ -1,96 +0,0 @@
-;;; go-mode-load.el --- automatically extracted autoloads
-;;; Commentary:
-
-;; To install go-mode, add the following lines to your .emacs file:
-;; (add-to-list 'load-path "PATH CONTAINING go-mode-load.el" t)
-;; (require 'go-mode-load)
-;;
-;; After this, go-mode will be used for files ending in '.go'.
-;;
-;; To compile go-mode from the command line, run the following
-;; emacs -batch -f batch-byte-compile go-mode.el
-;;
-;; See go-mode.el for documentation.
-;;
-;; To update this file, evaluate the following form
-;; (let ((generated-autoload-file buffer-file-name)) (update-file-autoloads "go-mode.el"))
-
-;;; Code:
-
-
-;;;### (autoloads (go-download-play godoc gofmt-before-save go-mode)
-;;;;;; "go-mode" "go-mode.el" (20767 50749))
-;;; Generated autoloads from go-mode.el
-
-(autoload 'go-mode "go-mode" "\
-Major mode for editing Go source text.
-
-This mode provides (not just) basic editing capabilities for
-working with Go code. It offers almost complete syntax
-highlighting, indentation that is almost identical to gofmt,
-proper parsing of the buffer content to allow features such as
-navigation by function, manipulation of comments or detection of
-strings.
-
-Additionally to these core features, it offers various features to
-help with writing Go code. You can directly run buffer content
-through gofmt, read godoc documentation from within Emacs, modify
-and clean up the list of package imports or interact with the
-Playground (uploading and downloading pastes).
-
-The following extra functions are defined:
-
-- `gofmt'
-- `godoc'
-- `go-import-add'
-- `go-remove-unused-imports'
-- `go-goto-imports'
-- `go-play-buffer' and `go-play-region'
-- `go-download-play'
-
-If you want to automatically run `gofmt' before saving a file,
-add the following hook to your emacs configuration:
-
-\(add-hook 'before-save-hook 'gofmt-before-save)
-
-If you're looking for even more integration with Go, namely
-on-the-fly syntax checking, auto-completion and snippets, it is
-recommended to look at goflymake
-\(https://github.com/dougm/goflymake), gocode
-\(https://github.com/nsf/gocode) and yasnippet-go
-\(https://github.com/dominikh/yasnippet-go)
-
-\(fn)" t nil)
-
-(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode))
-
-(autoload 'gofmt-before-save "go-mode" "\
-Add this to .emacs to run gofmt on the current buffer when saving:
- (add-hook 'before-save-hook 'gofmt-before-save).
-
-Note that this will cause go-mode to get loaded the first time
-you save any file, kind of defeating the point of autoloading.
-
-\(fn)" t nil)
-
-(autoload 'godoc "go-mode" "\
-Show go documentation for a query, much like M-x man.
-
-\(fn QUERY)" t nil)
-
-(autoload 'go-download-play "go-mode" "\
-Downloads a paste from the playground and inserts it in a Go
-buffer. Tries to look for a URL at point.
-
-\(fn URL)" t nil)
-
-;;;***
-
-(provide 'go-mode-load)
-;; Local Variables:
-;; version-control: never
-;; no-byte-compile: t
-;; no-update-autoloads: t
-;; coding: utf-8
-;; End:
-;;; go-mode-load.el ends here
diff --git a/misc/emacs/go-mode.el b/misc/emacs/go-mode.el
deleted file mode 100644
index 6333ff966..000000000
--- a/misc/emacs/go-mode.el
+++ /dev/null
@@ -1,1196 +0,0 @@
-;;; go-mode.el --- Major mode for the Go programming language
-
-;; Copyright 2013 The Go Authors. All rights reserved.
-;; Use of this source code is governed by a BSD-style
-;; license that can be found in the LICENSE file.
-
-(require 'cl)
-(require 'etags)
-(require 'ffap)
-(require 'find-file)
-(require 'ring)
-(require 'url)
-
-;; XEmacs compatibility guidelines
-;; - Minimum required version of XEmacs: 21.5.32
-;; - Feature that cannot be backported: POSIX character classes in
-;; regular expressions
-;; - Functions that could be backported but won't because 21.5.32
-;; covers them: plenty.
-;; - Features that are still partly broken:
-;; - godef will not work correctly if multibyte characters are
-;; being used
-;; - Fontification will not handle unicode correctly
-;;
-;; - Do not use \_< and \_> regexp delimiters directly; use
-;; go--regexp-enclose-in-symbol
-;;
-;; - The character `_` must not be a symbol constituent but a
-;; character constituent
-;;
-;; - Do not use process-lines
-;;
-;; - Use go--old-completion-list-style when using a plain list as the
-;; collection for completing-read
-;;
-;; - Use go--kill-whole-line instead of kill-whole-line (called
-;; kill-entire-line in XEmacs)
-;;
-;; - Use go--position-bytes instead of position-bytes
-(defmacro go--xemacs-p ()
- `(featurep 'xemacs))
-
-(defalias 'go--kill-whole-line
- (if (fboundp 'kill-whole-line)
- #'kill-whole-line
- #'kill-entire-line))
-
-;; Delete the current line without putting it in the kill-ring.
-(defun go--delete-whole-line (&optional arg)
- ;; Emacs uses both kill-region and kill-new, Xemacs only uses
- ;; kill-region. In both cases we turn them into operations that do
- ;; not modify the kill ring. This solution does depend on the
- ;; implementation of kill-line, but it's the only viable solution
- ;; that does not require to write kill-line from scratch.
- (flet ((kill-region (beg end)
- (delete-region beg end))
- (kill-new (s) ()))
- (go--kill-whole-line arg)))
-
-;; declare-function is an empty macro that only byte-compile cares
-;; about. Wrap in always false if to satisfy Emacsen without that
-;; macro.
-(if nil
- (declare-function go--position-bytes "go-mode" (point)))
-
-;; XEmacs unfortunately does not offer position-bytes. We can fall
-;; back to just using (point), but it will be incorrect as soon as
-;; multibyte characters are being used.
-(if (fboundp 'position-bytes)
- (defalias 'go--position-bytes #'position-bytes)
- (defun go--position-bytes (point) point))
-
-(defun go--old-completion-list-style (list)
- (mapcar (lambda (x) (cons x nil)) list))
-
-;; GNU Emacs 24 has prog-mode, older GNU Emacs and XEmacs do not, so
-;; copy its definition for those.
-(if (not (fboundp 'prog-mode))
- (define-derived-mode prog-mode fundamental-mode "Prog"
- "Major mode for editing source code."
- (set (make-local-variable 'require-final-newline) mode-require-final-newline)
- (set (make-local-variable 'parse-sexp-ignore-comments) t)
- (setq bidi-paragraph-direction 'left-to-right)))
-
-(defun go--regexp-enclose-in-symbol (s)
- ;; XEmacs does not support \_<, GNU Emacs does. In GNU Emacs we make
- ;; extensive use of \_< to support unicode in identifiers. Until we
- ;; come up with a better solution for XEmacs, this solution will
- ;; break fontification in XEmacs for identifiers such as "typeµ".
- ;; XEmacs will consider "type" a keyword, GNU Emacs won't.
-
- (if (go--xemacs-p)
- (concat "\\<" s "\\>")
- (concat "\\_<" s "\\_>")))
-
-;; Move up one level of parentheses.
-(defun go-goto-opening-parenthesis (&optional legacy-unused)
- ;; The old implementation of go-goto-opening-parenthesis had an
- ;; optional argument to speed up the function. It didn't change the
- ;; function's outcome.
-
- ;; Silently fail if there's no matching opening parenthesis.
- (condition-case nil
- (backward-up-list)
- (scan-error nil)))
-
-
-(defconst go-dangling-operators-regexp "[^-]-\\|[^+]\\+\\|[/*&><.=|^]")
-(defconst go-identifier-regexp "[[:word:][:multibyte:]]+")
-(defconst go-label-regexp go-identifier-regexp)
-(defconst go-type-regexp "[[:word:][:multibyte:]*]+")
-(defconst go-func-regexp (concat (go--regexp-enclose-in-symbol "func") "\\s *\\(" go-identifier-regexp "\\)"))
-(defconst go-func-meth-regexp (concat
- (go--regexp-enclose-in-symbol "func") "\\s *\\(?:(\\s *"
- "\\(" go-identifier-regexp "\\s +\\)?" go-type-regexp
- "\\s *)\\s *\\)?\\("
- go-identifier-regexp
- "\\)("))
-(defconst go-builtins
- '("append" "cap" "close" "complex" "copy"
- "delete" "imag" "len" "make" "new"
- "panic" "print" "println" "real" "recover")
- "All built-in functions in the Go language. Used for font locking.")
-
-(defconst go-mode-keywords
- '("break" "default" "func" "interface" "select"
- "case" "defer" "go" "map" "struct"
- "chan" "else" "goto" "package" "switch"
- "const" "fallthrough" "if" "range" "type"
- "continue" "for" "import" "return" "var")
- "All keywords in the Go language. Used for font locking.")
-
-(defconst go-constants '("nil" "true" "false" "iota"))
-(defconst go-type-name-regexp (concat "\\(?:[*(]\\)*\\(?:" go-identifier-regexp "\\.\\)?\\(" go-identifier-regexp "\\)"))
-
-(defvar go-dangling-cache)
-(defvar go-godoc-history nil)
-(defvar go--coverage-current-file-name)
-
-(defgroup go nil
- "Major mode for editing Go code"
- :group 'languages)
-
-(defgroup go-cover nil
- "Options specific to `cover`"
- :group 'go)
-
-(defcustom go-fontify-function-calls t
- "Fontify function and method calls if this is non-nil."
- :type 'boolean
- :group 'go)
-
-(defcustom go-mode-hook nil
- "Hook called by `go-mode'."
- :type 'hook
- :group 'go)
-
-(defcustom go-command "go"
- "The 'go' command. Some users have multiple Go development
-trees and invoke the 'go' tool via a wrapper that sets GOROOT and
-GOPATH based on the current directory. Such users should
-customize this variable to point to the wrapper script."
- :type 'string
- :group 'go)
-
-(defcustom gofmt-command "gofmt"
- "The 'gofmt' command. Some users may replace this with 'goimports'
-from https://github.com/bradfitz/goimports."
- :type 'string
- :group 'go)
-
-(defcustom go-other-file-alist
- '(("_test\\.go\\'" (".go"))
- ("\\.go\\'" ("_test.go")))
- "See the documentation of `ff-other-file-alist' for details."
- :type '(repeat (list regexp (choice (repeat string) function)))
- :group 'go)
-
-(defface go-coverage-untracked
- '((t (:foreground "#505050")))
- "Coverage color of untracked code."
- :group 'go-cover)
-
-(defface go-coverage-0
- '((t (:foreground "#c00000")))
- "Coverage color for uncovered code."
- :group 'go-cover)
-(defface go-coverage-1
- '((t (:foreground "#808080")))
- "Coverage color for covered code with weight 1."
- :group 'go-cover)
-(defface go-coverage-2
- '((t (:foreground "#748c83")))
- "Coverage color for covered code with weight 2."
- :group 'go-cover)
-(defface go-coverage-3
- '((t (:foreground "#689886")))
- "Coverage color for covered code with weight 3."
- :group 'go-cover)
-(defface go-coverage-4
- '((t (:foreground "#5ca489")))
- "Coverage color for covered code with weight 4."
- :group 'go-cover)
-(defface go-coverage-5
- '((t (:foreground "#50b08c")))
- "Coverage color for covered code with weight 5."
- :group 'go-cover)
-(defface go-coverage-6
- '((t (:foreground "#44bc8f")))
- "Coverage color for covered code with weight 6."
- :group 'go-cover)
-(defface go-coverage-7
- '((t (:foreground "#38c892")))
- "Coverage color for covered code with weight 7."
- :group 'go-cover)
-(defface go-coverage-8
- '((t (:foreground "#2cd495")))
- "Coverage color for covered code with weight 8.
-For mode=set, all covered lines will have this weight."
- :group 'go-cover)
-(defface go-coverage-9
- '((t (:foreground "#20e098")))
- "Coverage color for covered code with weight 9."
- :group 'go-cover)
-(defface go-coverage-10
- '((t (:foreground "#14ec9b")))
- "Coverage color for covered code with weight 10."
- :group 'go-cover)
-(defface go-coverage-covered
- '((t (:foreground "#2cd495")))
- "Coverage color of covered code."
- :group 'go-cover)
-
-(defvar go-mode-syntax-table
- (let ((st (make-syntax-table)))
- (modify-syntax-entry ?+ "." st)
- (modify-syntax-entry ?- "." st)
- (modify-syntax-entry ?% "." st)
- (modify-syntax-entry ?& "." st)
- (modify-syntax-entry ?| "." st)
- (modify-syntax-entry ?^ "." st)
- (modify-syntax-entry ?! "." st)
- (modify-syntax-entry ?= "." st)
- (modify-syntax-entry ?< "." st)
- (modify-syntax-entry ?> "." st)
- (modify-syntax-entry ?/ (if (go--xemacs-p) ". 1456" ". 124b") st)
- (modify-syntax-entry ?* ". 23" st)
- (modify-syntax-entry ?\n "> b" st)
- (modify-syntax-entry ?\" "\"" st)
- (modify-syntax-entry ?\' "\"" st)
- (modify-syntax-entry ?` "\"" st)
- (modify-syntax-entry ?\\ "\\" st)
- ;; It would be nicer to have _ as a symbol constituent, but that
- ;; would trip up XEmacs, which does not support the \_< anchor
- (modify-syntax-entry ?_ "w" st)
-
- st)
- "Syntax table for Go mode.")
-
-(defun go--build-font-lock-keywords ()
- ;; we cannot use 'symbols in regexp-opt because GNU Emacs <24
- ;; doesn't understand that
- (append
- `((,(go--regexp-enclose-in-symbol (regexp-opt go-mode-keywords t)) . font-lock-keyword-face)
- (,(concat "\\(" (go--regexp-enclose-in-symbol (regexp-opt go-builtins t)) "\\)[[:space:]]*(") 1 font-lock-builtin-face)
- (,(go--regexp-enclose-in-symbol (regexp-opt go-constants t)) . font-lock-constant-face)
- (,go-func-regexp 1 font-lock-function-name-face)) ;; function (not method) name
-
- (if go-fontify-function-calls
- `((,(concat "\\(" go-identifier-regexp "\\)[[:space:]]*(") 1 font-lock-function-name-face) ;; function call/method name
- (,(concat "[^[:word:][:multibyte:]](\\(" go-identifier-regexp "\\))[[:space:]]*(") 1 font-lock-function-name-face)) ;; bracketed function call
- `((,go-func-meth-regexp 2 font-lock-function-name-face))) ;; method name
-
- `(
- ("\\(`[^`]*`\\)" 1 font-lock-multiline) ;; raw string literal, needed for font-lock-syntactic-keywords
- (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+\\([^[:space:]]+\\)") 1 font-lock-type-face) ;; types
- (,(concat (go--regexp-enclose-in-symbol "type") "[[:space:]]+" go-identifier-regexp "[[:space:]]*" go-type-name-regexp) 1 font-lock-type-face) ;; types
- (,(concat "[^[:word:][:multibyte:]]\\[\\([[:digit:]]+\\|\\.\\.\\.\\)?\\]" go-type-name-regexp) 2 font-lock-type-face) ;; Arrays/slices
- (,(concat "\\(" go-identifier-regexp "\\)" "{") 1 font-lock-type-face)
- (,(concat (go--regexp-enclose-in-symbol "map") "\\[[^]]+\\]" go-type-name-regexp) 1 font-lock-type-face) ;; map value type
- (,(concat (go--regexp-enclose-in-symbol "map") "\\[" go-type-name-regexp) 1 font-lock-type-face) ;; map key type
- (,(concat (go--regexp-enclose-in-symbol "chan") "[[:space:]]*\\(?:<-\\)?" go-type-name-regexp) 1 font-lock-type-face) ;; channel type
- (,(concat (go--regexp-enclose-in-symbol "\\(?:new\\|make\\)") "\\(?:[[:space:]]\\|)\\)*(" go-type-name-regexp) 1 font-lock-type-face) ;; new/make type
- ;; TODO do we actually need this one or isn't it just a function call?
- (,(concat "\\.\\s *(" go-type-name-regexp) 1 font-lock-type-face) ;; Type conversion
- (,(concat (go--regexp-enclose-in-symbol "func") "[[:space:]]+(" go-identifier-regexp "[[:space:]]+" go-type-name-regexp ")") 1 font-lock-type-face) ;; Method receiver
- (,(concat (go--regexp-enclose-in-symbol "func") "[[:space:]]+(" go-type-name-regexp ")") 1 font-lock-type-face) ;; Method receiver without variable name
- ;; Like the original go-mode this also marks compound literal
- ;; fields. There, it was marked as to fix, but I grew quite
- ;; accustomed to it, so it'll stay for now.
- (,(concat "^[[:space:]]*\\(" go-label-regexp "\\)[[:space:]]*:\\(\\S.\\|$\\)") 1 font-lock-constant-face) ;; Labels and compound literal fields
- (,(concat (go--regexp-enclose-in-symbol "\\(goto\\|break\\|continue\\)") "[[:space:]]*\\(" go-label-regexp "\\)") 2 font-lock-constant-face)))) ;; labels in goto/break/continue
-
-(defconst go--font-lock-syntactic-keywords
- ;; Override syntax property of raw string literal contents, so that
- ;; backslashes have no special meaning in ``. Used in Emacs 23 or older.
- '((go--match-raw-string-literal
- (1 (7 . ?`))
- (2 (15 . nil)) ;; 15 = "generic string"
- (3 (7 . ?`)))))
-
-(defvar go-mode-map
- (let ((m (make-sparse-keymap)))
- (define-key m "}" #'go-mode-insert-and-indent)
- (define-key m ")" #'go-mode-insert-and-indent)
- (define-key m "," #'go-mode-insert-and-indent)
- (define-key m ":" #'go-mode-insert-and-indent)
- (define-key m "=" #'go-mode-insert-and-indent)
- (define-key m (kbd "C-c C-a") #'go-import-add)
- (define-key m (kbd "C-c C-j") #'godef-jump)
- (define-key m (kbd "C-x 4 C-c C-j") #'godef-jump-other-window)
- (define-key m (kbd "C-c C-d") #'godef-describe)
- m)
- "Keymap used by Go mode to implement electric keys.")
-
-(defun go-mode-insert-and-indent (key)
- "Invoke the global binding of KEY, then reindent the line."
-
- (interactive (list (this-command-keys)))
- (call-interactively (lookup-key (current-global-map) key))
- (indent-according-to-mode))
-
-(defmacro go-paren-level ()
- `(car (syntax-ppss)))
-
-(defmacro go-in-string-or-comment-p ()
- `(nth 8 (syntax-ppss)))
-
-(defmacro go-in-string-p ()
- `(nth 3 (syntax-ppss)))
-
-(defmacro go-in-comment-p ()
- `(nth 4 (syntax-ppss)))
-
-(defmacro go-goto-beginning-of-string-or-comment ()
- `(goto-char (nth 8 (syntax-ppss))))
-
-(defun go--backward-irrelevant (&optional stop-at-string)
- "Skips backwards over any characters that are irrelevant for
-indentation and related tasks.
-
-It skips over whitespace, comments, cases and labels and, if
-STOP-AT-STRING is not true, over strings."
-
- (let (pos (start-pos (point)))
- (skip-chars-backward "\n\s\t")
- (if (and (save-excursion (beginning-of-line) (go-in-string-p)) (looking-back "`") (not stop-at-string))
- (backward-char))
- (if (and (go-in-string-p) (not stop-at-string))
- (go-goto-beginning-of-string-or-comment))
- (if (looking-back "\\*/")
- (backward-char))
- (if (go-in-comment-p)
- (go-goto-beginning-of-string-or-comment))
- (setq pos (point))
- (beginning-of-line)
- (if (or (looking-at (concat "^" go-label-regexp ":")) (looking-at "^[[:space:]]*\\(case .+\\|default\\):"))
- (end-of-line 0)
- (goto-char pos))
- (if (/= start-pos (point))
- (go--backward-irrelevant stop-at-string))
- (/= start-pos (point))))
-
-(defun go--buffer-narrowed-p ()
- "Return non-nil if the current buffer is narrowed."
- (/= (buffer-size)
- (- (point-max)
- (point-min))))
-
-(defun go--match-raw-string-literal (end)
- "Search for a raw string literal. Set point to the end of the
-occurence found on success. Returns nil on failure."
- (when (search-forward "`" end t)
- (goto-char (match-beginning 0))
- (if (go-in-string-or-comment-p)
- (progn (goto-char (match-end 0))
- (go--match-raw-string-literal end))
- (when (looking-at "\\(`\\)\\([^`]*\\)\\(`\\)")
- (goto-char (match-end 0))
- t))))
-
-(defun go-previous-line-has-dangling-op-p ()
- "Returns non-nil if the current line is a continuation line."
- (let* ((cur-line (line-number-at-pos))
- (val (gethash cur-line go-dangling-cache 'nope)))
- (if (or (go--buffer-narrowed-p) (equal val 'nope))
- (save-excursion
- (beginning-of-line)
- (go--backward-irrelevant t)
- (setq val (looking-back go-dangling-operators-regexp))
- (if (not (go--buffer-narrowed-p))
- (puthash cur-line val go-dangling-cache))))
- val))
-
-(defun go--at-function-definition ()
- "Return non-nil if point is on the opening curly brace of a
-function definition.
-
-We do this by first calling (beginning-of-defun), which will take
-us to the start of *some* function. We then look for the opening
-curly brace of that function and compare its position against the
-curly brace we are checking. If they match, we return non-nil."
- (if (= (char-after) ?\{)
- (save-excursion
- (let ((old-point (point))
- start-nesting)
- (beginning-of-defun)
- (when (looking-at "func ")
- (setq start-nesting (go-paren-level))
- (skip-chars-forward "^{")
- (while (> (go-paren-level) start-nesting)
- (forward-char)
- (skip-chars-forward "^{") 0)
- (if (and (= (go-paren-level) start-nesting) (= old-point (point)))
- t))))))
-
-(defun go--indentation-for-opening-parenthesis ()
- "Return the semantic indentation for the current opening parenthesis.
-
-If point is on an opening curly brace and said curly brace
-belongs to a function declaration, the indentation of the func
-keyword will be returned. Otherwise the indentation of the
-current line will be returned."
- (save-excursion
- (if (go--at-function-definition)
- (progn
- (beginning-of-defun)
- (current-indentation))
- (current-indentation))))
-
-(defun go-indentation-at-point ()
- (save-excursion
- (let (start-nesting)
- (back-to-indentation)
- (setq start-nesting (go-paren-level))
-
- (cond
- ((go-in-string-p)
- (current-indentation))
- ((looking-at "[])}]")
- (go-goto-opening-parenthesis)
- (if (go-previous-line-has-dangling-op-p)
- (- (current-indentation) tab-width)
- (go--indentation-for-opening-parenthesis)))
- ((progn (go--backward-irrelevant t) (looking-back go-dangling-operators-regexp))
- ;; only one nesting for all dangling operators in one operation
- (if (go-previous-line-has-dangling-op-p)
- (current-indentation)
- (+ (current-indentation) tab-width)))
- ((zerop (go-paren-level))
- 0)
- ((progn (go-goto-opening-parenthesis) (< (go-paren-level) start-nesting))
- (if (go-previous-line-has-dangling-op-p)
- (current-indentation)
- (+ (go--indentation-for-opening-parenthesis) tab-width)))
- (t
- (current-indentation))))))
-
-(defun go-mode-indent-line ()
- (interactive)
- (let (indent
- shift-amt
- (pos (- (point-max) (point)))
- (point (point))
- (beg (line-beginning-position)))
- (back-to-indentation)
- (if (go-in-string-or-comment-p)
- (goto-char point)
- (setq indent (go-indentation-at-point))
- (if (looking-at (concat go-label-regexp ":\\([[:space:]]*/.+\\)?$\\|case .+:\\|default:"))
- (decf indent tab-width))
- (setq shift-amt (- indent (current-column)))
- (if (zerop shift-amt)
- nil
- (delete-region beg (point))
- (indent-to indent))
- ;; If initial point was within line's indentation,
- ;; position after the indentation. Else stay at same point in text.
- (if (> (- (point-max) pos) (point))
- (goto-char (- (point-max) pos))))))
-
-(defun go-beginning-of-defun (&optional count)
- (setq count (or count 1))
- (let ((first t)
- failure)
- (dotimes (i (abs count))
- (while (and (not failure)
- (or first (go-in-string-or-comment-p)))
- (if (>= count 0)
- (progn
- (go--backward-irrelevant)
- (if (not (re-search-backward go-func-meth-regexp nil t))
- (setq failure t)))
- (if (looking-at go-func-meth-regexp)
- (forward-char))
- (if (not (re-search-forward go-func-meth-regexp nil t))
- (setq failure t)))
- (setq first nil)))
- (if (< count 0)
- (beginning-of-line))
- (not failure)))
-
-(defun go-end-of-defun ()
- (let (orig-level)
- ;; It can happen that we're not placed before a function by emacs
- (if (not (looking-at "func"))
- (go-beginning-of-defun -1))
- (skip-chars-forward "^{")
- (forward-char)
- (setq orig-level (go-paren-level))
- (while (>= (go-paren-level) orig-level)
- (skip-chars-forward "^}")
- (forward-char))))
-
-;;;###autoload
-(define-derived-mode go-mode prog-mode "Go"
- "Major mode for editing Go source text.
-
-This mode provides (not just) basic editing capabilities for
-working with Go code. It offers almost complete syntax
-highlighting, indentation that is almost identical to gofmt and
-proper parsing of the buffer content to allow features such as
-navigation by function, manipulation of comments or detection of
-strings.
-
-In addition to these core features, it offers various features to
-help with writing Go code. You can directly run buffer content
-through gofmt, read godoc documentation from within Emacs, modify
-and clean up the list of package imports or interact with the
-Playground (uploading and downloading pastes).
-
-The following extra functions are defined:
-
-- `gofmt'
-- `godoc'
-- `go-import-add'
-- `go-remove-unused-imports'
-- `go-goto-imports'
-- `go-play-buffer' and `go-play-region'
-- `go-download-play'
-- `godef-describe' and `godef-jump'
-- `go-coverage'
-
-If you want to automatically run `gofmt' before saving a file,
-add the following hook to your emacs configuration:
-
-\(add-hook 'before-save-hook #'gofmt-before-save)
-
-If you want to use `godef-jump' instead of etags (or similar),
-consider binding godef-jump to `M-.', which is the default key
-for `find-tag':
-
-\(add-hook 'go-mode-hook (lambda ()
- (local-set-key (kbd \"M-.\") #'godef-jump)))
-
-Please note that godef is an external dependency. You can install
-it with
-
-go get code.google.com/p/rog-go/exp/cmd/godef
-
-
-If you're looking for even more integration with Go, namely
-on-the-fly syntax checking, auto-completion and snippets, it is
-recommended that you look at goflymake
-\(https://github.com/dougm/goflymake), gocode
-\(https://github.com/nsf/gocode), go-eldoc
-\(github.com/syohex/emacs-go-eldoc) and yasnippet-go
-\(https://github.com/dominikh/yasnippet-go)"
-
- ;; Font lock
- (set (make-local-variable 'font-lock-defaults)
- '(go--build-font-lock-keywords))
-
- ;; Indentation
- (set (make-local-variable 'indent-line-function) #'go-mode-indent-line)
-
- ;; Comments
- (set (make-local-variable 'comment-start) "// ")
- (set (make-local-variable 'comment-end) "")
- (set (make-local-variable 'comment-use-syntax) t)
- (set (make-local-variable 'comment-start-skip) "\\(//+\\|/\\*+\\)\\s *")
-
- (set (make-local-variable 'beginning-of-defun-function) #'go-beginning-of-defun)
- (set (make-local-variable 'end-of-defun-function) #'go-end-of-defun)
-
- (set (make-local-variable 'parse-sexp-lookup-properties) t)
- (if (boundp 'syntax-propertize-function)
- (set (make-local-variable 'syntax-propertize-function) #'go-propertize-syntax)
- (set (make-local-variable 'font-lock-syntactic-keywords)
- go--font-lock-syntactic-keywords)
- (set (make-local-variable 'font-lock-multiline) t))
-
- (set (make-local-variable 'go-dangling-cache) (make-hash-table :test 'eql))
- (add-hook 'before-change-functions (lambda (x y) (setq go-dangling-cache (make-hash-table :test 'eql))) t t)
-
- ;; ff-find-other-file
- (setq ff-other-file-alist 'go-other-file-alist)
-
- (setq imenu-generic-expression
- '(("type" "^type *\\([^ \t\n\r\f]*\\)" 1)
- ("func" "^func *\\(.*\\) {" 1)))
- (imenu-add-to-menubar "Index")
-
- ;; Go style
- (setq indent-tabs-mode t)
-
- ;; Handle unit test failure output in compilation-mode
- ;;
- ;; Note the final t argument to add-to-list for append, ie put these at the
- ;; *ends* of compilation-error-regexp-alist[-alist]. We want go-test to be
- ;; handled first, otherwise other elements will match that don't work, and
- ;; those alists are traversed in *reverse* order:
- ;; http://lists.gnu.org/archive/html/bug-gnu-emacs/2001-12/msg00674.html
- (when (and (boundp 'compilation-error-regexp-alist)
- (boundp 'compilation-error-regexp-alist-alist))
- (add-to-list 'compilation-error-regexp-alist 'go-test t)
- (add-to-list 'compilation-error-regexp-alist-alist
- '(go-test . ("^\t+\\([^()\t\n]+\\):\\([0-9]+\\):? .*$" 1 2)) t)))
-
-;;;###autoload
-(add-to-list 'auto-mode-alist (cons "\\.go\\'" 'go-mode))
-
-(defun go--apply-rcs-patch (patch-buffer)
- "Apply an RCS-formatted diff from PATCH-BUFFER to the current
-buffer."
- (let ((target-buffer (current-buffer))
- ;; Relative offset between buffer line numbers and line numbers
- ;; in patch.
- ;;
- ;; Line numbers in the patch are based on the source file, so
- ;; we have to keep an offset when making changes to the
- ;; buffer.
- ;;
- ;; Appending lines decrements the offset (possibly making it
- ;; negative), deleting lines increments it. This order
- ;; simplifies the forward-line invocations.
- (line-offset 0))
- (save-excursion
- (with-current-buffer patch-buffer
- (goto-char (point-min))
- (while (not (eobp))
- (unless (looking-at "^\\([ad]\\)\\([0-9]+\\) \\([0-9]+\\)")
- (error "invalid rcs patch or internal error in go--apply-rcs-patch"))
- (forward-line)
- (let ((action (match-string 1))
- (from (string-to-number (match-string 2)))
- (len (string-to-number (match-string 3))))
- (cond
- ((equal action "a")
- (let ((start (point)))
- (forward-line len)
- (let ((text (buffer-substring start (point))))
- (with-current-buffer target-buffer
- (decf line-offset len)
- (goto-char (point-min))
- (forward-line (- from len line-offset))
- (insert text)))))
- ((equal action "d")
- (with-current-buffer target-buffer
- (go--goto-line (- from line-offset))
- (incf line-offset len)
- (go--delete-whole-line len)))
- (t
- (error "invalid rcs patch or internal error in go--apply-rcs-patch")))))))))
-
-(defun gofmt ()
- "Formats the current buffer according to the gofmt tool."
-
- (interactive)
- (let ((tmpfile (make-temp-file "gofmt" nil ".go"))
- (patchbuf (get-buffer-create "*Gofmt patch*"))
- (errbuf (get-buffer-create "*Gofmt Errors*"))
- (coding-system-for-read 'utf-8)
- (coding-system-for-write 'utf-8))
-
- (with-current-buffer errbuf
- (setq buffer-read-only nil)
- (erase-buffer))
- (with-current-buffer patchbuf
- (erase-buffer))
-
- (write-region nil nil tmpfile)
-
- ;; We're using errbuf for the mixed stdout and stderr output. This
- ;; is not an issue because gofmt -w does not produce any stdout
- ;; output in case of success.
- (if (zerop (call-process gofmt-command nil errbuf nil "-w" tmpfile))
- (if (zerop (call-process-region (point-min) (point-max) "diff" nil patchbuf nil "-n" "-" tmpfile))
- (progn
- (kill-buffer errbuf)
- (message "Buffer is already gofmted"))
- (go--apply-rcs-patch patchbuf)
- (kill-buffer errbuf)
- (message "Applied gofmt"))
- (message "Could not apply gofmt. Check errors for details")
- (gofmt--process-errors (buffer-file-name) tmpfile errbuf))
-
- (kill-buffer patchbuf)
- (delete-file tmpfile)))
-
-
-(defun gofmt--process-errors (filename tmpfile errbuf)
- ;; Convert the gofmt stderr to something understood by the compilation mode.
- (with-current-buffer errbuf
- (goto-char (point-min))
- (insert "gofmt errors:\n")
- (while (search-forward-regexp (concat "^\\(" (regexp-quote tmpfile) "\\):") nil t)
- (replace-match (file-name-nondirectory filename) t t nil 1))
- (compilation-mode)
- (display-buffer errbuf)))
-
-;;;###autoload
-(defun gofmt-before-save ()
- "Add this to .emacs to run gofmt on the current buffer when saving:
- (add-hook 'before-save-hook 'gofmt-before-save).
-
-Note that this will cause go-mode to get loaded the first time
-you save any file, kind of defeating the point of autoloading."
-
- (interactive)
- (when (eq major-mode 'go-mode) (gofmt)))
-
-(defun godoc--read-query ()
- "Read a godoc query from the minibuffer."
- ;; Compute the default query as the symbol under the cursor.
- ;; TODO: This does the wrong thing for e.g. multipart.NewReader (it only grabs
- ;; half) but I see no way to disambiguate that from e.g. foobar.SomeMethod.
- (let* ((bounds (bounds-of-thing-at-point 'symbol))
- (symbol (if bounds
- (buffer-substring-no-properties (car bounds)
- (cdr bounds)))))
- (completing-read (if symbol
- (format "godoc (default %s): " symbol)
- "godoc: ")
- (go--old-completion-list-style (go-packages)) nil nil nil 'go-godoc-history symbol)))
-
-(defun godoc--get-buffer (query)
- "Get an empty buffer for a godoc query."
- (let* ((buffer-name (concat "*godoc " query "*"))
- (buffer (get-buffer buffer-name)))
- ;; Kill the existing buffer if it already exists.
- (when buffer (kill-buffer buffer))
- (get-buffer-create buffer-name)))
-
-(defun godoc--buffer-sentinel (proc event)
- "Sentinel function run when godoc command completes."
- (with-current-buffer (process-buffer proc)
- (cond ((string= event "finished\n") ;; Successful exit.
- (goto-char (point-min))
- (view-mode 1)
- (display-buffer (current-buffer) t))
- ((/= (process-exit-status proc) 0) ;; Error exit.
- (let ((output (buffer-string)))
- (kill-buffer (current-buffer))
- (message (concat "godoc: " output)))))))
-
-;;;###autoload
-(defun godoc (query)
- "Show go documentation for a query, much like M-x man."
- (interactive (list (godoc--read-query)))
- (unless (string= query "")
- (set-process-sentinel
- (start-process-shell-command "godoc" (godoc--get-buffer query)
- (concat "godoc " query))
- 'godoc--buffer-sentinel)
- nil))
-
-(defun go-goto-imports ()
- "Move point to the block of imports.
-
-If using
-
- import (
- \"foo\"
- \"bar\"
- )
-
-it will move point directly behind the last import.
-
-If using
-
- import \"foo\"
- import \"bar\"
-
-it will move point to the next line after the last import.
-
-If no imports can be found, point will be moved after the package
-declaration."
- (interactive)
- ;; FIXME if there's a block-commented import before the real
- ;; imports, we'll jump to that one.
-
- ;; Generally, this function isn't very forgiving. it'll bark on
- ;; extra whitespace. It works well for clean code.
- (let ((old-point (point)))
- (goto-char (point-min))
- (cond
- ((re-search-forward "^import ()" nil t)
- (backward-char 1)
- 'block-empty)
- ((re-search-forward "^import ([^)]+)" nil t)
- (backward-char 2)
- 'block)
- ((re-search-forward "\\(^import \\([^\"]+ \\)?\"[^\"]+\"\n?\\)+" nil t)
- 'single)
- ((re-search-forward "^[[:space:]\n]*package .+?\n" nil t)
- (message "No imports found, moving point after package declaration")
- 'none)
- (t
- (goto-char old-point)
- (message "No imports or package declaration found. Is this really a Go file?")
- 'fail))))
-
-(defun go-play-buffer ()
- "Like `go-play-region', but acts on the entire buffer."
- (interactive)
- (go-play-region (point-min) (point-max)))
-
-(defun go-play-region (start end)
- "Send the region to the Playground and stores the resulting
-link in the kill ring."
- (interactive "r")
- (let* ((url-request-method "POST")
- (url-request-extra-headers
- '(("Content-Type" . "application/x-www-form-urlencoded")))
- (url-request-data
- (encode-coding-string
- (buffer-substring-no-properties start end)
- 'utf-8))
- (content-buf (url-retrieve
- "http://play.golang.org/share"
- (lambda (arg)
- (cond
- ((equal :error (car arg))
- (signal 'go-play-error (cdr arg)))
- (t
- (re-search-forward "\n\n")
- (kill-new (format "http://play.golang.org/p/%s" (buffer-substring (point) (point-max))))
- (message "http://play.golang.org/p/%s" (buffer-substring (point) (point-max)))))))))))
-
-;;;###autoload
-(defun go-download-play (url)
- "Downloads a paste from the playground and inserts it in a Go
-buffer. Tries to look for a URL at point."
- (interactive (list (read-from-minibuffer "Playground URL: " (ffap-url-p (ffap-string-at-point 'url)))))
- (with-current-buffer
- (let ((url-request-method "GET") url-request-data url-request-extra-headers)
- (url-retrieve-synchronously (concat url ".go")))
- (let ((buffer (generate-new-buffer (concat (car (last (split-string url "/"))) ".go"))))
- (goto-char (point-min))
- (re-search-forward "\n\n")
- (copy-to-buffer buffer (point) (point-max))
- (kill-buffer)
- (with-current-buffer buffer
- (go-mode)
- (switch-to-buffer buffer)))))
-
-(defun go-propertize-syntax (start end)
- (save-excursion
- (goto-char start)
- (while (search-forward "\\" end t)
- (put-text-property (1- (point)) (point) 'syntax-table (if (= (char-after) ?`) '(1) '(9))))))
-
-(defun go-import-add (arg import)
- "Add a new import to the list of imports.
-
-When called with a prefix argument asks for an alternative name
-to import the package as.
-
-If no list exists yet, one will be created if possible.
-
-If an identical import has been commented, it will be
-uncommented, otherwise a new import will be added."
-
- ;; - If there's a matching `// import "foo"`, uncomment it
- ;; - If we're in an import() block and there's a matching `"foo"`, uncomment it
- ;; - Otherwise add a new import, with the appropriate syntax
- (interactive
- (list
- current-prefix-arg
- (replace-regexp-in-string "^[\"']\\|[\"']$" "" (completing-read "Package: " (go--old-completion-list-style (go-packages))))))
- (save-excursion
- (let (as line import-start)
- (if arg
- (setq as (read-from-minibuffer "Import as: ")))
- (if as
- (setq line (format "%s \"%s\"" as import))
- (setq line (format "\"%s\"" import)))
-
- (goto-char (point-min))
- (if (re-search-forward (concat "^[[:space:]]*//[[:space:]]*import " line "$") nil t)
- (uncomment-region (line-beginning-position) (line-end-position))
- (case (go-goto-imports)
- ('fail (message "Could not find a place to add import."))
- ('block-empty
- (insert "\n\t" line "\n"))
- ('block
- (save-excursion
- (re-search-backward "^import (")
- (setq import-start (point)))
- (if (re-search-backward (concat "^[[:space:]]*//[[:space:]]*" line "$") import-start t)
- (uncomment-region (line-beginning-position) (line-end-position))
- (insert "\n\t" line)))
- ('single (insert "import " line "\n"))
- ('none (insert "\nimport (\n\t" line "\n)\n")))))))
-
-(defun go-root-and-paths ()
- (let* ((output (split-string (shell-command-to-string (concat go-command " env GOROOT GOPATH"))
- "\n"))
- (root (car output))
- (paths (split-string (cadr output) ":")))
- (append (list root) paths)))
-
-(defun go--string-prefix-p (s1 s2 &optional ignore-case)
- "Return non-nil if S1 is a prefix of S2.
-If IGNORE-CASE is non-nil, the comparison is case-insensitive."
- (eq t (compare-strings s1 nil nil
- s2 0 (length s1) ignore-case)))
-
-(defun go--directory-dirs (dir)
- "Recursively return all subdirectories in DIR."
- (if (file-directory-p dir)
- (let ((dir (directory-file-name dir))
- (dirs '())
- (files (directory-files dir nil nil t)))
- (dolist (file files)
- (unless (member file '("." ".."))
- (let ((file (concat dir "/" file)))
- (if (file-directory-p file)
- (setq dirs (append (cons file
- (go--directory-dirs file))
- dirs))))))
- dirs)
- '()))
-
-
-(defun go-packages ()
- (sort
- (delete-dups
- (mapcan
- (lambda (topdir)
- (let ((pkgdir (concat topdir "/pkg/")))
- (mapcan (lambda (dir)
- (mapcar (lambda (file)
- (let ((sub (substring file (length pkgdir) -2)))
- (unless (or (go--string-prefix-p "obj/" sub) (go--string-prefix-p "tool/" sub))
- (mapconcat #'identity (cdr (split-string sub "/")) "/"))))
- (if (file-directory-p dir)
- (directory-files dir t "\\.a$"))))
- (if (file-directory-p pkgdir)
- (go--directory-dirs pkgdir)))))
- (go-root-and-paths)))
- #'string<))
-
-(defun go-unused-imports-lines ()
- ;; FIXME Technically, -o /dev/null fails in quite some cases (on
- ;; Windows, when compiling from within GOPATH). Practically,
- ;; however, it has the same end result: There won't be a
- ;; compiled binary/archive, and we'll get our import errors when
- ;; there are any.
- (reverse (remove nil
- (mapcar
- (lambda (line)
- (if (string-match "^\\(.+\\):\\([[:digit:]]+\\): imported and not used: \".+\".*$" line)
- (if (string= (file-truename (match-string 1 line)) (file-truename buffer-file-name))
- (string-to-number (match-string 2 line)))))
- (split-string (shell-command-to-string
- (concat go-command
- (if (string-match "_test\.go$" buffer-file-truename)
- " test -c"
- " build -o /dev/null"))) "\n")))))
-
-(defun go-remove-unused-imports (arg)
- "Removes all unused imports. If ARG is non-nil, unused imports
-will be commented, otherwise they will be removed completely."
- (interactive "P")
- (save-excursion
- (let ((cur-buffer (current-buffer)) flymake-state lines)
- (when (boundp 'flymake-mode)
- (setq flymake-state flymake-mode)
- (flymake-mode-off))
- (save-some-buffers nil (lambda () (equal cur-buffer (current-buffer))))
- (if (buffer-modified-p)
- (message "Cannot operate on unsaved buffer")
- (setq lines (go-unused-imports-lines))
- (dolist (import lines)
- (go--goto-line import)
- (beginning-of-line)
- (if arg
- (comment-region (line-beginning-position) (line-end-position))
- (go--delete-whole-line)))
- (message "Removed %d imports" (length lines)))
- (if flymake-state (flymake-mode-on)))))
-
-(defun godef--find-file-line-column (specifier other-window)
- "Given a file name in the format of `filename:line:column',
-visit FILENAME and go to line LINE and column COLUMN."
- (if (not (string-match "\\(.+\\):\\([0-9]+\\):\\([0-9]+\\)" specifier))
- ;; We've only been given a directory name
- (funcall (if other-window #'find-file-other-window #'find-file) specifier)
- (let ((filename (match-string 1 specifier))
- (line (string-to-number (match-string 2 specifier)))
- (column (string-to-number (match-string 3 specifier))))
- (with-current-buffer (funcall (if other-window #'find-file-other-window #'find-file) filename)
- (go--goto-line line)
- (beginning-of-line)
- (forward-char (1- column))
- (if (buffer-modified-p)
- (message "Buffer is modified, file position might not have been correct"))))))
-
-(defun godef--call (point)
- "Call godef, acquiring definition position and expression
-description at POINT."
- (if (go--xemacs-p)
- (error "godef does not reliably work in XEmacs, expect bad results"))
- (if (not (buffer-file-name (go--coverage-origin-buffer)))
- (error "Cannot use godef on a buffer without a file name")
- (let ((outbuf (get-buffer-create "*godef*")))
- (with-current-buffer outbuf
- (erase-buffer))
- (call-process-region (point-min)
- (point-max)
- "godef"
- nil
- outbuf
- nil
- "-i"
- "-t"
- "-f"
- (file-truename (buffer-file-name (go--coverage-origin-buffer)))
- "-o"
- (number-to-string (go--position-bytes point)))
- (with-current-buffer outbuf
- (split-string (buffer-substring-no-properties (point-min) (point-max)) "\n")))))
-
-(defun godef-describe (point)
- "Describe the expression at POINT."
- (interactive "d")
- (condition-case nil
- (let ((description (cdr (butlast (godef--call point) 1))))
- (if (not description)
- (message "No description found for expression at point")
- (message "%s" (mapconcat #'identity description "\n"))))
- (file-error (message "Could not run godef binary"))))
-
-(defun godef-jump (point &optional other-window)
- "Jump to the definition of the expression at POINT."
- (interactive "d")
- (condition-case nil
- (let ((file (car (godef--call point))))
- (cond
- ((string= "-" file)
- (message "godef: expression is not defined anywhere"))
- ((string= "godef: no identifier found" file)
- (message "%s" file))
- ((go--string-prefix-p "godef: no declaration found for " file)
- (message "%s" file))
- ((go--string-prefix-p "error finding import path for " file)
- (message "%s" file))
- (t
- (push-mark)
- (ring-insert find-tag-marker-ring (point-marker))
- (godef--find-file-line-column file other-window))))
- (file-error (message "Could not run godef binary"))))
-
-(defun godef-jump-other-window (point)
- (interactive "d")
- (godef-jump point t))
-
-(defun go--goto-line (line)
- (goto-char (point-min))
- (forward-line (1- line)))
-
-(defun go--line-column-to-point (line column)
- (save-excursion
- (go--goto-line line)
- (forward-char (1- column))
- (point)))
-
-(defstruct go--covered
- start-line start-column end-line end-column covered count)
-
-(defun go--coverage-file ()
- "Return the coverage file to use, either by reading it from the
-current coverage buffer or by prompting for it."
- (if (boundp 'go--coverage-current-file-name)
- go--coverage-current-file-name
- (read-file-name "Coverage file: " nil nil t)))
-
-(defun go--coverage-origin-buffer ()
- "Return the buffer to base the coverage on."
- (or (buffer-base-buffer) (current-buffer)))
-
-(defun go--coverage-face (count divisor)
- "Return the intensity face for COUNT when using DIVISOR
-to scale it to a range [0,10].
-
-DIVISOR scales the absolute cover count to values from 0 to 10.
-For DIVISOR = 0 the count will always translate to 8."
- (let* ((norm (cond
- ((= count 0)
- -0.1) ;; Uncovered code, set to -0.1 so n becomes 0.
- ((= divisor 0)
- 0.8) ;; covermode=set, set to 0.8 so n becomes 8.
- (t
- (/ (log count) divisor))))
- (n (1+ (floor (* norm 9))))) ;; Convert normalized count [0,1] to intensity [0,10]
- (concat "go-coverage-" (number-to-string n))))
-
-(defun go--coverage-make-overlay (range divisor)
- "Create a coverage overlay for a RANGE of covered/uncovered
-code. Uses DIVISOR to scale absolute counts to a [0,10] scale."
- (let* ((count (go--covered-count range))
- (face (go--coverage-face count divisor))
- (ov (make-overlay (go--line-column-to-point (go--covered-start-line range)
- (go--covered-start-column range))
- (go--line-column-to-point (go--covered-end-line range)
- (go--covered-end-column range)))))
-
- (overlay-put ov 'face face)
- (overlay-put ov 'help-echo (format "Count: %d" count))))
-
-(defun go--coverage-clear-overlays ()
- "Remove existing overlays and put a single untracked overlay
-over the entire buffer."
- (remove-overlays)
- (overlay-put (make-overlay (point-min) (point-max))
- 'face
- 'go-coverage-untracked))
-
-(defun go--coverage-parse-file (coverage-file file-name)
- "Parse COVERAGE-FILE and extract coverage information and
-divisor for FILE-NAME."
- (let (ranges
- (max-count 0))
- (with-temp-buffer
- (insert-file-contents coverage-file)
- (go--goto-line 2) ;; Skip over mode
- (while (not (eobp))
- (let* ((parts (split-string (buffer-substring (point-at-bol) (point-at-eol)) ":"))
- (file (car parts))
- (rest (split-string (nth 1 parts) "[., ]")))
-
- (destructuring-bind
- (start-line start-column end-line end-column num count)
- (mapcar #'string-to-number rest)
-
- (when (string= (file-name-nondirectory file) file-name)
- (if (> count max-count)
- (setq max-count count))
- (push (make-go--covered :start-line start-line
- :start-column start-column
- :end-line end-line
- :end-column end-column
- :covered (/= count 0)
- :count count)
- ranges)))
-
- (forward-line)))
-
- (list ranges (if (> max-count 0) (log max-count) 0)))))
-
-(defun go-coverage (&optional coverage-file)
- "Open a clone of the current buffer and overlay it with
-coverage information gathered via go test -coverprofile=COVERAGE-FILE.
-
-If COVERAGE-FILE is nil, it will either be inferred from the
-current buffer if it's already a coverage buffer, or be prompted
-for."
- (interactive)
- (let* ((cur-buffer (current-buffer))
- (origin-buffer (go--coverage-origin-buffer))
- (gocov-buffer-name (concat (buffer-name origin-buffer) "<gocov>"))
- (coverage-file (or coverage-file (go--coverage-file)))
- (ranges-and-divisor (go--coverage-parse-file
- coverage-file
- (file-name-nondirectory (buffer-file-name origin-buffer))))
- (cov-mtime (nth 5 (file-attributes coverage-file)))
- (cur-mtime (nth 5 (file-attributes (buffer-file-name origin-buffer)))))
-
- (if (< (float-time cov-mtime) (float-time cur-mtime))
- (message "Coverage file is older than the source file."))
-
- (with-current-buffer (or (get-buffer gocov-buffer-name)
- (make-indirect-buffer origin-buffer gocov-buffer-name t))
- (set (make-local-variable 'go--coverage-current-file-name) coverage-file)
-
- (save-excursion
- (go--coverage-clear-overlays)
- (dolist (range (car ranges-and-divisor))
- (go--coverage-make-overlay range (cadr ranges-and-divisor))))
-
- (if (not (eq cur-buffer (current-buffer)))
- (display-buffer (current-buffer) #'display-buffer-reuse-window)))))
-
-(provide 'go-mode)
diff --git a/misc/fraise/go.plist b/misc/fraise/go.plist
deleted file mode 100644
index 17f416221..000000000
--- a/misc/fraise/go.plist
+++ /dev/null
@@ -1,92 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
-<plist version="1.0">
-<dict>
- <key>beginCommand</key>
- <string></string>
- <key>endCommand</key>
- <string></string>
- <key>beginInstruction</key>
- <string></string>
- <key>endInstruction</key>
- <string></string>
- <key>beginVariable</key>
- <string></string>
- <key>endVariable</key>
- <string></string>
- <key>firstString</key>
- <string>&quot;</string>
- <key>secondString</key>
- <string>&apos;</string>
- <key>firstSingleLineComment</key>
- <string>//</string>
- <key>secondSingleLineComment</key>
- <string></string>
- <key>beginFirstMultiLineComment</key>
- <string>/*</string>
- <key>endFirstMultiLineComment</key>
- <string>*/</string>
- <key>beginSecondMultiLineComment</key>
- <string></string>
- <key>endSecondMultiLineComment</key>
- <string></string>
- <key>functionDefinition</key>
- <string>^func\s*.*\(.*\)\s?\{</string>
- <key>removeFromFunction</key>
- <string></string>
- <key>keywordsCaseSensitive</key>
- <true/>
- <key>recolourKeywordIfAlreadyColoured</key>
- <true/>
- <key>keywords</key>
- <array>
- <string>break</string>
- <string>case</string>
- <string>chan</string>
- <string>const</string>
- <string>continue</string>
- <string>default</string>
- <string>defer</string>
- <string>else</string>
- <string>fallthrough</string>
- <string>for</string>
- <string>func</string>
- <string>go</string>
- <string>goto</string>
- <string>if</string>
- <string>import</string>
- <string>interface</string>
- <string>map</string>
- <string>package</string>
- <string>range</string>
- <string>return</string>
- <string>select</string>
- <string>struct</string>
- <string>switch</string>
- <string>type</string>
- <string>var</string>
- <string>bool</string>
- <string>byte</string>
- <string>chan</string>
- <string>complex64</string>
- <string>complex128</string>
- <string>float32</string>
- <string>float64</string>
- <string>int</string>
- <string>int8</string>
- <string>int16</string>
- <string>int32</string>
- <string>int64</string>
- <string>map</string>
- <string>string</string>
- <string>uint</string>
- <string>uintptr</string>
- <string>uint8</string>
- <string>uint16</string>
- <string>uint32</string>
- <string>uint64</string>
- </array>
- <key>autocompleteWords</key>
- <array/>
-</dict>
-</plist>
diff --git a/misc/fraise/readme.txt b/misc/fraise/readme.txt
deleted file mode 100644
index fb0f2c8c1..000000000
--- a/misc/fraise/readme.txt
+++ /dev/null
@@ -1,16 +0,0 @@
-##Instructions for enabling Go syntax highlighting in Fraise.app##
-1. Move go.plist to /Applications/Fraise.app/Contents/Resources/Syntax\ Definitions/
-2. Open /Applications/Fraise.app/Contents/Resources/SyntaxDefinitions.plist and add
-
- <dict>
- <key>name</key>
- <string>GoogleGo</string>
- <key>file</key>
- <string>go</string>
- <key>extensions</key>
- <string>go</string>
- </dict>
-
-before </array>
-
-3. Restart Fraise and you're good to Go! \ No newline at end of file
diff --git a/misc/kate/go.xml b/misc/kate/go.xml
deleted file mode 100644
index 03f4f2a0c..000000000
--- a/misc/kate/go.xml
+++ /dev/null
@@ -1,150 +0,0 @@
-<?xml version="1.0" encoding="UTF-8"?>
-<!DOCTYPE language SYSTEM "language.dtd">
-<language name="Go" section="Sources"
- version="1.00" kateversion="2.4"
- indenter="cstyle"
- extensions="*.go"
- mimetype=""
- priority="5"
- author="The Go Authors"
- license="BSD">
- <highlighting>
- <list name="keywords">
- <item> break </item>
- <item> case </item>
- <item> chan </item>
- <item> const </item>
- <item> continue </item>
- <item> default </item>
- <item> defer </item>
- <item> else </item>
- <item> fallthrough </item>
- <item> for </item>
- <item> func </item>
- <item> go </item>
- <item> goto </item>
- <item> if </item>
- <item> import </item>
- <item> interface </item>
- <item> map </item>
- <item> package </item>
- <item> range </item>
- <item> return </item>
- <item> select </item>
- <item> struct </item>
- <item> switch </item>
- <item> type </item>
- <item> var </item>
- </list>
- <list name="predeclared">
- <item> false </item>
- <item> iota </item>
- <item> nil </item>
- <item> true </item>
- </list>
- <list name="types">
- <item> bool </item>
- <item> byte </item>
- <item> complex64 </item>
- <item> complex128 </item>
- <item> error </item>
- <item> float32 </item>
- <item> float64 </item>
- <item> int </item>
- <item> int8 </item>
- <item> int16 </item>
- <item> int32 </item>
- <item> int64 </item>
- <item> rune </item>
- <item> string </item>
- <item> uint </item>
- <item> uintptr </item>
- <item> uint8 </item>
- <item> uint16 </item>
- <item> uint32 </item>
- <item> uint64 </item>
- </list>
- <list name="functions">
- <item> append </item>
- <item> cap </item>
- <item> close </item>
- <item> complex </item>
- <item> copy </item>
- <item> delete </item>
- <item> imag </item>
- <item> len </item>
- <item> make </item>
- <item> new </item>
- <item> panic </item>
- <item> print </item>
- <item> println </item>
- <item> real </item>
- <item> recover </item>
- </list>
- <contexts>
- <context attribute="Normal Text" lineEndContext="#stay" name="Normal">
- <DetectSpaces />
- <keyword attribute="Keyword" context="#stay" String="keywords"/>
- <keyword attribute="Predeclared Identifier" context="#stay"
- String="predeclared"/>
- <keyword attribute="Data Type" context="#stay" String="types"/>
- <keyword attribute="Builtin Function" context="#stay" String="functions"/>
- <DetectIdentifier />
- <DetectChar attribute="Symbol" context="#stay" char="{" beginRegion="Brace1" />
- <DetectChar attribute="Symbol" context="#stay" char="}" endRegion="Brace1" />
- <HlCOct attribute="Octal" context="#stay"/>
- <HlCHex attribute="Hex" context="#stay"/>
- <HlCChar attribute="Char" context="#stay"/>
- <DetectChar attribute="String" context="String" char="&quot;"/>
- <DetectChar attribute="String" context="Multiline String" char="`"/>
- <Detect2Chars attribute="Comment" context="Comment 1" char="/" char1="/"/>
- <Detect2Chars attribute="Comment" context="Comment 2" char="/" char1="*" beginRegion="Comment"/>
- <AnyChar attribute="Symbol" context="#stay" String=":!%&amp;()+,-/.*&lt;=&gt;?[]|~^&#59;"/>
- </context>
-
- <context attribute="String" lineEndContext="#pop" name="String">
- <LineContinue attribute="String" context="#stay"/>
- <HlCStringChar attribute="String Char" context="#stay"/>
- <DetectChar attribute="String" context="#pop" char="&quot;"/>
- </context>
-
- <context attribute="String" lineEndContext="#stay" name="Multiline String">
- <LineContinue attribute="String" context="#stay"/>
- <HlCStringChar attribute="String Char" context="#stay"/>
- <DetectChar attribute="String" context="#pop" char="`"/>
- </context>
-
- <context attribute="Comment" lineEndContext="#pop" name="Comment 1">
- <LineContinue attribute="Comment" context="#stay"/>
- </context>
-
- <context attribute="Comment" lineEndContext="#stay" name="Comment 2">
- <Detect2Chars attribute="Comment" context="#pop" char="*" char1="/" endRegion="Comment"/>
- </context>
- </contexts>
- <itemDatas>
- <itemData name="Normal Text" defStyleNum="dsNormal" spellChecking="false"/>
- <itemData name="Keyword" defStyleNum="dsKeyword" spellChecking="false"/>
- <itemData name="Predeclared Identifier" defStyleNum="dsOthers" spellChecking="false"/>
- <itemData name="Builtin Function" defStyleNum="dsFunction" spellChecking="false"/>
- <itemData name="Data Type" defStyleNum="dsDataType" spellChecking="false"/>
- <itemData name="Decimal" defStyleNum="dsDecVal" spellChecking="false"/>
- <itemData name="Octal" defStyleNum="dsBaseN" spellChecking="false"/>
- <itemData name="Hex" defStyleNum="dsBaseN" spellChecking="false"/>
- <itemData name="Float" defStyleNum="dsFloat" spellChecking="false"/>
- <itemData name="Char" defStyleNum="dsChar" spellChecking="false"/>
- <itemData name="String" defStyleNum="dsString"/>
- <itemData name="String Char" defStyleNum="dsChar"/>
- <itemData name="Comment" defStyleNum="dsComment"/>
- <itemData name="Symbol" defStyleNum="dsNormal" spellChecking="false"/>
- <itemData name="Error" defStyleNum="dsError" spellChecking="false"/>
- </itemDatas>
- </highlighting>
- <general>
- <comments>
- <comment name="singleLine" start="//" />
- <comment name="multiLine" start="/*" end="*/" />
- </comments>
- <keywords casesensitive="1" additionalDeliminator="'&quot;" />
- </general>
-</language>
diff --git a/misc/makerelease/makerelease.go b/misc/makerelease/makerelease.go
index 2496a865a..3b511b1db 100644
--- a/misc/makerelease/makerelease.go
+++ b/misc/makerelease/makerelease.go
@@ -14,6 +14,7 @@ import (
"compress/gzip"
"crypto/sha1"
"encoding/json"
+ "errors"
"flag"
"fmt"
"io"
@@ -30,7 +31,7 @@ import (
"strings"
"code.google.com/p/goauth2/oauth"
- "code.google.com/p/google-api-go-client/storage/v1beta2"
+ storage "code.google.com/p/google-api-go-client/storage/v1"
)
var (
@@ -53,20 +54,20 @@ var (
)
const (
- blogPath = "code.google.com/p/go.blog"
- toolPath = "code.google.com/p/go.tools"
+ blogPath = "golang.org/x/blog"
+ toolPath = "golang.org/x/tools"
tourPath = "code.google.com/p/go-tour"
- defaultToolTag = "release-branch.go1.2"
- defaultTourTag = "release-branch.go1.2"
+ defaultToolTag = "release-branch.go1.4"
+ defaultTourTag = "release-branch.go1.4"
)
// Import paths for tool commands.
// These must be the command that cmd/go knows to install to $GOROOT/bin
// or $GOROOT/pkg/tool.
var toolPaths = []string{
- "code.google.com/p/go.tools/cmd/cover",
- "code.google.com/p/go.tools/cmd/godoc",
- "code.google.com/p/go.tools/cmd/vet",
+ "golang.org/x/tools/cmd/cover",
+ "golang.org/x/tools/cmd/godoc",
+ "golang.org/x/tools/cmd/vet",
}
var preBuildCleanFiles = []string{
@@ -74,8 +75,8 @@ var preBuildCleanFiles = []string{
"misc/dashboard/godashboard",
"src/cmd/cov",
"src/cmd/prof",
- "src/pkg/exp",
- "src/pkg/old",
+ "src/exp",
+ "src/old",
}
var cleanFiles = []string{
@@ -154,6 +155,7 @@ func main() {
log.Fatalln("setupOAuthClient:", err)
}
}
+ ok := true
for _, targ := range flag.Args() {
var b Build
if m := fileRe.FindStringSubmatch(targ); m != nil {
@@ -205,8 +207,12 @@ func main() {
}
if err := b.Do(); err != nil {
log.Printf("%s: %v", targ, err)
+ ok = false
}
}
+ if !ok {
+ os.Exit(1)
+ }
}
type Build struct {
@@ -432,7 +438,8 @@ func (b *Build) Do() error {
// Build package.
_, err = b.run(work, "candle",
"-nologo",
- "-dVersion="+version,
+ "-dGoVersion="+version,
+ "-dWixGoVersion="+wixVersion(version),
"-dArch="+b.Arch,
"-dSourceDir=go",
installer, appfiles)
@@ -466,6 +473,22 @@ func (b *Build) Do() error {
return err
}
+var versionRe = regexp.MustCompile(`^go([0-9]+(\.[0-9]+)*)`)
+
+// The Microsoft installer requires version format major.minor.build
+// (http://msdn.microsoft.com/en-us/library/aa370859%28v=vs.85%29.aspx).
+// Where the major and minor field has a maximum value of 255 and build 65535.
+// The offical Go version format is goMAJOR.MINOR.PATCH at $GOROOT/VERSION.
+// It's based on the Mercurial tag. Remove prefix and suffix to make the
+// installer happy.
+func wixVersion(v string) string {
+ m := versionRe.FindStringSubmatch(v)
+ if m == nil {
+ return "0.0.0"
+ }
+ return m[1]
+}
+
// extras fetches the go.tools, go.blog, and go-tour repositories,
// builds them and copies the resulting binaries and static assets
// to the new GOROOT.
@@ -482,16 +505,38 @@ func (b *Build) extras() error {
}
func (b *Build) get(repoPath, revision string) error {
- // Fetch the packages (without building/installing).
- _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"),
- "get", "-d", repoPath+"/...")
- if err != nil {
- return err
+ dest := filepath.Join(b.gopath, "src", filepath.FromSlash(repoPath))
+
+ if strings.HasPrefix(repoPath, "golang.org/x/") {
+ // For sub-repos, fetch the old Mercurial repo; bypass "go get".
+ // DO NOT import this special case into the git tree.
+
+ if err := os.MkdirAll(filepath.Dir(dest), 0755); err != nil {
+ return err
+ }
+ repo := strings.Replace(repoPath, "golang.org/x/", "https://code.google.com/p/go.", 1)
+ if _, err := b.run(b.gopath, "hg", "clone", repo, dest); err != nil {
+ return err
+ }
+ } else {
+ // Fetch the packages (without building/installing).
+ _, err := b.run(b.gopath, filepath.Join(b.root, "bin", "go"),
+ "get", "-d", repoPath+"/...")
+ if err != nil {
+ return err
+ }
}
// Update the repo to the specified revision.
- p := filepath.Join(b.gopath, "src", filepath.FromSlash(repoPath))
- _, err = b.run(p, "hg", "update", revision)
+ var err error
+ switch {
+ case exists(filepath.Join(dest, ".git")):
+ _, err = b.run(dest, "git", "checkout", revision)
+ case exists(filepath.Join(dest, ".hg")):
+ _, err = b.run(dest, "hg", "update", revision)
+ default:
+ err = errors.New("unknown version control system")
+ }
return err
}
@@ -721,7 +766,7 @@ type File struct {
func setupOAuthClient() error {
config := &oauth.Config{
ClientId: "999119582588-h7kpj5pcm6d9solh5lgrbusmvvk4m9dn.apps.googleusercontent.com",
- ClientSecret: "8YLFgOhXIELWbO",
+ ClientSecret: "8YLFgOhXIELWbO-NtF3iqIQz",
Scope: storage.DevstorageRead_writeScope,
AuthURL: "https://accounts.google.com/o/oauth2/auth",
TokenURL: "https://accounts.google.com/o/oauth2/token",
diff --git a/misc/makerelease/windows/installer.wxs b/misc/makerelease/windows/installer.wxs
index b170b98dc..01178e265 100644
--- a/misc/makerelease/windows/installer.wxs
+++ b/misc/makerelease/windows/installer.wxs
@@ -18,13 +18,12 @@
<Product
Id="FF5B30B2-08C2-11E1-85A2-6ACA4824019B"
- Name="Go Programming Language $(var.Arch) $(var.Version)"
+ Name="Go Programming Language $(var.Arch) $(var.GoVersion)"
Language="1033"
Codepage="1252"
- Version="0.0.0.0"
+ Version="$(var.WixGoVersion)"
Manufacturer="http://golang.org"
UpgradeCode="$(var.UpgradeCode)" >
- <!-- Version="$(var.Version)" TODO: Version requires X.X.X.X format -->
<Package
Id='*'
@@ -40,9 +39,9 @@
<Property Id="ARPCOMMENTS" Value="The Go programming language is a fast, statically typed, compiled language that feels like a dynamically typed, interpreted language." />
<Property Id="ARPCONTACT" Value="golang-nuts@googlegroups.com" />
-<Property Id="ARPHELPLINK" Value="golang.org/doc/community.html" />
-<Property Id="ARPREADME" Value="golang.org" />
-<Property Id="ARPURLINFOABOUT" Value="golang.org" />
+<Property Id="ARPHELPLINK" Value="https://golang.org/help/" />
+<Property Id="ARPREADME" Value="https://golang.org" />
+<Property Id="ARPURLINFOABOUT" Value="https://golang.org" />
<Property Id="LicenseAccepted">1</Property>
<Icon Id="gopher.ico" SourceFile="images\gopher.ico"/>
<Property Id="ARPPRODUCTICON" Value="gopher.ico" />
diff --git a/misc/nacl/README b/misc/nacl/README
index 9cc2bda60..72d0e08ad 100644
--- a/misc/nacl/README
+++ b/misc/nacl/README
@@ -1,63 +1,122 @@
Native Client
=============
-This document outlines the basics of building and developing the Go runtime and programs in the Native Client (NaCl) environment.
+This document outlines the basics of building and developing the Go runtime and
+programs in the Native Client (NaCl) environment.
-Go 1.3 supports two architectures
+Go 1.3 supports three architectures
* nacl/386 which is standard 386.
- * nacl/amd64p32 which is a 64 bit architecture, where the address space is limited to a 4gb window.
+ * nacl/amd64p32 which is a 64 bit architecture, where the address space is
+ limited to a 4gb window.
+ * nacl/arm which is 32-bit ARMv7A architecture with 1GB address space.
For background it is recommended that you read http://golang.org/s/go13nacl.
Prerequisites
-------------
-Native Client programs are executed inside a sandbox, the NaCl runtime. This runtime must be installed before you can use NaCl programs.
+Native Client programs are executed inside a sandbox, the NaCl runtime. This
+runtime must be installed before you can use NaCl programs.
-The NaCl distribution comes with an installer which ensures you have access to the latest version of the runtime. The version tracks the Chrome numbering scheme.
+The NaCl distribution comes with an installer which ensures you have access to
+the latest version of the runtime. The version tracks the Chrome numbering
+scheme.
# Download NaCl
-Download nacl_sdk.zip file from https://developers.google.com/native-client/dev/sdk/download, and unpack it. I chose /opt/nacl_sdk
+Download nacl_sdk.zip file from
+ https://developers.google.com/native-client/dev/sdk/download
+and unpack it. I chose /opt/nacl_sdk.
# Update
-The zip file contains a small skeleton that can be used to download the correct sdk. These are released every 6-8 weeks, in line with Chrome releases.
+The zip file contains a small skeleton that can be used to download the correct
+sdk. These are released every 6-8 weeks, in line with Chrome releases.
% cd /opt/nacl_sdk
% ./naclsdk update
-At this time pepper_33 is the stable version. If naclsdk downloads a later version, please adjust accordingly.
+At this time pepper_34 is the stable version. If naclsdk downloads a later
+version, please adjust accordingly. As of June 2014, only the canary sdk
+provides support for nacl/arm.
-The cmd/go helper scripts expect that the runtime loaders, sel_ldr_x86_{32,64} are in your path. I find it easiest to make a symlink from the NaCl distribution to my $GOPATH/bin directory.
+The cmd/go helper scripts expect that the loaders sel_ldr_{x86_{32,64},arm} and
+nacl_helper_bootstrap_arm are in your path. I find it easiest to make a symlink
+from the NaCl distribution to my $GOPATH/bin directory.
- % ln -nfs /opt/nacl_sdk/pepper_33/tools/sel_ldr_x86_32 $GOPATH/bin/sel_ldr_x86_32
- % ln -nfs /opt/nacl_sdk/pepper_33/tools/sel_ldr_x86_64 $GOPATH/bin/sel_ldr_x86_64
+ % ln -nfs /opt/nacl_sdk/pepper_34/tools/sel_ldr_x86_32 $GOPATH/bin/sel_ldr_x86_32
+ % ln -nfs /opt/nacl_sdk/pepper_34/tools/sel_ldr_x86_64 $GOPATH/bin/sel_ldr_x86_64
+ % ln -nfs /opt/nacl_sdk/pepper_canary/tools/sel_ldr_arm $GOPATH/bin/sel_ldr_arm
+
+Additionally, for NaCl/ARM only:
+
+ % ln -nfs /opt/nacl_sdk/pepper_canary/tools/nacl_helper_bootstrap_arm $GOPATH/bin/nacl_helper_bootstrap_arm
Support scripts
---------------
-Symlink the two scripts in this directory into your $PATH, just as you did with NaCl sdk above.
+Symlink the two scripts in this directory into your $PATH, just as you did with
+NaCl sdk above.
- % ln -nfs $GOROOT/go/misc/nacl/go_nacl_amd64p32_exec $GOPATH/bin/go_nacl_amd64p32_exec
- % ln -nfs $GOROOT/go/misc/nacl/go_nacl_386_exec $GOPATH/bin/go_nacl_386_exec
+ % ln -nfs $GOROOT/misc/nacl/go_nacl_amd64p32_exec $GOPATH/bin/go_nacl_amd64p32_exec
+ % ln -nfs $GOROOT/misc/nacl/go_nacl_386_exec $GOPATH/bin/go_nacl_386_exec
+ % ln -nfs $GOROOT/misc/nacl/go_nacl_arm_exec $GOPATH/bin/go_nacl_arm_exec
Building and testing
--------------------
-Building for NaCl is similar to cross compiling for other platforms. However, as it is not possible to ever build in a `native` NaCl environment, the cmd/go tool has been enhanced to allow the full build, all.bash, to be executed, rather than just the compile stage, make.bash.
+Building for NaCl is similar to cross compiling for other platforms. However,
+as it is not possible to ever build in a `native` NaCl environment, the cmd/go
+tool has been enhanced to allow the full build, all.bash, to be executed,
+rather than just the compile stage, make.bash.
-The cmd/go tool knows that if GOOS is set to `nacl` it should not try to execute any binaries itself. Instead it passes their execution to a support script which sets up a Native Client environment and invokes the NaCl sandbox.
+The cmd/go tool knows that if GOOS is set to `nacl` it should not try to
+execute any binaries itself. Instead it passes their execution to a support
+script which sets up a Native Client environment and invokes the NaCl sandbox.
-The script's name has a special format, go_$GOOS_$GOARCH_exec, so cmd/go can find it.
+The script's name has a special format, go_$GOOS_$GOARCH_exec, so cmd/go can
+find it.
-In short, if the support scripts are in place, the cmd/go tool can be used as per normal.
+In short, if the support scripts are in place, the cmd/go tool can be used as
+per normal.
# Build and test Go for NaCl
-NaCl does not permit direct file system access. Instead, package syscall provides a simulated file system served by in-memory data. The script nacltest.bash is the NaCl equivalent of all.bash. It builds NaCl with an in-memory file system containing files needed for tests, and then it runs the tests.
+NaCl does not permit direct file system access. Instead, package syscall
+provides a simulated file system served by in-memory data. The script
+nacltest.bash is the NaCl equivalent of all.bash. It builds NaCl with an
+in-memory file system containing files needed for tests, and then it runs the
+tests.
% cd go/src
% env GOARCH=amd64p32 ./nacltest.bash
+Debugging
+---------
+
+Assuming that you have built nacl/amd64p32 binary ./mybin and can run as:
+
+ % sel_ldr_x86_64 -l /dev/null -S -e ./mybin
+
+Create the nacl manifest file mybin.manifest with the following contents:
+
+ { "program": { "x86-64": { "url": "mybin" } } }
+
+url is the path to the binary relative to the manifest file.
+Then, run the program as:
+
+ % sel_ldr_x86_64 -g -l /dev/null -S -e ./mybin
+
+The -g flag instructs the loader to stop at startup. Then, in another console:
+
+ % /opt/nacl_sdk/pepper_34/toolchain/linux_x86_glibc/bin/x86_64-nacl-gdb
+ % nacl-manifest mybin.manifest
+ % target remote :4014
+
+If you see that the program is stopped in _rt0_amd64p32_nacl, then symbols are
+loaded successfully and you can type 'c' to start the program.
+Next time you can automate it as:
+
+ % /opt/nacl_sdk/pepper_34/toolchain/linux_x86_glibc/bin/x86_64-nacl-gdb \
+ -ex 'nacl-manifest mybin.manifest' -ex 'target remote :4014'
diff --git a/misc/nacl/go_nacl_arm_exec b/misc/nacl/go_nacl_arm_exec
new file mode 100755
index 000000000..4275cb111
--- /dev/null
+++ b/misc/nacl/go_nacl_arm_exec
@@ -0,0 +1,10 @@
+#!/bin/bash
+
+eval $(go env)
+
+export NACLENV_GOARCH=$GOARCH
+export NACLENV_GOOS=$GOOS
+export NACLENV_GOROOT=/go
+export NACLENV_NACLPWD=$(pwd | sed "s;$GOROOT;/go;")
+
+exec nacl_helper_bootstrap_arm $(which sel_ldr_arm) --reserved_at_zero=0xXXXXXXXXXXXXXXXX -l /dev/null -S -e "$@"
diff --git a/misc/nacl/mkzip.go b/misc/nacl/mkzip.go
index 7b2de7d47..aaf37f120 100644
--- a/misc/nacl/mkzip.go
+++ b/misc/nacl/mkzip.go
@@ -71,7 +71,13 @@ func main() {
var w io.Writer = zf
if *gopackage != "" {
- fmt.Fprintf(zf, "package %s\n\nfunc init() {\n\tunzip(\"", *gopackage)
+ fmt.Fprintf(zf, `package %s
+import "sync"
+func init() {
+ var once sync.Once
+ fsinit = func() {
+ once.Do(func() {
+ unzip("`, *gopackage)
gw := &goWriter{b: bufio.NewWriter(w)}
defer func() {
if err := gw.Close(); err != nil {
@@ -214,7 +220,7 @@ func (w *goWriter) Write(b []byte) (int, error) {
}
func (w *goWriter) Close() error {
- fmt.Fprintf(w.b, "\")\n}\n")
+ fmt.Fprintf(w.b, "\")\n\t\t})\n\t}\n}")
w.b.Flush()
return nil
}
diff --git a/misc/nacl/testzip.proto b/misc/nacl/testzip.proto
index 2701ff463..07d4a1db0 100644
--- a/misc/nacl/testzip.proto
+++ b/misc/nacl/testzip.proto
@@ -10,101 +10,105 @@ usr src=../misc/nacl/testdata
go src=..
src
cmd
+ internal
+ objfile
+ objfile.go
gofmt
+ gofmt.go
+ gofmt_test.go
testdata
+
- pkg
- archive
- tar
- testdata
- +
- zip
- testdata
- +
- compress
- bzip2
- testdata
- +
- flate
- gzip
- testdata
- +
- lzw
- testdata
- +
- zlib
- crypto
- rsa
- testdata
- +
- tls
- testdata
- +
- debug
- dwarf
- testdata
- +
- elf
- testdata
- +
- macho
- testdata
- +
- pe
- testdata
- +
- plan9obj
- testdata
- +
- go
- build
- +
- doc
- testdata
- +
- format
- +
- parser
- +
- printer
- +
- image
- testdata
- +
- draw
- gif
- jpeg
- png
- testdata
- +
- io
+ archive
+ tar
+ testdata
+ +
+ zip
+ testdata
+ +
+ compress
+ bzip2
+ testdata
+ +
+ flate
+ gzip
+ testdata
+ +
+ lzw
+ testdata
+
- mime
+ zlib
+ crypto
+ rsa
+ testdata
+ +
+ tls
+ testdata
+ +
+ debug
+ dwarf
+ testdata
+ +
+ elf
+ testdata
+ +
+ macho
testdata
+
- multipart
- testdata
- +
- net
- http
+ pe
+ testdata
+
+ plan9obj
testdata
+
- os
+ go
+ build
+
- path
- filepath
+ doc
+ testdata
+
- regexp
+ format
+ +
+ parser
+ +
+ printer
+ +
+ image
+ testdata
+ +
+ draw
+ gif
+ jpeg
+ png
testdata
+
- strconv
+ io
+ +
+ mime
+ testdata
+ +
+ multipart
+ testdata
+ +
+ net
+ http
+ +
+ testdata
+ +
+ os
+ +
+ path
+ filepath
+ +
+ regexp
+ testdata
+ +
+ strconv
+ testdata
+ +
+ text
+ template
testdata
+
- text
- template
- testdata
- +
lib
time
zoneinfo.zip
diff --git a/misc/notepadplus/README b/misc/notepadplus/README
deleted file mode 100644
index fca4f5f09..000000000
--- a/misc/notepadplus/README
+++ /dev/null
@@ -1,59 +0,0 @@
-Notepad++ syntax highlighting
------------------------------
-
-The userDefineLang.xml uses the new User Defined Language system (UDL2),
-which needs Notepad++ v6.2 or higher.
-
-Installing from Notepad++ Installer
-
- 1. Add the contents of userDefineLang.xml at %APPDATA%\Notepad++\userDefineLang.xml
- between <NotepadPlus> ... </NotepadPlus>
- 2. Restart Notepad++
-
-Installing from Notepad++ zip/7z package
-
- 1. Given a Notepad++ installation at <DIR>.
- 2. Add the contents of userDefineLang.xml at <DIR>\userDefineLang.xml
- between <NotepadPlus> ... </NotepadPlus>
- 3. Restart Notepad++
-
-Reference
-
- 1. http://sourceforge.net/apps/mediawiki/notepad-plus/index.php?title=User_Defined_Languages
- 2. http://notepad-plus-plus.org/news/notepad-6.2-release-udl2.html
- 3. http://udl20.weebly.com/index.html
-
-
-Notepad++ keyword auto-completion
----------------------------------
-
- 1. Given a Notepad++ installation at <DIR>.
- 2. Copy go.xml to <DIR>\plugins\APIs
- 3. Restart Notepad++
-
-Reference
-
- 1. http://sourceforge.net/apps/mediawiki/notepad-plus/index.php?title=Auto_Completion
-
-Notepad++ Function List
------------------------
-
-The functionList.xml uses the Function List Pane new feature,
-which needs Notepad++ v6.4 or higher.
-
-Installing from Notepad++ Installer
-
- 1. Add the contents of userDefineLang.xml at %APPDATA%\Notepad++\functionList.xml
- between <associationMap> ... </associationMap> and <parsers> ... </parsers>
- 2. Restart Notepad++
-
-Installing from Notepad++ zip/7z package
-
- 1. Given a Notepad++ installation at <DIR>.
- 2. Add the contents of functionList.xml at <DIR>\functionList.xml
- between <associationMap> ... </associationMap> and <parsers> ... </parsers>
- 3. Restart Notepad++
-
-Reference
-
- 1. http://notepad-plus-plus.org/features/function-list.html
diff --git a/misc/notepadplus/functionList.xml b/misc/notepadplus/functionList.xml
deleted file mode 100644
index 7c605db4f..000000000
--- a/misc/notepadplus/functionList.xml
+++ /dev/null
@@ -1,50 +0,0 @@
-<!-- <NotepadPlus> -->
- <!-- <functionList> -->
- <!-- <associationMap> -->
-
- <!--
- if npp version == 6.4:
- <association ext=".go" id="go"/>
-
- if npp version >= 6.5:
- <association userDefinedLangName="go" id="go"/>
- -->
-
- <!-- </associationMap> -->
- <!-- <parsers> -->
- <parser id="go" displayName="Go" commentExpr="((/\*.*?\*)/|(//.*?$))">
- <classRange
- mainExpr="^type\s+\w+\s+interface\s*\{"
- openSymbole = "\{"
- closeSymbole = "\}"
- displayMode="node">
- <className>
- <nameExpr expr="type\s+\w+"/>
- <nameExpr expr="\s+\w+"/>
- <nameExpr expr="\w+"/>
- </className>
- <function
- mainExpr="^\s+\w+\s*\(">
- <functionName>
- <funcNameExpr expr="\w+"/>
- </functionName>
- </function>
- </classRange>
- <function
- mainExpr="(^func\s+\w+)|(^func\s*\(\s*\w*\s*\*?\s*\w+\s*\)\s*\w+)"
- displayMode="$className->$functionName">
- <functionName>
- <nameExpr expr="((func\s+\w+)|(\)\s*\w+))"/>
- <nameExpr expr="((\s+\w+)|(\)\s*\w+))"/>
- <nameExpr expr="\w+"/>
- </functionName>
- <className>
- <nameExpr expr="\w+\s*\)"/>
- <nameExpr expr="\w+"/>
- </className>
- </function>
- </parser>
-
- <!-- </parsers> -->
- <!-- </functionList> -->
-<!-- </NotepadPlus> -->
diff --git a/misc/notepadplus/go.xml b/misc/notepadplus/go.xml
deleted file mode 100644
index 59e4116d6..000000000
--- a/misc/notepadplus/go.xml
+++ /dev/null
@@ -1,133 +0,0 @@
-<NotepadPlus>
- <!-- Go Programming Language builtins and keywords -->
- <AutoComplete>
- <KeyWord name="append" func="yes">
- <Overload retVal="[]Type">
- <Param name="slice []Type" />
- <Param name="elems ...Type" />
- </Overload>
- </KeyWord>
- <KeyWord name="bool" />
- <KeyWord name="break" />
- <KeyWord name="byte" />
- <KeyWord name="cap" func="yes">
- <Overload retVal="int">
- <Param name="v Type" />
- </Overload>
- </KeyWord>
- <KeyWord name="case" />
- <KeyWord name="chan" />
- <KeyWord name="close" func="yes">
- <Overload retVal="">
- <Param name="c chan<- Type" />
- </Overload>
- </KeyWord>
- <KeyWord name="complex" func="yes">
- <Overload retVal="ComplexType">
- <Param name="r" />
- <Param name="i FloatType" />
- </Overload>
- </KeyWord>
- <KeyWord name="complex128" />
- <KeyWord name="complex64" />
- <KeyWord name="const" />
- <KeyWord name="continue" />
- <KeyWord name="copy" func="yes">
- <Overload retVal="int">
- <Param name="dst" />
- <Param name="src []Type" />
- </Overload>
- </KeyWord>
- <KeyWord name="default" />
- <KeyWord name="defer" />
- <KeyWord name="delete" func="yes">
- <Overload retVal="">
- <Param name="m map[Type]Type1" />
- <Param name="key Type" />
- </Overload>
- </KeyWord>
- <KeyWord name="else" />
- <KeyWord name="error" />
- <KeyWord name="fallthrough" />
- <KeyWord name="false" />
- <KeyWord name="float32" />
- <KeyWord name="float64" />
- <KeyWord name="for" />
- <KeyWord name="func" />
- <KeyWord name="go" />
- <KeyWord name="goto" />
- <KeyWord name="if" />
- <KeyWord name="iota" />
- <KeyWord name="imag" func="yes">
- <Overload retVal="FloatType">
- <Param name="c ComplexType" />
- </Overload>
- </KeyWord>
- <KeyWord name="import" />
- <KeyWord name="int" />
- <KeyWord name="int16" />
- <KeyWord name="int32" />
- <KeyWord name="int64" />
- <KeyWord name="int8" />
- <KeyWord name="interface" />
- <KeyWord name="len" func="yes">
- <Overload retVal="int">
- <Param name="v Type" />
- </Overload>
- </KeyWord>
- <KeyWord name="make" func="yes">
- <Overload retVal="Type">
- <Param name="Type" />
- <Param name="size IntegerType" />
- </Overload>
- </KeyWord>
- <KeyWord name="map" />
- <KeyWord name="new" func="yes">
- <Overload retVal="*Type">
- <Param name="Type" />
- </Overload>
- </KeyWord>
- <KeyWord name="nil" />
- <KeyWord name="package" />
- <KeyWord name="panic" func="yes">
- <Overload retVal="">
- <Param name="v interface{}" />
- </Overload>
- </KeyWord>
- <KeyWord name="print" func="yes">
- <Overload retVal="">
- <Param name="args ...Type" />
- </Overload>
- </KeyWord>
- <KeyWord name="println" func="yes">
- <Overload retVal="" >
- <Param name="args ...Type" />
- </Overload>
- </KeyWord>
- <KeyWord name="range" />
- <KeyWord name="real" func="yes">
- <Overload retVal="FloatType">
- <Param name="c ComplexType" />
- </Overload>
- </KeyWord>
- <KeyWord name="recover" func="yes">
- <Overload retVal="interface{}">
- </Overload>
- </KeyWord>
- <KeyWord name="return" />
- <KeyWord name="rune" />
- <KeyWord name="select" />
- <KeyWord name="string" />
- <KeyWord name="struct" />
- <KeyWord name="switch" />
- <KeyWord name="true" />
- <KeyWord name="type" />
- <KeyWord name="uint" />
- <KeyWord name="uint16" />
- <KeyWord name="uint32" />
- <KeyWord name="uint64" />
- <KeyWord name="uint8" />
- <KeyWord name="uintptr" />
- <KeyWord name="var" />
- </AutoComplete>
-</NotepadPlus>
diff --git a/misc/notepadplus/userDefineLang.xml b/misc/notepadplus/userDefineLang.xml
deleted file mode 100644
index 9751f7901..000000000
--- a/misc/notepadplus/userDefineLang.xml
+++ /dev/null
@@ -1,64 +0,0 @@
-<!-- <NotepadPlus> -->
- <UserLang name="go" ext="go" udlVersion="2.1">
- <Settings>
- <Global caseIgnored="no" allowFoldOfComments="yes" foldCompact="no" forcePureLC="0" decimalSeparator="0" />
- <Prefix Keywords1="no" Keywords2="no" Keywords3="no" Keywords4="no" Keywords5="no" Keywords6="no" Keywords7="no" Keywords8="no" />
- </Settings>
- <KeywordLists>
- <Keywords name="Comments">00// 01 02 03/* 04*/</Keywords>
- <Keywords name="Numbers, prefix1"></Keywords>
- <Keywords name="Numbers, prefix2">0x 0X</Keywords>
- <Keywords name="Numbers, extras1">a b c d e f A B C D E F</Keywords>
- <Keywords name="Numbers, extras2"></Keywords>
- <Keywords name="Numbers, suffix1"></Keywords>
- <Keywords name="Numbers, suffix2">i</Keywords>
- <Keywords name="Numbers, range"></Keywords>
- <Keywords name="Operators1">( ) [ ] { } ... , ; &amp; ^ % &gt; &lt; ! = + - * / | :</Keywords>
- <Keywords name="Operators2"></Keywords>
- <Keywords name="Folders in code1, open"></Keywords>
- <Keywords name="Folders in code1, middle"></Keywords>
- <Keywords name="Folders in code1, close"></Keywords>
- <Keywords name="Folders in code2, open"></Keywords>
- <Keywords name="Folders in code2, middle"></Keywords>
- <Keywords name="Folders in code2, close"></Keywords>
- <Keywords name="Folders in comment, open"></Keywords>
- <Keywords name="Folders in comment, middle"></Keywords>
- <Keywords name="Folders in comment, close"></Keywords>
- <Keywords name="Keywords1">break default func interface select case defer go map struct chan else goto package switch const fallthrough if range type continue for import return var</Keywords>
- <Keywords name="Keywords2">bool byte complex64 complex128 error float32 float64 int int8 int16 int32 int64 rune string uint uint8 uint16 uint32 uint64 uintptr true false iota nil</Keywords>
- <Keywords name="Keywords3">append cap close complex copy delete imag len make new panic print println real recover _</Keywords>
- <Keywords name="Keywords4"></Keywords>
- <Keywords name="Keywords5"></Keywords>
- <Keywords name="Keywords6"></Keywords>
- <Keywords name="Keywords7"></Keywords>
- <Keywords name="Keywords8"></Keywords>
- <Keywords name="Delimiters">00&quot; 01\ 02&quot; 03&apos; 04\ 05&apos; 06` 07 08` 09 10 11 12 13 14 15 16 17 18 19 20 21 22 23</Keywords>
- </KeywordLists>
- <Styles>
- <WordsStyle name="DEFAULT" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="COMMENTS" fgColor="008000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="LINE COMMENTS" fgColor="008000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="NUMBERS" fgColor="A52A2A" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="KEYWORDS1" fgColor="AA0000" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="0" />
- <WordsStyle name="KEYWORDS2" fgColor="AA0000" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="0" />
- <WordsStyle name="KEYWORDS3" fgColor="AA0000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="KEYWORDS4" fgColor="A00000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="KEYWORDS5" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="KEYWORDS6" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="KEYWORDS7" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="KEYWORDS8" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="OPERATORS" fgColor="8000FF" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="0" />
- <WordsStyle name="FOLDER IN CODE1" fgColor="8000FF" bgColor="FFFFFF" fontName="" fontStyle="1" nesting="0" />
- <WordsStyle name="FOLDER IN CODE2" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="FOLDER IN COMMENT" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="DELIMITERS1" fgColor="0000FF" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="DELIMITERS2" fgColor="0000FF" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="DELIMITERS3" fgColor="0000FF" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="DELIMITERS4" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="DELIMITERS5" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="DELIMITERS6" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="DELIMITERS7" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- <WordsStyle name="DELIMITERS8" fgColor="000000" bgColor="FFFFFF" fontName="" fontStyle="0" nesting="0" />
- </Styles>
- </UserLang>
-<!-- </NotepadPlus> -->
diff --git a/misc/pprof b/misc/pprof
deleted file mode 100755
index ad3f1ebe1..000000000
--- a/misc/pprof
+++ /dev/null
@@ -1,5094 +0,0 @@
-#! /usr/bin/env perl
-
-# This is a copy of http://google-perftools.googlecode.com/svn/trunk/src/pprof
-# with local modifications to handle generation of SVG images and
-# the Go-style pprof paths. These modifications will probably filter
-# back into the official source before long.
-# It's convenient to have a copy here because we need just the one
-# Perl script, not all the C++ libraries that surround it.
-
-# Copyright (c) 1998-2007, Google Inc.
-# All rights reserved.
-#
-# Redistribution and use in source and binary forms, with or without
-# modification, are permitted provided that the following conditions are
-# met:
-#
-# * Redistributions of source code must retain the above copyright
-# notice, this list of conditions and the following disclaimer.
-# * Redistributions in binary form must reproduce the above
-# copyright notice, this list of conditions and the following disclaimer
-# in the documentation and/or other materials provided with the
-# distribution.
-# * Neither the name of Google Inc. nor the names of its
-# contributors may be used to endorse or promote products derived from
-# this software without specific prior written permission.
-#
-# THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
-# "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
-# LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
-# A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
-# OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
-# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
-# LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
-# DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
-# THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
-# (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
-# OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
-
-# ---
-# Program for printing the profile generated by common/profiler.cc,
-# or by the heap profiler (common/debugallocation.cc)
-#
-# The profile contains a sequence of entries of the form:
-# <count> <stack trace>
-# This program parses the profile, and generates user-readable
-# output.
-#
-# Examples:
-#
-# % tools/pprof "program" "profile"
-# Enters "interactive" mode
-#
-# % tools/pprof --text "program" "profile"
-# Generates one line per procedure
-#
-# % tools/pprof --gv "program" "profile"
-# Generates annotated call-graph and displays via "gv"
-#
-# % tools/pprof --gv --focus=Mutex "program" "profile"
-# Restrict to code paths that involve an entry that matches "Mutex"
-#
-# % tools/pprof --gv --focus=Mutex --ignore=string "program" "profile"
-# Restrict to code paths that involve an entry that matches "Mutex"
-# and does not match "string"
-#
-# % tools/pprof --list=IBF_CheckDocid "program" "profile"
-# Generates disassembly listing of all routines with at least one
-# sample that match the --list=<regexp> pattern. The listing is
-# annotated with the flat and cumulative sample counts at each line.
-#
-# % tools/pprof --disasm=IBF_CheckDocid "program" "profile"
-# Generates disassembly listing of all routines with at least one
-# sample that match the --disasm=<regexp> pattern. The listing is
-# annotated with the flat and cumulative sample counts at each PC value.
-#
-# TODO: Use color to indicate files?
-
-use strict;
-use warnings;
-use Getopt::Long;
-use File::Temp;
-use File::Copy;
-
-my $PPROF_VERSION = "1.5";
-
-# NOTE: All mentions of c++filt have been expunged from this script
-# because (1) we don't use C++, and (2) the copy of c++filt that ships
-# on OS X is from 2007 and destroys nm output by "demangling" the
-# first two columns (address and symbol type).
-
-# These are the object tools we use which can come from a
-# user-specified location using --tools, from the PPROF_TOOLS
-# environment variable, or from the environment.
-my %obj_tool_map = (
- "objdump" => "objdump",
- "nm" => "nm",
- "addr2line" => "addr2line",
- ## ConfigureObjTools may add architecture-specific entries:
- #"nm_pdb" => "nm-pdb", # for reading windows (PDB-format) executables
- #"addr2line_pdb" => "addr2line-pdb", # ditto
- #"otool" => "otool", # equivalent of objdump on OS X
-);
-my $DOT = "dot"; # leave non-absolute, since it may be in /usr/local
-my $GV = "gv";
-my $KCACHEGRIND = "kcachegrind";
-my $PS2PDF = "ps2pdf";
-# These are used for dynamic profiles
-
-# These are the web pages that servers need to support for dynamic profiles
-my $HEAP_PAGE = "/pprof/heap";
-my $THREAD_PAGE = "/pprof/thread";
-my $PROFILE_PAGE = "/pprof/profile"; # must support cgi-param "?seconds=#"
-my $BLOCK_PAGE = "/pprof/block";
-my $PMUPROFILE_PAGE = "/pprof/pmuprofile(?:\\?.*)?"; # must support cgi-param
- # ?seconds=#&event=x&period=n
-my $GROWTH_PAGE = "/pprof/growth";
-my $CONTENTION_PAGE = "/pprof/contention";
-my $WALL_PAGE = "/pprof/wall(?:\\?.*)?"; # accepts options like namefilter
-my $FILTEREDPROFILE_PAGE = "/pprof/filteredprofile(?:\\?.*)?";
-my $SYMBOL_PAGE = "/pprof/symbol"; # must support symbol lookup via POST
-my $PROGRAM_NAME_PAGE = "/pprof/cmdline";
-
-# default binary name
-my $UNKNOWN_BINARY = "(unknown)";
-
-# There is a pervasive dependency on the length (in hex characters,
-# i.e., nibbles) of an address, distinguishing between 32-bit and
-# 64-bit profiles. To err on the safe size, default to 64-bit here:
-my $address_length = 16;
-
-# A list of paths to search for shared object files
-my @prefix_list = ();
-
-# Special routine name that should not have any symbols.
-# Used as separator to parse "addr2line -i" output.
-my $sep_symbol = '_fini';
-my $sep_address = undef;
-
-my $OS = $^O;
-my $DEVNULL = "/dev/null";
-if ($^O =~ /MSWin32|cygwin|msys/) {
- $OS = "windows";
- $DEVNULL = "NUL";
-}
-
-##### Argument parsing #####
-
-sub usage_string {
- return <<EOF;
-Usage:
-pprof [options] <program> <profiles>
- <profiles> is a space separated list of profile names.
-pprof [options] <symbolized-profiles>
- <symbolized-profiles> is a list of profile files where each file contains
- the necessary symbol mappings as well as profile data (likely generated
- with --raw).
-pprof [options] <profile>
- <profile> is a remote form. Symbols are obtained from host:port$SYMBOL_PAGE
-
- Each name can be:
- /path/to/profile - a path to a profile file
- host:port[/<service>] - a location of a service to get profile from
-
- The /<service> can be $HEAP_PAGE, $PROFILE_PAGE, /pprof/pmuprofile,
- $GROWTH_PAGE, $CONTENTION_PAGE, /pprof/wall,
- $THREAD_PAGE, $BLOCK_PAGE or /pprof/filteredprofile.
- For instance:
- pprof http://myserver.com:80$HEAP_PAGE
- If /<service> is omitted, the service defaults to $PROFILE_PAGE (cpu profiling).
-pprof --symbols <program>
- Maps addresses to symbol names. In this mode, stdin should be a
- list of library mappings, in the same format as is found in the heap-
- and cpu-profile files (this loosely matches that of /proc/self/maps
- on linux), followed by a list of hex addresses to map, one per line.
-
- For more help with querying remote servers, including how to add the
- necessary server-side support code, see this filename (or one like it):
-
- /usr/doc/google-perftools-$PPROF_VERSION/pprof_remote_servers.html
-
-Options:
- --cum Sort by cumulative data
- --base=<base> Subtract <base> from <profile> before display
- --interactive Run in interactive mode (interactive "help" gives help) [default]
- --seconds=<n> Length of time for dynamic profiles [default=30 secs]
- --add_lib=<file> Read additional symbols and line info from the given library
- --lib_prefix=<dir> Comma separated list of library path prefixes
-
-Reporting Granularity:
- --addresses Report at address level
- --lines Report at source line level
- --functions Report at function level [default]
- --files Report at source file level
-
-Output type:
- --text Generate text report
- --callgrind Generate callgrind format to stdout
- --gv Generate Postscript and display
- --web Generate SVG and display
- --list=<regexp> Generate source listing of matching routines
- --disasm=<regexp> Generate disassembly of matching routines
- --symbols Print demangled symbol names found at given addresses
- --dot Generate DOT file to stdout
- --ps Generate Postcript to stdout
- --pdf Generate PDF to stdout
- --svg Generate SVG to stdout
- --gif Generate GIF to stdout
- --raw Generate symbolized pprof data (useful with remote fetch)
-
-Heap-Profile Options:
- --inuse_space Display in-use (mega)bytes [default]
- --inuse_objects Display in-use objects
- --alloc_space Display allocated (mega)bytes
- --alloc_objects Display allocated objects
- --show_bytes Display space in bytes
- --drop_negative Ignore negative differences
-
-Contention-profile options:
- --total_delay Display total delay at each region [default]
- --contentions Display number of delays at each region
- --mean_delay Display mean delay at each region
-
-Call-graph Options:
- --nodecount=<n> Show at most so many nodes [default=80]
- --nodefraction=<f> Hide nodes below <f>*total [default=.005]
- --edgefraction=<f> Hide edges below <f>*total [default=.001]
- --focus=<regexp> Focus on nodes matching <regexp>
- --ignore=<regexp> Ignore nodes matching <regexp>
- --scale=<n> Set GV scaling [default=0]
- --heapcheck Make nodes with non-0 object counts
- (i.e. direct leak generators) more visible
-
-Miscellaneous:
- --tools=<prefix> Prefix for object tool pathnames
- --test Run unit tests
- --help This message
- --version Version information
-
-Environment Variables:
- PPROF_TMPDIR Profiles directory. Defaults to \$HOME/pprof
- PPROF_TOOLS Prefix for object tools pathnames
-
-Examples:
-
-pprof /bin/ls ls.prof
- Enters "interactive" mode
-pprof --text /bin/ls ls.prof
- Outputs one line per procedure
-pprof --web /bin/ls ls.prof
- Displays annotated call-graph in web browser
-pprof --gv /bin/ls ls.prof
- Displays annotated call-graph via 'gv'
-pprof --gv --focus=Mutex /bin/ls ls.prof
- Restricts to code paths including a .*Mutex.* entry
-pprof --gv --focus=Mutex --ignore=string /bin/ls ls.prof
- Code paths including Mutex but not string
-pprof --list=getdir /bin/ls ls.prof
- (Per-line) annotated source listing for getdir()
-pprof --disasm=getdir /bin/ls ls.prof
- (Per-PC) annotated disassembly for getdir()
-
-pprof http://localhost:1234/
- Enters "interactive" mode
-pprof --text localhost:1234
- Outputs one line per procedure for localhost:1234
-pprof --raw localhost:1234 > ./local.raw
-pprof --text ./local.raw
- Fetches a remote profile for later analysis and then
- analyzes it in text mode.
-EOF
-}
-
-sub version_string {
- return <<EOF
-pprof (part of google-perftools $PPROF_VERSION)
-
-Copyright 1998-2007 Google Inc.
-
-This is BSD licensed software; see the source for copying conditions
-and license information.
-There is NO warranty; not even for MERCHANTABILITY or FITNESS FOR A
-PARTICULAR PURPOSE.
-EOF
-}
-
-sub usage {
- my $msg = shift;
- print STDERR "$msg\n\n";
- print STDERR usage_string();
- print STDERR "\nFATAL ERROR: $msg\n"; # just as a reminder
- exit(1);
-}
-
-sub Init() {
- # Setup tmp-file name and handler to clean it up.
- # We do this in the very beginning so that we can use
- # error() and cleanup() function anytime here after.
- $main::tmpfile_sym = File::Temp->new()->filename;
- $main::tmpfile_ps = File::Temp->new()->filename;
-
- $main::next_tmpfile = 0;
- $SIG{'INT'} = \&sighandler;
-
- # Cache from filename/linenumber to source code
- $main::source_cache = ();
-
- $main::opt_help = 0;
- $main::opt_version = 0;
-
- $main::opt_cum = 0;
- $main::opt_base = '';
- $main::opt_addresses = 0;
- $main::opt_lines = 0;
- $main::opt_functions = 0;
- $main::opt_files = 0;
- $main::opt_lib_prefix = "";
-
- $main::opt_text = 0;
- $main::opt_callgrind = 0;
- $main::opt_list = "";
- $main::opt_disasm = "";
- $main::opt_symbols = 0;
- $main::opt_gv = 0;
- $main::opt_web = 0;
- $main::opt_dot = 0;
- $main::opt_ps = 0;
- $main::opt_pdf = 0;
- $main::opt_gif = 0;
- $main::opt_svg = 0;
- $main::opt_raw = 0;
-
- $main::opt_nodecount = 80;
- $main::opt_nodefraction = 0.005;
- $main::opt_edgefraction = 0.001;
- $main::opt_focus = '';
- $main::opt_ignore = '';
- $main::opt_scale = 0;
- $main::opt_heapcheck = 0;
- $main::opt_seconds = 30;
- $main::opt_lib = "";
-
- $main::opt_inuse_space = 0;
- $main::opt_inuse_objects = 0;
- $main::opt_alloc_space = 0;
- $main::opt_alloc_objects = 0;
- $main::opt_show_bytes = 0;
- $main::opt_drop_negative = 0;
- $main::opt_interactive = 0;
-
- $main::opt_total_delay = 0;
- $main::opt_contentions = 0;
- $main::opt_mean_delay = 0;
-
- $main::opt_tools = "";
- $main::opt_debug = 0;
- $main::opt_test = 0;
-
- # These are undocumented flags used only by unittests.
- $main::opt_test_stride = 0;
-
- # Are we using $SYMBOL_PAGE?
- $main::use_symbol_page = 0;
-
- # Files returned by TempName.
- %main::tempnames = ();
-
- # Type of profile we are dealing with
- # Supported types:
- # cpu
- # heap
- # growth
- # contention
- $main::profile_type = ''; # Empty type means "unknown"
-
- GetOptions("help!" => \$main::opt_help,
- "version!" => \$main::opt_version,
- "cum!" => \$main::opt_cum,
- "base=s" => \$main::opt_base,
- "seconds=i" => \$main::opt_seconds,
- "add_lib=s" => \$main::opt_lib,
- "lib_prefix=s" => \$main::opt_lib_prefix,
- "functions!" => \$main::opt_functions,
- "lines!" => \$main::opt_lines,
- "addresses!" => \$main::opt_addresses,
- "files!" => \$main::opt_files,
- "text!" => \$main::opt_text,
- "callgrind!" => \$main::opt_callgrind,
- "list=s" => \$main::opt_list,
- "disasm=s" => \$main::opt_disasm,
- "symbols!" => \$main::opt_symbols,
- "gv!" => \$main::opt_gv,
- "web!" => \$main::opt_web,
- "dot!" => \$main::opt_dot,
- "ps!" => \$main::opt_ps,
- "pdf!" => \$main::opt_pdf,
- "svg!" => \$main::opt_svg,
- "gif!" => \$main::opt_gif,
- "raw!" => \$main::opt_raw,
- "interactive!" => \$main::opt_interactive,
- "nodecount=i" => \$main::opt_nodecount,
- "nodefraction=f" => \$main::opt_nodefraction,
- "edgefraction=f" => \$main::opt_edgefraction,
- "focus=s" => \$main::opt_focus,
- "ignore=s" => \$main::opt_ignore,
- "scale=i" => \$main::opt_scale,
- "heapcheck" => \$main::opt_heapcheck,
- "inuse_space!" => \$main::opt_inuse_space,
- "inuse_objects!" => \$main::opt_inuse_objects,
- "alloc_space!" => \$main::opt_alloc_space,
- "alloc_objects!" => \$main::opt_alloc_objects,
- "show_bytes!" => \$main::opt_show_bytes,
- "drop_negative!" => \$main::opt_drop_negative,
- "total_delay!" => \$main::opt_total_delay,
- "contentions!" => \$main::opt_contentions,
- "mean_delay!" => \$main::opt_mean_delay,
- "tools=s" => \$main::opt_tools,
- "test!" => \$main::opt_test,
- "debug!" => \$main::opt_debug,
- # Undocumented flags used only by unittests:
- "test_stride=i" => \$main::opt_test_stride,
- ) || usage("Invalid option(s)");
-
- # Deal with the standard --help and --version
- if ($main::opt_help) {
- print usage_string();
- exit(0);
- }
-
- if ($main::opt_version) {
- print version_string();
- exit(0);
- }
-
- # Disassembly/listing/symbols mode requires address-level info
- if ($main::opt_disasm || $main::opt_list || $main::opt_symbols) {
- $main::opt_functions = 0;
- $main::opt_lines = 0;
- $main::opt_addresses = 1;
- $main::opt_files = 0;
- }
-
- # Check heap-profiling flags
- if ($main::opt_inuse_space +
- $main::opt_inuse_objects +
- $main::opt_alloc_space +
- $main::opt_alloc_objects > 1) {
- usage("Specify at most on of --inuse/--alloc options");
- }
-
- # Check output granularities
- my $grains =
- $main::opt_functions +
- $main::opt_lines +
- $main::opt_addresses +
- $main::opt_files +
- 0;
- if ($grains > 1) {
- usage("Only specify one output granularity option");
- }
- if ($grains == 0) {
- $main::opt_functions = 1;
- }
-
- # Check output modes
- my $modes =
- $main::opt_text +
- $main::opt_callgrind +
- ($main::opt_list eq '' ? 0 : 1) +
- ($main::opt_disasm eq '' ? 0 : 1) +
- ($main::opt_symbols == 0 ? 0 : 1) +
- $main::opt_gv +
- $main::opt_web +
- $main::opt_dot +
- $main::opt_ps +
- $main::opt_pdf +
- $main::opt_svg +
- $main::opt_gif +
- $main::opt_raw +
- $main::opt_interactive +
- 0;
- if ($modes > 1) {
- usage("Only specify one output mode");
- }
- if ($modes == 0) {
- if (-t STDOUT) { # If STDOUT is a tty, activate interactive mode
- $main::opt_interactive = 1;
- } else {
- $main::opt_text = 1;
- }
- }
-
- if ($main::opt_test) {
- RunUnitTests();
- # Should not return
- exit(1);
- }
-
- # Binary name and profile arguments list
- $main::prog = "";
- @main::pfile_args = ();
-
- # Remote profiling without a binary (using $SYMBOL_PAGE instead)
- if (IsProfileURL($ARGV[0])) {
- $main::use_symbol_page = 1;
- } elsif ($ARGV[0] && IsSymbolizedProfileFile($ARGV[0])) {
- $main::use_symbolized_profile = 1;
- $main::prog = $UNKNOWN_BINARY; # will be set later from the profile file
- }
-
- if ($main::use_symbol_page || $main::use_symbolized_profile) {
- # We don't need a binary!
- my %disabled = ('--lines' => $main::opt_lines,
- '--disasm' => $main::opt_disasm);
- for my $option (keys %disabled) {
- usage("$option cannot be used without a binary") if $disabled{$option};
- }
- # Set $main::prog later...
- scalar(@ARGV) || usage("Did not specify profile file");
- } elsif ($main::opt_symbols) {
- # --symbols needs a binary-name (to run nm on, etc) but not profiles
- $main::prog = shift(@ARGV) || usage("Did not specify program");
- } else {
- $main::prog = shift(@ARGV) || usage("Did not specify program");
- scalar(@ARGV) || usage("Did not specify profile file");
- }
-
- # Parse profile file/location arguments
- foreach my $farg (@ARGV) {
- if ($farg =~ m/(.*)\@([0-9]+)(|\/.*)$/ ) {
- my $machine = $1;
- my $num_machines = $2;
- my $path = $3;
- for (my $i = 0; $i < $num_machines; $i++) {
- unshift(@main::pfile_args, "$i.$machine$path");
- }
- } else {
- unshift(@main::pfile_args, $farg);
- }
- }
-
- if ($main::use_symbol_page) {
- unless (IsProfileURL($main::pfile_args[0])) {
- error("The first profile should be a remote form to use $SYMBOL_PAGE\n");
- }
- CheckSymbolPage();
- $main::prog = FetchProgramName();
- } elsif (!$main::use_symbolized_profile) { # may not need objtools!
- ConfigureObjTools($main::prog)
- }
-
- # Break the opt_lib_prefix into the prefix_list array
- @prefix_list = split (',', $main::opt_lib_prefix);
-
- # Remove trailing / from the prefixes, in the list to prevent
- # searching things like /my/path//lib/mylib.so
- foreach (@prefix_list) {
- s|/+$||;
- }
-}
-
-sub Main() {
- Init();
- $main::collected_profile = undef;
- @main::profile_files = ();
- $main::op_time = time();
-
- # Printing symbols is special and requires a lot less info that most.
- if ($main::opt_symbols) {
- PrintSymbols(*STDIN); # Get /proc/maps and symbols output from stdin
- return;
- }
-
- # Fetch all profile data
- FetchDynamicProfiles();
-
- # this will hold symbols that we read from the profile files
- my $symbol_map = {};
-
- # Read one profile, pick the last item on the list
- my $data = ReadProfile($main::prog, pop(@main::profile_files));
- my $profile = $data->{profile};
- my $pcs = $data->{pcs};
- my $libs = $data->{libs}; # Info about main program and shared libraries
- $symbol_map = MergeSymbols($symbol_map, $data->{symbols});
-
- # Add additional profiles, if available.
- if (scalar(@main::profile_files) > 0) {
- foreach my $pname (@main::profile_files) {
- my $data2 = ReadProfile($main::prog, $pname);
- $profile = AddProfile($profile, $data2->{profile});
- $pcs = AddPcs($pcs, $data2->{pcs});
- $symbol_map = MergeSymbols($symbol_map, $data2->{symbols});
- }
- }
-
- # Subtract base from profile, if specified
- if ($main::opt_base ne '') {
- my $base = ReadProfile($main::prog, $main::opt_base);
- $profile = SubtractProfile($profile, $base->{profile});
- $pcs = AddPcs($pcs, $base->{pcs});
- $symbol_map = MergeSymbols($symbol_map, $base->{symbols});
- }
-
- # Get total data in profile
- my $total = TotalProfile($profile);
-
- # Collect symbols
- my $symbols;
- if ($main::use_symbolized_profile) {
- $symbols = FetchSymbols($pcs, $symbol_map);
- } elsif ($main::use_symbol_page) {
- $symbols = FetchSymbols($pcs);
- } else {
- $symbols = ExtractSymbols($libs, $pcs);
- }
-
- # Remove uniniteresting stack items
- $profile = RemoveUninterestingFrames($symbols, $profile);
-
- # Focus?
- if ($main::opt_focus ne '') {
- $profile = FocusProfile($symbols, $profile, $main::opt_focus);
- }
-
- # Ignore?
- if ($main::opt_ignore ne '') {
- $profile = IgnoreProfile($symbols, $profile, $main::opt_ignore);
- }
-
- my $calls = ExtractCalls($symbols, $profile);
-
- # Reduce profiles to required output granularity, and also clean
- # each stack trace so a given entry exists at most once.
- my $reduced = ReduceProfile($symbols, $profile);
-
- # Get derived profiles
- my $flat = FlatProfile($reduced);
- my $cumulative = CumulativeProfile($reduced);
-
- # Print
- if (!$main::opt_interactive) {
- if ($main::opt_disasm) {
- PrintDisassembly($libs, $flat, $cumulative, $main::opt_disasm, $total);
- } elsif ($main::opt_list) {
- PrintListing($total, $libs, $flat, $cumulative, $main::opt_list, 0);
- } elsif ($main::opt_text) {
- # Make sure the output is empty when have nothing to report
- # (only matters when --heapcheck is given but we must be
- # compatible with old branches that did not pass --heapcheck always):
- if ($total != 0) {
- Infof("Total: %s %s\n", Unparse($total), Units());
- }
- PrintText($symbols, $flat, $cumulative, $total, -1);
- } elsif ($main::opt_raw) {
- PrintSymbolizedProfile($symbols, $profile, $main::prog);
- } elsif ($main::opt_callgrind) {
- PrintCallgrind($calls);
- } else {
- if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
- if ($main::opt_gv) {
- RunGV(TempName($main::next_tmpfile, "ps"), "");
- } elsif ($main::opt_web) {
- my $tmp = TempName($main::next_tmpfile, "svg");
- RunWeb($tmp);
- # The command we run might hand the file name off
- # to an already running browser instance and then exit.
- # Normally, we'd remove $tmp on exit (right now),
- # but fork a child to remove $tmp a little later, so that the
- # browser has time to load it first.
- delete $main::tempnames{$tmp};
- if (fork() == 0) {
- sleep 5;
- unlink($tmp);
- exit(0);
- }
- }
- } else {
- exit(1);
- }
- }
- } else {
- InteractiveMode($profile, $symbols, $libs, $total);
- }
-
- cleanup();
- exit(0);
-}
-
-##### Entry Point #####
-
-Main();
-
-# Temporary code to detect if we're running on a Goobuntu system.
-# These systems don't have the right stuff installed for the special
-# Readline libraries to work, so as a temporary workaround, we default
-# to using the normal stdio code, rather than the fancier readline-based
-# code
-sub ReadlineMightFail {
- if (-e '/lib/libtermcap.so.2') {
- return 0; # libtermcap exists, so readline should be okay
- } else {
- return 1;
- }
-}
-
-sub RunGV {
- my $fname = shift;
- my $bg = shift; # "" or " &" if we should run in background
- if (!system("$GV --version >$DEVNULL 2>&1")) {
- # Options using double dash are supported by this gv version.
- # Also, turn on noantialias to better handle bug in gv for
- # postscript files with large dimensions.
- # TODO: Maybe we should not pass the --noantialias flag
- # if the gv version is known to work properly without the flag.
- system("$GV --scale=$main::opt_scale --noantialias " . $fname . $bg);
- } else {
- # Old gv version - only supports options that use single dash.
- print STDERR "$GV -scale $main::opt_scale\n";
- system("$GV -scale $main::opt_scale " . $fname . $bg);
- }
-}
-
-sub RunWeb {
- my $fname = shift;
- print STDERR "Loading web page file:///$fname\n";
-
- if (`uname` =~ /Darwin/) {
- # OS X: open will use standard preference for SVG files.
- system("/usr/bin/open", $fname);
- return;
- }
-
- if (`uname` =~ /CYGWIN/) {
- # Windows(cygwin): open will use standard preference for SVG files.
- my $winname = `cygpath -wa $fname`;
- system("explorer.exe", $winname);
- return;
- }
-
- # Some kind of Unix; try generic symlinks, then specific browsers.
- # (Stop once we find one.)
- # Works best if the browser is already running.
- my @alt = (
- "/etc/alternatives/gnome-www-browser",
- "/etc/alternatives/x-www-browser",
- "google-chrome",
- "firefox",
- );
- foreach my $b (@alt) {
- if (system($b, $fname) == 0) {
- return;
- }
- }
-
- print STDERR "Could not load web browser.\n";
-}
-
-sub RunKcachegrind {
- my $fname = shift;
- my $bg = shift; # "" or " &" if we should run in background
- print STDERR "Starting '$KCACHEGRIND " . $fname . $bg . "'\n";
- system("$KCACHEGRIND " . $fname . $bg);
-}
-
-
-##### Interactive helper routines #####
-
-sub InteractiveMode {
- $| = 1; # Make output unbuffered for interactive mode
- my ($orig_profile, $symbols, $libs, $total) = @_;
-
- print STDERR "Welcome to pprof! For help, type 'help'.\n";
-
- # Use ReadLine if it's installed and input comes from a console.
- if ( -t STDIN &&
- !ReadlineMightFail() &&
- defined(eval {require Term::ReadLine}) ) {
- my $term = new Term::ReadLine 'pprof';
- while ( defined ($_ = $term->readline('(pprof) '))) {
- $term->addhistory($_) if /\S/;
- if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
- last; # exit when we get an interactive command to quit
- }
- }
- } else { # don't have readline
- while (1) {
- print STDERR "(pprof) ";
- $_ = <STDIN>;
- last if ! defined $_ ;
- s/\r//g; # turn windows-looking lines into unix-looking lines
-
- # Save some flags that might be reset by InteractiveCommand()
- my $save_opt_lines = $main::opt_lines;
-
- if (!InteractiveCommand($orig_profile, $symbols, $libs, $total, $_)) {
- last; # exit when we get an interactive command to quit
- }
-
- # Restore flags
- $main::opt_lines = $save_opt_lines;
- }
- }
-}
-
-# Takes two args: orig profile, and command to run.
-# Returns 1 if we should keep going, or 0 if we were asked to quit
-sub InteractiveCommand {
- my($orig_profile, $symbols, $libs, $total, $command) = @_;
- $_ = $command; # just to make future m//'s easier
- if (!defined($_)) {
- print STDERR "\n";
- return 0;
- }
- if (m/^\s*quit/) {
- return 0;
- }
- if (m/^\s*help/) {
- InteractiveHelpMessage();
- return 1;
- }
- # Clear all the mode options -- mode is controlled by "$command"
- $main::opt_text = 0;
- $main::opt_callgrind = 0;
- $main::opt_disasm = 0;
- $main::opt_list = 0;
- $main::opt_gv = 0;
- $main::opt_cum = 0;
-
- if (m/^\s*(text|top)(\d*)\s*(.*)/) {
- $main::opt_text = 1;
-
- my $line_limit = ($2 ne "") ? int($2) : 10;
-
- my $routine;
- my $ignore;
- ($routine, $ignore) = ParseInteractiveArgs($3);
-
- my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
- my $reduced = ReduceProfile($symbols, $profile);
-
- # Get derived profiles
- my $flat = FlatProfile($reduced);
- my $cumulative = CumulativeProfile($reduced);
-
- PrintText($symbols, $flat, $cumulative, $total, $line_limit);
- return 1;
- }
- if (m/^\s*callgrind\s*([^ \n]*)/) {
- $main::opt_callgrind = 1;
-
- # Get derived profiles
- my $calls = ExtractCalls($symbols, $orig_profile);
- my $filename = $1;
- if ( $1 eq '' ) {
- $filename = TempName($main::next_tmpfile, "callgrind");
- }
- PrintCallgrind($calls, $filename);
- if ( $1 eq '' ) {
- RunKcachegrind($filename, " & ");
- $main::next_tmpfile++;
- }
-
- return 1;
- }
- if (m/^\s*(web)?list\s*(.+)/) {
- my $html = (defined($1) && ($1 eq "web"));
- $main::opt_list = 1;
-
- my $routine;
- my $ignore;
- ($routine, $ignore) = ParseInteractiveArgs($2);
-
- my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
- my $reduced = ReduceProfile($symbols, $profile);
-
- # Get derived profiles
- my $flat = FlatProfile($reduced);
- my $cumulative = CumulativeProfile($reduced);
-
- PrintListing($total, $libs, $flat, $cumulative, $routine, $html);
- return 1;
- }
- if (m/^\s*disasm\s*(.+)/) {
- $main::opt_disasm = 1;
-
- my $routine;
- my $ignore;
- ($routine, $ignore) = ParseInteractiveArgs($1);
-
- # Process current profile to account for various settings
- my $profile = ProcessProfile($total, $orig_profile, $symbols, "", $ignore);
- my $reduced = ReduceProfile($symbols, $profile);
-
- # Get derived profiles
- my $flat = FlatProfile($reduced);
- my $cumulative = CumulativeProfile($reduced);
-
- PrintDisassembly($libs, $flat, $cumulative, $routine, $total);
- return 1;
- }
- if (m/^\s*(gv|web)\s*(.*)/) {
- $main::opt_gv = 0;
- $main::opt_web = 0;
- if ($1 eq "gv") {
- $main::opt_gv = 1;
- } elsif ($1 eq "web") {
- $main::opt_web = 1;
- }
-
- my $focus;
- my $ignore;
- ($focus, $ignore) = ParseInteractiveArgs($2);
-
- # Process current profile to account for various settings
- my $profile = ProcessProfile($total, $orig_profile, $symbols, $focus, $ignore);
- my $reduced = ReduceProfile($symbols, $profile);
-
- # Get derived profiles
- my $flat = FlatProfile($reduced);
- my $cumulative = CumulativeProfile($reduced);
-
- if (PrintDot($main::prog, $symbols, $profile, $flat, $cumulative, $total)) {
- if ($main::opt_gv) {
- RunGV(TempName($main::next_tmpfile, "ps"), " &");
- } elsif ($main::opt_web) {
- RunWeb(TempName($main::next_tmpfile, "svg"));
- }
- $main::next_tmpfile++;
- }
- return 1;
- }
- if (m/^\s*$/) {
- return 1;
- }
- print STDERR "Unknown command: try 'help'.\n";
- return 1;
-}
-
-
-sub ProcessProfile {
- my $total_count = shift;
- my $orig_profile = shift;
- my $symbols = shift;
- my $focus = shift;
- my $ignore = shift;
-
- # Process current profile to account for various settings
- my $profile = $orig_profile;
- printf("Total: %s %s\n", Unparse($total_count), Units());
- if ($focus ne '') {
- $profile = FocusProfile($symbols, $profile, $focus);
- my $focus_count = TotalProfile($profile);
- Infof("After focusing on '%s': %s %s of %s (%0.1f%%)\n",
- $focus,
- Unparse($focus_count), Units(),
- Unparse($total_count), ($focus_count*100.0) / $total_count);
- }
- if ($ignore ne '') {
- $profile = IgnoreProfile($symbols, $profile, $ignore);
- my $ignore_count = TotalProfile($profile);
- Infof("After ignoring '%s': %s %s of %s (%0.1f%%)\n",
- $ignore,
- Unparse($ignore_count), Units(),
- Unparse($total_count),
- ($ignore_count*100.0) / $total_count);
- }
-
- return $profile;
-}
-
-sub InteractiveHelpMessage {
- print STDERR <<ENDOFHELP;
-Interactive pprof mode
-
-Commands:
- gv
- gv [focus] [-ignore1] [-ignore2]
- Show graphical hierarchical display of current profile. Without
- any arguments, shows all samples in the profile. With the optional
- "focus" argument, restricts the samples shown to just those where
- the "focus" regular expression matches a routine name on the stack
- trace.
-
- web
- web [focus] [-ignore1] [-ignore2]
- Like GV, but displays profile in your web browser instead of using
- Ghostview. Works best if your web browser is already running.
- To change the browser that gets used:
- On Linux, set the /etc/alternatives/gnome-www-browser symlink.
- On OS X, change the Finder association for SVG files.
-
- list [routine_regexp] [-ignore1] [-ignore2]
- Show source listing of routines whose names match "routine_regexp"
-
- weblist [routine_regexp] [-ignore1] [-ignore2]
- Displays a source listing of routines whose names match "routine_regexp"
- in a web browser. You can click on source lines to view the
- corresponding disassembly.
-
- top [--cum] [-ignore1] [-ignore2]
- top20 [--cum] [-ignore1] [-ignore2]
- top37 [--cum] [-ignore1] [-ignore2]
- Show top lines ordered by flat profile count, or cumulative count
- if --cum is specified. If a number is present after 'top', the
- top K routines will be shown (defaults to showing the top 10)
-
- disasm [routine_regexp] [-ignore1] [-ignore2]
- Show disassembly of routines whose names match "routine_regexp",
- annotated with sample counts.
-
- callgrind
- callgrind [filename]
- Generates callgrind file. If no filename is given, kcachegrind is called.
-
- help - This listing
- quit or ^D - End pprof
-
-For commands that accept optional -ignore tags, samples where any routine in
-the stack trace matches the regular expression in any of the -ignore
-parameters will be ignored.
-
-Further pprof details are available at this location (or one similar):
-
- /usr/doc/google-perftools-$PPROF_VERSION/cpu_profiler.html
- /usr/doc/google-perftools-$PPROF_VERSION/heap_profiler.html
-
-ENDOFHELP
-}
-sub ParseInteractiveArgs {
- my $args = shift;
- my $focus = "";
- my $ignore = "";
- my @x = split(/ +/, $args);
- foreach $a (@x) {
- if ($a =~ m/^(--|-)lines$/) {
- $main::opt_lines = 1;
- } elsif ($a =~ m/^(--|-)cum$/) {
- $main::opt_cum = 1;
- } elsif ($a =~ m/^-(.*)/) {
- $ignore .= (($ignore ne "") ? "|" : "" ) . $1;
- } else {
- $focus .= (($focus ne "") ? "|" : "" ) . $a;
- }
- }
- if ($ignore ne "") {
- print STDERR "Ignoring samples in call stacks that match '$ignore'\n";
- }
- return ($focus, $ignore);
-}
-
-##### Output code #####
-
-sub TempName {
- my $fnum = shift;
- my $ext = shift;
- my $file = "$main::tmpfile_ps.$fnum.$ext";
- $main::tempnames{$file} = 1;
- return $file;
-}
-
-# Print profile data in packed binary format (64-bit) to standard out
-sub PrintProfileData {
- my $profile = shift;
-
- # print header (64-bit style)
- # (zero) (header-size) (version) (sample-period) (zero)
- print pack('L*', 0, 0, 3, 0, 0, 0, 1, 0, 0, 0);
-
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- if ($#addrs >= 0) {
- my $depth = $#addrs + 1;
- # int(foo / 2**32) is the only reliable way to get rid of bottom
- # 32 bits on both 32- and 64-bit systems.
- print pack('L*', $count & 0xFFFFFFFF, int($count / 2**32));
- print pack('L*', $depth & 0xFFFFFFFF, int($depth / 2**32));
-
- foreach my $full_addr (@addrs) {
- my $addr = $full_addr;
- $addr =~ s/0x0*//; # strip off leading 0x, zeroes
- if (length($addr) > 16) {
- print STDERR "Invalid address in profile: $full_addr\n";
- next;
- }
- my $low_addr = substr($addr, -8); # get last 8 hex chars
- my $high_addr = substr($addr, -16, 8); # get up to 8 more hex chars
- print pack('L*', hex('0x' . $low_addr), hex('0x' . $high_addr));
- }
- }
- }
-}
-
-# Print symbols and profile data
-sub PrintSymbolizedProfile {
- my $symbols = shift;
- my $profile = shift;
- my $prog = shift;
-
- $SYMBOL_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $symbol_marker = $&;
-
- print '--- ', $symbol_marker, "\n";
- if (defined($prog)) {
- print 'binary=', $prog, "\n";
- }
- while (my ($pc, $name) = each(%{$symbols})) {
- my $sep = ' ';
- print '0x', $pc;
- # We have a list of function names, which include the inlined
- # calls. They are separated (and terminated) by --, which is
- # illegal in function names.
- for (my $j = 2; $j <= $#{$name}; $j += 3) {
- print $sep, $name->[$j];
- $sep = '--';
- }
- print "\n";
- }
- print '---', "\n";
-
- $PROFILE_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $profile_marker = $&;
- print '--- ', $profile_marker, "\n";
- if (defined($main::collected_profile)) {
- # if used with remote fetch, simply dump the collected profile to output.
- open(SRC, "<$main::collected_profile");
- while (<SRC>) {
- print $_;
- }
- close(SRC);
- } else {
- # dump a cpu-format profile to standard out
- PrintProfileData($profile);
- }
-}
-
-# Print information conditionally filtered out depending on the output
-# format.
-sub Infof {
- my $format = shift;
- my @args = @_;
- return if $main::opt_svg;
- printf($format, @args);
-}
-
-# Print text output
-sub PrintText {
- my $symbols = shift;
- my $flat = shift;
- my $cumulative = shift;
- my $total = shift;
- my $line_limit = shift;
-
- # Which profile to sort by?
- my $s = $main::opt_cum ? $cumulative : $flat;
-
- my $running_sum = 0;
- my $lines = 0;
- foreach my $k (sort { GetEntry($s, $b) <=> GetEntry($s, $a) || $a cmp $b }
- keys(%{$cumulative})) {
- my $f = GetEntry($flat, $k);
- my $c = GetEntry($cumulative, $k);
- $running_sum += $f;
-
- my $sym = $k;
- if (exists($symbols->{$k})) {
- $sym = $symbols->{$k}->[0] . " " . $symbols->{$k}->[1];
- if ($main::opt_addresses) {
- $sym = $k . " " . $sym;
- }
- }
-
- if ($f != 0 || $c != 0) {
- printf("%8s %6s %6s %8s %6s %s\n",
- Unparse($f),
- Percent($f, $total),
- Percent($running_sum, $total),
- Unparse($c),
- Percent($c, $total),
- $sym);
- }
- $lines++;
- last if ($line_limit >= 0 && $lines >= $line_limit);
- }
-}
-
-# Print the call graph in a way that's suiteable for callgrind.
-sub PrintCallgrind {
- my $calls = shift;
- my $filename;
- if ($main::opt_interactive) {
- $filename = shift;
- print STDERR "Writing callgrind file to '$filename'.\n"
- } else {
- $filename = "&STDOUT";
- }
- open(CG, ">".$filename );
- printf CG ("events: Hits\n\n");
- foreach my $call ( map { $_->[0] }
- sort { $a->[1] cmp $b ->[1] ||
- $a->[2] <=> $b->[2] }
- map { /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
- [$_, $1, $2] }
- keys %$calls ) {
- my $count = int($calls->{$call});
- $call =~ /([^:]+):(\d+):([^ ]+)( -> ([^:]+):(\d+):(.+))?/;
- my ( $caller_file, $caller_line, $caller_function,
- $callee_file, $callee_line, $callee_function ) =
- ( $1, $2, $3, $5, $6, $7 );
-
- printf CG ("fl=$caller_file\nfn=$caller_function\n");
- if (defined $6) {
- printf CG ("cfl=$callee_file\n");
- printf CG ("cfn=$callee_function\n");
- printf CG ("calls=$count $callee_line\n");
- }
- printf CG ("$caller_line $count\n\n");
- }
-}
-
-# Print disassembly for all all routines that match $main::opt_disasm
-sub PrintDisassembly {
- my $libs = shift;
- my $flat = shift;
- my $cumulative = shift;
- my $disasm_opts = shift;
- my $total = shift;
-
- foreach my $lib (@{$libs}) {
- my $symbol_table = GetProcedureBoundaries($lib->[0], $disasm_opts);
- my $offset = AddressSub($lib->[1], $lib->[3]);
- foreach my $routine (sort ByName keys(%{$symbol_table})) {
- my $start_addr = $symbol_table->{$routine}->[0];
- my $end_addr = $symbol_table->{$routine}->[1];
- # See if there are any samples in this routine
- my $length = hex(AddressSub($end_addr, $start_addr));
- my $addr = AddressAdd($start_addr, $offset);
- for (my $i = 0; $i < $length; $i++) {
- if (defined($cumulative->{$addr})) {
- PrintDisassembledFunction($lib->[0], $offset,
- $routine, $flat, $cumulative,
- $start_addr, $end_addr, $total);
- last;
- }
- $addr = AddressInc($addr);
- }
- }
- }
-}
-
-# Return reference to array of tuples of the form:
-# [start_address, filename, linenumber, instruction, limit_address]
-# E.g.,
-# ["0x806c43d", "/foo/bar.cc", 131, "ret", "0x806c440"]
-sub Disassemble {
- my $prog = shift;
- my $offset = shift;
- my $start_addr = shift;
- my $end_addr = shift;
-
- my $objdump = $obj_tool_map{"objdump"};
- my $cmd = sprintf("$objdump -C -d -l --no-show-raw-insn " .
- "--start-address=0x$start_addr " .
- "--stop-address=0x$end_addr $prog");
-
- if (system("$objdump --help >$DEVNULL 2>&1") != 0) {
- # objdump must not exist. Fall back to go tool objdump.
- $objdump = "go tool objdump";
- $cmd = "$objdump $prog 0x$start_addr 0x$end_addr";
- }
-
- open(OBJDUMP, "$cmd |") || error("$objdump: $!\n");
- my @result = ();
- my $filename = "";
- my $linenumber = -1;
- my $last = ["", "", "", ""];
- while (<OBJDUMP>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- chop;
- if (m|\s*(.+):(\d+)\s*$|) {
- # Location line of the form:
- # <filename>:<linenumber>
- $filename = $1;
- $linenumber = $2;
- } elsif (m/^ +([0-9a-f]+):\s*(.*)/) {
- # Disassembly line -- zero-extend address to full length
- my $addr = HexExtend($1);
- my $k = AddressAdd($addr, $offset);
- $last->[4] = $k; # Store ending address for previous instruction
- $last = [$k, $filename, $linenumber, $2, $end_addr];
- push(@result, $last);
- }
- }
- close(OBJDUMP);
- return @result;
-}
-
-# The input file should contain lines of the form /proc/maps-like
-# output (same format as expected from the profiles) or that looks
-# like hex addresses (like "0xDEADBEEF"). We will parse all
-# /proc/maps output, and for all the hex addresses, we will output
-# "short" symbol names, one per line, in the same order as the input.
-sub PrintSymbols {
- my $maps_and_symbols_file = shift;
-
- # ParseLibraries expects pcs to be in a set. Fine by us...
- my @pclist = (); # pcs in sorted order
- my $pcs = {};
- my $map = "";
- foreach my $line (<$maps_and_symbols_file>) {
- $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
- if ($line =~ /\b(0x[0-9a-f]+)\b/i) {
- push(@pclist, HexExtend($1));
- $pcs->{$pclist[-1]} = 1;
- } else {
- $map .= $line;
- }
- }
-
- my $libs = ParseLibraries($main::prog, $map, $pcs);
- my $symbols = ExtractSymbols($libs, $pcs);
-
- foreach my $pc (@pclist) {
- # ->[0] is the shortname, ->[2] is the full name
- print(($symbols->{$pc}->[0] || "??") . "\n");
- }
-}
-
-
-# For sorting functions by name
-sub ByName {
- return ShortFunctionName($a) cmp ShortFunctionName($b);
-}
-
-# Print source-listing for all all routines that match $main::opt_list
-sub PrintListing {
- my $total = shift;
- my $libs = shift;
- my $flat = shift;
- my $cumulative = shift;
- my $list_opts = shift;
- my $html = shift;
-
- my $output = \*STDOUT;
- my $fname = "";
-
-
- if ($html) {
- # Arrange to write the output to a temporary file
- $fname = TempName($main::next_tmpfile, "html");
- $main::next_tmpfile++;
- if (!open(TEMP, ">$fname")) {
- print STDERR "$fname: $!\n";
- return;
- }
- $output = \*TEMP;
- print $output HtmlListingHeader();
- printf $output ("<div class=\"legend\">%s<br>Total: %s %s</div>\n",
- $main::prog, Unparse($total), Units());
- }
-
- my $listed = 0;
- foreach my $lib (@{$libs}) {
- my $symbol_table = GetProcedureBoundaries($lib->[0], $list_opts);
- my $offset = AddressSub($lib->[1], $lib->[3]);
- foreach my $routine (sort ByName keys(%{$symbol_table})) {
- # Print if there are any samples in this routine
- my $start_addr = $symbol_table->{$routine}->[0];
- my $end_addr = $symbol_table->{$routine}->[1];
- my $length = hex(AddressSub($end_addr, $start_addr));
- my $addr = AddressAdd($start_addr, $offset);
- for (my $i = 0; $i < $length; $i++) {
- if (defined($cumulative->{$addr})) {
- $listed += PrintSource(
- $lib->[0], $offset,
- $routine, $flat, $cumulative,
- $start_addr, $end_addr,
- $html,
- $output);
- last;
- }
- $addr = AddressInc($addr);
- }
- }
- }
-
- if ($html) {
- if ($listed > 0) {
- print $output HtmlListingFooter();
- close($output);
- RunWeb($fname);
- } else {
- close($output);
- unlink($fname);
- }
- }
-}
-
-sub HtmlListingHeader {
- return <<'EOF';
-<!DOCTYPE html>
-<html>
-<head>
-<title>Pprof listing</title>
-<style type="text/css">
-body {
- font-family: sans-serif;
-}
-h1 {
- font-size: 1.5em;
- margin-bottom: 4px;
-}
-.legend {
- font-size: 1.25em;
-}
-.line {
- color: #aaaaaa;
-}
-.livesrc {
- color: #0000ff;
- cursor: pointer;
-}
-.livesrc:hover {
- background-color: #cccccc;
-}
-.asm {
- color: #888888;
- display: none;
-}
-</style>
-<script type="text/javascript">
-function pprof_toggle_asm(e) {
- var target;
- if (!e) e = window.event;
- if (e.target) target = e.target;
- else if (e.srcElement) target = e.srcElement;
-
- if (target && target.className == "livesrc") {
- var asm = target.nextSibling;
- if (asm && asm.className == "asm") {
- asm.style.display = (asm.style.display == "block" ? "none" : "block");
- e.preventDefault();
- return false;
- }
- }
-}
-</script>
-</head>
-<body>
-EOF
-}
-
-sub HtmlListingFooter {
- return <<'EOF';
-</body>
-</html>
-EOF
-}
-
-sub HtmlEscape {
- my $text = shift;
- $text =~ s/&/&amp;/g;
- $text =~ s/</&lt;/g;
- $text =~ s/>/&gt;/g;
- return $text;
-}
-
-# Returns the indentation of the line, if it has any non-whitespace
-# characters. Otherwise, returns -1.
-sub Indentation {
- my $line = shift;
- if (m/^(\s*)\S/) {
- return length($1);
- } else {
- return -1;
- }
-}
-
-# Print source-listing for one routine
-sub PrintSource {
- my $prog = shift;
- my $offset = shift;
- my $routine = shift;
- my $flat = shift;
- my $cumulative = shift;
- my $start_addr = shift;
- my $end_addr = shift;
- my $html = shift;
- my $output = shift;
-
- # Disassemble all instructions (just to get line numbers)
- my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
-
- # Hack 1: assume that the first source file encountered in the
- # disassembly contains the routine
- my $filename = undef;
- for (my $i = 0; $i <= $#instructions; $i++) {
- if ($instructions[$i]->[2] >= 0) {
- $filename = $instructions[$i]->[1];
- last;
- }
- }
- if (!defined($filename)) {
- print STDERR "no filename found in $routine\n";
- return 0;
- }
-
- # Hack 2: assume that the largest line number from $filename is the
- # end of the procedure. This is typically safe since if P1 contains
- # an inlined call to P2, then P2 usually occurs earlier in the
- # source file. If this does not work, we might have to compute a
- # density profile or just print all regions we find.
- my $lastline = 0;
- for (my $i = 0; $i <= $#instructions; $i++) {
- my $f = $instructions[$i]->[1];
- my $l = $instructions[$i]->[2];
- if (($f eq $filename) && ($l > $lastline)) {
- $lastline = $l;
- }
- }
-
- # Hack 3: assume the first source location from "filename" is the start of
- # the source code.
- my $firstline = 1;
- for (my $i = 0; $i <= $#instructions; $i++) {
- if ($instructions[$i]->[1] eq $filename) {
- $firstline = $instructions[$i]->[2];
- last;
- }
- }
-
- # Hack 4: Extend last line forward until its indentation is less than
- # the indentation we saw on $firstline
- my $oldlastline = $lastline;
- {
- if (!open(FILE, "<$filename")) {
- print STDERR "$filename: $!\n";
- return 0;
- }
- my $l = 0;
- my $first_indentation = -1;
- while (<FILE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- $l++;
- my $indent = Indentation($_);
- if ($l >= $firstline) {
- if ($first_indentation < 0 && $indent >= 0) {
- $first_indentation = $indent;
- last if ($first_indentation == 0);
- }
- }
- if ($l >= $lastline && $indent >= 0) {
- if ($indent >= $first_indentation) {
- $lastline = $l+1;
- } else {
- last;
- }
- }
- }
- close(FILE);
- }
-
- # Assign all samples to the range $firstline,$lastline,
- # Hack 4: If an instruction does not occur in the range, its samples
- # are moved to the next instruction that occurs in the range.
- my $samples1 = {}; # Map from line number to flat count
- my $samples2 = {}; # Map from line number to cumulative count
- my $running1 = 0; # Unassigned flat counts
- my $running2 = 0; # Unassigned cumulative counts
- my $total1 = 0; # Total flat counts
- my $total2 = 0; # Total cumulative counts
- my %disasm = (); # Map from line number to disassembly
- my $running_disasm = ""; # Unassigned disassembly
- my $skip_marker = "---\n";
- if ($html) {
- $skip_marker = "";
- for (my $l = $firstline; $l <= $lastline; $l++) {
- $disasm{$l} = "";
- }
- }
- foreach my $e (@instructions) {
- # Add up counts for all address that fall inside this instruction
- my $c1 = 0;
- my $c2 = 0;
- for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
- $c1 += GetEntry($flat, $a);
- $c2 += GetEntry($cumulative, $a);
- }
-
- if ($html) {
- $running_disasm .= sprintf(" %6s %6s \t\t%8s: %s\n",
- HtmlPrintNumber($c1),
- HtmlPrintNumber($c2),
- $e->[0],
- CleanDisassembly($e->[3]));
- }
-
- $running1 += $c1;
- $running2 += $c2;
- $total1 += $c1;
- $total2 += $c2;
- my $file = $e->[1];
- my $line = $e->[2];
- if (($file eq $filename) &&
- ($line >= $firstline) &&
- ($line <= $lastline)) {
- # Assign all accumulated samples to this line
- AddEntry($samples1, $line, $running1);
- AddEntry($samples2, $line, $running2);
- $running1 = 0;
- $running2 = 0;
- if ($html) {
- $disasm{$line} .= $running_disasm;
- $running_disasm = '';
- }
- }
- }
-
- # Assign any leftover samples to $lastline
- AddEntry($samples1, $lastline, $running1);
- AddEntry($samples2, $lastline, $running2);
-
- if ($html) {
- printf $output (
- "<h1>%s</h1>%s\n<pre onClick=\"pprof_toggle_asm()\">\n" .
- "Total:%6s %6s (flat / cumulative %s)\n",
- HtmlEscape(ShortFunctionName($routine)),
- HtmlEscape($filename),
- Unparse($total1),
- Unparse($total2),
- Units());
- } else {
- printf $output (
- "ROUTINE ====================== %s in %s\n" .
- "%6s %6s Total %s (flat / cumulative)\n",
- ShortFunctionName($routine),
- $filename,
- Unparse($total1),
- Unparse($total2),
- Units());
- }
- if (!open(FILE, "<$filename")) {
- print STDERR "$filename: $!\n";
- return 0;
- }
- my $l = 0;
- while (<FILE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- $l++;
- if ($l >= $firstline - 5 &&
- (($l <= $oldlastline + 5) || ($l <= $lastline))) {
- chop;
- my $text = $_;
- if ($l == $firstline) { print $output $skip_marker; }
- my $n1 = GetEntry($samples1, $l);
- my $n2 = GetEntry($samples2, $l);
- if ($html) {
- my $dis = $disasm{$l};
- if (!defined($dis) || $n1 + $n2 == 0) {
- # No samples/disassembly for this source line
- printf $output (
- "<span class=\"line\">%5d</span> " .
- "<span class=\"deadsrc\">%6s %6s %s</span>\n",
- $l,
- HtmlPrintNumber($n1),
- HtmlPrintNumber($n2),
- HtmlEscape($text));
- } else {
- printf $output (
- "<span class=\"line\">%5d</span> " .
- "<span class=\"livesrc\">%6s %6s %s</span>" .
- "<span class=\"asm\">%s</span>\n",
- $l,
- HtmlPrintNumber($n1),
- HtmlPrintNumber($n2),
- HtmlEscape($text),
- HtmlEscape($dis));
- }
- } else {
- printf $output(
- "%6s %6s %4d: %s\n",
- UnparseAlt($n1),
- UnparseAlt($n2),
- $l,
- $text);
- }
- if ($l == $lastline) { print $output $skip_marker; }
- };
- }
- close(FILE);
- if ($html) {
- print $output "</pre>\n";
- }
- return 1;
-}
-
-# Return the source line for the specified file/linenumber.
-# Returns undef if not found.
-sub SourceLine {
- my $file = shift;
- my $line = shift;
-
- # Look in cache
- if (!defined($main::source_cache{$file})) {
- if (100 < scalar keys(%main::source_cache)) {
- # Clear the cache when it gets too big
- $main::source_cache = ();
- }
-
- # Read all lines from the file
- if (!open(FILE, "<$file")) {
- print STDERR "$file: $!\n";
- $main::source_cache{$file} = []; # Cache the negative result
- return undef;
- }
- my $lines = [];
- push(@{$lines}, ""); # So we can use 1-based line numbers as indices
- while (<FILE>) {
- push(@{$lines}, $_);
- }
- close(FILE);
-
- # Save the lines in the cache
- $main::source_cache{$file} = $lines;
- }
-
- my $lines = $main::source_cache{$file};
- if (($line < 0) || ($line > $#{$lines})) {
- return undef;
- } else {
- return $lines->[$line];
- }
-}
-
-# Print disassembly for one routine with interspersed source if available
-sub PrintDisassembledFunction {
- my $prog = shift;
- my $offset = shift;
- my $routine = shift;
- my $flat = shift;
- my $cumulative = shift;
- my $start_addr = shift;
- my $end_addr = shift;
- my $total = shift;
-
- # Disassemble all instructions
- my @instructions = Disassemble($prog, $offset, $start_addr, $end_addr);
-
- # Make array of counts per instruction
- my @flat_count = ();
- my @cum_count = ();
- my $flat_total = 0;
- my $cum_total = 0;
- foreach my $e (@instructions) {
- # Add up counts for all address that fall inside this instruction
- my $c1 = 0;
- my $c2 = 0;
- for (my $a = $e->[0]; $a lt $e->[4]; $a = AddressInc($a)) {
- $c1 += GetEntry($flat, $a);
- $c2 += GetEntry($cumulative, $a);
- }
- push(@flat_count, $c1);
- push(@cum_count, $c2);
- $flat_total += $c1;
- $cum_total += $c2;
- }
-
- # Print header with total counts
- printf("ROUTINE ====================== %s\n" .
- "%6s %6s %s (flat, cumulative) %.1f%% of total\n",
- ShortFunctionName($routine),
- Unparse($flat_total),
- Unparse($cum_total),
- Units(),
- ($cum_total * 100.0) / $total);
-
- # Process instructions in order
- my $current_file = "";
- for (my $i = 0; $i <= $#instructions; ) {
- my $e = $instructions[$i];
-
- # Print the new file name whenever we switch files
- if ($e->[1] ne $current_file) {
- $current_file = $e->[1];
- my $fname = $current_file;
- $fname =~ s|^\./||; # Trim leading "./"
-
- # Shorten long file names
- if (length($fname) >= 58) {
- $fname = "..." . substr($fname, -55);
- }
- printf("-------------------- %s\n", $fname);
- }
-
- # TODO: Compute range of lines to print together to deal with
- # small reorderings.
- my $first_line = $e->[2];
- my $last_line = $first_line;
- my %flat_sum = ();
- my %cum_sum = ();
- for (my $l = $first_line; $l <= $last_line; $l++) {
- $flat_sum{$l} = 0;
- $cum_sum{$l} = 0;
- }
-
- # Find run of instructions for this range of source lines
- my $first_inst = $i;
- while (($i <= $#instructions) &&
- ($instructions[$i]->[2] >= $first_line) &&
- ($instructions[$i]->[2] <= $last_line)) {
- $e = $instructions[$i];
- $flat_sum{$e->[2]} += $flat_count[$i];
- $cum_sum{$e->[2]} += $cum_count[$i];
- $i++;
- }
- my $last_inst = $i - 1;
-
- # Print source lines
- for (my $l = $first_line; $l <= $last_line; $l++) {
- my $line = SourceLine($current_file, $l);
- if (!defined($line)) {
- $line = "?\n";
- next;
- } else {
- $line =~ s/^\s+//;
- }
- printf("%6s %6s %5d: %s",
- UnparseAlt($flat_sum{$l}),
- UnparseAlt($cum_sum{$l}),
- $l,
- $line);
- }
-
- # Print disassembly
- for (my $x = $first_inst; $x <= $last_inst; $x++) {
- my $e = $instructions[$x];
- my $address = $e->[0];
- $address = AddressSub($address, $offset); # Make relative to section
- $address =~ s/^0x//;
- $address =~ s/^0*//;
-
- printf("%6s %6s %8s: %6s\n",
- UnparseAlt($flat_count[$x]),
- UnparseAlt($cum_count[$x]),
- $address,
- CleanDisassembly($e->[3]));
- }
- }
-}
-
-# Print DOT graph
-sub PrintDot {
- my $prog = shift;
- my $symbols = shift;
- my $raw = shift;
- my $flat = shift;
- my $cumulative = shift;
- my $overall_total = shift;
-
- # Get total
- my $local_total = TotalProfile($flat);
- my $nodelimit = int($main::opt_nodefraction * $local_total);
- my $edgelimit = int($main::opt_edgefraction * $local_total);
- my $nodecount = $main::opt_nodecount;
-
- # Find nodes to include
- my @list = (sort { abs(GetEntry($cumulative, $b)) <=>
- abs(GetEntry($cumulative, $a))
- || $a cmp $b }
- keys(%{$cumulative}));
- my $last = $nodecount - 1;
- if ($last > $#list) {
- $last = $#list;
- }
- while (($last >= 0) &&
- (abs(GetEntry($cumulative, $list[$last])) <= $nodelimit)) {
- $last--;
- }
- if ($last < 0) {
- print STDERR "No nodes to print\n";
- cleanup();
- return 0;
- }
-
- if ($nodelimit > 0 || $edgelimit > 0) {
- printf STDERR ("Dropping nodes with <= %s %s; edges with <= %s abs(%s)\n",
- Unparse($nodelimit), Units(),
- Unparse($edgelimit), Units());
- }
-
- # Open DOT output file
- my $output;
- if ($main::opt_gv) {
- $output = "| $DOT -Tps2 >" . TempName($main::next_tmpfile, "ps");
- } elsif ($main::opt_ps) {
- $output = "| $DOT -Tps2";
- } elsif ($main::opt_pdf) {
- $output = "| $DOT -Tps2 | $PS2PDF - -";
- } elsif ($main::opt_web || $main::opt_svg) {
- # We need to post-process the SVG, so write to a temporary file always.
- $output = "| $DOT -Tsvg >" . TempName($main::next_tmpfile, "svg");
- } elsif ($main::opt_gif) {
- $output = "| $DOT -Tgif";
- } else {
- $output = ">&STDOUT";
- }
- open(DOT, $output) || error("$output: $!\n");
-
- # Title
- printf DOT ("digraph \"%s; %s %s\" {\n",
- $prog,
- Unparse($overall_total),
- Units());
- if ($main::opt_pdf) {
- # The output is more printable if we set the page size for dot.
- printf DOT ("size=\"8,11\"\n");
- }
- printf DOT ("node [width=0.375,height=0.25];\n");
-
- # Print legend
- printf DOT ("Legend [shape=box,fontsize=24,shape=plaintext," .
- "label=\"%s\\l%s\\l%s\\l%s\\l%s\\l\"];\n",
- $prog,
- sprintf("Total %s: %s", Units(), Unparse($overall_total)),
- sprintf("Focusing on: %s", Unparse($local_total)),
- sprintf("Dropped nodes with <= %s abs(%s)",
- Unparse($nodelimit), Units()),
- sprintf("Dropped edges with <= %s %s",
- Unparse($edgelimit), Units())
- );
-
- # Print nodes
- my %node = ();
- my $nextnode = 1;
- foreach my $a (@list[0..$last]) {
- # Pick font size
- my $f = GetEntry($flat, $a);
- my $c = GetEntry($cumulative, $a);
-
- my $fs = 8;
- if ($local_total > 0) {
- $fs = 8 + (50.0 * sqrt(abs($f * 1.0 / $local_total)));
- }
-
- $node{$a} = $nextnode++;
- my $sym = $a;
- $sym =~ s/\s+/\\n/g;
- $sym =~ s/::/\\n/g;
-
- # Extra cumulative info to print for non-leaves
- my $extra = "";
- if ($f != $c) {
- $extra = sprintf("\\rof %s (%s)",
- Unparse($c),
- Percent($c, $overall_total));
- }
- my $style = "";
- if ($main::opt_heapcheck) {
- if ($f > 0) {
- # make leak-causing nodes more visible (add a background)
- $style = ",style=filled,fillcolor=gray"
- } elsif ($f < 0) {
- # make anti-leak-causing nodes (which almost never occur)
- # stand out as well (triple border)
- $style = ",peripheries=3"
- }
- }
-
- printf DOT ("N%d [label=\"%s\\n%s (%s)%s\\r" .
- "\",shape=box,fontsize=%.1f%s];\n",
- $node{$a},
- $sym,
- Unparse($f),
- Percent($f, $overall_total),
- $extra,
- $fs,
- $style,
- );
- }
-
- # Get edges and counts per edge
- my %edge = ();
- my $n;
- foreach my $k (keys(%{$raw})) {
- # TODO: omit low %age edges
- $n = $raw->{$k};
- my @translated = TranslateStack($symbols, $k);
- for (my $i = 1; $i <= $#translated; $i++) {
- my $src = $translated[$i];
- my $dst = $translated[$i-1];
- #next if ($src eq $dst); # Avoid self-edges?
- if (exists($node{$src}) && exists($node{$dst})) {
- my $edge_label = "$src\001$dst";
- if (!exists($edge{$edge_label})) {
- $edge{$edge_label} = 0;
- }
- $edge{$edge_label} += $n;
- }
- }
- }
-
- # Print edges
- foreach my $e (keys(%edge)) {
- my @x = split(/\001/, $e);
- $n = $edge{$e};
-
- if (abs($n) > $edgelimit) {
- # Compute line width based on edge count
- my $fraction = abs($local_total ? (3 * ($n / $local_total)) : 0);
- if ($fraction > 1) { $fraction = 1; }
- my $w = $fraction * 2;
- if ($w < 1 && ($main::opt_web || $main::opt_svg)) {
- # SVG output treats line widths < 1 poorly.
- $w = 1;
- }
-
- # Dot sometimes segfaults if given edge weights that are too large, so
- # we cap the weights at a large value
- my $edgeweight = abs($n) ** 0.7;
- if ($edgeweight > 100000) { $edgeweight = 100000; }
- $edgeweight = int($edgeweight);
-
- my $style = sprintf("setlinewidth(%f)", $w);
- if ($x[1] =~ m/\(inline\)/) {
- $style .= ",dashed";
- }
-
- # Use a slightly squashed function of the edge count as the weight
- printf DOT ("N%s -> N%s [label=%s, weight=%d, style=\"%s\"];\n",
- $node{$x[0]},
- $node{$x[1]},
- Unparse($n),
- $edgeweight,
- $style);
- }
- }
-
- print DOT ("}\n");
- close(DOT);
-
- if ($main::opt_web || $main::opt_svg) {
- # Rewrite SVG to be more usable inside web browser.
- RewriteSvg(TempName($main::next_tmpfile, "svg"));
- }
-
- return 1;
-}
-
-sub RewriteSvg {
- my $svgfile = shift;
-
- open(SVG, $svgfile) || die "open temp svg: $!";
- my @svg = <SVG>;
- close(SVG);
- unlink $svgfile;
- my $svg = join('', @svg);
-
- # Dot's SVG output is
- #
- # <svg width="___" height="___"
- # viewBox="___" xmlns=...>
- # <g id="graph0" transform="...">
- # ...
- # </g>
- # </svg>
- #
- # Change it to
- #
- # <svg width="100%" height="100%"
- # xmlns=...>
- # $svg_javascript
- # <g id="viewport" transform="translate(0,0)">
- # <g id="graph0" transform="...">
- # ...
- # </g>
- # </g>
- # </svg>
-
- # Fix width, height; drop viewBox.
- $svg =~ s/(?s)<svg width="[^"]+" height="[^"]+"(.*?)viewBox="[^"]+"/<svg width="100%" height="100%"$1/;
-
- # Insert script, viewport <g> above first <g>
- my $svg_javascript = SvgJavascript();
- my $viewport = "<g id=\"viewport\" transform=\"translate(0,0)\">\n";
- $svg =~ s/<g id="graph\d"/$svg_javascript$viewport$&/;
-
- # Insert final </g> above </svg>.
- $svg =~ s/(.*)(<\/svg>)/$1<\/g>$2/;
- $svg =~ s/<g id="graph\d"(.*?)/<g id="viewport"$1/;
-
- if ($main::opt_svg) {
- # --svg: write to standard output.
- print $svg;
- } else {
- # Write back to temporary file.
- open(SVG, ">$svgfile") || die "open $svgfile: $!";
- print SVG $svg;
- close(SVG);
- }
-}
-
-sub SvgJavascript {
- return <<'EOF';
-<script type="text/ecmascript"><![CDATA[
-// SVGPan
-// http://www.cyberz.org/blog/2009/12/08/svgpan-a-javascript-svg-panzoomdrag-library/
-// Local modification: if(true || ...) below to force panning, never moving.
-// Local modification: add clamping to fix bug in handleMouseWheel.
-
-/**
- * SVGPan library 1.2
- * ====================
- *
- * Given an unique existing element with id "viewport", including the
- * the library into any SVG adds the following capabilities:
- *
- * - Mouse panning
- * - Mouse zooming (using the wheel)
- * - Object dargging
- *
- * Known issues:
- *
- * - Zooming (while panning) on Safari has still some issues
- *
- * Releases:
- *
- * 1.2, Sat Mar 20 08:42:50 GMT 2010, Zeng Xiaohui
- * Fixed a bug with browser mouse handler interaction
- *
- * 1.1, Wed Feb 3 17:39:33 GMT 2010, Zeng Xiaohui
- * Updated the zoom code to support the mouse wheel on Safari/Chrome
- *
- * 1.0, Andrea Leofreddi
- * First release
- *
- * This code is licensed under the following BSD license:
- *
- * Copyright 2009-2010 Andrea Leofreddi <a.leofreddi@itcharm.com>. All rights reserved.
- *
- * Redistribution and use in source and binary forms, with or without modification, are
- * permitted provided that the following conditions are met:
- *
- * 1. Redistributions of source code must retain the above copyright notice, this list of
- * conditions and the following disclaimer.
- *
- * 2. Redistributions in binary form must reproduce the above copyright notice, this list
- * of conditions and the following disclaimer in the documentation and/or other materials
- * provided with the distribution.
- *
- * THIS SOFTWARE IS PROVIDED BY Andrea Leofreddi ``AS IS'' AND ANY EXPRESS OR IMPLIED
- * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND
- * FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL Andrea Leofreddi OR
- * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
- * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
- * SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- * ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
- * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
- * ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- * The views and conclusions contained in the software and documentation are those of the
- * authors and should not be interpreted as representing official policies, either expressed
- * or implied, of Andrea Leofreddi.
- */
-
-var root = document.documentElement;
-
-var state = 'none', stateTarget, stateOrigin, stateTf;
-
-setupHandlers(root);
-
-/**
- * Register handlers
- */
-function setupHandlers(root){
- setAttributes(root, {
- "onmouseup" : "add(evt)",
- "onmousedown" : "handleMouseDown(evt)",
- "onmousemove" : "handleMouseMove(evt)",
- "onmouseup" : "handleMouseUp(evt)",
- //"onmouseout" : "handleMouseUp(evt)", // Decomment this to stop the pan functionality when dragging out of the SVG element
- });
-
- if(navigator.userAgent.toLowerCase().indexOf('webkit') >= 0)
- window.addEventListener('mousewheel', handleMouseWheel, false); // Chrome/Safari
- else
- window.addEventListener('DOMMouseScroll', handleMouseWheel, false); // Others
-
- var g = svgDoc.getElementById("svg");
- g.width = "100%";
- g.height = "100%";
-}
-
-/**
- * Instance an SVGPoint object with given event coordinates.
- */
-function getEventPoint(evt) {
- var p = root.createSVGPoint();
-
- p.x = evt.clientX;
- p.y = evt.clientY;
-
- return p;
-}
-
-/**
- * Sets the current transform matrix of an element.
- */
-function setCTM(element, matrix) {
- var s = "matrix(" + matrix.a + "," + matrix.b + "," + matrix.c + "," + matrix.d + "," + matrix.e + "," + matrix.f + ")";
-
- element.setAttribute("transform", s);
-}
-
-/**
- * Dumps a matrix to a string (useful for debug).
- */
-function dumpMatrix(matrix) {
- var s = "[ " + matrix.a + ", " + matrix.c + ", " + matrix.e + "\n " + matrix.b + ", " + matrix.d + ", " + matrix.f + "\n 0, 0, 1 ]";
-
- return s;
-}
-
-/**
- * Sets attributes of an element.
- */
-function setAttributes(element, attributes){
- for (i in attributes)
- element.setAttributeNS(null, i, attributes[i]);
-}
-
-/**
- * Handle mouse move event.
- */
-function handleMouseWheel(evt) {
- if(evt.preventDefault)
- evt.preventDefault();
-
- evt.returnValue = false;
-
- var svgDoc = evt.target.ownerDocument;
-
- var delta;
-
- if(evt.wheelDelta)
- delta = evt.wheelDelta / 3600; // Chrome/Safari
- else
- delta = evt.detail / -90; // Mozilla
-
- var z = 1 + delta; // Zoom factor: 0.9/1.1
-
- // Clamp to reasonable values.
- // The 0.1 check is important because
- // a very large scroll can turn into a
- // negative z, which rotates the image 180 degrees.
- if(z < 0.1)
- z = 0.1;
- if(z > 10.0)
- z = 10.0;
-
- var g = svgDoc.getElementById("viewport");
-
- var p = getEventPoint(evt);
-
- p = p.matrixTransform(g.getCTM().inverse());
-
- // Compute new scale matrix in current mouse position
- var k = root.createSVGMatrix().translate(p.x, p.y).scale(z).translate(-p.x, -p.y);
-
- setCTM(g, g.getCTM().multiply(k));
-
- stateTf = stateTf.multiply(k.inverse());
-}
-
-/**
- * Handle mouse move event.
- */
-function handleMouseMove(evt) {
- if(evt.preventDefault)
- evt.preventDefault();
-
- evt.returnValue = false;
-
- var svgDoc = evt.target.ownerDocument;
-
- var g = svgDoc.getElementById("viewport");
-
- if(state == 'pan') {
- // Pan mode
- var p = getEventPoint(evt).matrixTransform(stateTf);
-
- setCTM(g, stateTf.inverse().translate(p.x - stateOrigin.x, p.y - stateOrigin.y));
- } else if(state == 'move') {
- // Move mode
- var p = getEventPoint(evt).matrixTransform(g.getCTM().inverse());
-
- setCTM(stateTarget, root.createSVGMatrix().translate(p.x - stateOrigin.x, p.y - stateOrigin.y).multiply(g.getCTM().inverse()).multiply(stateTarget.getCTM()));
-
- stateOrigin = p;
- }
-}
-
-/**
- * Handle click event.
- */
-function handleMouseDown(evt) {
- if(evt.preventDefault)
- evt.preventDefault();
-
- evt.returnValue = false;
-
- var svgDoc = evt.target.ownerDocument;
-
- var g = svgDoc.getElementById("viewport");
-
- if(true || evt.target.tagName == "svg") {
- // Pan mode
- state = 'pan';
-
- stateTf = g.getCTM().inverse();
-
- stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
- } else {
- // Move mode
- state = 'move';
-
- stateTarget = evt.target;
-
- stateTf = g.getCTM().inverse();
-
- stateOrigin = getEventPoint(evt).matrixTransform(stateTf);
- }
-}
-
-/**
- * Handle mouse button release event.
- */
-function handleMouseUp(evt) {
- if(evt.preventDefault)
- evt.preventDefault();
-
- evt.returnValue = false;
-
- var svgDoc = evt.target.ownerDocument;
-
- if(state == 'pan' || state == 'move') {
- // Quit pan mode
- state = '';
- }
-}
-
-]]></script>
-EOF
-}
-
-# Translate a stack of addresses into a stack of symbols
-sub TranslateStack {
- my $symbols = shift;
- my $k = shift;
-
- my @addrs = split(/\n/, $k);
- my @result = ();
- for (my $i = 0; $i <= $#addrs; $i++) {
- my $a = $addrs[$i];
-
- # Skip large addresses since they sometimes show up as fake entries on RH9
- if (length($a) > 8 && $a gt "7fffffffffffffff") {
- next;
- }
-
- if ($main::opt_disasm || $main::opt_list) {
- # We want just the address for the key
- push(@result, $a);
- next;
- }
-
- my $symlist = $symbols->{$a};
- if (!defined($symlist)) {
- $symlist = [$a, "", $a];
- }
-
- # We can have a sequence of symbols for a particular entry
- # (more than one symbol in the case of inlining). Callers
- # come before callees in symlist, so walk backwards since
- # the translated stack should contain callees before callers.
- for (my $j = $#{$symlist}; $j >= 2; $j -= 3) {
- my $func = $symlist->[$j-2];
- my $fileline = $symlist->[$j-1];
- my $fullfunc = $symlist->[$j];
- if ($j > 2) {
- $func = "$func (inline)";
- }
- if ($main::opt_addresses) {
- push(@result, "$a $func $fileline");
- } elsif ($main::opt_lines) {
- if ($func eq '??' && $fileline eq '??:0') {
- push(@result, "$a");
- } else {
- push(@result, "$func $fileline");
- }
- } elsif ($main::opt_functions) {
- if ($func eq '??') {
- push(@result, "$a");
- } else {
- push(@result, $func);
- }
- } elsif ($main::opt_files) {
- if ($fileline eq '??:0' || $fileline eq '') {
- push(@result, "$a");
- } else {
- my $f = $fileline;
- $f =~ s/:\d+$//;
- push(@result, $f);
- }
- } else {
- push(@result, $a);
- last; # Do not print inlined info
- }
- }
- }
-
- # print join(",", @addrs), " => ", join(",", @result), "\n";
- return @result;
-}
-
-# Generate percent string for a number and a total
-sub Percent {
- my $num = shift;
- my $tot = shift;
- if ($tot != 0) {
- return sprintf("%.1f%%", $num * 100.0 / $tot);
- } else {
- return ($num == 0) ? "nan" : (($num > 0) ? "+inf" : "-inf");
- }
-}
-
-# Generate pretty-printed form of number
-sub Unparse {
- my $num = shift;
- if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
- if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
- return sprintf("%d", $num);
- } else {
- if ($main::opt_show_bytes) {
- return sprintf("%d", $num);
- } else {
- return sprintf("%.1f", $num / 1048576.0);
- }
- }
- } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
- return sprintf("%.3f", $num / 1e9); # Convert nanoseconds to seconds
- } else {
- return sprintf("%d", $num);
- }
-}
-
-# Alternate pretty-printed form: 0 maps to "."
-sub UnparseAlt {
- my $num = shift;
- if ($num == 0) {
- return ".";
- } else {
- return Unparse($num);
- }
-}
-
-# Alternate pretty-printed form: 0 maps to ""
-sub HtmlPrintNumber {
- my $num = shift;
- if ($num == 0) {
- return "";
- } else {
- return Unparse($num);
- }
-}
-
-# Return output units
-sub Units {
- if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
- if ($main::opt_inuse_objects || $main::opt_alloc_objects) {
- return "objects";
- } else {
- if ($main::opt_show_bytes) {
- return "B";
- } else {
- return "MB";
- }
- }
- } elsif ($main::profile_type eq 'contention' && !$main::opt_contentions) {
- return "seconds";
- } elsif ($main::profile_type eq 'thread') {
- return "threads";
- } else {
- return "samples";
- }
-}
-
-##### Profile manipulation code #####
-
-# Generate flattened profile:
-# If count is charged to stack [a,b,c,d], in generated profile,
-# it will be charged to [a]
-sub FlatProfile {
- my $profile = shift;
- my $result = {};
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- if ($#addrs >= 0) {
- AddEntry($result, $addrs[0], $count);
- }
- }
- return $result;
-}
-
-# Generate cumulative profile:
-# If count is charged to stack [a,b,c,d], in generated profile,
-# it will be charged to [a], [b], [c], [d]
-sub CumulativeProfile {
- my $profile = shift;
- my $result = {};
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- foreach my $a (@addrs) {
- AddEntry($result, $a, $count);
- }
- }
- return $result;
-}
-
-# If the second-youngest PC on the stack is always the same, returns
-# that pc. Otherwise, returns undef.
-sub IsSecondPcAlwaysTheSame {
- my $profile = shift;
-
- my $second_pc = undef;
- foreach my $k (keys(%{$profile})) {
- my @addrs = split(/\n/, $k);
- if ($#addrs < 1) {
- return undef;
- }
- if (not defined $second_pc) {
- $second_pc = $addrs[1];
- } else {
- if ($second_pc ne $addrs[1]) {
- return undef;
- }
- }
- }
- return $second_pc;
-}
-
-sub ExtractSymbolLocation {
- my $symbols = shift;
- my $address = shift;
- # 'addr2line' outputs "??:0" for unknown locations; we do the
- # same to be consistent.
- my $location = "??:0:unknown";
- if (exists $symbols->{$address}) {
- my $file = $symbols->{$address}->[1];
- if ($file eq "?") {
- $file = "??:0"
- }
- $location = $file . ":" . $symbols->{$address}->[0];
- }
- return $location;
-}
-
-# Extracts a graph of calls.
-sub ExtractCalls {
- my $symbols = shift;
- my $profile = shift;
-
- my $calls = {};
- while( my ($stack_trace, $count) = each %$profile ) {
- my @address = split(/\n/, $stack_trace);
- my $destination = ExtractSymbolLocation($symbols, $address[0]);
- AddEntry($calls, $destination, $count);
- for (my $i = 1; $i <= $#address; $i++) {
- my $source = ExtractSymbolLocation($symbols, $address[$i]);
- my $call = "$source -> $destination";
- AddEntry($calls, $call, $count);
- $destination = $source;
- }
- }
-
- return $calls;
-}
-
-sub RemoveUninterestingFrames {
- my $symbols = shift;
- my $profile = shift;
-
- # List of function names to skip
- my %skip = ();
- my $skip_regexp = 'NOMATCH';
- if ($main::profile_type eq 'heap' || $main::profile_type eq 'growth') {
- foreach my $name ('calloc',
- 'cfree',
- 'malloc',
- 'free',
- 'memalign',
- 'posix_memalign',
- 'pvalloc',
- 'valloc',
- 'realloc',
- 'tc_calloc',
- 'tc_cfree',
- 'tc_malloc',
- 'tc_free',
- 'tc_memalign',
- 'tc_posix_memalign',
- 'tc_pvalloc',
- 'tc_valloc',
- 'tc_realloc',
- 'tc_new',
- 'tc_delete',
- 'tc_newarray',
- 'tc_deletearray',
- 'tc_new_nothrow',
- 'tc_newarray_nothrow',
- 'do_malloc',
- '::do_malloc', # new name -- got moved to an unnamed ns
- '::do_malloc_or_cpp_alloc',
- 'DoSampledAllocation',
- 'simple_alloc::allocate',
- '__malloc_alloc_template::allocate',
- '__builtin_delete',
- '__builtin_new',
- '__builtin_vec_delete',
- '__builtin_vec_new',
- 'operator new',
- 'operator new[]',
- # Go
- 'catstring',
- 'cnew',
- 'copyin',
- 'gostring',
- 'gostringsize',
- 'growslice1',
- 'appendslice1',
- 'hash_init',
- 'hash_subtable_new',
- 'hash_conv',
- 'hash_grow',
- 'hash_insert_internal',
- 'hash_insert',
- 'mapassign',
- 'runtime.mapassign',
- 'runtime.appendslice',
- 'runtime.mapassign1',
- 'makechan',
- 'makemap',
- 'mal',
- 'profilealloc',
- 'runtime.new',
- 'makeslice1',
- 'runtime.malloc',
- 'unsafe.New',
- 'runtime.mallocgc',
- 'runtime.catstring',
- 'runtime.cnew',
- 'runtime.cnewarray',
- 'runtime.growslice',
- 'runtime.ifaceT2E',
- 'runtime.ifaceT2I',
- 'runtime.makechan',
- 'runtime.makechan_c',
- 'runtime.makemap',
- 'runtime.makemap_c',
- 'runtime.makeslice',
- 'runtime.mal',
- 'runtime.settype',
- 'runtime.settype_flush',
- 'runtime.slicebytetostring',
- 'runtime.sliceinttostring',
- 'runtime.stringtoslicebyte',
- 'runtime.stringtosliceint',
- # These mark the beginning/end of our custom sections
- '__start_google_malloc',
- '__stop_google_malloc',
- '__start_malloc_hook',
- '__stop_malloc_hook') {
- $skip{$name} = 1;
- $skip{"_" . $name} = 1; # Mach (OS X) adds a _ prefix to everything
- }
- # TODO: Remove TCMalloc once everything has been
- # moved into the tcmalloc:: namespace and we have flushed
- # old code out of the system.
- $skip_regexp = "TCMalloc|^tcmalloc::";
- } elsif ($main::profile_type eq 'contention') {
- foreach my $vname ('Mutex::Unlock', 'Mutex::UnlockSlow') {
- $skip{$vname} = 1;
- }
- } elsif ($main::profile_type eq 'cpu') {
- # Drop signal handlers used for CPU profile collection
- # TODO(dpeng): this should not be necessary; it's taken
- # care of by the general 2nd-pc mechanism below.
- foreach my $name ('ProfileData::Add', # historical
- 'ProfileData::prof_handler', # historical
- 'CpuProfiler::prof_handler',
- '__FRAME_END__',
- '__pthread_sighandler',
- '__restore') {
- $skip{$name} = 1;
- }
- } else {
- # Nothing skipped for unknown types
- }
-
- # Go doesn't have the problem that this heuristic tries to fix. Disable.
- if (0 && $main::profile_type eq 'cpu') {
- # If all the second-youngest program counters are the same,
- # this STRONGLY suggests that it is an artifact of measurement,
- # i.e., stack frames pushed by the CPU profiler signal handler.
- # Hence, we delete them.
- # (The topmost PC is read from the signal structure, not from
- # the stack, so it does not get involved.)
- while (my $second_pc = IsSecondPcAlwaysTheSame($profile)) {
- my $result = {};
- my $func = '';
- if (exists($symbols->{$second_pc})) {
- $second_pc = $symbols->{$second_pc}->[0];
- }
- print STDERR "Removing $second_pc from all stack traces.\n";
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- splice @addrs, 1, 1;
- my $reduced_path = join("\n", @addrs);
- AddEntry($result, $reduced_path, $count);
- }
- $profile = $result;
- }
- }
-
- my $result = {};
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- my @path = ();
- foreach my $a (@addrs) {
- if (exists($symbols->{$a})) {
- my $func = $symbols->{$a}->[0];
- if ($skip{$func} || ($func =~ m/$skip_regexp/)) {
- next;
- }
- }
- push(@path, $a);
- }
- my $reduced_path = join("\n", @path);
- AddEntry($result, $reduced_path, $count);
- }
- return $result;
-}
-
-# Reduce profile to granularity given by user
-sub ReduceProfile {
- my $symbols = shift;
- my $profile = shift;
- my $result = {};
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @translated = TranslateStack($symbols, $k);
- my @path = ();
- my %seen = ();
- $seen{''} = 1; # So that empty keys are skipped
- foreach my $e (@translated) {
- # To avoid double-counting due to recursion, skip a stack-trace
- # entry if it has already been seen
- if (!$seen{$e}) {
- $seen{$e} = 1;
- push(@path, $e);
- }
- }
- my $reduced_path = join("\n", @path);
- AddEntry($result, $reduced_path, $count);
- }
- return $result;
-}
-
-# Does the specified symbol array match the regexp?
-sub SymbolMatches {
- my $sym = shift;
- my $re = shift;
- if (defined($sym)) {
- for (my $i = 0; $i < $#{$sym}; $i += 3) {
- if ($sym->[$i] =~ m/$re/ || $sym->[$i+1] =~ m/$re/) {
- return 1;
- }
- }
- }
- return 0;
-}
-
-# Focus only on paths involving specified regexps
-sub FocusProfile {
- my $symbols = shift;
- my $profile = shift;
- my $focus = shift;
- my $result = {};
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- foreach my $a (@addrs) {
- # Reply if it matches either the address/shortname/fileline
- if (($a =~ m/$focus/) || SymbolMatches($symbols->{$a}, $focus)) {
- AddEntry($result, $k, $count);
- last;
- }
- }
- }
- return $result;
-}
-
-# Focus only on paths not involving specified regexps
-sub IgnoreProfile {
- my $symbols = shift;
- my $profile = shift;
- my $ignore = shift;
- my $result = {};
- foreach my $k (keys(%{$profile})) {
- my $count = $profile->{$k};
- my @addrs = split(/\n/, $k);
- my $matched = 0;
- foreach my $a (@addrs) {
- # Reply if it matches either the address/shortname/fileline
- if (($a =~ m/$ignore/) || SymbolMatches($symbols->{$a}, $ignore)) {
- $matched = 1;
- last;
- }
- }
- if (!$matched) {
- AddEntry($result, $k, $count);
- }
- }
- return $result;
-}
-
-# Get total count in profile
-sub TotalProfile {
- my $profile = shift;
- my $result = 0;
- foreach my $k (keys(%{$profile})) {
- $result += $profile->{$k};
- }
- return $result;
-}
-
-# Add A to B
-sub AddProfile {
- my $A = shift;
- my $B = shift;
-
- my $R = {};
- # add all keys in A
- foreach my $k (keys(%{$A})) {
- my $v = $A->{$k};
- AddEntry($R, $k, $v);
- }
- # add all keys in B
- foreach my $k (keys(%{$B})) {
- my $v = $B->{$k};
- AddEntry($R, $k, $v);
- }
- return $R;
-}
-
-# Merges symbol maps
-sub MergeSymbols {
- my $A = shift;
- my $B = shift;
-
- my $R = {};
- foreach my $k (keys(%{$A})) {
- $R->{$k} = $A->{$k};
- }
- if (defined($B)) {
- foreach my $k (keys(%{$B})) {
- $R->{$k} = $B->{$k};
- }
- }
- return $R;
-}
-
-
-# Add A to B
-sub AddPcs {
- my $A = shift;
- my $B = shift;
-
- my $R = {};
- # add all keys in A
- foreach my $k (keys(%{$A})) {
- $R->{$k} = 1
- }
- # add all keys in B
- foreach my $k (keys(%{$B})) {
- $R->{$k} = 1
- }
- return $R;
-}
-
-# Subtract B from A
-sub SubtractProfile {
- my $A = shift;
- my $B = shift;
-
- my $R = {};
- foreach my $k (keys(%{$A})) {
- my $v = $A->{$k} - GetEntry($B, $k);
- if ($v < 0 && $main::opt_drop_negative) {
- $v = 0;
- }
- AddEntry($R, $k, $v);
- }
- if (!$main::opt_drop_negative) {
- # Take care of when subtracted profile has more entries
- foreach my $k (keys(%{$B})) {
- if (!exists($A->{$k})) {
- AddEntry($R, $k, 0 - $B->{$k});
- }
- }
- }
- return $R;
-}
-
-# Get entry from profile; zero if not present
-sub GetEntry {
- my $profile = shift;
- my $k = shift;
- if (exists($profile->{$k})) {
- return $profile->{$k};
- } else {
- return 0;
- }
-}
-
-# Add entry to specified profile
-sub AddEntry {
- my $profile = shift;
- my $k = shift;
- my $n = shift;
- if (!exists($profile->{$k})) {
- $profile->{$k} = 0;
- }
- $profile->{$k} += $n;
-}
-
-# Add a stack of entries to specified profile, and add them to the $pcs
-# list.
-sub AddEntries {
- my $profile = shift;
- my $pcs = shift;
- my $stack = shift;
- my $count = shift;
- my @k = ();
-
- foreach my $e (split(/\s+/, $stack)) {
- my $pc = HexExtend($e);
- $pcs->{$pc} = 1;
- push @k, $pc;
- }
- AddEntry($profile, (join "\n", @k), $count);
-}
-
-sub IsSymbolizedProfileFile {
- my $file_name = shift;
-
- if (!(-e $file_name) || !(-r $file_name)) {
- return 0;
- }
-
- $SYMBOL_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $symbol_marker = $&;
- # Check if the file contains a symbol-section marker.
- open(TFILE, "<$file_name");
- my @lines = <TFILE>;
- my $result = grep(/^--- *$symbol_marker/, @lines);
- close(TFILE);
- return $result > 0;
-}
-
-##### Code to profile a server dynamically #####
-
-sub CheckSymbolPage {
- my $url = SymbolPageURL();
-print STDERR "Read $url\n";
-
- my $line = FetchHTTP($url);
- $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
- unless (defined($line)) {
- error("$url doesn't exist\n");
- }
-
- if ($line =~ /^num_symbols:\s+(\d+)$/) {
- if ($1 == 0) {
- error("Stripped binary. No symbols available.\n");
- }
- } else {
- error("Failed to get the number of symbols from $url\n");
- }
-}
-
-sub IsProfileURL {
- my $profile_name = shift;
- my ($scheme, $host, $port, $prefix, $path) = ParseProfileURL($profile_name);
- return defined($host) and defined($port) and defined($path);
-}
-
-sub ParseProfileURL {
- my $profile_name = shift;
- if (defined($profile_name) &&
- $profile_name =~ m,^(?:(https?)://|)([^/:]+):(\d+)(|\@\d+)(|/|(.*?)($PROFILE_PAGE|$PMUPROFILE_PAGE|$HEAP_PAGE|$GROWTH_PAGE|$THREAD_PAGE|$BLOCK_PAGE|$CONTENTION_PAGE|$WALL_PAGE|$FILTEREDPROFILE_PAGE))$,o) {
- # $7 is $PROFILE_PAGE/$HEAP_PAGE/etc. $5 is *everything* after
- # the hostname, as long as that everything is the empty string,
- # a slash, or something ending in $PROFILE_PAGE/$HEAP_PAGE/etc.
- # So "$7 || $5" is $PROFILE_PAGE/etc if there, or else it's "/" or "".
- return ($1 || "http", $2, $3, $6, $7 || $5);
- }
- return ();
-}
-
-# We fetch symbols from the first profile argument.
-sub SymbolPageURL {
- my ($scheme, $host, $port, $prefix, $path) = ParseProfileURL($main::pfile_args[0]);
- return "$scheme://$host:$port$prefix$SYMBOL_PAGE";
-}
-
-sub FetchProgramName() {
- my ($scheme, $host, $port, $prefix, $path) = ParseProfileURL($main::pfile_args[0]);
- my $url = "$scheme://$host:$port$prefix$PROGRAM_NAME_PAGE";
-
- my $cmdline = FetchHTTP($url);
- $cmdline =~ s/\n.*//s; # first line only
- $cmdline =~ s/\r//g; # turn windows-looking lines into unix-looking lines
- error("Failed to get program name from $url\n") unless defined($cmdline);
- $cmdline =~ s/\x00.+//; # Remove argv[1] and latters.
- $cmdline =~ s!\n!!g; # Remove LFs.
- return $cmdline;
-}
-
-# Reads a symbol map from the file handle name given as $1, returning
-# the resulting symbol map. Also processes variables relating to symbols.
-# Currently, the only variable processed is 'binary=<value>' which updates
-# $main::prog to have the correct program name.
-sub ReadSymbols {
- my $in = shift;
- my $map = shift;
- while (<$in>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- # Removes all the leading zeroes from the symbols, see comment below.
- if (m/^0x0*([0-9a-f]+)\s+(.+)/) {
- $map->{$1} = $2;
- } elsif (m/^---/) {
- last;
- } elsif (m/^([a-z][^=]*)=(.*)$/ ) {
- my ($variable, $value) = ($1, $2);
- for ($variable, $value) {
- s/^\s+//;
- s/\s+$//;
- }
- if ($variable eq "binary") {
- if ($main::prog ne $UNKNOWN_BINARY && $main::prog ne $value) {
- printf STDERR ("Warning: Mismatched binary name '%s', using '%s'.\n",
- $main::prog, $value);
- }
- $main::prog = $value;
- } else {
- printf STDERR ("Ignoring unknown variable in symbols list: " .
- "'%s' = '%s'\n", $variable, $value);
- }
- }
- }
- return $map;
-}
-
-# Fetches and processes symbols to prepare them for use in the profile output
-# code. If the optional 'symbol_map' arg is not given, fetches symbols from
-# $SYMBOL_PAGE for all PC values found in profile. Otherwise, the raw symbols
-# are assumed to have already been fetched into 'symbol_map' and are simply
-# extracted and processed.
-sub FetchSymbols {
- my $pcset = shift;
- my $symbol_map = shift;
-
- my %seen = ();
- my @pcs = grep { !$seen{$_}++ } keys(%$pcset); # uniq
-
- if (!defined($symbol_map)) {
- $symbol_map = {};
-
- my $post_data = join("+", sort((map {"0x" . "$_"} @pcs)));
- my $url = SymbolPageURL();
- my $content = PostHTTP($url, $post_data);
-
- my $tmp_symbol = File::Temp->new()->filename;
- open(SYMBOL, ">$tmp_symbol");
- print SYMBOL $content;
- close(SYMBOL);
-
- open(SYMBOL, "<$tmp_symbol") || error("$tmp_symbol");
- ReadSymbols(*SYMBOL{IO}, $symbol_map);
- close(SYMBOL);
- }
-
- my $symbols = {};
- foreach my $pc (@pcs) {
- my $fullname;
- # For 64 bits binaries, symbols are extracted with 8 leading zeroes.
- # Then /symbol reads the long symbols in as uint64, and outputs
- # the result with a "0x%08llx" format which get rid of the zeroes.
- # By removing all the leading zeroes in both $pc and the symbols from
- # /symbol, the symbols match and are retrievable from the map.
- my $shortpc = $pc;
- $shortpc =~ s/^0*//;
- # Each line may have a list of names, which includes the function
- # and also other functions it has inlined. They are separated
- # (in PrintSymbolizedFile), by --, which is illegal in function names.
- my $fullnames;
- if (defined($symbol_map->{$shortpc})) {
- $fullnames = $symbol_map->{$shortpc};
- } else {
- $fullnames = "0x" . $pc; # Just use addresses
- }
- my $sym = [];
- $symbols->{$pc} = $sym;
- foreach my $fullname (split("--", $fullnames)) {
- my $name = ShortFunctionName($fullname);
- push(@{$sym}, $name, "?", $fullname);
- }
- }
- return $symbols;
-}
-
-sub BaseName {
- my $file_name = shift;
- $file_name =~ s!^.*/!!; # Remove directory name
- return $file_name;
-}
-
-sub MakeProfileBaseName {
- my ($binary_name, $profile_name) = @_;
- my ($scheme, $host, $port, $prefix, $path) = ParseProfileURL($profile_name);
- my $binary_shortname = BaseName($binary_name);
- return sprintf("%s.%s.%s-port%s",
- $binary_shortname, $main::op_time, $host, $port);
-}
-
-sub FetchDynamicProfile {
- my $binary_name = shift;
- my $profile_name = shift;
- my $fetch_name_only = shift;
- my $encourage_patience = shift;
-
- if (!IsProfileURL($profile_name)) {
- return $profile_name;
- } else {
- my ($scheme, $host, $port, $prefix, $path) = ParseProfileURL($profile_name);
- if ($path eq "" || $path eq "/") {
- # Missing type specifier defaults to cpu-profile
- $path = $PROFILE_PAGE;
- }
-
- my $profile_file = MakeProfileBaseName($binary_name, $profile_name);
-
- my $url;
- my $timeout;
- if (($path =~ m/$PROFILE_PAGE/) || ($path =~ m/$PMUPROFILE_PAGE/)) {
- if ($path =~ m/$PROFILE_PAGE/) {
- $url = sprintf("$scheme://$host:$port$prefix$path?seconds=%d",
- $main::opt_seconds);
- } else {
- if ($profile_name =~ m/[?]/) {
- $profile_name .= "&"
- } else {
- $profile_name .= "?"
- }
- $url = sprintf("$scheme://$profile_name" . "seconds=%d",
- $main::opt_seconds);
- }
- $timeout = int($main::opt_seconds * 1.01 + 60);
- } else {
- # For non-CPU profiles, we add a type-extension to
- # the target profile file name.
- my $suffix = $path;
- $suffix =~ s,/,.,g;
- $profile_file .= "$suffix";
- $url = "$scheme://$host:$port$prefix$path";
- }
-
- my $tmp_profile = File::Temp->new()->filename;
- my $real_profile = File::Temp->new()->filename;
-
- if ($fetch_name_only > 0) {
- return $real_profile;
- }
-
- if (($path =~ m/$PROFILE_PAGE/) || ($path =~ m/$PMUPROFILE_PAGE/)){
- print STDERR "Gathering CPU profile from $url for $main::opt_seconds seconds to\n ${real_profile}\n";
- if ($encourage_patience) {
- print STDERR "Be patient...\n";
- }
- } else {
- print STDERR "Fetching $path profile from $host:$port to\n ${real_profile}\n";
- }
-
- my $content = FetchHTTP($url, $timeout);
-
- open(OUTFILE, ">$tmp_profile");
- binmode(OUTFILE);
- print OUTFILE $content;
- close(OUTFILE);
-
- my $line = $content;
- $line !~ /^Could not enable CPU profiling/ || error($line);
-
- copy($tmp_profile, $real_profile) || error("Unable to copy profile\n");
- print STDERR "Wrote profile to $real_profile\n";
- $main::collected_profile = $real_profile;
- return $main::collected_profile;
- }
-}
-
-# Collect profiles in parallel
-sub FetchDynamicProfiles {
- my $items = scalar(@main::pfile_args);
- my $levels = log($items) / log(2);
-
- if ($items == 1) {
- $main::profile_files[0] = FetchDynamicProfile($main::prog, $main::pfile_args[0], 0, 1);
- } else {
- # math rounding issues
- if ((2 ** $levels) < $items) {
- $levels++;
- }
- my $count = scalar(@main::pfile_args);
- for (my $i = 0; $i < $count; $i++) {
- $main::profile_files[$i] = FetchDynamicProfile($main::prog, $main::pfile_args[$i], 1, 0);
- }
- print STDERR "Fetching $count profiles, Be patient...\n";
- FetchDynamicProfilesRecurse($levels, 0, 0);
- $main::collected_profile = join(" \\\n ", @main::profile_files);
- }
-}
-
-# Recursively fork a process to get enough processes
-# collecting profiles
-sub FetchDynamicProfilesRecurse {
- my $maxlevel = shift;
- my $level = shift;
- my $position = shift;
-
- if (my $pid = fork()) {
- $position = 0 | ($position << 1);
- TryCollectProfile($maxlevel, $level, $position);
- wait;
- } else {
- $position = 1 | ($position << 1);
- TryCollectProfile($maxlevel, $level, $position);
- exit(0);
- }
-}
-
-# Collect a single profile
-sub TryCollectProfile {
- my $maxlevel = shift;
- my $level = shift;
- my $position = shift;
-
- if ($level >= ($maxlevel - 1)) {
- if ($position < scalar(@main::pfile_args)) {
- FetchDynamicProfile($main::prog, $main::pfile_args[$position], 0, 0);
- }
- } else {
- FetchDynamicProfilesRecurse($maxlevel, $level+1, $position);
- }
-}
-
-##### Parsing code #####
-
-# Provide a small streaming-read module to handle very large
-# cpu-profile files. Stream in chunks along a sliding window.
-# Provides an interface to get one 'slot', correctly handling
-# endian-ness differences. A slot is one 32-bit or 64-bit word
-# (depending on the input profile). We tell endianness and bit-size
-# for the profile by looking at the first 8 bytes: in cpu profiles,
-# the second slot is always 3 (we'll accept anything that's not 0).
-BEGIN {
- package CpuProfileStream;
-
- sub new {
- my ($class, $file, $fname) = @_;
- my $self = { file => $file,
- base => 0,
- stride => 512 * 1024, # must be a multiple of bitsize/8
- slots => [],
- unpack_code => "", # N for big-endian, V for little
- };
- bless $self, $class;
- # Let unittests adjust the stride
- if ($main::opt_test_stride > 0) {
- $self->{stride} = $main::opt_test_stride;
- }
- # Read the first two slots to figure out bitsize and endianness.
- my $slots = $self->{slots};
- my $str;
- read($self->{file}, $str, 8);
- # Set the global $address_length based on what we see here.
- # 8 is 32-bit (8 hexadecimal chars); 16 is 64-bit (16 hexadecimal chars).
- $address_length = ($str eq (chr(0)x8)) ? 16 : 8;
- if ($address_length == 8) {
- if (substr($str, 6, 2) eq chr(0)x2) {
- $self->{unpack_code} = 'V'; # Little-endian.
- } elsif (substr($str, 4, 2) eq chr(0)x2) {
- $self->{unpack_code} = 'N'; # Big-endian
- } else {
- ::error("$fname: header size >= 2**16\n");
- }
- @$slots = unpack($self->{unpack_code} . "*", $str);
- } else {
- # If we're a 64-bit profile, make sure we're a 64-bit-capable
- # perl. Otherwise, each slot will be represented as a float
- # instead of an int64, losing precision and making all the
- # 64-bit addresses right. We *could* try to handle this with
- # software emulation of 64-bit ints, but that's added complexity
- # for no clear benefit (yet). We use 'Q' to test for 64-bit-ness;
- # perl docs say it's only available on 64-bit perl systems.
- my $has_q = 0;
- eval { $has_q = pack("Q", "1") ? 1 : 1; };
- if (!$has_q) {
- ::error("$fname: need a 64-bit perl to process this 64-bit profile.\n");
- }
- read($self->{file}, $str, 8);
- if (substr($str, 4, 4) eq chr(0)x4) {
- # We'd love to use 'Q', but it's a) not universal, b) not endian-proof.
- $self->{unpack_code} = 'V'; # Little-endian.
- } elsif (substr($str, 0, 4) eq chr(0)x4) {
- $self->{unpack_code} = 'N'; # Big-endian
- } else {
- ::error("$fname: header size >= 2**32\n");
- }
- my @pair = unpack($self->{unpack_code} . "*", $str);
- # Since we know one of the pair is 0, it's fine to just add them.
- @$slots = (0, $pair[0] + $pair[1]);
- }
- return $self;
- }
-
- # Load more data when we access slots->get(X) which is not yet in memory.
- sub overflow {
- my ($self) = @_;
- my $slots = $self->{slots};
- $self->{base} += $#$slots + 1; # skip over data we're replacing
- my $str;
- read($self->{file}, $str, $self->{stride});
- if ($address_length == 8) { # the 32-bit case
- # This is the easy case: unpack provides 32-bit unpacking primitives.
- @$slots = unpack($self->{unpack_code} . "*", $str);
- } else {
- # We need to unpack 32 bits at a time and combine.
- my @b32_values = unpack($self->{unpack_code} . "*", $str);
- my @b64_values = ();
- for (my $i = 0; $i < $#b32_values; $i += 2) {
- # TODO(csilvers): if this is a 32-bit perl, the math below
- # could end up in a too-large int, which perl will promote
- # to a double, losing necessary precision. Deal with that.
- if ($self->{unpack_code} eq 'V') { # little-endian
- push(@b64_values, $b32_values[$i] + $b32_values[$i+1] * (2**32));
- } else {
- push(@b64_values, $b32_values[$i] * (2**32) + $b32_values[$i+1]);
- }
- }
- @$slots = @b64_values;
- }
- }
-
- # Access the i-th long in the file (logically), or -1 at EOF.
- sub get {
- my ($self, $idx) = @_;
- my $slots = $self->{slots};
- while ($#$slots >= 0) {
- if ($idx < $self->{base}) {
- # The only time we expect a reference to $slots[$i - something]
- # after referencing $slots[$i] is reading the very first header.
- # Since $stride > |header|, that shouldn't cause any lookback
- # errors. And everything after the header is sequential.
- print STDERR "Unexpected look-back reading CPU profile";
- return -1; # shrug, don't know what better to return
- } elsif ($idx > $self->{base} + $#$slots) {
- $self->overflow();
- } else {
- return $slots->[$idx - $self->{base}];
- }
- }
- # If we get here, $slots is [], which means we've reached EOF
- return -1; # unique since slots is supposed to hold unsigned numbers
- }
-}
-
-# Parse profile generated by common/profiler.cc and return a reference
-# to a map:
-# $result->{version} Version number of profile file
-# $result->{period} Sampling period (in microseconds)
-# $result->{profile} Profile object
-# $result->{map} Memory map info from profile
-# $result->{pcs} Hash of all PC values seen, key is hex address
-sub ReadProfile {
- my $prog = shift;
- my $fname = shift;
-
- if (IsSymbolizedProfileFile($fname) && !$main::use_symbolized_profile) {
- # we have both a binary and symbolized profiles, abort
- usage("Symbolized profile '$fname' cannot be used with a binary arg. " .
- "Try again without passing '$prog'.");
- }
-
- $main::profile_type = '';
-
- $CONTENTION_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $contention_marker = $&;
- $GROWTH_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $growth_marker = $&;
- $SYMBOL_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $symbol_marker = $&;
- $PROFILE_PAGE =~ m,[^/]+$,; # matches everything after the last slash
- my $profile_marker = $&;
-
- # Look at first line to see if it is a heap or a CPU profile.
- # CPU profile may start with no header at all, and just binary data
- # (starting with \0\0\0\0) -- in that case, don't try to read the
- # whole firstline, since it may be gigabytes(!) of data.
- open(PROFILE, "<$fname") || error("$fname: $!\n");
- binmode PROFILE; # New perls do UTF-8 processing
- my $firstchar = "";
- my $header = "";
- read(PROFILE, $firstchar, 1);
- seek(PROFILE, -1, 1); # unread the firstchar
- if ($firstchar ne "\0") {
- $header = <PROFILE>;
- if (!defined($header)) {
- error("Profile is empty.\n");
- }
- $header =~ s/\r//g; # turn windows-looking lines into unix-looking lines
- }
-
- my $symbols;
- if ($header =~ m/^--- *$symbol_marker/o) {
- # read the symbol section of the symbolized profile file
- $symbols = ReadSymbols(*PROFILE{IO});
-
- # read the next line to get the header for the remaining profile
- $header = "";
- read(PROFILE, $firstchar, 1);
- seek(PROFILE, -1, 1); # unread the firstchar
- if ($firstchar ne "\0") {
- $header = <PROFILE>;
- $header =~ s/\r//g;
- }
- }
-
- my $result;
-
- if ($header =~ m/^heap profile:.*$growth_marker/o) {
- $main::profile_type = 'growth';
- $result = ReadHeapProfile($prog, $fname, $header);
- } elsif ($header =~ m/^heap profile:/) {
- $main::profile_type = 'heap';
- $result = ReadHeapProfile($prog, $fname, $header);
- } elsif ($header =~ m/^--- *$contention_marker/o) {
- $main::profile_type = 'contention';
- $result = ReadSynchProfile($prog, $fname);
- } elsif ($header =~ m/^--- *Stacks:/) {
- print STDERR
- "Old format contention profile: mistakenly reports " .
- "condition variable signals as lock contentions.\n";
- $main::profile_type = 'contention';
- $result = ReadSynchProfile($prog, $fname);
- } elsif ($header =~ m/^thread creation profile:/) {
- $main::profile_type = 'thread';
- $result = ReadThreadProfile($prog, $fname);
- } elsif ($header =~ m/^--- *$profile_marker/) {
- # the binary cpu profile data starts immediately after this line
- $main::profile_type = 'cpu';
- $result = ReadCPUProfile($prog, $fname);
- } else {
- if (defined($symbols)) {
- # a symbolized profile contains a format we don't recognize, bail out
- error("$fname: Cannot recognize profile section after symbols.\n");
- }
- # no ascii header present -- must be a CPU profile
- $main::profile_type = 'cpu';
- $result = ReadCPUProfile($prog, $fname);
- }
-
- # if we got symbols along with the profile, return those as well
- if (defined($symbols)) {
- $result->{symbols} = $symbols;
- }
-
- return $result;
-}
-
-# Subtract one from caller pc so we map back to call instr.
-# However, don't do this if we're reading a symbolized profile
-# file, in which case the subtract-one was done when the file
-# was written.
-#
-# We apply the same logic to all readers, though ReadCPUProfile uses an
-# independent implementation.
-sub FixCallerAddresses {
- my $stack = shift;
- if ($main::use_symbolized_profile) {
- return $stack;
- } else {
- $stack =~ /(\s)/;
- my $delimiter = $1;
- my @addrs = split(' ', $stack);
- my @fixedaddrs;
- $#fixedaddrs = $#addrs;
- if ($#addrs >= 0) {
- $fixedaddrs[0] = $addrs[0];
- }
- for (my $i = 1; $i <= $#addrs; $i++) {
- $fixedaddrs[$i] = AddressSub($addrs[$i], "0x1");
- }
- return join $delimiter, @fixedaddrs;
- }
-}
-
-# CPU profile reader
-sub ReadCPUProfile {
- my $prog = shift;
- my $fname = shift;
- my $version;
- my $period;
- my $i;
- my $profile = {};
- my $pcs = {};
-
- # Parse string into array of slots.
- my $slots = CpuProfileStream->new(*PROFILE, $fname);
-
- # Read header. The current header version is a 5-element structure
- # containing:
- # 0: header count (always 0)
- # 1: header "words" (after this one: 3)
- # 2: format version (0)
- # 3: sampling period (usec)
- # 4: unused padding (always 0)
- if ($slots->get(0) != 0 ) {
- error("$fname: not a profile file, or old format profile file\n");
- }
- $i = 2 + $slots->get(1);
- $version = $slots->get(2);
- $period = $slots->get(3);
- # Do some sanity checking on these header values.
- if ($version > (2**32) || $period > (2**32) || $i > (2**32) || $i < 5) {
- error("$fname: not a profile file, or corrupted profile file\n");
- }
-
- # Parse profile
- while ($slots->get($i) != -1) {
- my $n = $slots->get($i++);
- my $d = $slots->get($i++);
- if ($d > (2**16)) { # TODO(csilvers): what's a reasonable max-stack-depth?
- my $addr = sprintf("0%o", $i * ($address_length == 8 ? 4 : 8));
- print STDERR "At index $i (address $addr):\n";
- error("$fname: stack trace depth >= 2**32\n");
- }
- if ($slots->get($i) == 0) {
- # End of profile data marker
- $i += $d;
- last;
- }
-
- # Make key out of the stack entries
- my @k = ();
- for (my $j = 0; $j < $d; $j++) {
- my $pc = $slots->get($i+$j);
- # Subtract one from caller pc so we map back to call instr.
- # However, don't do this if we're reading a symbolized profile
- # file, in which case the subtract-one was done when the file
- # was written.
- if ($j > 0 && !$main::use_symbolized_profile) {
- $pc--;
- }
- $pc = sprintf("%0*x", $address_length, $pc);
- $pcs->{$pc} = 1;
- push @k, $pc;
- }
-
- AddEntry($profile, (join "\n", @k), $n);
- $i += $d;
- }
-
- # Parse map
- my $map = '';
- seek(PROFILE, $i * 4, 0);
- read(PROFILE, $map, (stat PROFILE)[7]);
- close(PROFILE);
-
- my $r = {};
- $r->{version} = $version;
- $r->{period} = $period;
- $r->{profile} = $profile;
- $r->{libs} = ParseLibraries($prog, $map, $pcs);
- $r->{pcs} = $pcs;
-
- return $r;
-}
-
-sub ReadHeapProfile {
- my $prog = shift;
- my $fname = shift;
- my $header = shift;
-
- my $index = 1;
- if ($main::opt_inuse_space) {
- $index = 1;
- } elsif ($main::opt_inuse_objects) {
- $index = 0;
- } elsif ($main::opt_alloc_space) {
- $index = 3;
- } elsif ($main::opt_alloc_objects) {
- $index = 2;
- }
-
- # Find the type of this profile. The header line looks like:
- # heap profile: 1246: 8800744 [ 1246: 8800744] @ <heap-url>/266053
- # There are two pairs <count: size>, the first inuse objects/space, and the
- # second allocated objects/space. This is followed optionally by a profile
- # type, and if that is present, optionally by a sampling frequency.
- # For remote heap profiles (v1):
- # The interpretation of the sampling frequency is that the profiler, for
- # each sample, calculates a uniformly distributed random integer less than
- # the given value, and records the next sample after that many bytes have
- # been allocated. Therefore, the expected sample interval is half of the
- # given frequency. By default, if not specified, the expected sample
- # interval is 128KB. Only remote-heap-page profiles are adjusted for
- # sample size.
- # For remote heap profiles (v2):
- # The sampling frequency is the rate of a Poisson process. This means that
- # the probability of sampling an allocation of size X with sampling rate Y
- # is 1 - exp(-X/Y)
- # For version 2, a typical header line might look like this:
- # heap profile: 1922: 127792360 [ 1922: 127792360] @ <heap-url>_v2/524288
- # the trailing number (524288) is the sampling rate. (Version 1 showed
- # double the 'rate' here)
- my $sampling_algorithm = 0;
- my $sample_adjustment = 0;
- chomp($header);
- my $type = "unknown";
- if ($header =~ m"^heap profile:\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\](\s*@\s*([^/]*)(/(\d+))?)?") {
- if (defined($6) && ($6 ne '')) {
- $type = $6;
- my $sample_period = $8;
- # $type is "heapprofile" for profiles generated by the
- # heap-profiler, and either "heap" or "heap_v2" for profiles
- # generated by sampling directly within tcmalloc. It can also
- # be "growth" for heap-growth profiles. The first is typically
- # found for profiles generated locally, and the others for
- # remote profiles.
- if (($type eq "heapprofile") || ($type !~ /heap/) ) {
- # No need to adjust for the sampling rate with heap-profiler-derived data
- $sampling_algorithm = 0;
- } elsif ($type =~ /_v2/) {
- $sampling_algorithm = 2; # version 2 sampling
- if (defined($sample_period) && ($sample_period ne '')) {
- $sample_adjustment = int($sample_period);
- }
- } else {
- $sampling_algorithm = 1; # version 1 sampling
- if (defined($sample_period) && ($sample_period ne '')) {
- $sample_adjustment = int($sample_period)/2;
- }
- }
- } else {
- # We detect whether or not this is a remote-heap profile by checking
- # that the total-allocated stats ($n2,$s2) are exactly the
- # same as the in-use stats ($n1,$s1). It is remotely conceivable
- # that a non-remote-heap profile may pass this check, but it is hard
- # to imagine how that could happen.
- # In this case it's so old it's guaranteed to be remote-heap version 1.
- my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
- if (($n1 == $n2) && ($s1 == $s2)) {
- # This is likely to be a remote-heap based sample profile
- $sampling_algorithm = 1;
- }
- }
- }
-
- if ($sampling_algorithm > 0) {
- # For remote-heap generated profiles, adjust the counts and sizes to
- # account for the sample rate (we sample once every 128KB by default).
- if ($sample_adjustment == 0) {
- # Turn on profile adjustment.
- $sample_adjustment = 128*1024;
- print STDERR "Adjusting heap profiles for 1-in-128KB sampling rate\n";
- } else {
- printf STDERR ("Adjusting heap profiles for 1-in-%d sampling rate\n",
- $sample_adjustment);
- }
- if ($sampling_algorithm > 1) {
- # We don't bother printing anything for the original version (version 1)
- printf STDERR "Heap version $sampling_algorithm\n";
- }
- }
-
- my $profile = {};
- my $pcs = {};
- my $map = "";
-
- while (<PROFILE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- if (/^MAPPED_LIBRARIES:/) {
- # Read the /proc/self/maps data
- while (<PROFILE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- $map .= $_;
- }
- last;
- }
-
- if (/^--- Memory map:/) {
- # Read /proc/self/maps data as formatted by DumpAddressMap()
- my $buildvar = "";
- while (<PROFILE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- # Parse "build=<dir>" specification if supplied
- if (m/^\s*build=(.*)\n/) {
- $buildvar = $1;
- }
-
- # Expand "$build" variable if available
- $_ =~ s/\$build\b/$buildvar/g;
-
- $map .= $_;
- }
- last;
- }
-
- # Read entry of the form:
- # <count1>: <bytes1> [<count2>: <bytes2>] @ a1 a2 a3 ... an
- s/^\s*//;
- s/\s*$//;
- if (m/^\s*(\d+):\s+(\d+)\s+\[\s*(\d+):\s+(\d+)\]\s+@\s+(.*)$/) {
- my $stack = $5;
- my ($n1, $s1, $n2, $s2) = ($1, $2, $3, $4);
-
- if ($sample_adjustment) {
- if ($sampling_algorithm == 2) {
- # Remote-heap version 2
- # The sampling frequency is the rate of a Poisson process.
- # This means that the probability of sampling an allocation of
- # size X with sampling rate Y is 1 - exp(-X/Y)
- my $ratio;
- $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
- my $scale_factor;
- $scale_factor = 1/(1 - exp(-$ratio));
- $n1 *= $scale_factor;
- $s1 *= $scale_factor;
- $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
- $scale_factor = 1/(1 - exp(-$ratio));
- $n2 *= $scale_factor;
- $s2 *= $scale_factor;
- } else {
- # Remote-heap version 1
- my $ratio;
- if ($n1 > 0) {
- $ratio = (($s1*1.0)/$n1)/($sample_adjustment);
- if ($ratio < 1) {
- $n1 /= $ratio;
- $s1 /= $ratio;
- }
- }
- if ($n2 > 0) {
- $ratio = (($s2*1.0)/$n2)/($sample_adjustment);
- if ($ratio < 1) {
- $n2 /= $ratio;
- $s2 /= $ratio;
- }
- }
- }
- }
-
- my @counts = ($n1, $s1, $n2, $s2);
- AddEntries($profile, $pcs, FixCallerAddresses($stack), $counts[$index]);
- }
- }
-
- my $r = {};
- $r->{version} = "heap";
- $r->{period} = 1;
- $r->{profile} = $profile;
- $r->{libs} = ParseLibraries($prog, $map, $pcs);
- $r->{pcs} = $pcs;
- return $r;
-}
-
-sub ReadThreadProfile {
- my $prog = shift;
- my $fname = shift;
-
- my $profile = {};
- my $pcs = {};
- my $map = "";
-
- while (<PROFILE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- if (/^MAPPED_LIBRARIES:/) {
- # Read the /proc/self/maps data
- while (<PROFILE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- $map .= $_;
- }
- last;
- }
-
- if (/^--- Memory map:/) {
- # Read /proc/self/maps data as formatted by DumpAddressMap()
- my $buildvar = "";
- while (<PROFILE>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- # Parse "build=<dir>" specification if supplied
- if (m/^\s*build=(.*)\n/) {
- $buildvar = $1;
- }
-
- # Expand "$build" variable if available
- $_ =~ s/\$build\b/$buildvar/g;
-
- $map .= $_;
- }
- last;
- }
-
- # Read entry of the form:
- # @ a1 a2 a3 ... an
- s/^\s*//;
- s/\s*$//;
- if (m/^@\s+(.*)$/) {
- AddEntries($profile, $pcs, FixCallerAddresses($1), 1);
- }
- }
-
- my $r = {};
- $r->{version} = "thread";
- $r->{period} = 1;
- $r->{profile} = $profile;
- $r->{libs} = ParseLibraries($prog, $map, $pcs);
- $r->{pcs} = $pcs;
- return $r;
-}
-
-sub ReadSynchProfile {
- my ($prog, $fname, $header) = @_;
-
- my $map = '';
- my $profile = {};
- my $pcs = {};
- my $sampling_period = 1;
- my $cyclespernanosec = 2.8; # Default assumption for old binaries
- my $seen_clockrate = 0;
- my $line;
-
- my $index = 0;
- if ($main::opt_total_delay) {
- $index = 0;
- } elsif ($main::opt_contentions) {
- $index = 1;
- } elsif ($main::opt_mean_delay) {
- $index = 2;
- }
-
- while ( $line = <PROFILE> ) {
- $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
- if ( $line =~ /^\s*(\d+)\s+(\d+) \@\s*(.*?)\s*$/ ) {
- my ($cycles, $count, $stack) = ($1, $2, $3);
-
- # Convert cycles to nanoseconds
- $cycles /= $cyclespernanosec;
-
- # Adjust for sampling done by application
- $cycles *= $sampling_period;
- $count *= $sampling_period;
-
- my @values = ($cycles, $count, $cycles / $count);
- AddEntries($profile, $pcs, FixCallerAddresses($stack), $values[$index]);
-
- } elsif ( $line =~ /^(slow release).*thread \d+ \@\s*(.*?)\s*$/ ||
- $line =~ /^\s*(\d+) \@\s*(.*?)\s*$/ ) {
- my ($cycles, $stack) = ($1, $2);
- if ($cycles !~ /^\d+$/) {
- next;
- }
-
- # Convert cycles to nanoseconds
- $cycles /= $cyclespernanosec;
-
- # Adjust for sampling done by application
- $cycles *= $sampling_period;
-
- AddEntries($profile, $pcs, FixCallerAddresses($stack), $cycles);
-
- } elsif ( $line =~ m/^([a-z][^=]*)=(.*)$/ ) {
- my ($variable, $value) = ($1,$2);
- for ($variable, $value) {
- s/^\s+//;
- s/\s+$//;
- }
- if ($variable eq "cycles/second") {
- $cyclespernanosec = $value / 1e9;
- $seen_clockrate = 1;
- } elsif ($variable eq "sampling period") {
- $sampling_period = $value;
- } elsif ($variable eq "ms since reset") {
- # Currently nothing is done with this value in pprof
- # So we just silently ignore it for now
- } elsif ($variable eq "discarded samples") {
- # Currently nothing is done with this value in pprof
- # So we just silently ignore it for now
- } else {
- printf STDERR ("Ignoring unnknown variable in /contention output: " .
- "'%s' = '%s'\n",$variable,$value);
- }
- } else {
- # Memory map entry
- $map .= $line;
- }
- }
- close PROFILE;
-
- if (!$seen_clockrate) {
- printf STDERR ("No cycles/second entry in profile; Guessing %.1f GHz\n",
- $cyclespernanosec);
- }
-
- my $r = {};
- $r->{version} = 0;
- $r->{period} = $sampling_period;
- $r->{profile} = $profile;
- $r->{libs} = ParseLibraries($prog, $map, $pcs);
- $r->{pcs} = $pcs;
- return $r;
-}
-
-# Given a hex value in the form "0x1abcd" return "0001abcd" or
-# "000000000001abcd", depending on the current address length.
-# There's probably a more idiomatic (or faster) way to do this...
-sub HexExtend {
- my $addr = shift;
-
- $addr =~ s/^0x//;
-
- if (length $addr > $address_length) {
- printf STDERR "Warning: address $addr is longer than address length $address_length\n";
- }
-
- return substr("000000000000000".$addr, -$address_length);
-}
-
-##### Symbol extraction #####
-
-# Aggressively search the lib_prefix values for the given library
-# If all else fails, just return the name of the library unmodified.
-# If the lib_prefix is "/my/path,/other/path" and $file is "/lib/dir/mylib.so"
-# it will search the following locations in this order, until it finds a file:
-# /my/path/lib/dir/mylib.so
-# /other/path/lib/dir/mylib.so
-# /my/path/dir/mylib.so
-# /other/path/dir/mylib.so
-# /my/path/mylib.so
-# /other/path/mylib.so
-# /lib/dir/mylib.so (returned as last resort)
-sub FindLibrary {
- my $file = shift;
- my $suffix = $file;
-
- # Search for the library as described above
- do {
- foreach my $prefix (@prefix_list) {
- my $fullpath = $prefix . $suffix;
- if (-e $fullpath) {
- return $fullpath;
- }
- }
- } while ($suffix =~ s|^/[^/]+/|/|);
- return $file;
-}
-
-# Return path to library with debugging symbols.
-# For libc libraries, the copy in /usr/lib/debug contains debugging symbols
-sub DebuggingLibrary {
- my $file = shift;
- if ($file =~ m|^/| && -f "/usr/lib/debug$file") {
- return "/usr/lib/debug$file";
- }
- return undef;
-}
-
-# Parse text section header of a library using objdump
-sub ParseTextSectionHeaderFromObjdump {
- my $lib = shift;
-
- my $size = undef;
- my $vma;
- my $file_offset;
- # Get objdump output from the library file to figure out how to
- # map between mapped addresses and addresses in the library.
- my $objdump = $obj_tool_map{"objdump"};
- open(OBJDUMP, "$objdump -h $lib |")
- || error("$objdump $lib: $!\n");
- while (<OBJDUMP>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- # Idx Name Size VMA LMA File off Algn
- # 10 .text 00104b2c 420156f0 420156f0 000156f0 2**4
- # For 64-bit objects, VMA and LMA will be 16 hex digits, size and file
- # offset may still be 8. But AddressSub below will still handle that.
- my @x = split;
- if (($#x >= 6) && ($x[1] eq '.text')) {
- $size = $x[2];
- $vma = $x[3];
- $file_offset = $x[5];
- last;
- }
- }
- close(OBJDUMP);
-
- if (!defined($size)) {
- return undef;
- }
-
- my $r = {};
- $r->{size} = $size;
- $r->{vma} = $vma;
- $r->{file_offset} = $file_offset;
-
- return $r;
-}
-
-# Parse text section header of a library using otool (on OS X)
-sub ParseTextSectionHeaderFromOtool {
- my $lib = shift;
-
- my $size = undef;
- my $vma = undef;
- my $file_offset = undef;
- # Get otool output from the library file to figure out how to
- # map between mapped addresses and addresses in the library.
- my $otool = $obj_tool_map{"otool"};
- open(OTOOL, "$otool -l $lib |")
- || error("$otool $lib: $!\n");
- my $cmd = "";
- my $sectname = "";
- my $segname = "";
- foreach my $line (<OTOOL>) {
- $line =~ s/\r//g; # turn windows-looking lines into unix-looking lines
- # Load command <#>
- # cmd LC_SEGMENT
- # [...]
- # Section
- # sectname __text
- # segname __TEXT
- # addr 0x000009f8
- # size 0x00018b9e
- # offset 2552
- # align 2^2 (4)
- # We will need to strip off the leading 0x from the hex addresses,
- # and convert the offset into hex.
- if ($line =~ /Load command/) {
- $cmd = "";
- $sectname = "";
- $segname = "";
- } elsif ($line =~ /Section/) {
- $sectname = "";
- $segname = "";
- } elsif ($line =~ /cmd (\w+)/) {
- $cmd = $1;
- } elsif ($line =~ /sectname (\w+)/) {
- $sectname = $1;
- } elsif ($line =~ /segname (\w+)/) {
- $segname = $1;
- } elsif (!(($cmd eq "LC_SEGMENT" || $cmd eq "LC_SEGMENT_64") &&
- $sectname eq "__text" &&
- $segname eq "__TEXT")) {
- next;
- } elsif ($line =~ /\baddr 0x([0-9a-fA-F]+)/) {
- $vma = $1;
- } elsif ($line =~ /\bsize 0x([0-9a-fA-F]+)/) {
- $size = $1;
- } elsif ($line =~ /\boffset ([0-9]+)/) {
- $file_offset = sprintf("%016x", $1);
- }
- if (defined($vma) && defined($size) && defined($file_offset)) {
- last;
- }
- }
- close(OTOOL);
-
- if (!defined($vma) || !defined($size) || !defined($file_offset)) {
- return undef;
- }
-
- my $r = {};
- $r->{size} = $size;
- $r->{vma} = $vma;
- $r->{file_offset} = $file_offset;
-
- return $r;
-}
-
-sub ParseTextSectionHeader {
- # obj_tool_map("otool") is only defined if we're in a Mach-O environment
- if (defined($obj_tool_map{"otool"})) {
- my $r = ParseTextSectionHeaderFromOtool(@_);
- if (defined($r)){
- return $r;
- }
- }
- # If otool doesn't work, or we don't have it, fall back to objdump
- return ParseTextSectionHeaderFromObjdump(@_);
-}
-
-# Split /proc/pid/maps dump into a list of libraries
-sub ParseLibraries {
- return if $main::use_symbol_page; # We don't need libraries info.
- my $prog = shift;
- my $map = shift;
- my $pcs = shift;
-
- my $result = [];
- my $h = "[a-f0-9]+";
- my $zero_offset = HexExtend("0");
-
- my $buildvar = "";
- foreach my $l (split("\n", $map)) {
- if ($l =~ m/^\s*build=(.*)$/) {
- $buildvar = $1;
- }
-
- my $start;
- my $finish;
- my $offset;
- my $lib;
- if ($l =~ /^($h)-($h)\s+..x.\s+($h)\s+\S+:\S+\s+\d+\s+(\S+\.(so|dll|dylib|bundle)((\.\d+)+\w*(\.\d+){0,3})?)$/i) {
- # Full line from /proc/self/maps. Example:
- # 40000000-40015000 r-xp 00000000 03:01 12845071 /lib/ld-2.3.2.so
- $start = HexExtend($1);
- $finish = HexExtend($2);
- $offset = HexExtend($3);
- $lib = $4;
- $lib =~ s|\\|/|g; # turn windows-style paths into unix-style paths
- } elsif ($l =~ /^\s*($h)-($h):\s*(\S+\.so(\.\d+)*)/) {
- # Cooked line from DumpAddressMap. Example:
- # 40000000-40015000: /lib/ld-2.3.2.so
- $start = HexExtend($1);
- $finish = HexExtend($2);
- $offset = $zero_offset;
- $lib = $3;
- } else {
- next;
- }
-
- # Expand "$build" variable if available
- $lib =~ s/\$build\b/$buildvar/g;
-
- $lib = FindLibrary($lib);
-
- # Check for pre-relocated libraries, which use pre-relocated symbol tables
- # and thus require adjusting the offset that we'll use to translate
- # VM addresses into symbol table addresses.
- # Only do this if we're not going to fetch the symbol table from a
- # debugging copy of the library.
- if (!DebuggingLibrary($lib)) {
- my $text = ParseTextSectionHeader($lib);
- if (defined($text)) {
- my $vma_offset = AddressSub($text->{vma}, $text->{file_offset});
- $offset = AddressAdd($offset, $vma_offset);
- }
- }
-
- push(@{$result}, [$lib, $start, $finish, $offset]);
- }
-
- # Append special entry for additional library (not relocated)
- if ($main::opt_lib ne "") {
- my $text = ParseTextSectionHeader($main::opt_lib);
- if (defined($text)) {
- my $start = $text->{vma};
- my $finish = AddressAdd($start, $text->{size});
-
- push(@{$result}, [$main::opt_lib, $start, $finish, $start]);
- }
- }
-
- # Append special entry for the main program. This covers
- # 0..max_pc_value_seen, so that we assume pc values not found in one
- # of the library ranges will be treated as coming from the main
- # program binary.
- my $min_pc = HexExtend("0");
- my $max_pc = $min_pc; # find the maximal PC value in any sample
- foreach my $pc (keys(%{$pcs})) {
- if (HexExtend($pc) gt $max_pc) { $max_pc = HexExtend($pc); }
- }
- push(@{$result}, [$prog, $min_pc, $max_pc, $zero_offset]);
-
- return $result;
-}
-
-# Add two hex addresses of length $address_length.
-# Run pprof --test for unit test if this is changed.
-sub AddressAdd {
- my $addr1 = shift;
- my $addr2 = shift;
- my $sum;
-
- if ($address_length == 8) {
- # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
- $sum = (hex($addr1)+hex($addr2)) % (0x10000000 * 16);
- return sprintf("%08x", $sum);
-
- } else {
- # Do the addition in 7-nibble chunks to trivialize carry handling.
-
- if ($main::opt_debug and $main::opt_test) {
- print STDERR "AddressAdd $addr1 + $addr2 = ";
- }
-
- my $a1 = substr($addr1,-7);
- $addr1 = substr($addr1,0,-7);
- my $a2 = substr($addr2,-7);
- $addr2 = substr($addr2,0,-7);
- $sum = hex($a1) + hex($a2);
- my $c = 0;
- if ($sum > 0xfffffff) {
- $c = 1;
- $sum -= 0x10000000;
- }
- my $r = sprintf("%07x", $sum);
-
- $a1 = substr($addr1,-7);
- $addr1 = substr($addr1,0,-7);
- $a2 = substr($addr2,-7);
- $addr2 = substr($addr2,0,-7);
- $sum = hex($a1) + hex($a2) + $c;
- $c = 0;
- if ($sum > 0xfffffff) {
- $c = 1;
- $sum -= 0x10000000;
- }
- $r = sprintf("%07x", $sum) . $r;
-
- $sum = hex($addr1) + hex($addr2) + $c;
- if ($sum > 0xff) { $sum -= 0x100; }
- $r = sprintf("%02x", $sum) . $r;
-
- if ($main::opt_debug and $main::opt_test) { print STDERR "$r\n"; }
-
- return $r;
- }
-}
-
-
-# Subtract two hex addresses of length $address_length.
-# Run pprof --test for unit test if this is changed.
-sub AddressSub {
- my $addr1 = shift;
- my $addr2 = shift;
- my $diff;
-
- if ($address_length == 8) {
- # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
- $diff = (hex($addr1)-hex($addr2)) % (0x10000000 * 16);
- return sprintf("%08x", $diff);
-
- } else {
- # Do the addition in 7-nibble chunks to trivialize borrow handling.
- # if ($main::opt_debug) { print STDERR "AddressSub $addr1 - $addr2 = "; }
-
- my $a1 = hex(substr($addr1,-7));
- $addr1 = substr($addr1,0,-7);
- my $a2 = hex(substr($addr2,-7));
- $addr2 = substr($addr2,0,-7);
- my $b = 0;
- if ($a2 > $a1) {
- $b = 1;
- $a1 += 0x10000000;
- }
- $diff = $a1 - $a2;
- my $r = sprintf("%07x", $diff);
-
- $a1 = hex(substr($addr1,-7));
- $addr1 = substr($addr1,0,-7);
- $a2 = hex(substr($addr2,-7)) + $b;
- $addr2 = substr($addr2,0,-7);
- $b = 0;
- if ($a2 > $a1) {
- $b = 1;
- $a1 += 0x10000000;
- }
- $diff = $a1 - $a2;
- $r = sprintf("%07x", $diff) . $r;
-
- $a1 = hex($addr1);
- $a2 = hex($addr2) + $b;
- if ($a2 > $a1) { $a1 += 0x100; }
- $diff = $a1 - $a2;
- $r = sprintf("%02x", $diff) . $r;
-
- # if ($main::opt_debug) { print STDERR "$r\n"; }
-
- return $r;
- }
-}
-
-# Increment a hex addresses of length $address_length.
-# Run pprof --test for unit test if this is changed.
-sub AddressInc {
- my $addr = shift;
- my $sum;
-
- if ($address_length == 8) {
- # Perl doesn't cope with wraparound arithmetic, so do it explicitly:
- $sum = (hex($addr)+1) % (0x10000000 * 16);
- return sprintf("%08x", $sum);
-
- } else {
- # Do the addition in 7-nibble chunks to trivialize carry handling.
- # We are always doing this to step through the addresses in a function,
- # and will almost never overflow the first chunk, so we check for this
- # case and exit early.
-
- # if ($main::opt_debug) { print STDERR "AddressInc $addr1 = "; }
-
- my $a1 = substr($addr,-7);
- $addr = substr($addr,0,-7);
- $sum = hex($a1) + 1;
- my $r = sprintf("%07x", $sum);
- if ($sum <= 0xfffffff) {
- $r = $addr . $r;
- # if ($main::opt_debug) { print STDERR "$r\n"; }
- return HexExtend($r);
- } else {
- $r = "0000000";
- }
-
- $a1 = substr($addr,-7);
- $addr = substr($addr,0,-7);
- $sum = hex($a1) + 1;
- $r = sprintf("%07x", $sum) . $r;
- if ($sum <= 0xfffffff) {
- $r = $addr . $r;
- # if ($main::opt_debug) { print STDERR "$r\n"; }
- return HexExtend($r);
- } else {
- $r = "00000000000000";
- }
-
- $sum = hex($addr) + 1;
- if ($sum > 0xff) { $sum -= 0x100; }
- $r = sprintf("%02x", $sum) . $r;
-
- # if ($main::opt_debug) { print STDERR "$r\n"; }
- return $r;
- }
-}
-
-# Extract symbols for all PC values found in profile
-sub ExtractSymbols {
- my $libs = shift;
- my $pcset = shift;
-
- my $symbols = {};
-
- # Map each PC value to the containing library
- my %seen = ();
- foreach my $lib (@{$libs}) {
- my $libname = $lib->[0];
- my $start = $lib->[1];
- my $finish = $lib->[2];
- my $offset = $lib->[3];
-
- # Get list of pcs that belong in this library.
- my $contained = [];
- foreach my $pc (keys(%{$pcset})) {
- if (!$seen{$pc} && ($pc ge $start) && ($pc le $finish)) {
- $seen{$pc} = 1;
- push(@{$contained}, $pc);
- }
- }
- # Map to symbols
- MapToSymbols($libname, AddressSub($start, $offset), $contained, $symbols);
- }
-
- return $symbols;
-}
-
-# Map list of PC values to symbols for a given image
-sub MapToSymbols {
- my $image = shift;
- my $offset = shift;
- my $pclist = shift;
- my $symbols = shift;
-
- my $debug = 0;
-
- # Ignore empty binaries
- if ($#{$pclist} < 0) { return; }
-
- # Figure out the addr2line command to use
- my $addr2line = $obj_tool_map{"addr2line"};
- my $cmd = "$addr2line -f -C -e $image";
- if (exists $obj_tool_map{"addr2line_pdb"}) {
- $addr2line = $obj_tool_map{"addr2line_pdb"};
- $cmd = "$addr2line --demangle -f -C -e $image";
- }
-
- # Use the go version because we know it works on all platforms
- $addr2line = "go tool addr2line";
- $cmd = "$addr2line $image";
-
- # If "addr2line" isn't installed on the system at all, just use
- # nm to get what info we can (function names, but not line numbers).
- if (system("$addr2line --help >$DEVNULL 2>&1") != 0) {
- MapSymbolsWithNM($image, $offset, $pclist, $symbols);
- return;
- }
-
- # "addr2line -i" can produce a variable number of lines per input
- # address, with no separator that allows us to tell when data for
- # the next address starts. So we find the address for a special
- # symbol (_fini) and interleave this address between all real
- # addresses passed to addr2line. The name of this special symbol
- # can then be used as a separator.
- $sep_address = undef; # May be filled in by MapSymbolsWithNM()
- my $nm_symbols = {};
- MapSymbolsWithNM($image, $offset, $pclist, $nm_symbols);
- # TODO(csilvers): only add '-i' if addr2line supports it.
- if (defined($sep_address)) {
- # Only add " -i" to addr2line if the binary supports it.
- # addr2line --help returns 0, but not if it sees an unknown flag first.
- if (system("$cmd -i --help >$DEVNULL 2>&1") == 0) {
- $cmd .= " -i";
- } else {
- $sep_address = undef; # no need for sep_address if we don't support -i
- }
- }
-
- # Make file with all PC values with intervening 'sep_address' so
- # that we can reliably detect the end of inlined function list
- open(ADDRESSES, ">$main::tmpfile_sym") || error("$main::tmpfile_sym: $!\n");
- if ($debug) { print("---- $image ---\n"); }
- for (my $i = 0; $i <= $#{$pclist}; $i++) {
- # addr2line always reads hex addresses, and does not need '0x' prefix.
- if ($debug) { printf STDERR ("%s\n", $pclist->[$i]); }
- printf ADDRESSES ("%s\n", AddressSub($pclist->[$i], $offset));
- if (defined($sep_address)) {
- printf ADDRESSES ("%s\n", $sep_address);
- }
- }
- close(ADDRESSES);
- if ($debug) {
- print("----\n");
- system("cat $main::tmpfile_sym");
- print("---- $cmd\n");
- system("$cmd <$main::tmpfile_sym");
- print("----\n");
- }
-
- open(SYMBOLS, "$cmd <$main::tmpfile_sym |") || error("$cmd: $!\n");
- my $count = 0; # Index in pclist
- while (<SYMBOLS>) {
- # Read fullfunction and filelineinfo from next pair of lines
- s/\r?\n$//g;
- my $fullfunction = $_;
- $_ = <SYMBOLS>;
- s/\r?\n$//g;
- my $filelinenum = $_;
-
- if (defined($sep_address) && $fullfunction eq $sep_symbol) {
- # Terminating marker for data for this address
- $count++;
- next;
- }
-
- $filelinenum =~ s|\\|/|g; # turn windows-style paths into unix-style paths
-
- my $pcstr = $pclist->[$count];
- my $function = ShortFunctionName($fullfunction);
- if ($fullfunction eq '??') {
- # See if nm found a symbol
- my $nms = $nm_symbols->{$pcstr};
- if (defined($nms)) {
- $function = $nms->[0];
- $fullfunction = $nms->[2];
- }
- }
-
- # Prepend to accumulated symbols for pcstr
- # (so that caller comes before callee)
- my $sym = $symbols->{$pcstr};
- if (!defined($sym)) {
- $sym = [];
- $symbols->{$pcstr} = $sym;
- }
- unshift(@{$sym}, $function, $filelinenum, $fullfunction);
- if ($debug) { printf STDERR ("%s => [%s]\n", $pcstr, join(" ", @{$sym})); }
- if (!defined($sep_address)) {
- # Inlining is off, se this entry ends immediately
- $count++;
- }
- }
- close(SYMBOLS);
-}
-
-# Use nm to map the list of referenced PCs to symbols. Return true iff we
-# are able to read procedure information via nm.
-sub MapSymbolsWithNM {
- my $image = shift;
- my $offset = shift;
- my $pclist = shift;
- my $symbols = shift;
-
- # Get nm output sorted by increasing address
- my $symbol_table = GetProcedureBoundaries($image, ".");
- if (!%{$symbol_table}) {
- return 0;
- }
- # Start addresses are already the right length (8 or 16 hex digits).
- my @names = sort { $symbol_table->{$a}->[0] cmp $symbol_table->{$b}->[0] }
- keys(%{$symbol_table});
-
- if ($#names < 0) {
- # No symbols: just use addresses
- foreach my $pc (@{$pclist}) {
- my $pcstr = "0x" . $pc;
- $symbols->{$pc} = [$pcstr, "?", $pcstr];
- }
- return 0;
- }
-
- # Sort addresses so we can do a join against nm output
- my $index = 0;
- my $fullname = $names[0];
- my $name = ShortFunctionName($fullname);
- foreach my $pc (sort { $a cmp $b } @{$pclist}) {
- # Adjust for mapped offset
- my $mpc = AddressSub($pc, $offset);
- while (($index < $#names) && ($mpc ge $symbol_table->{$fullname}->[1])){
- $index++;
- $fullname = $names[$index];
- $name = ShortFunctionName($fullname);
- }
- if ($mpc lt $symbol_table->{$fullname}->[1]) {
- $symbols->{$pc} = [$name, "?", $fullname];
- } else {
- my $pcstr = "0x" . $pc;
- $symbols->{$pc} = [$pcstr, "?", $pcstr];
- }
- }
- return 1;
-}
-
-sub ShortFunctionName {
- my $function = shift;
- while ($function =~ s/(?<!\.)\([^()]*\)(\s*const)?//g) { } # Argument types
- while ($function =~ s/<[^<>]*>//g) { } # Remove template arguments
- $function =~ s/^.*\s+(\w+::)/$1/; # Remove leading type
- return $function;
-}
-
-# Trim overly long symbols found in disassembler output
-sub CleanDisassembly {
- my $d = shift;
- while ($d =~ s/(?<!\.)\([^()%A-Z]*\)(\s*const)?//g) { } # Argument types, not (%rax)
- while ($d =~ s/(\w+)<[^<>]*>/$1/g) { } # Remove template arguments
- return $d;
-}
-
-##### Miscellaneous #####
-
-# Find the right versions of the above object tools to use. The
-# argument is the program file being analyzed, and should be an ELF
-# 32-bit or ELF 64-bit executable file. The location of the tools
-# is determined by considering the following options in this order:
-# 1) --tools option, if set
-# 2) PPROF_TOOLS environment variable, if set
-# 3) the environment
-sub ConfigureObjTools {
- my $prog_file = shift;
-
- # Check for the existence of $prog_file because /usr/bin/file does not
- # predictably return error status in prod.
- (-e $prog_file) || error("$prog_file does not exist.\n");
-
- # Follow symlinks (at least for systems where "file" supports that)
- my $file_cmd = "/usr/bin/file -L $prog_file 2>$DEVNULL || /usr/bin/file $prog_file 2>$DEVNULL";
- if ($^O eq "MSWin32") {
- $file_cmd = "file -L $prog_file 2>NUL || file $prog_file 2>NUL";
- }
- my $file_type = `$file_cmd`;
-
- if ($file_type =~ /64-bit/) {
- # Change $address_length to 16 if the program file is ELF 64-bit.
- # We can't detect this from many (most?) heap or lock contention
- # profiles, since the actual addresses referenced are generally in low
- # memory even for 64-bit programs.
- $address_length = 16;
- }
-
- if (($file_type =~ /MS Windows/) || ($OS eq "windows")) {
- # For windows, we provide a version of nm and addr2line as part of
- # the opensource release, which is capable of parsing
- # Windows-style PDB executables. It should live in the path, or
- # in the same directory as pprof.
- $obj_tool_map{"nm_pdb"} = "nm-pdb";
- $obj_tool_map{"addr2line_pdb"} = "addr2line-pdb";
- $obj_tool_map{"objdump"} = "false"; # no objdump
- }
-
- if ($file_type =~ /Mach-O/) {
- # OS X uses otool to examine Mach-O files, rather than objdump.
- $obj_tool_map{"otool"} = "otool";
- $obj_tool_map{"addr2line"} = "false"; # no addr2line
- $obj_tool_map{"objdump"} = "false"; # no objdump
- }
-
- # Go fill in %obj_tool_map with the pathnames to use:
- foreach my $tool (keys %obj_tool_map) {
- $obj_tool_map{$tool} = ConfigureTool($obj_tool_map{$tool});
- }
-}
-
-# Returns the path of a caller-specified object tool. If --tools or
-# PPROF_TOOLS are specified, then returns the full path to the tool
-# with that prefix. Otherwise, returns the path unmodified (which
-# means we will look for it on PATH).
-sub ConfigureTool {
- my $tool = shift;
- my $path;
-
- if ($main::opt_tools ne "") {
- # Use a prefix specified by the --tools option...
- $path = $main::opt_tools . $tool;
- if (!-x $path) {
- error("No '$tool' found with prefix specified by --tools $main::opt_tools\n");
- }
- } elsif (exists $ENV{"PPROF_TOOLS"} &&
- $ENV{"PPROF_TOOLS"} ne "") {
- #... or specified with the PPROF_TOOLS environment variable...
- $path = $ENV{"PPROF_TOOLS"} . $tool;
- if (!-x $path) {
- error("No '$tool' found with prefix specified by PPROF_TOOLS=$ENV{PPROF_TOOLS}\n");
- }
- } else {
- # ... otherwise use the version that exists in the same directory as
- # pprof. If there's nothing there, use $PATH.
- $0 =~ m,[^/]*$,; # this is everything after the last slash
- my $dirname = $`; # this is everything up to and including the last slash
- if (-x "$dirname$tool") {
- $path = "$dirname$tool";
- } else {
- $path = $tool;
- }
- }
- if ($main::opt_debug) { print STDERR "Using '$path' for '$tool'.\n"; }
- return $path;
-}
-
-# FetchHTTP retrieves a URL using either curl or LWP::UserAgent.
-# It returns the entire body of the page on success, or exits the program
-# with an error message on any failure.
-sub FetchHTTP {
- my $url = shift;
- my $timeout = shift; # optional, in seconds
- eval "use LWP::UserAgent ();";
- if ($@) {
- my @max;
- push @max, "--max-time", $timeout if $timeout;
- open(my $fh, "-|", "curl", @max, "-s", $url) or error("Neither LWP::UserAgent nor curl is installed: $!\n");
- my $slurp = do { local $/; <$fh> };
- close($fh);
- if ($? != 0) {
- error("Error fetching $url with curl: exit $?")
- }
- return $slurp;
- }
- my $ua = LWP::UserAgent->new;
- $ua->timeout($timeout) if $timeout;
- my $res = $ua->get($url);
- error("Failed to fetch $url\n") unless $res->is_success();
- return $res->content();
-}
-
-sub PostHTTP {
- my ($url, $post_data) = @_;
- eval "use LWP::UserAgent ();";
- if ($@) {
- open(POSTFILE, ">$main::tmpfile_sym");
- print POSTFILE $post_data;
- close(POSTFILE);
-
- open(my $fh, "-|", "curl", "-s", "-d", "\@$main::tmpfile_sym", $url) or error("Neither LWP::UserAgent nor curl is installed: $!\n");
- my $slurp = do { local $/; <$fh> };
- close($fh);
- if ($? != 0) {
- error("Error fetching $url with curl: exit $?")
- }
- return $slurp;
- }
- my $req = HTTP::Request->new(POST => $url);
- $req->content($post_data);
- my $ua = LWP::UserAgent->new;
- my $res = $ua->request($req);
- error("Failed to POST to $url\n") unless $res->is_success();
- return $res->content();
-}
-
-sub cleanup {
- unlink($main::tmpfile_sym) if defined $main::tmpfile_sym;
- unlink(keys %main::tempnames) if %main::tempnames;
- unlink($main::collected_profile) if defined $main::collected_profile;
-
- # We leave any collected profiles in $HOME/pprof in case the user wants
- # to look at them later. We print a message informing them of this.
- if ((scalar(@main::profile_files) > 0) &&
- defined($main::collected_profile)) {
- if (scalar(@main::profile_files) == 1) {
- print STDERR "Dynamically gathered profile is in $main::collected_profile\n";
- }
- print STDERR "If you want to investigate this profile further, you can do:\n";
- print STDERR "\n";
- print STDERR " pprof \\\n";
- print STDERR " $main::prog \\\n";
- print STDERR " $main::collected_profile\n";
- print STDERR "\n";
- }
-}
-
-sub sighandler {
- cleanup();
- exit(1);
-}
-
-sub error {
- my $msg = shift;
- print STDERR $msg;
- cleanup();
- exit(1);
-}
-
-
-# Run $nm_command and get all the resulting procedure boundaries whose
-# names match "$regexp" and returns them in a hashtable mapping from
-# procedure name to a two-element vector of [start address, end address]
-sub GetProcedureBoundariesViaNm {
- my $nm_command = shift;
- my $regexp = shift;
-
- my $symbol_table = {};
- open(NM, "$nm_command |") || error("$nm_command: $!\n");
- my $last_start = "0";
- my $routine = "";
- while (<NM>) {
- s/\r//g; # turn windows-looking lines into unix-looking lines
- if (m/^\s*([0-9a-f]+) (.) (..*)/) {
- my $start_val = $1;
- my $type = $2;
- my $this_routine = $3;
-
- # It's possible for two symbols to share the same address, if
- # one is a zero-length variable (like __start_google_malloc) or
- # one symbol is a weak alias to another (like __libc_malloc).
- # In such cases, we want to ignore all values except for the
- # actual symbol, which in nm-speak has type "T". The logic
- # below does this, though it's a bit tricky: what happens when
- # we have a series of lines with the same address, is the first
- # one gets queued up to be processed. However, it won't
- # *actually* be processed until later, when we read a line with
- # a different address. That means that as long as we're reading
- # lines with the same address, we have a chance to replace that
- # item in the queue, which we do whenever we see a 'T' entry --
- # that is, a line with type 'T'. If we never see a 'T' entry,
- # we'll just go ahead and process the first entry (which never
- # got touched in the queue), and ignore the others.
- if ($start_val eq $last_start && $type =~ /t/i) {
- # We are the 'T' symbol at this address, replace previous symbol.
- $routine = $this_routine;
- next;
- } elsif ($start_val eq $last_start) {
- # We're not the 'T' symbol at this address, so ignore us.
- next;
- }
-
- if ($this_routine eq $sep_symbol) {
- $sep_address = HexExtend($start_val);
- }
-
- # Tag this routine with the starting address in case the image
- # has multiple occurrences of this routine. We use a syntax
- # that resembles template paramters that are automatically
- # stripped out by ShortFunctionName()
- $this_routine .= "<$start_val>";
-
- if (defined($routine) && $routine =~ m/$regexp/) {
- $symbol_table->{$routine} = [HexExtend($last_start),
- HexExtend($start_val)];
- }
- $last_start = $start_val;
- $routine = $this_routine;
- } elsif (m/^Loaded image name: (.+)/) {
- # The win32 nm workalike emits information about the binary it is using.
- if ($main::opt_debug) { print STDERR "Using Image $1\n"; }
- } elsif (m/^PDB file name: (.+)/) {
- # The win32 nm workalike emits information about the pdb it is using.
- if ($main::opt_debug) { print STDERR "Using PDB $1\n"; }
- }
- }
- close(NM);
- # Handle the last line in the nm output. Unfortunately, we don't know
- # how big this last symbol is, because we don't know how big the file
- # is. For now, we just give it a size of 0.
- # TODO(csilvers): do better here.
- if (defined($routine) && $routine =~ m/$regexp/) {
- $symbol_table->{$routine} = [HexExtend($last_start),
- HexExtend($last_start)];
- }
- return $symbol_table;
-}
-
-# Gets the procedure boundaries for all routines in "$image" whose names
-# match "$regexp" and returns them in a hashtable mapping from procedure
-# name to a two-element vector of [start address, end address].
-# Will return an empty map if nm is not installed or not working properly.
-sub GetProcedureBoundaries {
- my $image = shift;
- my $regexp = shift;
-
- # For libc libraries, the copy in /usr/lib/debug contains debugging symbols
- my $debugging = DebuggingLibrary($image);
- if ($debugging) {
- $image = $debugging;
- }
-
- my $nm = $obj_tool_map{"nm"};
-
- # nm can fail for two reasons: 1) $image isn't a debug library; 2) nm
- # binary doesn't support --demangle. In addition, for OS X we need
- # to use the -f flag to get 'flat' nm output (otherwise we don't sort
- # properly and get incorrect results). Unfortunately, GNU nm uses -f
- # in an incompatible way. So first we test whether our nm supports
- # --demangle and -f.
- my $demangle_flag = "";
- if (system("$nm --demangle $image >$DEVNULL 2>&1") == 0) {
- # In this mode, we do "nm --demangle <foo>"
- $demangle_flag = "--demangle";
- }
- my $flatten_flag = "";
- if (system("$nm -f $image >$DEVNULL 2>&1") == 0) {
- $flatten_flag = "-f";
- }
-
- # Finally, in the case $image isn't a debug library, we try again with
- # -D to at least get *exported* symbols. If we can't use --demangle, too bad.
- my @nm_commands = ("$nm -n $flatten_flag $demangle_flag" .
- " $image 2>$DEVNULL",
- "$nm -D -n $flatten_flag $demangle_flag" .
- " $image 2>$DEVNULL",
- # go tool nm is for Go binaries
- "go tool nm $image 2>$DEVNULL | sort");
-
- foreach my $nm_command (@nm_commands) {
- my $symbol_table = GetProcedureBoundariesViaNm($nm_command, $regexp);
- return $symbol_table if (%{$symbol_table});
- }
- my $symbol_table = {};
- return $symbol_table;
-}
-
-
-# The test vectors for AddressAdd/Sub/Inc are 8-16-nibble hex strings.
-# To make them more readable, we add underscores at interesting places.
-# This routine removes the underscores, producing the canonical representation
-# used by pprof to represent addresses, particularly in the tested routines.
-sub CanonicalHex {
- my $arg = shift;
- return join '', (split '_',$arg);
-}
-
-
-# Unit test for AddressAdd:
-sub AddressAddUnitTest {
- my $test_data_8 = shift;
- my $test_data_16 = shift;
- my $error_count = 0;
- my $fail_count = 0;
- my $pass_count = 0;
- # print STDERR "AddressAddUnitTest: ", 1+$#{$test_data_8}, " tests\n";
-
- # First a few 8-nibble addresses. Note that this implementation uses
- # plain old arithmetic, so a quick sanity check along with verifying what
- # happens to overflow (we want it to wrap):
- $address_length = 8;
- foreach my $row (@{$test_data_8}) {
- if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
- my $sum = AddressAdd ($row->[0], $row->[1]);
- if ($sum ne $row->[2]) {
- printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
- $row->[0], $row->[1], $row->[2];
- ++$fail_count;
- } else {
- ++$pass_count;
- }
- }
- printf STDERR "AddressAdd 32-bit tests: %d passes, %d failures\n",
- $pass_count, $fail_count;
- $error_count = $fail_count;
- $fail_count = 0;
- $pass_count = 0;
-
- # Now 16-nibble addresses.
- $address_length = 16;
- foreach my $row (@{$test_data_16}) {
- if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
- my $sum = AddressAdd (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
- my $expected = join '', (split '_',$row->[2]);
- if ($sum ne CanonicalHex($row->[2])) {
- printf STDERR "ERROR: %s != %s + %s = %s\n", $sum,
- $row->[0], $row->[1], $row->[2];
- ++$fail_count;
- } else {
- ++$pass_count;
- }
- }
- printf STDERR "AddressAdd 64-bit tests: %d passes, %d failures\n",
- $pass_count, $fail_count;
- $error_count += $fail_count;
-
- return $error_count;
-}
-
-
-# Unit test for AddressSub:
-sub AddressSubUnitTest {
- my $test_data_8 = shift;
- my $test_data_16 = shift;
- my $error_count = 0;
- my $fail_count = 0;
- my $pass_count = 0;
- # print STDERR "AddressSubUnitTest: ", 1+$#{$test_data_8}, " tests\n";
-
- # First a few 8-nibble addresses. Note that this implementation uses
- # plain old arithmetic, so a quick sanity check along with verifying what
- # happens to overflow (we want it to wrap):
- $address_length = 8;
- foreach my $row (@{$test_data_8}) {
- if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
- my $sum = AddressSub ($row->[0], $row->[1]);
- if ($sum ne $row->[3]) {
- printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
- $row->[0], $row->[1], $row->[3];
- ++$fail_count;
- } else {
- ++$pass_count;
- }
- }
- printf STDERR "AddressSub 32-bit tests: %d passes, %d failures\n",
- $pass_count, $fail_count;
- $error_count = $fail_count;
- $fail_count = 0;
- $pass_count = 0;
-
- # Now 16-nibble addresses.
- $address_length = 16;
- foreach my $row (@{$test_data_16}) {
- if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
- my $sum = AddressSub (CanonicalHex($row->[0]), CanonicalHex($row->[1]));
- if ($sum ne CanonicalHex($row->[3])) {
- printf STDERR "ERROR: %s != %s - %s = %s\n", $sum,
- $row->[0], $row->[1], $row->[3];
- ++$fail_count;
- } else {
- ++$pass_count;
- }
- }
- printf STDERR "AddressSub 64-bit tests: %d passes, %d failures\n",
- $pass_count, $fail_count;
- $error_count += $fail_count;
-
- return $error_count;
-}
-
-
-# Unit test for AddressInc:
-sub AddressIncUnitTest {
- my $test_data_8 = shift;
- my $test_data_16 = shift;
- my $error_count = 0;
- my $fail_count = 0;
- my $pass_count = 0;
- # print STDERR "AddressIncUnitTest: ", 1+$#{$test_data_8}, " tests\n";
-
- # First a few 8-nibble addresses. Note that this implementation uses
- # plain old arithmetic, so a quick sanity check along with verifying what
- # happens to overflow (we want it to wrap):
- $address_length = 8;
- foreach my $row (@{$test_data_8}) {
- if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
- my $sum = AddressInc ($row->[0]);
- if ($sum ne $row->[4]) {
- printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
- $row->[0], $row->[4];
- ++$fail_count;
- } else {
- ++$pass_count;
- }
- }
- printf STDERR "AddressInc 32-bit tests: %d passes, %d failures\n",
- $pass_count, $fail_count;
- $error_count = $fail_count;
- $fail_count = 0;
- $pass_count = 0;
-
- # Now 16-nibble addresses.
- $address_length = 16;
- foreach my $row (@{$test_data_16}) {
- if ($main::opt_debug and $main::opt_test) { print STDERR "@{$row}\n"; }
- my $sum = AddressInc (CanonicalHex($row->[0]));
- if ($sum ne CanonicalHex($row->[4])) {
- printf STDERR "ERROR: %s != %s + 1 = %s\n", $sum,
- $row->[0], $row->[4];
- ++$fail_count;
- } else {
- ++$pass_count;
- }
- }
- printf STDERR "AddressInc 64-bit tests: %d passes, %d failures\n",
- $pass_count, $fail_count;
- $error_count += $fail_count;
-
- return $error_count;
-}
-
-
-# Driver for unit tests.
-# Currently just the address add/subtract/increment routines for 64-bit.
-sub RunUnitTests {
- my $error_count = 0;
-
- # This is a list of tuples [a, b, a+b, a-b, a+1]
- my $unit_test_data_8 = [
- [qw(aaaaaaaa 50505050 fafafafa 5a5a5a5a aaaaaaab)],
- [qw(50505050 aaaaaaaa fafafafa a5a5a5a6 50505051)],
- [qw(ffffffff aaaaaaaa aaaaaaa9 55555555 00000000)],
- [qw(00000001 ffffffff 00000000 00000002 00000002)],
- [qw(00000001 fffffff0 fffffff1 00000011 00000002)],
- ];
- my $unit_test_data_16 = [
- # The implementation handles data in 7-nibble chunks, so those are the
- # interesting boundaries.
- [qw(aaaaaaaa 50505050
- 00_000000f_afafafa 00_0000005_a5a5a5a 00_000000a_aaaaaab)],
- [qw(50505050 aaaaaaaa
- 00_000000f_afafafa ff_ffffffa_5a5a5a6 00_0000005_0505051)],
- [qw(ffffffff aaaaaaaa
- 00_000001a_aaaaaa9 00_0000005_5555555 00_0000010_0000000)],
- [qw(00000001 ffffffff
- 00_0000010_0000000 ff_ffffff0_0000002 00_0000000_0000002)],
- [qw(00000001 fffffff0
- 00_000000f_ffffff1 ff_ffffff0_0000011 00_0000000_0000002)],
-
- [qw(00_a00000a_aaaaaaa 50505050
- 00_a00000f_afafafa 00_a000005_a5a5a5a 00_a00000a_aaaaaab)],
- [qw(0f_fff0005_0505050 aaaaaaaa
- 0f_fff000f_afafafa 0f_ffefffa_5a5a5a6 0f_fff0005_0505051)],
- [qw(00_000000f_fffffff 01_800000a_aaaaaaa
- 01_800001a_aaaaaa9 fe_8000005_5555555 00_0000010_0000000)],
- [qw(00_0000000_0000001 ff_fffffff_fffffff
- 00_0000000_0000000 00_0000000_0000002 00_0000000_0000002)],
- [qw(00_0000000_0000001 ff_fffffff_ffffff0
- ff_fffffff_ffffff1 00_0000000_0000011 00_0000000_0000002)],
- ];
-
- $error_count += AddressAddUnitTest($unit_test_data_8, $unit_test_data_16);
- $error_count += AddressSubUnitTest($unit_test_data_8, $unit_test_data_16);
- $error_count += AddressIncUnitTest($unit_test_data_8, $unit_test_data_16);
- if ($error_count > 0) {
- print STDERR $error_count, " errors: FAILED\n";
- } else {
- print STDERR "PASS\n";
- }
- exit ($error_count);
-}
diff --git a/misc/vim/autoload/go/complete.vim b/misc/vim/autoload/go/complete.vim
deleted file mode 100644
index a4fa6b668..000000000
--- a/misc/vim/autoload/go/complete.vim
+++ /dev/null
@@ -1,103 +0,0 @@
-" Copyright 2011 The Go Authors. All rights reserved.
-" Use of this source code is governed by a BSD-style
-" license that can be found in the LICENSE file.
-"
-" This file provides a utility function that performs auto-completion of
-" package names, for use by other commands.
-
-let s:goos = $GOOS
-let s:goarch = $GOARCH
-
-if len(s:goos) == 0
- if exists('g:golang_goos')
- let s:goos = g:golang_goos
- elseif has('win32') || has('win64')
- let s:goos = 'windows'
- elseif has('macunix')
- let s:goos = 'darwin'
- else
- let s:goos = '*'
- endif
-endif
-
-if len(s:goarch) == 0
- if exists('g:golang_goarch')
- let s:goarch = g:golang_goarch
- else
- let s:goarch = '*'
- endif
-endif
-
-function! go#complete#PackageMembers(package, member)
- silent! let content = system('godoc ' . a:package)
- if v:shell_error || !len(content)
- return []
- endif
- let lines = filter(split(content, "\n"),"v:val !~ '^\\s\\+$'")
- try
- let mx1 = '^\s\+\(\S+\)\s\+=\s\+.*'
- let mx2 = '^\%(const\|var\|type\|func\) \([A-Z][^ (]\+\).*'
- let candidates =
- \ map(filter(copy(lines), 'v:val =~ mx1'), 'substitute(v:val, mx1, "\\1", "")')
- \ + map(filter(copy(lines), 'v:val =~ mx2'), 'substitute(v:val, mx2, "\\1", "")')
- return filter(candidates, '!stridx(v:val, a:member)')
- catch
- return []
- endtry
-endfunction
-
-function! go#complete#Package(ArgLead, CmdLine, CursorPos)
- let dirs = []
-
- let words = split(a:CmdLine, '\s\+', 1)
- if len(words) > 2
- " Complete package members
- return go#complete#PackageMembers(words[1], words[2])
- endif
-
- if executable('go')
- let goroot = substitute(system('go env GOROOT'), '\n', '', 'g')
- if v:shell_error
- echomsg '''go env GOROOT'' failed'
- endif
- else
- let goroot = $GOROOT
- endif
-
- if len(goroot) != 0 && isdirectory(goroot)
- let dirs += [goroot]
- endif
-
- let pathsep = ':'
- if s:goos == 'windows'
- let pathsep = ';'
- endif
- let workspaces = split($GOPATH, pathsep)
- if workspaces != []
- let dirs += workspaces
- endif
-
- if len(dirs) == 0
- " should not happen
- return []
- endif
-
- let ret = {}
- for dir in dirs
- " this may expand to multiple lines
- let root = split(expand(dir . '/pkg/' . s:goos . '_' . s:goarch), "\n")
- call add(root, expand(dir . '/src'))
- for r in root
- for i in split(globpath(r, a:ArgLead.'*'), "\n")
- if isdirectory(i)
- let i .= '/'
- elseif i !~ '\.a$'
- continue
- endif
- let i = substitute(substitute(i[len(r)+1:], '[\\]', '/', 'g'), '\.a$', '', 'g')
- let ret[i] = i
- endfor
- endfor
- endfor
- return sort(keys(ret))
-endfunction
diff --git a/misc/vim/compiler/go.vim b/misc/vim/compiler/go.vim
deleted file mode 100644
index 2c8cce497..000000000
--- a/misc/vim/compiler/go.vim
+++ /dev/null
@@ -1,30 +0,0 @@
-" Copyright 2013 The Go Authors. All rights reserved.
-" Use of this source code is governed by a BSD-style
-" license that can be found in the LICENSE file.
-"
-" compiler/go.vim: Vim compiler file for Go.
-
-if exists("current_compiler")
- finish
-endif
-let current_compiler = "go"
-
-if exists(":CompilerSet") != 2
- command -nargs=* CompilerSet setlocal <args>
-endif
-
-let s:save_cpo = &cpo
-set cpo-=C
-
-CompilerSet makeprg=go\ build
-CompilerSet errorformat=
- \%-G#\ %.%#,
- \%A%f:%l:%c:\ %m,
- \%A%f:%l:\ %m,
- \%C%*\\s%m,
- \%-G%.%#
-
-let &cpo = s:save_cpo
-unlet s:save_cpo
-
-" vim:ts=4:sw=4:et
diff --git a/misc/vim/ftdetect/gofiletype.vim b/misc/vim/ftdetect/gofiletype.vim
deleted file mode 100644
index b658f6b0e..000000000
--- a/misc/vim/ftdetect/gofiletype.vim
+++ /dev/null
@@ -1,23 +0,0 @@
-" We take care to preserve the user's fileencodings and fileformats,
-" because those settings are global (not buffer local), yet we want
-" to override them for loading Go files, which are defined to be UTF-8.
-let s:current_fileformats = ''
-let s:current_fileencodings = ''
-
-" define fileencodings to open as utf-8 encoding even if it's ascii.
-function! s:gofiletype_pre()
- let s:current_fileformats = &g:fileformats
- let s:current_fileencodings = &g:fileencodings
- set fileencodings=utf-8 fileformats=unix
- setlocal filetype=go
-endfunction
-
-" restore fileencodings as others
-function! s:gofiletype_post()
- let &g:fileformats = s:current_fileformats
- let &g:fileencodings = s:current_fileencodings
-endfunction
-
-au BufNewFile *.go setlocal filetype=go fileencoding=utf-8 fileformat=unix
-au BufRead *.go call s:gofiletype_pre()
-au BufReadPost *.go call s:gofiletype_post()
diff --git a/misc/vim/ftplugin/go.vim b/misc/vim/ftplugin/go.vim
deleted file mode 100644
index 532fb1723..000000000
--- a/misc/vim/ftplugin/go.vim
+++ /dev/null
@@ -1,19 +0,0 @@
-" Copyright 2013 The Go Authors. All rights reserved.
-" Use of this source code is governed by a BSD-style
-" license that can be found in the LICENSE file.
-"
-" go.vim: Vim filetype plugin for Go.
-
-if exists("b:did_ftplugin")
- finish
-endif
-let b:did_ftplugin = 1
-
-setlocal formatoptions-=t
-
-setlocal comments=s1:/*,mb:*,ex:*/,://
-setlocal commentstring=//\ %s
-
-let b:undo_ftplugin = "setl fo< com< cms<"
-
-" vim:ts=4:sw=4:et
diff --git a/misc/vim/ftplugin/go/fmt.vim b/misc/vim/ftplugin/go/fmt.vim
deleted file mode 100644
index 359545bd4..000000000
--- a/misc/vim/ftplugin/go/fmt.vim
+++ /dev/null
@@ -1,69 +0,0 @@
-" Copyright 2011 The Go Authors. All rights reserved.
-" Use of this source code is governed by a BSD-style
-" license that can be found in the LICENSE file.
-"
-" fmt.vim: Vim command to format Go files with gofmt.
-"
-" This filetype plugin add a new commands for go buffers:
-"
-" :Fmt
-"
-" Filter the current Go buffer through gofmt.
-" It tries to preserve cursor position and avoids
-" replacing the buffer with stderr output.
-"
-" Options:
-"
-" g:go_fmt_commands [default=1]
-"
-" Flag to indicate whether to enable the commands listed above.
-"
-" g:gofmt_command [default="gofmt"]
-"
-" Flag naming the gofmt executable to use.
-"
-if exists("b:did_ftplugin_go_fmt")
- finish
-endif
-
-if !exists("g:go_fmt_commands")
- let g:go_fmt_commands = 1
-endif
-
-if !exists("g:gofmt_command")
- let g:gofmt_command = "gofmt"
-endif
-
-if g:go_fmt_commands
- command! -buffer Fmt call s:GoFormat()
-endif
-
-function! s:GoFormat()
- let view = winsaveview()
- silent execute "%!" . g:gofmt_command
- if v:shell_error
- let errors = []
- for line in getline(1, line('$'))
- let tokens = matchlist(line, '^\(.\{-}\):\(\d\+\):\(\d\+\)\s*\(.*\)')
- if !empty(tokens)
- call add(errors, {"filename": @%,
- \"lnum": tokens[2],
- \"col": tokens[3],
- \"text": tokens[4]})
- endif
- endfor
- if empty(errors)
- % | " Couldn't detect gofmt error format, output errors
- endif
- undo
- if !empty(errors)
- call setqflist(errors, 'r')
- endif
- echohl Error | echomsg "Gofmt returned error" | echohl None
- endif
- call winrestview(view)
-endfunction
-
-let b:did_ftplugin_go_fmt = 1
-
-" vim:ts=4:sw=4:et
diff --git a/misc/vim/ftplugin/go/import.vim b/misc/vim/ftplugin/go/import.vim
deleted file mode 100644
index 91c8697a4..000000000
--- a/misc/vim/ftplugin/go/import.vim
+++ /dev/null
@@ -1,250 +0,0 @@
-" Copyright 2011 The Go Authors. All rights reserved.
-" Use of this source code is governed by a BSD-style
-" license that can be found in the LICENSE file.
-"
-" import.vim: Vim commands to import/drop Go packages.
-"
-" This filetype plugin adds three new commands for go buffers:
-"
-" :Import {path}
-"
-" Import ensures that the provided package {path} is imported
-" in the current Go buffer, using proper style and ordering.
-" If {path} is already being imported, an error will be
-" displayed and the buffer will be untouched.
-"
-" :ImportAs {localname} {path}
-"
-" Same as Import, but uses a custom local name for the package.
-"
-" :Drop {path}
-"
-" Remove the import line for the provided package {path}, if
-" present in the current Go buffer. If {path} is not being
-" imported, an error will be displayed and the buffer will be
-" untouched.
-"
-" If you would like to add shortcuts, you can do so by doing the following:
-"
-" Import fmt
-" au Filetype go nnoremap <buffer> <LocalLeader>f :Import fmt<CR>
-"
-" Drop fmt
-" au Filetype go nnoremap <buffer> <LocalLeader>F :Drop fmt<CR>
-"
-" Import the word under your cursor
-" au Filetype go nnoremap <buffer> <LocalLeader>k
-" \ :exe 'Import ' . expand('<cword>')<CR>
-"
-" The backslash '\' is the default maplocalleader, so it is possible that
-" your vim is set to use a different character (:help maplocalleader).
-"
-" Options:
-"
-" g:go_import_commands [default=1]
-"
-" Flag to indicate whether to enable the commands listed above.
-"
-if exists("b:did_ftplugin_go_import")
- finish
-endif
-
-if !exists("g:go_import_commands")
- let g:go_import_commands = 1
-endif
-
-if g:go_import_commands
- command! -buffer -nargs=? -complete=customlist,go#complete#Package Drop call s:SwitchImport(0, '', <f-args>)
- command! -buffer -nargs=1 -complete=customlist,go#complete#Package Import call s:SwitchImport(1, '', <f-args>)
- command! -buffer -nargs=* -complete=customlist,go#complete#Package ImportAs call s:SwitchImport(1, <f-args>)
-endif
-
-function! s:SwitchImport(enabled, localname, path)
- let view = winsaveview()
- let path = a:path
-
- " Quotes are not necessary, so remove them if provided.
- if path[0] == '"'
- let path = strpart(path, 1)
- endif
- if path[len(path)-1] == '"'
- let path = strpart(path, 0, len(path) - 1)
- endif
- if path == ''
- call s:Error('Import path not provided')
- return
- endif
-
- " Extract any site prefix (e.g. github.com/).
- " If other imports with the same prefix are grouped separately,
- " we will add this new import with them.
- " Only up to and including the first slash is used.
- let siteprefix = matchstr(path, "^[^/]*/")
-
- let qpath = '"' . path . '"'
- if a:localname != ''
- let qlocalpath = a:localname . ' ' . qpath
- else
- let qlocalpath = qpath
- endif
- let indentstr = 0
- let packageline = -1 " Position of package name statement
- let appendline = -1 " Position to introduce new import
- let deleteline = -1 " Position of line with existing import
- let linesdelta = 0 " Lines added/removed
-
- " Find proper place to add/remove import.
- let line = 0
- while line <= line('$')
- let linestr = getline(line)
-
- if linestr =~# '^package\s'
- let packageline = line
- let appendline = line
-
- elseif linestr =~# '^import\s\+('
- let appendstr = qlocalpath
- let indentstr = 1
- let appendline = line
- let firstblank = -1
- let lastprefix = ""
- while line <= line("$")
- let line = line + 1
- let linestr = getline(line)
- let m = matchlist(getline(line), '^\()\|\(\s\+\)\(\S*\s*\)"\(.\+\)"\)')
- if empty(m)
- if siteprefix == "" && a:enabled
- " must be in the first group
- break
- endif
- " record this position, but keep looking
- if firstblank < 0
- let firstblank = line
- endif
- continue
- endif
- if m[1] == ')'
- " if there's no match, add it to the first group
- if appendline < 0 && firstblank >= 0
- let appendline = firstblank
- endif
- break
- endif
- let lastprefix = matchstr(m[4], "^[^/]*/")
- if a:localname != '' && m[3] != ''
- let qlocalpath = printf('%-' . (len(m[3])-1) . 's %s', a:localname, qpath)
- endif
- let appendstr = m[2] . qlocalpath
- let indentstr = 0
- if m[4] == path
- let appendline = -1
- let deleteline = line
- break
- elseif m[4] < path
- " don't set candidate position if we have a site prefix,
- " we've passed a blank line, and this doesn't share the same
- " site prefix.
- if siteprefix == "" || firstblank < 0 || match(m[4], "^" . siteprefix) >= 0
- let appendline = line
- endif
- elseif siteprefix != "" && match(m[4], "^" . siteprefix) >= 0
- " first entry of site group
- let appendline = line - 1
- break
- endif
- endwhile
- break
-
- elseif linestr =~# '^import '
- if appendline == packageline
- let appendstr = 'import ' . qlocalpath
- let appendline = line - 1
- endif
- let m = matchlist(linestr, '^import\(\s\+\)\(\S*\s*\)"\(.\+\)"')
- if !empty(m)
- if m[3] == path
- let appendline = -1
- let deleteline = line
- break
- endif
- if m[3] < path
- let appendline = line
- endif
- if a:localname != '' && m[2] != ''
- let qlocalpath = printf("%s %" . len(m[2])-1 . "s", a:localname, qpath)
- endif
- let appendstr = 'import' . m[1] . qlocalpath
- endif
-
- elseif linestr =~# '^\(var\|const\|type\|func\)\>'
- break
-
- endif
- let line = line + 1
- endwhile
-
- " Append or remove the package import, as requested.
- if a:enabled
- if deleteline != -1
- call s:Error(qpath . ' already being imported')
- elseif appendline == -1
- call s:Error('No package line found')
- else
- if appendline == packageline
- call append(appendline + 0, '')
- call append(appendline + 1, 'import (')
- call append(appendline + 2, ')')
- let appendline += 2
- let linesdelta += 3
- let appendstr = qlocalpath
- let indentstr = 1
- endif
- call append(appendline, appendstr)
- execute appendline + 1
- if indentstr
- execute 'normal >>'
- endif
- let linesdelta += 1
- endif
- else
- if deleteline == -1
- call s:Error(qpath . ' not being imported')
- else
- execute deleteline . 'd'
- let linesdelta -= 1
-
- if getline(deleteline-1) =~# '^import\s\+(' && getline(deleteline) =~# '^)'
- " Delete empty import block
- let deleteline -= 1
- execute deleteline . "d"
- execute deleteline . "d"
- let linesdelta -= 2
- endif
-
- if getline(deleteline) == '' && getline(deleteline - 1) == ''
- " Delete spacing for removed line too.
- execute deleteline . "d"
- let linesdelta -= 1
- endif
- endif
- endif
-
- " Adjust view for any changes.
- let view.lnum += linesdelta
- let view.topline += linesdelta
- if view.topline < 0
- let view.topline = 0
- endif
-
- " Put buffer back where it was.
- call winrestview(view)
-
-endfunction
-
-function! s:Error(s)
- echohl Error | echo a:s | echohl None
-endfunction
-
-let b:did_ftplugin_go_import = 1
-
-" vim:ts=4:sw=4:et
diff --git a/misc/vim/ftplugin/go/test.sh b/misc/vim/ftplugin/go/test.sh
deleted file mode 100755
index d8a5b8951..000000000
--- a/misc/vim/ftplugin/go/test.sh
+++ /dev/null
@@ -1,78 +0,0 @@
-#!/bin/bash -e
-#
-# Copyright 2012 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-#
-# Tests for import.vim.
-
-cd $(dirname $0)
-
-cat > base.go <<EOF
-package test
-
-import (
- "bytes"
- "io"
- "net"
-
- "mycorp/foo"
-)
-EOF
-
-fail=0
-
-# usage: test_one command pattern
-# Pattern is a PCRE expression that will match across lines.
-test_one() {
- echo 2>&1 -n "$1: "
- vim -e -s -u /dev/null -U /dev/null --noplugin -c "source import.vim" \
- -c "$1" -c 'wq! test.go' base.go
- # ensure blank lines are treated correctly
- if ! gofmt test.go | cmp test.go -; then
- echo 2>&1 "gofmt conflict"
- gofmt test.go | diff -u test.go - | sed "s/^/ /" 2>&1
- fail=1
- return
- fi
- if ! [[ $(cat test.go) =~ $2 ]]; then
- echo 2>&1 "$2 did not match"
- cat test.go | sed "s/^/ /" 2>&1
- fail=1
- return
- fi
- echo 2>&1 "ok"
-}
-
-# Tests for Import
-
-test_one "Import baz" '"baz".*"bytes"'
-test_one "Import io/ioutil" '"io".*"io/ioutil".*"net"'
-test_one "Import myc" '"io".*"myc".*"net"' # prefix of a site prefix
-test_one "Import nat" '"io".*"nat".*"net"'
-test_one "Import net/http" '"net".*"net/http".*"mycorp/foo"'
-test_one "Import zoo" '"net".*"zoo".*"mycorp/foo"'
-test_one "Import mycorp/bar" '"net".*"mycorp/bar".*"mycorp/foo"'
-test_one "Import mycorp/goo" '"net".*"mycorp/foo".*"mycorp/goo"'
-
-# Tests for Drop
-
-cat > base.go <<EOF
-package test
-
-import (
- "foo"
-
- "something"
- "zoo"
-)
-EOF
-
-test_one "Drop something" '\([^"]*"foo"[^"]*"zoo"[^"]*\)'
-
-rm -f base.go test.go
-if [ $fail -gt 0 ]; then
- echo 2>&1 "FAIL"
- exit 1
-fi
-echo 2>&1 "PASS"
diff --git a/misc/vim/indent/go.vim b/misc/vim/indent/go.vim
deleted file mode 100644
index e3d6e8416..000000000
--- a/misc/vim/indent/go.vim
+++ /dev/null
@@ -1,77 +0,0 @@
-" Copyright 2011 The Go Authors. All rights reserved.
-" Use of this source code is governed by a BSD-style
-" license that can be found in the LICENSE file.
-"
-" indent/go.vim: Vim indent file for Go.
-"
-" TODO:
-" - function invocations split across lines
-" - general line splits (line ends in an operator)
-
-if exists("b:did_indent")
- finish
-endif
-let b:did_indent = 1
-
-" C indentation is too far off useful, mainly due to Go's := operator.
-" Let's just define our own.
-setlocal nolisp
-setlocal autoindent
-setlocal indentexpr=GoIndent(v:lnum)
-setlocal indentkeys+=<:>,0=},0=)
-
-if exists("*GoIndent")
- finish
-endif
-
-" The shiftwidth() function is relatively new.
-" Don't require it to exist.
-if exists('*shiftwidth')
- func s:sw()
- return shiftwidth()
- endfunc
-else
- func s:sw()
- return &shiftwidth
- endfunc
-endif
-
-function! GoIndent(lnum)
- let prevlnum = prevnonblank(a:lnum-1)
- if prevlnum == 0
- " top of file
- return 0
- endif
-
- " grab the previous and current line, stripping comments.
- let prevl = substitute(getline(prevlnum), '//.*$', '', '')
- let thisl = substitute(getline(a:lnum), '//.*$', '', '')
- let previ = indent(prevlnum)
-
- let ind = previ
-
- if prevl =~ '[({]\s*$'
- " previous line opened a block
- let ind += s:sw()
- endif
- if prevl =~# '^\s*\(case .*\|default\):$'
- " previous line is part of a switch statement
- let ind += s:sw()
- endif
- " TODO: handle if the previous line is a label.
-
- if thisl =~ '^\s*[)}]'
- " this line closed a block
- let ind -= s:sw()
- endif
-
- " Colons are tricky.
- " We want to outdent if it's part of a switch ("case foo:" or "default:").
- " We ignore trying to deal with jump labels because (a) they're rare, and
- " (b) they're hard to disambiguate from a composite literal key.
- if thisl =~# '^\s*\(case .*\|default\):$'
- let ind -= s:sw()
- endif
-
- return ind
-endfunction
diff --git a/misc/vim/plugin/godoc.vim b/misc/vim/plugin/godoc.vim
deleted file mode 100644
index a145d313f..000000000
--- a/misc/vim/plugin/godoc.vim
+++ /dev/null
@@ -1,130 +0,0 @@
-" Copyright 2011 The Go Authors. All rights reserved.
-" Use of this source code is governed by a BSD-style
-" license that can be found in the LICENSE file.
-"
-" godoc.vim: Vim command to see godoc.
-"
-"
-" Commands:
-"
-" :Godoc
-"
-" Open the relevant Godoc for either the word[s] passed to the command or
-" the, by default, the word under the cursor.
-"
-" Options:
-"
-" g:go_godoc_commands [default=1]
-"
-" Flag to indicate whether to enable the commands listed above.
-
-if exists("g:loaded_godoc")
- finish
-endif
-let g:loaded_godoc = 1
-
-let s:buf_nr = -1
-let s:last_word = ''
-
-if !exists('g:go_godoc_commands')
- let g:go_godoc_commands = 1
-endif
-
-if g:go_godoc_commands
- command! -nargs=* -range -complete=customlist,go#complete#Package Godoc :call s:Godoc(<f-args>)
-endif
-
-nnoremap <silent> <Plug>(godoc-keyword) :<C-u>call <SID>Godoc('')<CR>
-
-function! s:GodocView()
- if !bufexists(s:buf_nr)
- leftabove new
- file `="[Godoc]"`
- let s:buf_nr = bufnr('%')
- elseif bufwinnr(s:buf_nr) == -1
- leftabove split
- execute s:buf_nr . 'buffer'
- delete _
- elseif bufwinnr(s:buf_nr) != bufwinnr('%')
- execute bufwinnr(s:buf_nr) . 'wincmd w'
- endif
-
- setlocal filetype=godoc
- setlocal bufhidden=delete
- setlocal buftype=nofile
- setlocal noswapfile
- setlocal nobuflisted
- setlocal modifiable
- setlocal nocursorline
- setlocal nocursorcolumn
- setlocal iskeyword+=:
- setlocal iskeyword-=-
-
- nnoremap <buffer> <silent> K :Godoc<cr>
-
- au BufHidden <buffer> call let <SID>buf_nr = -1
-endfunction
-
-function! s:GodocWord(word)
- if !executable('godoc')
- echohl WarningMsg
- echo "godoc command not found."
- echo " install with: go get code.google.com/p/go.tools/cmd/godoc"
- echohl None
- return 0
- endif
- let word = a:word
- silent! let content = system('godoc ' . word)
- if v:shell_error || !len(content)
- if len(s:last_word)
- silent! let content = system('godoc ' . s:last_word.'/'.word)
- if v:shell_error || !len(content)
- echo 'No documentation found for "' . word . '".'
- return 0
- endif
- let word = s:last_word.'/'.word
- else
- echo 'No documentation found for "' . word . '".'
- return 0
- endif
- endif
- let s:last_word = word
- silent! call s:GodocView()
- setlocal modifiable
- silent! %d _
- silent! put! =content
- silent! normal gg
- setlocal nomodifiable
- setfiletype godoc
- return 1
-endfunction
-
-function! s:Godoc(...)
- if !len(a:000)
- let oldiskeyword = &iskeyword
- setlocal iskeyword+=.
- let word = expand('<cword>')
- let &iskeyword = oldiskeyword
- let word = substitute(word, '[^a-zA-Z0-9\\/._~-]', '', 'g')
- let words = split(word, '\.\ze[^./]\+$')
- else
- let words = a:000
- endif
- if !len(words)
- return
- endif
- if s:GodocWord(words[0])
- if len(words) > 1
- if search('^\%(const\|var\|type\|\s\+\) ' . words[1] . '\s\+=\s')
- return
- endif
- if search('^func ' . words[1] . '(')
- silent! normal zt
- return
- endif
- echo 'No documentation found for "' . words[1] . '".'
- endif
- endif
-endfunction
-
-" vim:ts=4:sw=4:et
diff --git a/misc/vim/readme.txt b/misc/vim/readme.txt
deleted file mode 100644
index 9a9e22870..000000000
--- a/misc/vim/readme.txt
+++ /dev/null
@@ -1,103 +0,0 @@
-Vim plugins for Go (http://golang.org)
-======================================
-
-To use all the Vim plugins, add these lines to your $HOME/.vimrc.
-
- " Some Linux distributions set filetype in /etc/vimrc.
- " Clear filetype flags before changing runtimepath to force Vim to reload them.
- if exists("g:did_load_filetypes")
- filetype off
- filetype plugin indent off
- endif
- set runtimepath+=$GOROOT/misc/vim " replace $GOROOT with the output of: go env GOROOT
- filetype plugin indent on
- syntax on
-
-If you want to select fewer plugins, use the instructions in the rest of
-this file.
-
-A popular configuration is to gofmt Go source files when they are saved.
-To do that, add this line to the end of your $HOME/.vimrc.
-
- autocmd FileType go autocmd BufWritePre <buffer> Fmt
-
-
-Vim syntax highlighting
------------------------
-
-To install automatic syntax highlighting for GO programs:
-
- 1. Copy or link the filetype detection script to the ftdetect directory
- underneath your vim runtime directory (normally $HOME/.vim/ftdetect)
- 2. Copy or link syntax/go.vim to the syntax directory underneath your vim
- runtime directory (normally $HOME/.vim/syntax). Linking this file rather
- than just copying it will ensure any changes are automatically reflected
- in your syntax highlighting.
- 3. Add the following line to your .vimrc file (normally $HOME/.vimrc):
-
- syntax on
-
-In a typical unix environment you might accomplish this using the following
-commands:
-
- mkdir -p $HOME/.vim/ftdetect
- mkdir -p $HOME/.vim/syntax
- mkdir -p $HOME/.vim/autoload/go
- ln -s $GOROOT/misc/vim/ftdetect/gofiletype.vim $HOME/.vim/ftdetect/
- ln -s $GOROOT/misc/vim/syntax/go.vim $HOME/.vim/syntax
- ln -s $GOROOT/misc/vim/autoload/go/complete.vim $HOME/.vim/autoload/go
- echo "syntax on" >> $HOME/.vimrc
-
-
-Vim filetype plugins
---------------------
-
-To install one of the available filetype plugins:
-
- 1. Same as 1 above.
- 2. Copy or link ftplugin/go.vim to the ftplugin directory underneath your vim
- runtime directory (normally $HOME/.vim/ftplugin). Copy or link one or more
- additional plugins from ftplugin/go/*.vim to the Go-specific subdirectory
- in the same place ($HOME/.vim/ftplugin/go/*.vim).
- 3. Add the following line to your .vimrc file (normally $HOME/.vimrc):
-
- filetype plugin on
-
-
-Vim indentation plugin
-----------------------
-
-To install automatic indentation:
-
- 1. Same as 1 above.
- 2. Copy or link indent/go.vim to the indent directory underneath your vim
- runtime directory (normally $HOME/.vim/indent).
- 3. Add the following line to your .vimrc file (normally $HOME/.vimrc):
-
- filetype indent on
-
-
-Vim compiler plugin
--------------------
-
-To install the compiler plugin:
-
- 1. Same as 1 above.
- 2. Copy or link compiler/go.vim to the compiler directory underneath your vim
- runtime directory (normally $HOME/.vim/compiler).
- 3. Activate the compiler plugin with ":compiler go". To always enable the
- compiler plugin in Go source files add an autocommand to your .vimrc file
- (normally $HOME/.vimrc):
-
- autocmd FileType go compiler go
-
-
-Godoc plugin
-------------
-
-To install godoc plugin:
-
- 1. Same as 1 above.
- 2. Copy or link plugin/godoc.vim to $HOME/.vim/plugin/godoc,
- syntax/godoc.vim to $HOME/.vim/syntax/godoc.vim,
- and autoload/go/complete.vim to $HOME/.vim/autoload/go/complete.vim.
diff --git a/misc/vim/syntax/go.vim b/misc/vim/syntax/go.vim
deleted file mode 100644
index 1ce6cb27f..000000000
--- a/misc/vim/syntax/go.vim
+++ /dev/null
@@ -1,207 +0,0 @@
-" Copyright 2009 The Go Authors. All rights reserved.
-" Use of this source code is governed by a BSD-style
-" license that can be found in the LICENSE file.
-"
-" go.vim: Vim syntax file for Go.
-"
-" Options:
-" There are some options for customizing the highlighting; the recommended
-" settings are the default values, but you can write:
-" let OPTION_NAME = 0
-" in your ~/.vimrc file to disable particular options. You can also write:
-" let OPTION_NAME = 1
-" to enable particular options. At present, all options default to on.
-"
-" - go_highlight_array_whitespace_error
-" Highlights white space after "[]".
-" - go_highlight_chan_whitespace_error
-" Highlights white space around the communications operator that don't follow
-" the standard style.
-" - go_highlight_extra_types
-" Highlights commonly used library types (io.Reader, etc.).
-" - go_highlight_space_tab_error
-" Highlights instances of tabs following spaces.
-" - go_highlight_trailing_whitespace_error
-" Highlights trailing white space.
-
-" Quit when a (custom) syntax file was already loaded
-if exists("b:current_syntax")
- finish
-endif
-
-if !exists("go_highlight_array_whitespace_error")
- let go_highlight_array_whitespace_error = 1
-endif
-if !exists("go_highlight_chan_whitespace_error")
- let go_highlight_chan_whitespace_error = 1
-endif
-if !exists("go_highlight_extra_types")
- let go_highlight_extra_types = 1
-endif
-if !exists("go_highlight_space_tab_error")
- let go_highlight_space_tab_error = 1
-endif
-if !exists("go_highlight_trailing_whitespace_error")
- let go_highlight_trailing_whitespace_error = 1
-endif
-
-syn case match
-
-syn keyword goDirective package import
-syn keyword goDeclaration var const type
-syn keyword goDeclType struct interface
-
-hi def link goDirective Statement
-hi def link goDeclaration Keyword
-hi def link goDeclType Keyword
-
-" Keywords within functions
-syn keyword goStatement defer go goto return break continue fallthrough
-syn keyword goConditional if else switch select
-syn keyword goLabel case default
-syn keyword goRepeat for range
-
-hi def link goStatement Statement
-hi def link goConditional Conditional
-hi def link goLabel Label
-hi def link goRepeat Repeat
-
-" Predefined types
-syn keyword goType chan map bool string error
-syn keyword goSignedInts int int8 int16 int32 int64 rune
-syn keyword goUnsignedInts byte uint uint8 uint16 uint32 uint64 uintptr
-syn keyword goFloats float32 float64
-syn keyword goComplexes complex64 complex128
-
-hi def link goType Type
-hi def link goSignedInts Type
-hi def link goUnsignedInts Type
-hi def link goFloats Type
-hi def link goComplexes Type
-
-" Treat func specially: it's a declaration at the start of a line, but a type
-" elsewhere. Order matters here.
-syn match goType /\<func\>/
-syn match goDeclaration /^func\>/
-
-" Predefined functions and values
-syn keyword goBuiltins append cap close complex copy delete imag len
-syn keyword goBuiltins make new panic print println real recover
-syn keyword goConstants iota true false nil
-
-hi def link goBuiltins Keyword
-hi def link goConstants Keyword
-
-" Comments; their contents
-syn keyword goTodo contained TODO FIXME XXX BUG
-syn cluster goCommentGroup contains=goTodo
-syn region goComment start="/\*" end="\*/" contains=@goCommentGroup,@Spell
-syn region goComment start="//" end="$" contains=@goCommentGroup,@Spell
-
-hi def link goComment Comment
-hi def link goTodo Todo
-
-" Go escapes
-syn match goEscapeOctal display contained "\\[0-7]\{3}"
-syn match goEscapeC display contained +\\[abfnrtv\\'"]+
-syn match goEscapeX display contained "\\x\x\{2}"
-syn match goEscapeU display contained "\\u\x\{4}"
-syn match goEscapeBigU display contained "\\U\x\{8}"
-syn match goEscapeError display contained +\\[^0-7xuUabfnrtv\\'"]+
-
-hi def link goEscapeOctal goSpecialString
-hi def link goEscapeC goSpecialString
-hi def link goEscapeX goSpecialString
-hi def link goEscapeU goSpecialString
-hi def link goEscapeBigU goSpecialString
-hi def link goSpecialString Special
-hi def link goEscapeError Error
-
-" Strings and their contents
-syn cluster goStringGroup contains=goEscapeOctal,goEscapeC,goEscapeX,goEscapeU,goEscapeBigU,goEscapeError
-syn region goString start=+"+ skip=+\\\\\|\\"+ end=+"+ contains=@goStringGroup
-syn region goRawString start=+`+ end=+`+
-
-hi def link goString String
-hi def link goRawString String
-
-" Characters; their contents
-syn cluster goCharacterGroup contains=goEscapeOctal,goEscapeC,goEscapeX,goEscapeU,goEscapeBigU
-syn region goCharacter start=+'+ skip=+\\\\\|\\'+ end=+'+ contains=@goCharacterGroup
-
-hi def link goCharacter Character
-
-" Regions
-syn region goBlock start="{" end="}" transparent fold
-syn region goParen start='(' end=')' transparent
-
-" Integers
-syn match goDecimalInt "\<\d\+\([Ee]\d\+\)\?\>"
-syn match goHexadecimalInt "\<0x\x\+\>"
-syn match goOctalInt "\<0\o\+\>"
-syn match goOctalError "\<0\o*[89]\d*\>"
-
-hi def link goDecimalInt Integer
-hi def link goHexadecimalInt Integer
-hi def link goOctalInt Integer
-hi def link Integer Number
-
-" Floating point
-syn match goFloat "\<\d\+\.\d*\([Ee][-+]\d\+\)\?\>"
-syn match goFloat "\<\.\d\+\([Ee][-+]\d\+\)\?\>"
-syn match goFloat "\<\d\+[Ee][-+]\d\+\>"
-
-hi def link goFloat Float
-
-" Imaginary literals
-syn match goImaginary "\<\d\+i\>"
-syn match goImaginary "\<\d\+\.\d*\([Ee][-+]\d\+\)\?i\>"
-syn match goImaginary "\<\.\d\+\([Ee][-+]\d\+\)\?i\>"
-syn match goImaginary "\<\d\+[Ee][-+]\d\+i\>"
-
-hi def link goImaginary Number
-
-" Spaces after "[]"
-if go_highlight_array_whitespace_error != 0
- syn match goSpaceError display "\(\[\]\)\@<=\s\+"
-endif
-
-" Spacing errors around the 'chan' keyword
-if go_highlight_chan_whitespace_error != 0
- " receive-only annotation on chan type
- syn match goSpaceError display "\(<-\)\@<=\s\+\(chan\>\)\@="
- " send-only annotation on chan type
- syn match goSpaceError display "\(\<chan\)\@<=\s\+\(<-\)\@="
- " value-ignoring receives in a few contexts
- syn match goSpaceError display "\(\(^\|[={(,;]\)\s*<-\)\@<=\s\+"
-endif
-
-" Extra types commonly seen
-if go_highlight_extra_types != 0
- syn match goExtraType /\<bytes\.\(Buffer\)\>/
- syn match goExtraType /\<io\.\(Reader\|Writer\|ReadWriter\|ReadWriteCloser\)\>/
- syn match goExtraType /\<reflect\.\(Kind\|Type\|Value\)\>/
- syn match goExtraType /\<unsafe\.Pointer\>/
-endif
-
-" Space-tab error
-if go_highlight_space_tab_error != 0
- syn match goSpaceError display " \+\t"me=e-1
-endif
-
-" Trailing white space error
-if go_highlight_trailing_whitespace_error != 0
- syn match goSpaceError display excludenl "\s\+$"
-endif
-
-hi def link goExtraType Type
-hi def link goSpaceError Error
-
-" Search backwards for a global declaration to start processing the syntax.
-"syn sync match goSync grouphere NONE /^\(const\|var\|type\|func\)\>/
-
-" There's a bug in the implementation of grouphere. For now, use the
-" following as a more expensive/less precise workaround.
-syn sync minlines=500
-
-let b:current_syntax = "go"
diff --git a/misc/vim/syntax/godoc.vim b/misc/vim/syntax/godoc.vim
deleted file mode 100644
index bd4443f7c..000000000
--- a/misc/vim/syntax/godoc.vim
+++ /dev/null
@@ -1,20 +0,0 @@
-" Copyright 2011 The Go Authors. All rights reserved.
-" Use of this source code is governed by a BSD-style
-" license that can be found in the LICENSE file.
-
-if exists("b:current_syntax")
- finish
-endif
-
-syn case match
-syn match godocTitle "^\([A-Z][A-Z ]*\)$"
-
-command -nargs=+ HiLink hi def link <args>
-
-HiLink godocTitle Title
-
-delcommand HiLink
-
-let b:current_syntax = "godoc"
-
-" vim:ts=4 sts=2 sw=2:
diff --git a/misc/xcode/3/README b/misc/xcode/3/README
deleted file mode 100644
index 69f371c2c..000000000
--- a/misc/xcode/3/README
+++ /dev/null
@@ -1,3 +0,0 @@
-This directory contains files for Go syntax highlighting in Xcode 3.x.
-See the comments in go.pbfilespec and go.xclangspec for installation
-instructions.
diff --git a/misc/xcode/3/go.pbfilespec b/misc/xcode/3/go.pbfilespec
deleted file mode 100644
index 1034778f5..000000000
--- a/misc/xcode/3/go.pbfilespec
+++ /dev/null
@@ -1,31 +0,0 @@
-/*
- Copyright 2009 The Go Authors. All rights reserved.
- Use of this source code is governed by a BSD-style
- license that can be found in the LICENSE file.
-
- go.pbfilespec
- Go source file spec for Xcode 3
-
- There is not much documentation available regarding the format
- of .pbfilespec files. As a starting point, see for instance the
- outdated documentation at:
- http://maxao.free.fr/xcode-plugin-interface/specifications.html
- and the files in:
- /Developer/Library/PrivateFrameworks/XcodeEdit.framework/Versions/A/Resources/
-
- Place this file in directory:
- ~/Library/Application Support/Developer/Shared/Xcode/Specifications/
-*/
-
-(
- {
- Identifier = sourcecode.go;
- BasedOn = sourcecode;
- Name = "Go Files";
- Extensions = ("go");
- MIMETypes = ("text/go");
- Language = "xcode.lang.go";
- IsTextFile = YES;
- IsSourceFile = YES;
- }
-)
diff --git a/misc/xcode/3/go.xclangspec b/misc/xcode/3/go.xclangspec
deleted file mode 100644
index 4a8c94d5b..000000000
--- a/misc/xcode/3/go.xclangspec
+++ /dev/null
@@ -1,293 +0,0 @@
-/*
- Copyright 2009 The Go Authors. All rights reserved.
- Use of this source code is governed by a BSD-style
- license that can be found in the LICENSE file.
-
- Go.xclangspec
- Go language specification for Xcode 3
-
- This is a preliminary version that supports basic syntax high-lighting
- (such as keywords, literals, and comments) and an attempt to provide
- some structure information (incomplete).
-
- There is not much documentation available regarding the format
- of .xclangspec files. As a starting point, see for instance the
- outdated documentation at:
- http://maxao.free.fr/xcode-plugin-interface/specifications.html
- and the files in:
- /Developer/Library/PrivateFrameworks/XcodeEdit.framework/Versions/A/Resources/
-
- Place this file in directory:
- ~/Library/Application Support/Developer/Shared/Xcode/Specifications/
-*/
-
-(
-
-// ----------------------------------------------------------------------------
-// Keywords
-
-// TODO How do we get general Unicode identifiers?
-
- {
- Identifier = "xcode.lang.go.identifier";
- Syntax = {
- StartChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
- Chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
- Words = (
- "break",
- "case",
- "chan",
- "const",
- "continue",
-
- "default",
- "defer",
- "else",
- "fallthrough",
- "for",
-
- "func",
- "go",
- "goto",
- "if",
- "import",
-
- "interface",
- "map",
- "package",
- "range",
- "return",
-
- "select",
- "struct",
- "switch",
- "type",
- "var",
- );
- Type = "xcode.syntax.keyword";
- AltType = "xcode.syntax.identifier"; // non-keywords are identifiers
- };
- },
-
-// TODO decide what should go here, if anything
- {
- Identifier = "xcode.lang.go.interestingOperators";
- Syntax = {
- Words = (
- "...",
- ".",
- "*",
- ",",
- ":",
- );
- Type = "xcode.syntax.plain";
- };
- },
-
- {
- Identifier = "xcode.lang.go.rawstring";
- Syntax = {
- Start = "`";
- End = "`";
- Type = "xcode.syntax.string";
- };
- },
-
-// ----------------------------------------------------------------------------
-// Syntax Coloring
-
- {
- Identifier = "xcode.lang.go";
- Description = "Go Coloring";
- BasedOn = "xcode.lang.simpleColoring";
- IncludeInMenu = YES;
- Name = "Go";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer.toplevel";
- IncludeRules = (
- "xcode.lang.go.block",
- "xcode.lang.go.bracketexpr",
- "xcode.lang.go.parenexpr",
- );
- Type = "xcode.syntax.plain";
- };
- },
-
- // The following rule returns tokens to the other rules
- {
- Identifier = "xcode.lang.go.lexer";
- Syntax = {
- IncludeRules = (
- "xcode.lang.go.comment",
- "xcode.lang.go.comment.singleline",
- "xcode.lang.string",
- "xcode.lang.character",
- "xcode.lang.go.rawstring",
- "xcode.lang.go.identifier",
- "xcode.lang.number",
- "xcode.lang.go.interestingOperators",
- );
- };
- },
-
- {
- Identifier = "xcode.lang.go.lexer.toplevel";
- Syntax = {
- IncludeRules = (
- "xcode.lang.go.comment",
- "xcode.lang.go.comment.singleline",
- "xcode.lang.string",
- "xcode.lang.character",
- "xcode.lang.go.rawstring",
- "xcode.lang.go.type.declaration",
- "xcode.lang.go.method.declaration",
- "xcode.lang.go.function.declaration",
- "xcode.lang.go.identifier",
- "xcode.lang.number",
- );
- };
- },
-
- {
- Identifier = "xcode.lang.go.method.declaration";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer";
- Rules = (
- "func",
- "xcode.lang.go.parenexpr",
- "xcode.lang.go.identifier",
- "xcode.lang.go.parenexpr",
- );
- Type = "xcode.syntax.declaration.method";
- };
- },
-
- {
- Identifier = "xcode.lang.go.type.declaration";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer";
- Rules = (
- "type",
- "xcode.lang.go.identifier",
- );
- Type = "xcode.syntax.typedef";
- };
- },
-
- {
- Identifier = "xcode.lang.go.function.declaration";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer";
- Rules = (
- "func",
- "xcode.lang.go.identifier",
- "xcode.lang.go.parenexpr",
- );
- Type = "xcode.syntax.declaration.function";
- };
- },
-
-// ----------------------------------------------------------------------------
-// Blocks
-
- {
- Identifier = "xcode.lang.go.block";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer";
- Start = "{";
- End = "}";
- Foldable = YES;
- Recursive = YES;
- IncludeRules = (
- "xcode.lang.go.bracketexpr",
- "xcode.lang.go.parenexpr",
- );
- };
- },
-
- {
- Identifier = "xcode.lang.go.parenexpr";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer";
- Start = "(";
- End = ")";
- Recursive = YES;
- IncludeRules = (
- "xcode.lang.go.bracketexpr",
- "xcode.lang.go.block",
- );
- };
- },
-
- {
- Identifier = "xcode.lang.go.bracketexpr";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer";
- Start = "[";
- End = "]";
- Recursive = YES;
- IncludeRules = (
- "xcode.lang.go.parenexpr",
- );
- };
- },
-
- {
- Identifier = "xcode.lang.go.comment";
- Syntax = {
- Start = "/*";
- End = "*/";
- Foldable = YES;
- IncludeRules = (
- "xcode.lang.url",
- "xcode.lang.url.mail",
- "xcode.lang.comment.mark",
- );
- Type = "xcode.syntax.comment";
- };
- },
-
- {
- Identifier = "xcode.lang.go.comment.singleline";
- Syntax = {
- Start = "//";
- End = "\n";
- IncludeRules = (
- "xcode.lang.url",
- "xcode.lang.url.mail",
- "xcode.lang.comment.mark",
- );
- Type = "xcode.syntax.comment";
- };
- },
-
- // This rule recognizes special comments markers and adds them
- // to the list of file markers at the top of the editor window.
- // This overrides the markers specified in
- // /Developer/Library/PrivateFrameworks/XcodeEdit.framework/Versions/A/Resources/BaseSupport.xclangspec
- // and appears to apply them to all languages. Thus, for now
- // "inherit" the existing markers here for backward-compatibility.
- {
- Identifier = "xcode.lang.comment.mark";
- Syntax = {
- StartChars = "BMTF!?";
- Match = (
- // Go-specific markers
- "^\(BUG.*$\)$", // include "BUG" in the markers list
- "^\(TODO.*$\)$", // include "TODO" in the markers list
- // inherited markers
- "^MARK:[ \t]+\(.*\)$",
- "^\(TODO:[ \t]+.*\)$", // include "TODO: " in the markers list
- "^\(FIXME:[ \t]+.*\)$", // include "FIXME: " in the markers list
- "^\(!!!:.*\)$", // include "!!!:" in the markers list
- "^\(\\?\\?\\?:.*\)$" // include "???:" in the markers list
- );
- // This is the order of captures. All of the match strings above need the same order.
- CaptureTypes = (
- "xcode.syntax.mark"
- );
- Type = "xcode.syntax.comment";
- };
- },
-
-)
diff --git a/misc/xcode/4/README b/misc/xcode/4/README
deleted file mode 100644
index 09576d6d5..000000000
--- a/misc/xcode/4/README
+++ /dev/null
@@ -1,2 +0,0 @@
-This directory contains files for Go syntax highlighting in Xcode 4.x.
-For installation, read, edit, and run go4xcode.sh . \ No newline at end of file
diff --git a/misc/xcode/4/go.xclangspec b/misc/xcode/4/go.xclangspec
deleted file mode 100644
index 96edc8000..000000000
--- a/misc/xcode/4/go.xclangspec
+++ /dev/null
@@ -1,290 +0,0 @@
-/*
- Copyright 2012 The Go Authors. All rights reserved.
- Use of this source code is governed by a BSD-style
- license that can be found in the LICENSE file.
-
- go.xclangspec
- Go language specification for Xcode 4.x.
-
- This is a preliminary version that supports basic syntax coloring
- (such as keywords, literals, and comments) and an attempt to provide
- some structure information (incomplete).
-
- There is not much documentation available regarding the format
- of .xclangspec files. As a starting point, see for instance the
- outdated documentation at:
-
- http://maxao.free.fr/xcode-plugin-interface/specifications.html
-
-*/
-
-(
-
-// ----------------------------------------------------------------------------
-// Keywords
-
-// TODO How do we get general Unicode identifiers?
-
- {
- Identifier = "xcode.lang.go.identifier";
- Syntax = {
- StartChars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ_";
- Chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789_";
- Words = (
- "break",
- "case",
- "chan",
- "const",
- "continue",
-
- "default",
- "defer",
- "else",
- "fallthrough",
- "for",
-
- "func",
- "go",
- "goto",
- "if",
- "import",
-
- "interface",
- "map",
- "package",
- "range",
- "return",
-
- "select",
- "struct",
- "switch",
- "type",
- "var",
- );
- Type = "xcode.syntax.keyword";
- AltType = "xcode.syntax.identifier"; // non-keywords are identifiers
- };
- },
-
-// TODO decide what should go here, if anything
- {
- Identifier = "xcode.lang.go.interestingOperators";
- Syntax = {
- Words = (
- "...",
- ".",
- "*",
- ",",
- ":",
- );
- Type = "xcode.syntax.plain";
- };
- },
-
- {
- Identifier = "xcode.lang.go.rawstring";
- Syntax = {
- Start = "`";
- End = "`";
- Type = "xcode.syntax.string";
- };
- },
-
-// ----------------------------------------------------------------------------
-// Syntax Coloring
-
- {
- Identifier = "xcode.lang.go";
- Description = "Go Coloring";
- BasedOn = "xcode.lang.simpleColoring";
- IncludeInMenu = YES;
- Name = "Go";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer.toplevel";
- IncludeRules = (
- "xcode.lang.go.block",
- "xcode.lang.go.bracketexpr",
- "xcode.lang.go.parenexpr",
- );
- Type = "xcode.syntax.plain";
- };
- },
-
- // The following rule returns tokens to the other rules
- {
- Identifier = "xcode.lang.go.lexer";
- Syntax = {
- IncludeRules = (
- "xcode.lang.go.comment",
- "xcode.lang.go.comment.singleline",
- "xcode.lang.string",
- "xcode.lang.character",
- "xcode.lang.go.rawstring",
- "xcode.lang.go.identifier",
- "xcode.lang.number",
- "xcode.lang.go.interestingOperators",
- );
- };
- },
-
- {
- Identifier = "xcode.lang.go.lexer.toplevel";
- Syntax = {
- IncludeRules = (
- "xcode.lang.go.comment",
- "xcode.lang.go.comment.singleline",
- "xcode.lang.string",
- "xcode.lang.character",
- "xcode.lang.go.rawstring",
- "xcode.lang.go.type.declaration",
- "xcode.lang.go.method.declaration",
- "xcode.lang.go.function.declaration",
- "xcode.lang.go.identifier",
- "xcode.lang.number",
- );
- };
- },
-
- {
- Identifier = "xcode.lang.go.method.declaration";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer";
- Rules = (
- "func",
- "xcode.lang.go.parenexpr",
- "xcode.lang.go.identifier",
- "xcode.lang.go.parenexpr",
- );
- Type = "xcode.syntax.declaration.method";
- };
- },
-
- {
- Identifier = "xcode.lang.go.type.declaration";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer";
- Rules = (
- "type",
- "xcode.lang.go.identifier",
- );
- Type = "xcode.syntax.typedef";
- };
- },
-
- {
- Identifier = "xcode.lang.go.function.declaration";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer";
- Rules = (
- "func",
- "xcode.lang.go.identifier",
- "xcode.lang.go.parenexpr",
- );
- Type = "xcode.syntax.declaration.function";
- };
- },
-
-// ----------------------------------------------------------------------------
-// Blocks
-
- {
- Identifier = "xcode.lang.go.block";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer";
- Start = "{";
- End = "}";
- Foldable = YES;
- Recursive = YES;
- IncludeRules = (
- "xcode.lang.go.bracketexpr",
- "xcode.lang.go.parenexpr",
- );
- };
- },
-
- {
- Identifier = "xcode.lang.go.parenexpr";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer";
- Start = "(";
- End = ")";
- Recursive = YES;
- IncludeRules = (
- "xcode.lang.go.bracketexpr",
- "xcode.lang.go.block",
- );
- };
- },
-
- {
- Identifier = "xcode.lang.go.bracketexpr";
- Syntax = {
- Tokenizer = "xcode.lang.go.lexer";
- Start = "[";
- End = "]";
- Recursive = YES;
- IncludeRules = (
- "xcode.lang.go.parenexpr",
- );
- };
- },
-
- {
- Identifier = "xcode.lang.go.comment";
- Syntax = {
- Start = "/*";
- End = "*/";
- Foldable = YES;
- IncludeRules = (
- "xcode.lang.url",
- "xcode.lang.url.mail",
- "xcode.lang.comment.mark",
- );
- Type = "xcode.syntax.comment";
- };
- },
-
- {
- Identifier = "xcode.lang.go.comment.singleline";
- Syntax = {
- Start = "//";
- End = "\n";
- IncludeRules = (
- "xcode.lang.url",
- "xcode.lang.url.mail",
- "xcode.lang.comment.mark",
- );
- Type = "xcode.syntax.comment";
- };
- },
-
- // This rule recognizes special comments markers and adds them
- // to the list of file markers at the top of the editor window.
- // This overrides the markers specified in
- // /Developer/Library/PrivateFrameworks/XcodeEdit.framework/Versions/A/Resources/BaseSupport.xclangspec
- // and appears to apply them to all languages. Thus, for now
- // "inherit" the existing markers here for backward-compatibility.
- {
- Identifier = "xcode.lang.comment.mark";
- Syntax = {
- StartChars = "BMTF!?";
- Match = (
- // Go-specific markers
- "^\(BUG.*$\)$", // include "BUG" in the markers list
- "^\(TODO.*$\)$", // include "TODO" in the markers list
- // inherited markers
- "^MARK:[ \t]+\(.*\)$",
- "^\(TODO:[ \t]+.*\)$", // include "TODO: " in the markers list
- "^\(FIXME:[ \t]+.*\)$", // include "FIXME: " in the markers list
- "^\(!!!:.*\)$", // include "!!!:" in the markers list
- "^\(\\?\\?\\?:.*\)$" // include "???:" in the markers list
- );
- // This is the order of captures. All of the match strings above need the same order.
- CaptureTypes = (
- "xcode.syntax.mark"
- );
- Type = "xcode.syntax.comment";
- };
- },
-
-)
diff --git a/misc/xcode/4/go4xcode.sh b/misc/xcode/4/go4xcode.sh
deleted file mode 100755
index 4b0125e46..000000000
--- a/misc/xcode/4/go4xcode.sh
+++ /dev/null
@@ -1,112 +0,0 @@
-#!/usr/bin/env bash
-# Copyright 2012 The Go Authors. All rights reserved.
-# Use of this source code is governed by a BSD-style
-# license that can be found in the LICENSE file.
-
-# Illustrates how a Go language specification can be installed for Xcode 4+,
-# to enable syntax coloring, by adding an entry to a plugindata file.
-#
-# FIXME: Write a decent Xcode plugin to handle the file type association and
-# language specification properly instead of altering Xcode library files.
-
-set -e
-
-# Assumes Xcode 4+.
-XCODE_MAJOR_VERSION=`xcodebuild -version | awk 'NR == 1 {print substr($2,1,1)}'`
-if [ "$XCODE_MAJOR_VERSION" -lt "4" ]; then
- echo "Xcode 4+ not found."
- exit 1
-fi
-
-# DVTFOUNDATION_DIR may vary depending on Xcode setup. If Xcode has installed
-# the `xcode-select` command, it will be determined automatically. Otherwise,
-# change it to reflect your current Xcode setup. Find suitable path with e.g.:
-#
-# find / -type f -name 'DVTFoundation.xcplugindata' 2> /dev/null
-#
-# Example of DVTFOUNDATION_DIR's from "default" Xcode 4+ setups;
-#
-# Xcode 4.1: /Developer/Library/PrivateFrameworks/DVTFoundation.framework/Versions/A/Resources/
-# Xcode 4.3: /Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/
-
-# Defaults to Xcode 4.3's DVTFOUNDATION_DIR. Path is modified automatically if
-# `xcode-select` command is available, as mentioned above.
-DVTFOUNDATION_DIR="/Applications/Xcode.app/Contents/SharedFrameworks/DVTFoundation.framework/Versions/A/Resources/"
-
-if type "xcode-select" > /dev/null; then
- DVTFOUNDATION_DIR=`xcode-select --print-path`
- DVTFOUNDATION_DIR+="/.."
- FRAMEWORK_NAME="DVTFoundation.framework"
- DVTFOUNDATION_DIR=`find $DVTFOUNDATION_DIR -name $FRAMEWORK_NAME -print`
- DVTFOUNDATION_DIR+="/Versions/A/Resources"
-fi
-
-PLUGINDATA_FILE="DVTFoundation.xcplugindata"
-
-PLISTBUDDY=/usr/libexec/PlistBuddy
-PLIST_FILE=tmp.plist
-
-# Provide means of deleting the Go entry from the plugindata file.
-if [ "$1" = "--delete-entry" ]; then
- echo "Removing Go language specification entry."
- $PLISTBUDDY -c "Delete :plug-in:extensions:Xcode.SourceCodeLanguage.Go" $DVTFOUNDATION_DIR/$PLUGINDATA_FILE
- echo "Run 'sudo rm -rf /var/folders/*' and restart Xcode to update change immediately."
- exit 0
-fi
-
-GO_VERSION="`go version`"
-
-GO_LANG_ENTRY="
- <?xml version=\"1.0\" encoding=\"UTF-8\"?>
- <!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">
- <plist version=\"1.0\">
- <dict>
- <key>Xcode.SourceCodeLanguage.Go</key>
- <dict>
- <key>conformsTo</key>
- <array>
- <dict>
- <key>identifier</key>
- <string>Xcode.SourceCodeLanguage.Generic</string>
- </dict>
- </array>
- <key>documentationAbbreviation</key>
- <string>go</string>
- <key>fileDataType</key>
- <array>
- <dict>
- <key>identifier</key>
- <string>com.apple.xcode.go-source</string>
- </dict>
- </array>
- <key>id</key>
- <string>Xcode.SourceCodeLanguage.Go</string>
- <key>languageName</key>
- <string>Go</string>
- <key>languageSpecification</key>
- <string>xcode.lang.go</string>
- <key>name</key>
- <string>The Go Programming Language</string>
- <key>point</key>
- <string>Xcode.SourceCodeLanguage</string>
- <key>version</key>
- <string>$GO_VERSION</string>
- </dict>
- </dict>
- </plist>
-"
-
-echo "Backing up plugindata file (copied to $PLUGINDATA_FILE.bak)."
-cp $DVTFOUNDATION_DIR/$PLUGINDATA_FILE $DVTFOUNDATION_DIR/$PLUGINDATA_FILE.bak
-
-echo "Adding Go language specification entry."
-echo $GO_LANG_ENTRY > $PLIST_FILE
-$PLISTBUDDY -c "Merge $PLIST_FILE plug-in:extensions" $DVTFOUNDATION_DIR/$PLUGINDATA_FILE
-
-rm -f $PLIST_FILE
-
-echo "Installing Go language specification file for Xcode."
-cp $GOROOT/misc/xcode/4/go.xclangspec $DVTFOUNDATION_DIR
-
-echo "Run 'sudo rm -rf /var/folders/*' and restart Xcode to update change immediately."
-echo "Syntax coloring must be manually selected from the Editor - Syntax Coloring menu in Xcode."
diff --git a/misc/zsh/go b/misc/zsh/go
deleted file mode 100644
index 066cf4065..000000000
--- a/misc/zsh/go
+++ /dev/null
@@ -1,161 +0,0 @@
-# install in /etc/zsh/zshrc or your personal .zshrc
-
-# gc
-prefixes=(5 6 8)
-for p in $prefixes; do
- compctl -g "*.${p}" ${p}l
- compctl -g "*.go" ${p}g
-done
-
-# standard go tools
-compctl -g "*.go" gofmt
-
-# gccgo
-compctl -g "*.go" gccgo
-
-# go tool
-__go_tool_complete() {
- typeset -a commands build_flags
- commands+=(
- 'build[compile packages and dependencies]'
- 'clean[remove object files]'
- 'env[print Go environment information]'
- 'fix[run go tool fix on packages]'
- 'fmt[run gofmt on package sources]'
- 'get[download and install packages and dependencies]'
- 'help[display help]'
- 'install[compile and install packages and dependencies]'
- 'list[list packages]'
- 'run[compile and run Go program]'
- 'test[test packages]'
- 'tool[run specified go tool]'
- 'version[print Go version]'
- 'vet[run go tool vet on packages]'
- )
- if (( CURRENT == 2 )); then
- # explain go commands
- _values 'go tool commands' ${commands[@]}
- return
- fi
- build_flags=(
- '-a[force reinstallation of packages that are already up-to-date]'
- '-n[print the commands but do not run them]'
- '-p[number of parallel builds]:number'
- '-race[enable data race detection]'
- '-x[print the commands]'
- '-work[print temporary directory name and keep it]'
- '-ccflags[flags for 5c/6c/8c]:flags'
- '-gcflags[flags for 5g/6g/8g]:flags'
- '-ldflags[flags for 5l/6l/8l]:flags'
- '-gccgoflags[flags for gccgo]:flags'
- '-compiler[name of compiler to use]:name'
- '-installsuffix[suffix to add to package directory]:suffix'
- '-tags[list of build tags to consider satisfied]:tags'
- )
- __go_list() {
- local expl importpaths
- declare -a importpaths
- importpaths=($(go list ${words[$CURRENT]}... 2>/dev/null))
- _wanted importpaths expl 'import paths' compadd "$@" - "${importpaths[@]}"
- }
- case ${words[2]} in
- clean|doc)
- _arguments -s -w : '*:importpaths:__go_list'
- ;;
- fix|fmt|list|vet)
- _alternative ':importpaths:__go_list' ':files:_path_files -g "*.go"'
- ;;
- install)
- _arguments -s -w : ${build_flags[@]} \
- "-v[show package names]" \
- '*:importpaths:__go_list'
- ;;
- get)
- _arguments -s -w : \
- ${build_flags[@]}
- ;;
- build)
- _arguments -s -w : \
- ${build_flags[@]} \
- "-v[show package names]" \
- "-o[output file]:file:_files" \
- "*:args:{ _alternative ':importpaths:__go_list' ':files:_path_files -g \"*.go\"' }"
- ;;
- test)
- _arguments -s -w : \
- ${build_flags[@]} \
- "-c[do not run, compile the test binary]" \
- "-i[do not run, install dependencies]" \
- "-v[print test output]" \
- "-x[print the commands]" \
- "-short[use short mode]" \
- "-parallel[number of parallel tests]:number" \
- "-cpu[values of GOMAXPROCS to use]:number list" \
- "-cover[enable coverage analysis]" \
- "-run[run tests and examples matching regexp]:regexp" \
- "-bench[run benchmarks matching regexp]:regexp" \
- "-benchmem[print memory allocation stats]" \
- "-benchtime[run each benchmark until taking this long]:duration" \
- "-blockprofile[write goroutine blocking profile to file]:file" \
- "-blockprofilerate[set sampling rate of goroutine blocking profile]:number" \
- "-timeout[kill test after that duration]:duration" \
- "-cpuprofile[write CPU profile to file]:file:_files" \
- "-memprofile[write heap profile to file]:file:_files" \
- "-memprofilerate[set heap profiling rate]:number" \
- "*:args:{ _alternative ':importpaths:__go_list' ':files:_path_files -g \"*.go\"' }"
- ;;
- help)
- _values "${commands[@]}" \
- 'c[how to call C code]' \
- 'importpath[description of import path]' \
- 'gopath[GOPATH environment variable]' \
- 'packages[description of package lists]' \
- 'testflag[description of testing flags]' \
- 'testfunc[description of testing functions]'
- ;;
- run)
- _arguments -s -w : \
- ${build_flags[@]} \
- '*:file:_path_files -g "*.go"'
- ;;
- tool)
- if (( CURRENT == 3 )); then
- _values "go tool" $(go tool)
- return
- fi
- case ${words[3]} in
- [568]g)
- _arguments -s -w : \
- '-I[search for packages in DIR]:includes:_path_files -/' \
- '-L[show full path in file:line prints]' \
- '-S[print the assembly language]' \
- '-V[print the compiler version]' \
- '-e[no limit on number of errors printed]' \
- '-h[panic on an error]' \
- '-l[disable inlining]' \
- '-m[print optimization decisions]' \
- '-o[file specify output file]:file' \
- '-p[assumed import path for this code]:importpath' \
- '-u[disable package unsafe]' \
- "*:file:_files -g '*.go'"
- ;;
- [568]l)
- local O=${words[3]%l}
- _arguments -s -w : \
- '-o[file specify output file]:file' \
- '-L[search for packages in DIR]:includes:_path_files -/' \
- "*:file:_files -g '*.[ao$O]'"
- ;;
- dist)
- _values "dist tool" banner bootstrap clean env install version
- ;;
- *)
- # use files by default
- _files
- ;;
- esac
- ;;
- esac
-}
-
-compdef __go_tool_complete go