diff options
author | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
---|---|---|
committer | Tianon Gravi <admwiggin@gmail.com> | 2015-01-15 11:54:00 -0700 |
commit | f154da9e12608589e8d5f0508f908a0c3e88a1bb (patch) | |
tree | f8255d51e10c6f1e0ed69702200b966c9556a431 /misc | |
parent | 8d8329ed5dfb9622c82a9fbec6fd99a580f9c9f6 (diff) | |
download | golang-upstream/1.4.tar.gz |
Imported Upstream version 1.4upstream/1.4
Diffstat (limited to 'misc')
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>"</string> - <key>secondString</key> - <string>'</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="""/> - <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=":!%&()+,-/.*<=>?[]|~^;"/> - </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="""/> - </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="'"" /> - </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">( ) [ ] { } ... , ; & ^ % > < ! = + - * / | :</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" 01\ 02" 03' 04\ 05' 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/&/&/g; - $text =~ s/</</g; - $text =~ s/>/>/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 |