<commit_before>package packager

import (
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	// "strings"
)

import (
	"butler/null"
	proto "code.google.com/p/goprotobuf/proto"
	yaml "goyaml"
	linker "tritium/linker"
	parser "tritium/parser"
	tp "tritium/proto"
)

func resolveDefinition(pkg *tp.Package, fun *tp.Function) {
	linkingContext := linker.NewLinkingContext(pkg)

	//	pkg.Log.Info("\t -- Resolving --\n")
	//	pkg.Log.Info("\t\t -- function: %v\n", fun)

	// Re-uses linker's logic to resolve function definitions
	if null.GetBool(fun.BuiltIn) == false {
		typeName := null.GetString(fun.ScopeType)

		// DON'T DO THE FOLLOWING HERE -- NEED TO RESOLVE INHERITANCE FIRST
		// // Make sure we're not replacing an existing function bacause it's (currently) a security risk
		// typeID := fun.GetScopeTypeId()
		// siblingFuncs := linkingContext.FunctionsIn(typeID)
		// // println("CHECKING FOR FRATRICIDE IN", typeName)
		// _, present := siblingFuncs[fun.Stub(pkg)]
		// if present {
		// 	msg := fmt.Sprintf("Redefining an existing function is not permitted: %s", fun.Stub(pkg))
		// 	panic(msg)
		// }
		// // for name, sib := range siblingFuncs {
		// // 	println("\t", name, sib)
		// // }
		// /////////////////////////////////////////////////////////////////////////////

		if len(typeName) != 0 {
			// When I pass in functions from the inheritance resolver, they're typeId is already set
			fun.ScopeTypeId = pkg.GetProtoTypeId(fun.ScopeType)
			fun.ScopeType = nil
		}

		localScope := make(linker.LocalDef, len(fun.Args))

		//		fun.ReturnTypeId = pkg.GetProtoTypeId(fun.ReturnType)
		for _, arg := range fun.Args {
			argTypeName := arg.TypeString
			var argTypeId int

			if argTypeName != nil {
				// Similar deal. Input functions from inheritance resolution already have ids set

				arg.TypeId = pkg.GetProtoTypeId(arg.TypeString)
				//println("Processing %", null.GetString(arg.Name))
				argTypeId = pkg.GetTypeId(null.GetString(arg.TypeString))
				arg.TypeString = nil
			} else {
				argTypeId = int(null.GetInt32(arg.TypeId))
			}

			localScope[null.GetString(arg.Name)] = argTypeId
		}

		//pkg.Log.Info("Some insitruction: %v, %s", fun.Instruction, null.GetString(fun.Name) )
		scopeTypeId := int(null.GetInt32(fun.ScopeTypeId))
		//pkg.Log.Info("\t\t -- opening scope type : %v\n", scopeTypeId)
		returnType := linkingContext.ProcessInstructionWithLocalScope(fun.Instruction, scopeTypeId, localScope, *fun.Name)

		if linkingContext.HasErrors() {
			message := ""
			for _, msg := range linkingContext.Errors {
				message = message + "\n" + msg
			}
			panic(message)
		}

		fun.ReturnTypeId = proto.Int32(int32(returnType))
		if fun.Instruction != nil {
			fun.Instruction.Iterate(func(ins *tp.Instruction) {
				if *ins.Type == tp.Instruction_FUNCTION_CALL {
					if null.GetString(ins.Value) == "yield" {
						fun.OpensTypeId = ins.YieldTypeId
					}
				}
			})
		}

	}
	//pkg.Log.Info("\t\t -- done --\n")
}

func (pkg *Package) inheritFunctions() {
	pkg.Log.Info("pkg types: %v", pkg.Types)
	for _, function := range pkg.Functions {
		pkg.resolveFunctionDescendants(function)
	}
}

// TODO(SJ) : Make this not suck. I think I could make this 50% shorter if I use reflection
// - Also, I'm assuming a single depth level of inheritance. I'd have to run this function n times for n levels
// - Well that should be fine as long as I run it at the end of every package load

func (pkg *Package) resolveFunctionDescendants(fun *tp.Function) {

	// Check if this function contains any types that have descendants
	name := fun.Stub(pkg.Package)
	pkg.Log.Info("Checking for inheritance on function: %v", name)

	newFun := &tp.Function{}
	inherit := false

	// Iterate over ScopeType, Arg types, return Type, opens Type

	// ScopeType

	thisTypeId := null.GetInt32(fun.ScopeTypeId)
	newType := pkg.Package.FindDescendantType(thisTypeId)

	if newType != -1 {
		if !inherit {
			pkg.Log.Info("\t -- ScopeType : Found ancestral type. Cloning function %v\n", null.GetString(fun.Name))
			newFun = fun.Clone()
			// pkg.Log.Info("\t -- New fun: %v", newFun)
			inherit = true
		}
		pkg.Log.Info("\t -- Resetting scopeId")
		newFun.ScopeTypeId = proto.Int32(int32(newType))
	}

	// ReturnType

	thisTypeId = null.GetInt32(fun.ReturnTypeId)
	newType = pkg.Package.FindDescendantType(thisTypeId)

	if newType != -1 {
		if !inherit {
			pkg.Log.Info("\t -- ReturnType : Found ancestral type. Cloning function %v\n", null.GetString(fun.Name))
			newFun = fun.Clone()
			// pkg.Log.Info("\t -- New fun: %v", newFun)
			inherit = true
		}
		pkg.Log.Info("\t -- Resetting returnId")
		newFun.ReturnTypeId = proto.Int32(int32(newType))
	}

	// OpensType

	thisTypeId = null.GetInt32(fun.OpensTypeId)
	newType = pkg.Package.FindDescendantType(thisTypeId)

	if newType != -1 {

		if !inherit {
			pkg.Log.Info("\t -- OpensType : Found ancestral type. Cloning function %v\n", null.GetString(fun.Name))
			newFun = fun.Clone()
			// pkg.Log.Info("\t -- New fun: %v", newFun)
			inherit = true
		}
		pkg.Log.Info("\t -- Resetting openTypeId")
		newFun.OpensTypeId = proto.Int32(int32(newType))
	}

	// Arguments

	for index, arg := range fun.Args {
		thisTypeId = null.GetInt32(arg.TypeId)
		newType = pkg.Package.FindDescendantType(thisTypeId)

		if newType != -1 {

			if !inherit {
				pkg.Log.Info("\t -- ArgType : Found ancestral type. Cloning function %v\n", null.GetString(fun.Name))
				newFun = fun.Clone()
				// pkg.Log.Info("\t -- New fun: %v", newFun)
				inherit = true
			}
			pkg.Log.Info("\t -- Resetting argument")
			newFun.Args[index].TypeId = proto.Int32(int32(newType))
		}

	}

	pkg.Log.Info("\t -- Old function: %v\n\t -- New function: %v\n", fun, newFun)

	if inherit {
		resolveDefinition(pkg.Package, newFun)
		pkg.Package.Functions = append(pkg.Package.Functions, newFun)

	}

}

func ReadPackageDefinitions(pkg *tp.Package, location string) {

	//pkg.Println(" -- reading definitions")
	_, err := ioutil.ReadFile(location)
	println("READING DEFINITIONS:", location)

	if err != nil {
		//pkg.Log.Info("\t -- no user defined functions found")
		return
	}

	definitions := parser.ParseFile(location)

	// Create a map of pre-packaged function signatures
	prepackaged := make(map[string]bool)
	for _, f := range pkg.Functions {
		sig := fmt.Sprintf("%s.%s", f.ScopeTypeString(pkg), f.Stub(pkg))
		prepackaged[sig] = true
		println(sig)
	}
	println("*****************")
	println("*****************")
	println()
	println()

	for _, function := range definitions.Functions {
		//pkg.Log.Info("\t -- function: %v", function)
		resolveDefinition(pkg, function)

		// After resolving a user-defined function, see if its fully resolved signature
		// is the same as the signature of a prepackaged function. If so, throw an error.
		// newSig := fmt.Sprintf("%s.%s", function.ScopeTypeString(pkg), function.Stub(pkg))
		// _, present := prepackaged[newSig]
		// if present {
		// 	panic(fmt.Sprintf("Attempt to redefine prepackaged function: %s", strings.Replace(newSig, ",", "(", 1) + ")"))
		// }

		pkg.Functions = append(pkg.Functions, function)
	}
}

func (pkg *Package) Marshal() []byte {
	bytes, err := proto.Marshal(pkg.Package)
	if err != nil {
		log.Panic(err)
	}
	return bytes
}

func (pkg *Package) findTypeIndex(name string) int {
	for index, typeObj := range pkg.Types {
		if name == null.GetString(typeObj.Name) {
			return index
		}
	}

	log.Panic("Bad type load order, type ", name, " unknown")
	return -1
}

func (pkg *Package) loadPackageDependency(name string) *Error {

	loaded := pkg.loadedDependency(name)

	if loaded {
		return nil
	}

	pkg.Load(name)

	return nil
}

func (pkg *Package) loadedDependency(name string) bool {
	for _, dependency := range pkg.Dependencies {
		if name == dependency {
			pkg.Log.Info("Already loaded dependency:" + name)
			return true
		}
	}
	return false
}

// Not fully functional. Dang it.
func ReadPackageInfoFile(location string) (info *PackageInfo, error *string) {
	packageInfo := &PackageInfo{}
	infoFile, err := ioutil.ReadFile(location + "/package.yml")
	if err != nil {
		message := "No package info file found at " + location + "/package.yml"
		return nil, &message
	}
	yaml.Unmarshal([]byte(infoFile), &packageInfo)
	//fmt.Printf("--- m:\n%v\n\n", packageInfo)
	return packageInfo, nil
}

func (pkg *Package) readHeaderFile(location string) {
	// TODO : plug in new go parser to do this
	input_file := filepath.Join(location, "headers.tf")

	if _, err := os.Open(input_file); err != nil {
		pkg.Log.Info("Warning -- found no headers.tf")
		return
	}

	stubs := parser.ParseFile(input_file)

	for _, function := range stubs.Functions {
		pkg.resolveHeader(function)

		function.BuiltIn = proto.Bool(true)

		pkg.Package.Functions = append(pkg.Package.Functions, function)
	}

}

func (pkg *Package) resolveHeader(function *tp.Function) {

	returnType := null.GetString(function.ReturnType)
	if len(returnType) > 0 {
		function.ReturnTypeId = proto.Int32(int32(pkg.findTypeIndex(returnType)))
		function.ReturnType = nil
	}

	scopeType := null.GetString(function.ScopeType)
	if len(scopeType) > 0 {
		function.ScopeTypeId = proto.Int32(int32(pkg.findTypeIndex(scopeType)))
		function.ScopeType = nil
	}

	opensType := null.GetString(function.OpensType)
	if len(opensType) > 0 {
		function.OpensTypeId = proto.Int32(int32(pkg.findTypeIndex(opensType)))
		function.OpensType = nil
	}

	for _, arg := range function.Args {
		typeName := null.GetString(arg.TypeString)
		if len(typeName) > 0 {
			arg.TypeId = proto.Int32(int32(pkg.findTypeIndex(typeName)))
			arg.TypeString = nil
		}
	}
}

func (pkg *Package) CollectFunctionDocs() {
	for _, function := range pkg.Package.Functions {
		if function.Instruction == nil {
			continue
		}

		for _, instruction := range function.Instruction.Children {
			if *instruction.Type == tp.Instruction_TEXT {
				function.Description = instruction.Value
			}
		}
	}
}
<commit_msg>Need to include the fmt package.<commit_after>package packager

import (
	"fmt"
	"io/ioutil"
	"log"
	"os"
	"path/filepath"
	// "strings"
)

import (
	"butler/null"
	proto "code.google.com/p/goprotobuf/proto"
	yaml "goyaml"
	linker "tritium/linker"
	parser "tritium/parser"
	tp "tritium/proto"
)

func resolveDefinition(pkg *tp.Package, fun *tp.Function) {
	linkingContext := linker.NewLinkingContext(pkg)

	//	pkg.Log.Info("\t -- Resolving --\n")
	//	pkg.Log.Info("\t\t -- function: %v\n", fun)

	// Re-uses linker's logic to resolve function definitions
	if null.GetBool(fun.BuiltIn) == false {
		typeName := null.GetString(fun.ScopeType)

		// DON'T DO THE FOLLOWING HERE -- NEED TO RESOLVE INHERITANCE FIRST
		// // Make sure we're not replacing an existing function bacause it's (currently) a security risk
		// typeID := fun.GetScopeTypeId()
		// siblingFuncs := linkingContext.FunctionsIn(typeID)
		// // println("CHECKING FOR FRATRICIDE IN", typeName)
		// _, present := siblingFuncs[fun.Stub(pkg)]
		// if present {
		// 	msg := fmt.Sprintf("Redefining an existing function is not permitted: %s", fun.Stub(pkg))
		// 	panic(msg)
		// }
		// // for name, sib := range siblingFuncs {
		// // 	println("\t", name, sib)
		// // }
		// /////////////////////////////////////////////////////////////////////////////

		if len(typeName) != 0 {
			// When I pass in functions from the inheritance resolver, they're typeId is already set
			fun.ScopeTypeId = pkg.GetProtoTypeId(fun.ScopeType)
			fun.ScopeType = nil
		}

		localScope := make(linker.LocalDef, len(fun.Args))

		//		fun.ReturnTypeId = pkg.GetProtoTypeId(fun.ReturnType)
		for _, arg := range fun.Args {
			argTypeName := arg.TypeString
			var argTypeId int

			if argTypeName != nil {
				// Similar deal. Input functions from inheritance resolution already have ids set

				arg.TypeId = pkg.GetProtoTypeId(arg.TypeString)
				//println("Processing %", null.GetString(arg.Name))
				argTypeId = pkg.GetTypeId(null.GetString(arg.TypeString))
				arg.TypeString = nil
			} else {
				argTypeId = int(null.GetInt32(arg.TypeId))
			}

			localScope[null.GetString(arg.Name)] = argTypeId
		}

		//pkg.Log.Info("Some insitruction: %v, %s", fun.Instruction, null.GetString(fun.Name) )
		scopeTypeId := int(null.GetInt32(fun.ScopeTypeId))
		//pkg.Log.Info("\t\t -- opening scope type : %v\n", scopeTypeId)
		returnType := linkingContext.ProcessInstructionWithLocalScope(fun.Instruction, scopeTypeId, localScope, *fun.Name)

		if linkingContext.HasErrors() {
			message := ""
			for _, msg := range linkingContext.Errors {
				message = message + "\n" + msg
			}
			panic(message)
		}

		fun.ReturnTypeId = proto.Int32(int32(returnType))
		if fun.Instruction != nil {
			fun.Instruction.Iterate(func(ins *tp.Instruction) {
				if *ins.Type == tp.Instruction_FUNCTION_CALL {
					if null.GetString(ins.Value) == "yield" {
						fun.OpensTypeId = ins.YieldTypeId
					}
				}
			})
		}

	}
	//pkg.Log.Info("\t\t -- done --\n")
}

func (pkg *Package) inheritFunctions() {
	pkg.Log.Info("pkg types: %v", pkg.Types)
	for _, function := range pkg.Functions {
		pkg.resolveFunctionDescendants(function)
	}
}

// TODO(SJ) : Make this not suck. I think I could make this 50% shorter if I use reflection
// - Also, I'm assuming a single depth level of inheritance. I'd have to run this function n times for n levels
// - Well that should be fine as long as I run it at the end of every package load

func (pkg *Package) resolveFunctionDescendants(fun *tp.Function) {

	// Check if this function contains any types that have descendants
	name := fun.Stub(pkg.Package)
	pkg.Log.Info("Checking for inheritance on function: %v", name)

	newFun := &tp.Function{}
	inherit := false

	// Iterate over ScopeType, Arg types, return Type, opens Type

	// ScopeType

	thisTypeId := null.GetInt32(fun.ScopeTypeId)
	newType := pkg.Package.FindDescendantType(thisTypeId)

	if newType != -1 {
		if !inherit {
			pkg.Log.Info("\t -- ScopeType : Found ancestral type. Cloning function %v\n", null.GetString(fun.Name))
			newFun = fun.Clone()
			// pkg.Log.Info("\t -- New fun: %v", newFun)
			inherit = true
		}
		pkg.Log.Info("\t -- Resetting scopeId")
		newFun.ScopeTypeId = proto.Int32(int32(newType))
	}

	// ReturnType

	thisTypeId = null.GetInt32(fun.ReturnTypeId)
	newType = pkg.Package.FindDescendantType(thisTypeId)

	if newType != -1 {
		if !inherit {
			pkg.Log.Info("\t -- ReturnType : Found ancestral type. Cloning function %v\n", null.GetString(fun.Name))
			newFun = fun.Clone()
			// pkg.Log.Info("\t -- New fun: %v", newFun)
			inherit = true
		}
		pkg.Log.Info("\t -- Resetting returnId")
		newFun.ReturnTypeId = proto.Int32(int32(newType))
	}

	// OpensType

	thisTypeId = null.GetInt32(fun.OpensTypeId)
	newType = pkg.Package.FindDescendantType(thisTypeId)

	if newType != -1 {

		if !inherit {
			pkg.Log.Info("\t -- OpensType : Found ancestral type. Cloning function %v\n", null.GetString(fun.Name))
			newFun = fun.Clone()
			// pkg.Log.Info("\t -- New fun: %v", newFun)
			inherit = true
		}
		pkg.Log.Info("\t -- Resetting openTypeId")
		newFun.OpensTypeId = proto.Int32(int32(newType))
	}

	// Arguments

	for index, arg := range fun.Args {
		thisTypeId = null.GetInt32(arg.TypeId)
		newType = pkg.Package.FindDescendantType(thisTypeId)

		if newType != -1 {

			if !inherit {
				pkg.Log.Info("\t -- ArgType : Found ancestral type. Cloning function %v\n", null.GetString(fun.Name))
				newFun = fun.Clone()
				// pkg.Log.Info("\t -- New fun: %v", newFun)
				inherit = true
			}
			pkg.Log.Info("\t -- Resetting argument")
			newFun.Args[index].TypeId = proto.Int32(int32(newType))
		}

	}

	pkg.Log.Info("\t -- Old function: %v\n\t -- New function: %v\n", fun, newFun)

	if inherit {
		resolveDefinition(pkg.Package, newFun)
		pkg.Package.Functions = append(pkg.Package.Functions, newFun)

	}

}

func ReadPackageDefinitions(pkg *tp.Package, location string) {

	//pkg.Println(" -- reading definitions")
	_, err := ioutil.ReadFile(location)
	println("READING DEFINITIONS:", location)

	if err != nil {
		//pkg.Log.Info("\t -- no user defined functions found")
		return
	}

	definitions := parser.ParseFile(location)

	// Create a map of pre-packaged function signatures
	prepackaged := make(map[string]bool)
	for _, f := range pkg.Functions {
		sig := fmt.Sprintf("%s.%s", f.ScopeTypeString(pkg), f.Stub(pkg))
		prepackaged[sig] = true
		println(sig)
	}
	println("*****************")
	println("*****************")
	println()
	println()

	for _, function := range definitions.Functions {
		//pkg.Log.Info("\t -- function: %v", function)
		resolveDefinition(pkg, function)

		// After resolving a user-defined function, see if its fully resolved signature
		// is the same as the signature of a prepackaged function. If so, throw an error.
		// newSig := fmt.Sprintf("%s.%s", function.ScopeTypeString(pkg), function.Stub(pkg))
		// _, present := prepackaged[newSig]
		// if present {
		// 	panic(fmt.Sprintf("Attempt to redefine prepackaged function: %s", strings.Replace(newSig, ",", "(", 1) + ")"))
		// }

		pkg.Functions = append(pkg.Functions, function)
	}
}

func (pkg *Package) Marshal() []byte {
	bytes, err := proto.Marshal(pkg.Package)
	if err != nil {
		log.Panic(err)
	}
	return bytes
}

func (pkg *Package) findTypeIndex(name string) int {
	for index, typeObj := range pkg.Types {
		if name == null.GetString(typeObj.Name) {
			return index
		}
	}

	log.Panic("Bad type load order, type ", name, " unknown")
	return -1
}

func (pkg *Package) loadPackageDependency(name string) *Error {

	loaded := pkg.loadedDependency(name)

	if loaded {
		return nil
	}

	pkg.Load(name)

	return nil
}

func (pkg *Package) loadedDependency(name string) bool {
	for _, dependency := range pkg.Dependencies {
		if name == dependency {
			pkg.Log.Info("Already loaded dependency:" + name)
			return true
		}
	}
	return false
}

// Not fully functional. Dang it.
func ReadPackageInfoFile(location string) (info *PackageInfo, error *string) {
	packageInfo := &PackageInfo{}
	infoFile, err := ioutil.ReadFile(location + "/package.yml")
	if err != nil {
		message := "No package info file found at " + location + "/package.yml"
		return nil, &message
	}
	yaml.Unmarshal([]byte(infoFile), &packageInfo)
	//fmt.Printf("--- m:\n%v\n\n", packageInfo)
	return packageInfo, nil
}

func (pkg *Package) readHeaderFile(location string) {
	// TODO : plug in new go parser to do this
	input_file := filepath.Join(location, "headers.tf")

	if _, err := os.Open(input_file); err != nil {
		pkg.Log.Info("Warning -- found no headers.tf")
		return
	}

	stubs := parser.ParseFile(input_file)

	for _, function := range stubs.Functions {
		pkg.resolveHeader(function)

		function.BuiltIn = proto.Bool(true)

		pkg.Package.Functions = append(pkg.Package.Functions, function)
	}

}

func (pkg *Package) resolveHeader(function *tp.Function) {

	returnType := null.GetString(function.ReturnType)
	if len(returnType) > 0 {
		function.ReturnTypeId = proto.Int32(int32(pkg.findTypeIndex(returnType)))
		function.ReturnType = nil
	}

	scopeType := null.GetString(function.ScopeType)
	if len(scopeType) > 0 {
		function.ScopeTypeId = proto.Int32(int32(pkg.findTypeIndex(scopeType)))
		function.ScopeType = nil
	}

	opensType := null.GetString(function.OpensType)
	if len(opensType) > 0 {
		function.OpensTypeId = proto.Int32(int32(pkg.findTypeIndex(opensType)))
		function.OpensType = nil
	}

	for _, arg := range function.Args {
		typeName := null.GetString(arg.TypeString)
		if len(typeName) > 0 {
			arg.TypeId = proto.Int32(int32(pkg.findTypeIndex(typeName)))
			arg.TypeString = nil
		}
	}
}

func (pkg *Package) CollectFunctionDocs() {
	for _, function := range pkg.Package.Functions {
		if function.Instruction == nil {
			continue
		}

		for _, instruction := range function.Instruction.Children {
			if *instruction.Type == tp.Instruction_TEXT {
				function.Description = instruction.Value
			}
		}
	}
}
<|endoftext|>


<commit_before>// Copyright 2018 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 x86

import (
	"cmd/internal/obj"
	"testing"
)

func init() {
	// Required for tests that access any of
	// opindex/ycover/reg/regrex global tables.
	var ctxt obj.Link
	instinit(&ctxt)
}

func TestRegisterListEncDec(t *testing.T) {
	tests := []struct {
		printed string
		reg0    int16
		reg1    int16
	}{
		{"[R10-R13]", REG_R10, REG_R13},
		{"[X0-AX]", REG_X0, REG_AX},

		{"[X0-X3]", REG_X0, REG_X3},
		{"[X21-X24]", REG_X21, REG_X24},

		{"[Y0-Y3]", REG_Y0, REG_Y3},
		{"[Y21-Y24]", REG_Y21, REG_Y24},

		{"[Z0-Z3]", REG_Z0, REG_Z3},
		{"[Z21-Z24]", REG_Z21, REG_Z24},
	}

	for _, test := range tests {
		enc := EncodeRegisterRange(test.reg0, test.reg1)
		reg0, reg1 := decodeRegisterRange(enc)

		if int16(reg0) != test.reg0 {
			t.Errorf("%s reg0 mismatch: have %d, want %d",
				test.printed, reg0, test.reg0)
		}
		if int16(reg1) != test.reg1 {
			t.Errorf("%s reg1 mismatch: have %d, want %d",
				test.printed, reg1, test.reg1)
		}
		wantPrinted := test.printed
		if rlconv(enc) != wantPrinted {
			t.Errorf("%s string mismatch: have %s, want %s",
				test.printed, rlconv(enc), wantPrinted)
		}
	}
}

func TestRegIndex(t *testing.T) {
	tests := []struct {
		regFrom int
		regTo   int
	}{
		{REG_AL, REG_R15B},
		{REG_AX, REG_R15},
		{REG_M0, REG_M7},
		{REG_K0, REG_K7},
		{REG_X0, REG_X31},
		{REG_Y0, REG_Y31},
		{REG_Z0, REG_Z31},
	}

	for _, test := range tests {
		for index, reg := 0, test.regFrom; reg <= test.regTo; index, reg = index+1, reg+1 {
			have := regIndex(int16(reg))
			want := index
			if have != want {
				regName := rconv(int(reg))
				t.Errorf("regIndex(%s):\nhave: %d\nwant: %d",
					regName, have, want)
			}
		}
	}
}
<commit_msg>cmd/internal/obj/x86: add oclass function tests<commit_after>// Copyright 2018 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 x86

import (
	"cmd/internal/obj"
	"testing"
)

type oclassTest struct {
	arg  *obj.Addr
	want int // Expected oclass return value for a given arg
}

// Filled inside init, because it's easier to do with helper functions.
var (
	oclassTestsAMD64 []*oclassTest
	oclassTests386   []*oclassTest
)

func init() {
	// Required for tests that access any of
	// opindex/ycover/reg/regrex global tables.
	var ctxt obj.Link
	instinit(&ctxt)

	regAddr := func(reg int16) *obj.Addr {
		return &obj.Addr{Type: obj.TYPE_REG, Reg: reg}
	}
	immAddr := func(v int64) *obj.Addr {
		return &obj.Addr{Type: obj.TYPE_CONST, Offset: v}
	}
	regListAddr := func(regFrom, regTo int16) *obj.Addr {
		return &obj.Addr{Type: obj.TYPE_REGLIST, Offset: EncodeRegisterRange(regFrom, regTo)}
	}
	memAddr := func(base, index int16) *obj.Addr {
		return &obj.Addr{Type: obj.TYPE_MEM, Reg: base, Index: index}
	}

	// TODO(quasilyte): oclass doesn't return Yxxx for X/Y regs with
	// ID higher than 7. We don't encode such instructions, but this
	// behavior seems inconsistent. It should probably either
	// never check for arch or do it in all cases.

	oclassTestsCommon := []*oclassTest{
		{&obj.Addr{Type: obj.TYPE_NONE}, Ynone},
		{&obj.Addr{Type: obj.TYPE_BRANCH}, Ybr},
		{&obj.Addr{Type: obj.TYPE_TEXTSIZE}, Ytextsize},

		{&obj.Addr{Type: obj.TYPE_INDIR, Name: obj.NAME_EXTERN}, Yindir},
		{&obj.Addr{Type: obj.TYPE_INDIR, Name: obj.NAME_GOTREF}, Yindir},

		{&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_AUTO}, Yiauto},
		{&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_PARAM}, Yiauto},
		{&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN}, Yiauto},
		{&obj.Addr{Type: obj.TYPE_ADDR, Sym: &obj.LSym{Name: "runtime.duff"}}, Yi32},
		{&obj.Addr{Type: obj.TYPE_ADDR, Offset: 4}, Yu7},
		{&obj.Addr{Type: obj.TYPE_ADDR, Offset: 255}, Yu8},

		{immAddr(0), Yi0},
		{immAddr(1), Yi1},
		{immAddr(2), Yu2},
		{immAddr(3), Yu2},
		{immAddr(4), Yu7},
		{immAddr(86), Yu7},
		{immAddr(127), Yu7},
		{immAddr(128), Yu8},
		{immAddr(200), Yu8},
		{immAddr(255), Yu8},
		{immAddr(-1), Yi8},
		{immAddr(-100), Yi8},
		{immAddr(-128), Yi8},

		{regAddr(REG_AL), Yal},
		{regAddr(REG_AX), Yax},
		{regAddr(REG_DL), Yrb},
		{regAddr(REG_DH), Yrb},
		{regAddr(REG_BH), Yrb},
		{regAddr(REG_CL), Ycl},
		{regAddr(REG_CX), Ycx},
		{regAddr(REG_DX), Yrx},
		{regAddr(REG_BX), Yrx},
		{regAddr(REG_F0), Yf0},
		{regAddr(REG_F3), Yrf},
		{regAddr(REG_F7), Yrf},
		{regAddr(REG_M0), Ymr},
		{regAddr(REG_M3), Ymr},
		{regAddr(REG_M7), Ymr},
		{regAddr(REG_X0), Yxr0},
		{regAddr(REG_X6), Yxr},
		{regAddr(REG_X13), Yxr},
		{regAddr(REG_X20), YxrEvex},
		{regAddr(REG_X31), YxrEvex},
		{regAddr(REG_Y0), Yyr},
		{regAddr(REG_Y6), Yyr},
		{regAddr(REG_Y13), Yyr},
		{regAddr(REG_Y20), YyrEvex},
		{regAddr(REG_Y31), YyrEvex},
		{regAddr(REG_Z0), Yzr},
		{regAddr(REG_Z6), Yzr},
		{regAddr(REG_K0), Yk0},
		{regAddr(REG_K5), Yknot0},
		{regAddr(REG_K7), Yknot0},
		{regAddr(REG_CS), Ycs},
		{regAddr(REG_SS), Yss},
		{regAddr(REG_DS), Yds},
		{regAddr(REG_ES), Yes},
		{regAddr(REG_FS), Yfs},
		{regAddr(REG_GS), Ygs},
		{regAddr(REG_TLS), Ytls},
		{regAddr(REG_GDTR), Ygdtr},
		{regAddr(REG_IDTR), Yidtr},
		{regAddr(REG_LDTR), Yldtr},
		{regAddr(REG_MSW), Ymsw},
		{regAddr(REG_TASK), Ytask},
		{regAddr(REG_CR0), Ycr0},
		{regAddr(REG_CR5), Ycr5},
		{regAddr(REG_CR8), Ycr8},
		{regAddr(REG_DR0), Ydr0},
		{regAddr(REG_DR5), Ydr5},
		{regAddr(REG_DR7), Ydr7},
		{regAddr(REG_TR0), Ytr0},
		{regAddr(REG_TR5), Ytr5},
		{regAddr(REG_TR7), Ytr7},

		{regListAddr(REG_X0, REG_X3), YxrEvexMulti4},
		{regListAddr(REG_X4, REG_X7), YxrEvexMulti4},
		{regListAddr(REG_Y0, REG_Y3), YyrEvexMulti4},
		{regListAddr(REG_Y4, REG_Y7), YyrEvexMulti4},
		{regListAddr(REG_Z0, REG_Z3), YzrMulti4},
		{regListAddr(REG_Z4, REG_Z7), YzrMulti4},

		{memAddr(REG_AL, REG_NONE), Ym},
		{memAddr(REG_AL, REG_SI), Ym},
		{memAddr(REG_SI, REG_CX), Ym},
		{memAddr(REG_DI, REG_X0), Yxvm},
		{memAddr(REG_DI, REG_X7), Yxvm},
		{memAddr(REG_DI, REG_Y0), Yyvm},
		{memAddr(REG_DI, REG_Y7), Yyvm},
		{memAddr(REG_DI, REG_Z0), Yzvm},
		{memAddr(REG_DI, REG_Z7), Yzvm},
	}

	oclassTestsAMD64 = []*oclassTest{
		{immAddr(-200), Ys32},
		{immAddr(500), Ys32},
		{immAddr(0x7FFFFFFF), Ys32},
		{immAddr(0x7FFFFFFF + 1), Yi32},
		{immAddr(0xFFFFFFFF), Yi32},
		{immAddr(0xFFFFFFFF + 1), Yi64},

		{regAddr(REG_BPB), Yrb},
		{regAddr(REG_SIB), Yrb},
		{regAddr(REG_DIB), Yrb},
		{regAddr(REG_R8B), Yrb},
		{regAddr(REG_R12B), Yrb},
		{regAddr(REG_R8), Yrl},
		{regAddr(REG_R13), Yrl},
		{regAddr(REG_R15), Yrl},
		{regAddr(REG_SP), Yrl},
		{regAddr(REG_SI), Yrl},
		{regAddr(REG_DI), Yrl},
		{regAddr(REG_Z13), Yzr},
		{regAddr(REG_Z20), Yzr},
		{regAddr(REG_Z31), Yzr},

		{regListAddr(REG_X10, REG_X13), YxrEvexMulti4},
		{regListAddr(REG_X24, REG_X27), YxrEvexMulti4},
		{regListAddr(REG_Y10, REG_Y13), YyrEvexMulti4},
		{regListAddr(REG_Y24, REG_Y27), YyrEvexMulti4},
		{regListAddr(REG_Z10, REG_Z13), YzrMulti4},
		{regListAddr(REG_Z24, REG_Z27), YzrMulti4},

		{memAddr(REG_DI, REG_X20), YxvmEvex},
		{memAddr(REG_DI, REG_X27), YxvmEvex},
		{memAddr(REG_DI, REG_Y20), YyvmEvex},
		{memAddr(REG_DI, REG_Y27), YyvmEvex},
		{memAddr(REG_DI, REG_Z20), Yzvm},
		{memAddr(REG_DI, REG_Z27), Yzvm},
	}

	oclassTests386 = []*oclassTest{
		{&obj.Addr{Type: obj.TYPE_ADDR, Name: obj.NAME_EXTERN, Sym: &obj.LSym{}}, Yi32},

		{immAddr(-200), Yi32},

		{regAddr(REG_SP), Yrl32},
		{regAddr(REG_SI), Yrl32},
		{regAddr(REG_DI), Yrl32},
	}

	// Add tests that are arch-independent for all sets.
	oclassTestsAMD64 = append(oclassTestsAMD64, oclassTestsCommon...)
	oclassTests386 = append(oclassTests386, oclassTestsCommon...)
}

func TestOclass(t *testing.T) {
	runTest := func(t *testing.T, ctxt *obj.Link, tests []*oclassTest) {
		var p obj.Prog
		for _, test := range tests {
			have := oclass(ctxt, &p, test.arg)
			if have != test.want {
				t.Errorf("oclass(%q):\nhave: %d\nwant: %d",
					obj.Dconv(&p, test.arg), have, test.want)
			}
		}
	}

	t.Run("AMD64", func(t *testing.T) {
		ctxtAMD64 := obj.Linknew(&Linkamd64)
		runTest(t, ctxtAMD64, oclassTestsAMD64)
	})

	t.Run("386", func(t *testing.T) {
		ctxt386 := obj.Linknew(&Link386)
		runTest(t, ctxt386, oclassTests386)
	})
}

func TestRegisterListEncDec(t *testing.T) {
	tests := []struct {
		printed string
		reg0    int16
		reg1    int16
	}{
		{"[R10-R13]", REG_R10, REG_R13},
		{"[X0-AX]", REG_X0, REG_AX},

		{"[X0-X3]", REG_X0, REG_X3},
		{"[X21-X24]", REG_X21, REG_X24},

		{"[Y0-Y3]", REG_Y0, REG_Y3},
		{"[Y21-Y24]", REG_Y21, REG_Y24},

		{"[Z0-Z3]", REG_Z0, REG_Z3},
		{"[Z21-Z24]", REG_Z21, REG_Z24},
	}

	for _, test := range tests {
		enc := EncodeRegisterRange(test.reg0, test.reg1)
		reg0, reg1 := decodeRegisterRange(enc)

		if int16(reg0) != test.reg0 {
			t.Errorf("%s reg0 mismatch: have %d, want %d",
				test.printed, reg0, test.reg0)
		}
		if int16(reg1) != test.reg1 {
			t.Errorf("%s reg1 mismatch: have %d, want %d",
				test.printed, reg1, test.reg1)
		}
		wantPrinted := test.printed
		if rlconv(enc) != wantPrinted {
			t.Errorf("%s string mismatch: have %s, want %s",
				test.printed, rlconv(enc), wantPrinted)
		}
	}
}

func TestRegIndex(t *testing.T) {
	tests := []struct {
		regFrom int
		regTo   int
	}{
		{REG_AL, REG_R15B},
		{REG_AX, REG_R15},
		{REG_M0, REG_M7},
		{REG_K0, REG_K7},
		{REG_X0, REG_X31},
		{REG_Y0, REG_Y31},
		{REG_Z0, REG_Z31},
	}

	for _, test := range tests {
		for index, reg := 0, test.regFrom; reg <= test.regTo; index, reg = index+1, reg+1 {
			have := regIndex(int16(reg))
			want := index
			if have != want {
				regName := rconv(int(reg))
				t.Errorf("regIndex(%s):\nhave: %d\nwant: %d",
					regName, have, want)
			}
		}
	}
}
<|endoftext|>


<commit_before>package blockdiag

import (
	"fmt"
	"sort"
	"strings"

	"github.com/ahmetalpbalkan/go-linq"
)

type Diag struct {
	Name       string
	Nodes      map[string]*Node
	Edges      map[string]*Edge
	Attributes map[string]string
	Circular   []*nodes
	Grid       grid
}

func NewDiag() Diag {
	diag := Diag{}

	diag.Nodes = make(map[string]*Node)
	diag.Edges = make(map[string]*Edge)
	diag.Attributes = make(map[string]string)
	diag.Grid = NewGrid()

	return diag
}

const (
	arrowRight     = '>'
	horizontal     = '\u2500' // ─ http://unicode-table.com/en/2500/
	vertical       = '\u2502' // │ http://unicode-table.com/en/2502/
	horizontalDown = '\u252C' // ┬ http://unicode-table.com/en/252C/
	upRight        = '\u2514' // └ http://unicode-table.com/en/2514/
)

func (diag *Diag) String() string {
	var outGrid [][]rune

	const (
		rowFactor = 2
		colFactor = 7
	)

	// Prepare Output Grid
	outGrid = make([][]rune, len(diag.Grid)*rowFactor)
	for y := 0; y < len(outGrid); y++ {
		outGrid[y] = make([]rune, len(diag.Grid[0])*colFactor)
		for x, _ := range outGrid[y] {
			outGrid[y][x] = ' '
		}
	}

	// Place Nodes
	for y, _ := range diag.Grid {
		for x, n := range diag.Grid[y] {
			if n != nil {
				// fmt.Println(y, x)
				outGrid[y*rowFactor+1][x*colFactor] = '['
				outGrid[y*rowFactor+1][x*colFactor+1] = rune(n.Name[0])
				outGrid[y*rowFactor+1][x*colFactor+2] = ']'
				// ret += "[" + string(n.Name[0]) + "]"
				// if x < len(diag.Grid[y])-1 && diag.Grid[y][x+1] != nil {
				// 	if len(n.getChildNodes()) > 1 {
				// 		ret += "|->"
				// 	} else {
				// 		ret += "-->"
				// 	}
				// }
				// } else {
				// 	ret += "      "
			}
		}
		//ret += "\n"
	}

	// Place Edges
	// Unicode Arrows
	// https://en.wikipedia.org/wiki/Arrow_(symbol)#Arrows_in_Unicode
	// https://en.wikipedia.org/wiki/Supplemental_Arrows-B
	// https://en.wikipedia.org/wiki/Tee_(symbol)
	// https://en.wikipedia.org/wiki/Up_tack
	// http://unicode-table.com/de/007C/ Senkrechter Strich
	// http://unicode-table.com/de/23AF/ Waagrechter Strich
	// http://unicode-table.com/de/blocks/miscellaneous-technical/
	// http://www.asciitable.com/
	// https://de.wikipedia.org/wiki/Unicodeblock_Rahmenzeichnung
	for _, e := range diag.Edges {
		fmt.Println(e.Start.Name, e.Start.PosX, e.Start.PosY, "|", e.End.Name, e.End.PosX, e.End.PosY)
		if e.Start.PosY == e.End.PosY && e.Start.PosX+1 == e.End.PosX {
			outGrid[e.Start.PosY*rowFactor+1][e.Start.PosX*colFactor+3] = horizontal
			outGrid[e.Start.PosY*rowFactor+1][e.Start.PosX*colFactor+4] = horizontal
			outGrid[e.Start.PosY*rowFactor+1][e.Start.PosX*colFactor+5] = horizontal
			outGrid[e.Start.PosY*rowFactor+1][e.Start.PosX*colFactor+6] = arrowRight
		}
		if e.Start.PosY < e.End.PosY && e.Start.PosX+1 == e.End.PosX {
			// if outGrid[(e.Start.PosY)*rowFactor+1][e.Start.PosX*colFactor+4] == horizontal {
			outGrid[(e.Start.PosY)*rowFactor+1][e.Start.PosX*colFactor+4] = horizontalDown
			// }
			for i := 1; i < (e.End.PosY-e.Start.PosY)*rowFactor+1; i++ {
				outGrid[e.Start.PosY+i+1][e.Start.PosX*colFactor+4] = vertical
			}
			outGrid[e.End.PosY*rowFactor+1][e.Start.PosX*colFactor+4] = upRight
			outGrid[e.End.PosY*rowFactor+1][e.Start.PosX*colFactor+5] = horizontal
			outGrid[e.End.PosY*rowFactor+1][e.Start.PosX*colFactor+6] = arrowRight
		}
	}

	// Prepare Output String
	ret := ""
	for _, rs := range outGrid {
		ret += string(rs) + "\n"
	}

	return ret
}

func (diag *Diag) GoString() string {
	var edges []string
	var ret string

	for _, edge := range diag.Edges {
		edges = append(edges, edge.Name)
	}
	sort.Strings(edges)

	ret += fmt.Sprintln("Name:", diag.Name)
	ret += fmt.Sprintln("Nodes:", diag.NodesString())
	ret += fmt.Sprintln("Edges:", diag.EdgesString())
	ret += fmt.Sprintln("Circulars:", diag.CircularString())
	ret += fmt.Sprintln("Attributes:", diag.AttributesString())
	return ret
}

func (diag *Diag) NodesString() string {
	var nodes []string

	for _, node := range diag.Nodes {
		// nodes = append(nodes, fmt.Sprintf("%s (%d, %d)", node.Name, node.PosX, node.PosY))
		nodes = append(nodes, node.Name)
	}
	sort.Strings(nodes)

	return strings.Join(nodes, ", ")
}

func (diag *Diag) EdgesString() string {
	var edges []string

	for _, edge := range diag.Edges {
		edges = append(edges, edge.Name)
	}
	sort.Strings(edges)

	return strings.Join(edges, ", ")
}

func (diag *Diag) CircularString() string {
	var circulars []string

	for _, circular := range diag.Circular {
		var circularPath []string
		for _, node := range circular.keys {
			circularPath = append(circularPath, node)
		}
		circulars = append(circulars, strings.Join(circularPath, " -> "))
	}
	sort.Strings(circulars)

	return strings.Join(circulars, "\n")
}

func (diag *Diag) AttributesString() string {
	var attributes []string

	for key, value := range diag.Attributes {
		attributes = append(attributes, key+"="+value)
	}
	sort.Strings(attributes)

	return strings.Join(attributes, "\n")
}

func (diag *Diag) GridString() string {
	return diag.Grid.String()
}

func (diag *Diag) FindCircular() bool {
	for _, n := range diag.Nodes {
		visitedNodes := &nodes{}

		if !visitedNodes.exists(n.Name) {
			visitedNodes.keys = append(visitedNodes.keys, n.Name)
		}
		for _, c := range n.getChildNodes() {
			diag.subFindCircular(c, visitedNodes)
		}
	}

	if len(diag.Circular) > 0 {
		return true
	}
	return false
}

func (diag *Diag) subFindCircular(n *Node, visitedNodes *nodes) {
	if visitedNodes.exists(n.Name) {
		circularNodes := &nodes{}
		for _, p := range visitedNodes.keys {
			circularNodes.keys = append(circularNodes.keys, p)
		}
		circularNodes.keys = append(circularNodes.keys, n.Name)

		diag.Circular = append(diag.Circular, circularNodes)
		return
	}
	visitedNodes.keys = append(visitedNodes.keys, n.Name)

	for _, c := range n.getChildNodes() {
		diag.subFindCircular(c, visitedNodes)
	}
	visitedNodes.keys = visitedNodes.keys[:len(visitedNodes.keys)-1]
}

func (diag *Diag) getNodes() Nodes {
	var nodes Nodes
	for _, n := range diag.Nodes {
		nodes = append(nodes, n)
	}
	sort.Sort(nodes)
	return nodes
}

func (diag *Diag) getStartNodes() Nodes {
	var startNodes Nodes

	for _, n := range diag.Nodes {
		startNode := true
		for _, e := range n.Edges {
			if e.End == n {
				startNode = false
				break
			}
		}
		if startNode {
			startNodes = append(startNodes, n)
		}
	}

	sort.Sort(startNodes)

	return startNodes
}

type Node struct {
	Name  string
	PosX  int
	PosY  int
	Edges []*Edge
}

func (n *Node) getChildNodes() (children Nodes) {
	for _, e := range n.Edges {
		if e.Start == n && e.End != n {
			children = append(children, e.End)
		}
	}
	sort.Sort(children)
	return
}

type Nodes []*Node

func (nodes Nodes) Len() int {
	return len(nodes)
}

func (nodes Nodes) Less(i, j int) bool {
	return nodes[i].Name < nodes[j].Name
}

func (nodes Nodes) Swap(i, j int) {
	nodes[i], nodes[j] = nodes[j], nodes[i]
}

func (nodes Nodes) String() string {
	var s, delim string
	sort.Sort(nodes)
	for _, n := range nodes {
		s += delim + n.Name
		delim = ", "
	}
	return s
}

type Edge struct {
	Start *Node
	End   *Node
	Name  string
}

type nodes struct {
	keys []string
}

func (n *nodes) exists(key string) bool {
	ret, _ := linq.From(n.keys).AnyWith(func(s linq.T) (bool, error) {
		return s.(string) == key, nil
	})
	return ret
}

func (diag *Diag) PlaceInGrid() {
	var x, y int

	placedNodes := make(map[*Node]bool)

	for _, n := range diag.getStartNodes() {
		_, ok := placedNodes[n]
		if ok {
			continue
		}
		placedNodes[n] = true
		err := diag.Grid.Set(x, y, n, diag)
		if err != nil {
			panic("Set failed")
		}
		y += diag.placeInGrid(n, x+1, y, placedNodes)
		y++
	}

	if len(placedNodes) != len(diag.Nodes) {
		for _, n := range diag.getNodes() {
			_, ok := placedNodes[n]
			if ok {
				continue
			}
			placedNodes[n] = true
			err := diag.Grid.Set(x, y, n, diag)
			if err != nil {
				panic("Set failed")
			}
			y += diag.placeInGrid(n, x+1, y, placedNodes)
			y++
		}
	}
}

func (diag *Diag) placeInGrid(n *Node, x int, y int, placedNodes map[*Node]bool) int {
	addedNodes := 0
	for _, n := range n.getChildNodes() {
		_, ok := placedNodes[n]
		if ok {
			continue
		}
		placedNodes[n] = true
		err := diag.Grid.Set(x, y+addedNodes, n, diag)
		if err != nil {
			panic("Set failed")
		}

		addedNodes += diag.placeInGrid(n, x+1, y+addedNodes, placedNodes)
		addedNodes++
	}

	if addedNodes > 0 {
		return addedNodes - 1
	}
	return addedNodes
}
<commit_msg>Sort edges<commit_after>package blockdiag

import (
	"fmt"
	"sort"
	"strings"

	"github.com/ahmetalpbalkan/go-linq"
)

type Diag struct {
	Name       string
	Nodes      map[string]*Node
	Edges      map[string]*Edge
	Attributes map[string]string
	Circular   []*nodes
	Grid       grid
}

func NewDiag() Diag {
	diag := Diag{}

	diag.Nodes = make(map[string]*Node)
	diag.Edges = make(map[string]*Edge)
	diag.Attributes = make(map[string]string)
	diag.Grid = NewGrid()

	return diag
}

const (
	arrowRight     = '>'
	horizontal     = '\u2500' // ─ http://unicode-table.com/en/2500/
	vertical       = '\u2502' // │ http://unicode-table.com/en/2502/
	horizontalDown = '\u252C' // ┬ http://unicode-table.com/en/252C/
	upRight        = '\u2514' // └ http://unicode-table.com/en/2514/
)

func (diag *Diag) String() string {
	var outGrid [][]rune

	const (
		rowFactor = 2
		colFactor = 7
	)

	// Prepare Output Grid
	outGrid = make([][]rune, len(diag.Grid)*rowFactor)
	for y := 0; y < len(outGrid); y++ {
		outGrid[y] = make([]rune, len(diag.Grid[0])*colFactor)
		for x, _ := range outGrid[y] {
			outGrid[y][x] = ' '
		}
	}

	// Place Nodes
	for y, _ := range diag.Grid {
		for x, n := range diag.Grid[y] {
			if n != nil {
				// fmt.Println(y, x)
				outGrid[y*rowFactor+1][x*colFactor] = '['
				outGrid[y*rowFactor+1][x*colFactor+1] = rune(n.Name[0])
				outGrid[y*rowFactor+1][x*colFactor+2] = ']'
				// ret += "[" + string(n.Name[0]) + "]"
				// if x < len(diag.Grid[y])-1 && diag.Grid[y][x+1] != nil {
				// 	if len(n.getChildNodes()) > 1 {
				// 		ret += "|->"
				// 	} else {
				// 		ret += "-->"
				// 	}
				// }
				// } else {
				// 	ret += "      "
			}
		}
		//ret += "\n"
	}

	// Place Edges
	// Unicode Arrows
	// https://en.wikipedia.org/wiki/Arrow_(symbol)#Arrows_in_Unicode
	// https://en.wikipedia.org/wiki/Supplemental_Arrows-B
	// https://en.wikipedia.org/wiki/Tee_(symbol)
	// https://en.wikipedia.org/wiki/Up_tack
	// http://unicode-table.com/de/007C/ Senkrechter Strich
	// http://unicode-table.com/de/23AF/ Waagrechter Strich
	// http://unicode-table.com/de/blocks/miscellaneous-technical/
	// http://www.asciitable.com/
	// https://de.wikipedia.org/wiki/Unicodeblock_Rahmenzeichnung
	for _, e := range diag.getEdges() {
		fmt.Println(e.Start.Name, e.Start.PosX, e.Start.PosY, "|", e.End.Name, e.End.PosX, e.End.PosY)
		if e.Start.PosY == e.End.PosY && e.Start.PosX+1 == e.End.PosX {
			outGrid[e.Start.PosY*rowFactor+1][e.Start.PosX*colFactor+3] = horizontal
			outGrid[e.Start.PosY*rowFactor+1][e.Start.PosX*colFactor+4] = horizontal
			outGrid[e.Start.PosY*rowFactor+1][e.Start.PosX*colFactor+5] = horizontal
			outGrid[e.Start.PosY*rowFactor+1][e.Start.PosX*colFactor+6] = arrowRight
		}
		if e.Start.PosY < e.End.PosY && e.Start.PosX+1 == e.End.PosX {
			// if outGrid[(e.Start.PosY)*rowFactor+1][e.Start.PosX*colFactor+4] == horizontal {
			outGrid[(e.Start.PosY)*rowFactor+1][e.Start.PosX*colFactor+4] = horizontalDown
			// }
			for i := 1; i < (e.End.PosY-e.Start.PosY)*rowFactor+1; i++ {
				outGrid[e.Start.PosY+i+1][e.Start.PosX*colFactor+4] = vertical
			}
			outGrid[e.End.PosY*rowFactor+1][e.Start.PosX*colFactor+4] = upRight
			outGrid[e.End.PosY*rowFactor+1][e.Start.PosX*colFactor+5] = horizontal
			outGrid[e.End.PosY*rowFactor+1][e.Start.PosX*colFactor+6] = arrowRight
		}
	}

	// Prepare Output String
	ret := ""
	for _, rs := range outGrid {
		ret += string(rs) + "\n"
	}

	return ret
}

func (diag *Diag) GoString() string {
	var edges []string
	var ret string

	for _, edge := range diag.Edges {
		edges = append(edges, edge.Name)
	}
	sort.Strings(edges)

	ret += fmt.Sprintln("Name:", diag.Name)
	ret += fmt.Sprintln("Nodes:", diag.NodesString())
	ret += fmt.Sprintln("Edges:", diag.EdgesString())
	ret += fmt.Sprintln("Circulars:", diag.CircularString())
	ret += fmt.Sprintln("Attributes:", diag.AttributesString())
	return ret
}

func (diag *Diag) NodesString() string {
	var nodes []string

	for _, node := range diag.Nodes {
		// nodes = append(nodes, fmt.Sprintf("%s (%d, %d)", node.Name, node.PosX, node.PosY))
		nodes = append(nodes, node.Name)
	}
	sort.Strings(nodes)

	return strings.Join(nodes, ", ")
}

func (diag *Diag) EdgesString() string {
	var edges []string

	for _, edge := range diag.Edges {
		edges = append(edges, edge.Name)
	}
	sort.Strings(edges)

	return strings.Join(edges, ", ")
}

func (diag *Diag) CircularString() string {
	var circulars []string

	for _, circular := range diag.Circular {
		var circularPath []string
		for _, node := range circular.keys {
			circularPath = append(circularPath, node)
		}
		circulars = append(circulars, strings.Join(circularPath, " -> "))
	}
	sort.Strings(circulars)

	return strings.Join(circulars, "\n")
}

func (diag *Diag) AttributesString() string {
	var attributes []string

	for key, value := range diag.Attributes {
		attributes = append(attributes, key+"="+value)
	}
	sort.Strings(attributes)

	return strings.Join(attributes, "\n")
}

func (diag *Diag) GridString() string {
	return diag.Grid.String()
}

func (diag *Diag) FindCircular() bool {
	for _, n := range diag.Nodes {
		visitedNodes := &nodes{}

		if !visitedNodes.exists(n.Name) {
			visitedNodes.keys = append(visitedNodes.keys, n.Name)
		}
		for _, c := range n.getChildNodes() {
			diag.subFindCircular(c, visitedNodes)
		}
	}

	if len(diag.Circular) > 0 {
		return true
	}
	return false
}

func (diag *Diag) subFindCircular(n *Node, visitedNodes *nodes) {
	if visitedNodes.exists(n.Name) {
		circularNodes := &nodes{}
		for _, p := range visitedNodes.keys {
			circularNodes.keys = append(circularNodes.keys, p)
		}
		circularNodes.keys = append(circularNodes.keys, n.Name)

		diag.Circular = append(diag.Circular, circularNodes)
		return
	}
	visitedNodes.keys = append(visitedNodes.keys, n.Name)

	for _, c := range n.getChildNodes() {
		diag.subFindCircular(c, visitedNodes)
	}
	visitedNodes.keys = visitedNodes.keys[:len(visitedNodes.keys)-1]
}

func (diag *Diag) getNodes() Nodes {
	var nodes Nodes
	for _, n := range diag.Nodes {
		nodes = append(nodes, n)
	}
	sort.Sort(nodes)
	return nodes
}

func (diag *Diag) getStartNodes() Nodes {
	var startNodes Nodes

	for _, n := range diag.Nodes {
		startNode := true
		for _, e := range n.Edges {
			if e.End == n {
				startNode = false
				break
			}
		}
		if startNode {
			startNodes = append(startNodes, n)
		}
	}

	sort.Sort(startNodes)

	return startNodes
}

func (diag *Diag) getEdges() Edges {
	var edges Edges
	for _, e := range diag.Edges {
		edges = append(edges, e)
	}
	sort.Sort(edges)
	return edges
}

type Node struct {
	Name  string
	PosX  int
	PosY  int
	Edges []*Edge
}

func (n *Node) getChildNodes() (children Nodes) {
	for _, e := range n.Edges {
		if e.Start == n && e.End != n {
			children = append(children, e.End)
		}
	}
	sort.Sort(children)
	return
}

type Nodes []*Node

func (nodes Nodes) Len() int {
	return len(nodes)
}

func (nodes Nodes) Less(i, j int) bool {
	return nodes[i].Name < nodes[j].Name
}

func (nodes Nodes) Swap(i, j int) {
	nodes[i], nodes[j] = nodes[j], nodes[i]
}

func (nodes Nodes) String() string {
	var s, delim string
	sort.Sort(nodes)
	for _, n := range nodes {
		s += delim + n.Name
		delim = ", "
	}
	return s
}

type Edge struct {
	Start *Node
	End   *Node
	Name  string
}

type Edges []*Edge

func (edges Edges) Len() int {
	return len(edges)
}

func (edges Edges) Less(i, j int) bool {
	return edges[i].Name < edges[j].Name
}

func (edges Edges) Swap(i, j int) {
	edges[i], edges[j] = edges[j], edges[i]
}

func (edges Edges) String() string {
	var s, delim string
	sort.Sort(edges)
	for _, e := range edges {
		s += delim + e.Name
		delim = ", "
	}
	return s
}

type nodes struct {
	keys []string
}

func (n *nodes) exists(key string) bool {
	ret, _ := linq.From(n.keys).AnyWith(func(s linq.T) (bool, error) {
		return s.(string) == key, nil
	})
	return ret
}

func (diag *Diag) PlaceInGrid() {
	var x, y int

	placedNodes := make(map[*Node]bool)

	for _, n := range diag.getStartNodes() {
		_, ok := placedNodes[n]
		if ok {
			continue
		}
		placedNodes[n] = true
		err := diag.Grid.Set(x, y, n, diag)
		if err != nil {
			panic("Set failed")
		}
		y += diag.placeInGrid(n, x+1, y, placedNodes)
		y++
	}

	if len(placedNodes) != len(diag.Nodes) {
		for _, n := range diag.getNodes() {
			_, ok := placedNodes[n]
			if ok {
				continue
			}
			placedNodes[n] = true
			err := diag.Grid.Set(x, y, n, diag)
			if err != nil {
				panic("Set failed")
			}
			y += diag.placeInGrid(n, x+1, y, placedNodes)
			y++
		}
	}
}

func (diag *Diag) placeInGrid(n *Node, x int, y int, placedNodes map[*Node]bool) int {
	addedNodes := 0
	for _, n := range n.getChildNodes() {
		_, ok := placedNodes[n]
		if ok {
			continue
		}
		placedNodes[n] = true
		err := diag.Grid.Set(x, y+addedNodes, n, diag)
		if err != nil {
			panic("Set failed")
		}

		addedNodes += diag.placeInGrid(n, x+1, y+addedNodes, placedNodes)
		addedNodes++
	}

	if addedNodes > 0 {
		return addedNodes - 1
	}
	return addedNodes
}
<|endoftext|>


<commit_before>package mockdb

import (
	"database/sql"
	"fmt"

	"github.com/Nivl/go-rest-tools/storage/db"
	"github.com/lib/pq"
	"github.com/stretchr/testify/mock"
)

var (
	// StringType represent a string argument
	StringType  = mock.AnythingOfType("string")
	serverError = &pq.Error{
		Code:    "08006",
		Message: "error: connection failure",
		Detail:  "the connection to the database failed",
	}
)

func newConflictError(fieldName string) *pq.Error {
	return &pq.Error{
		Code:    db.ErrDup,
		Message: "error: duplicate field",
		Detail:  fmt.Sprintf("Key (%s)=(Google) already exists.", fieldName),
	}
}

// ExpectGet is a helper that expects a Get
func (mdb *DB) ExpectGet(typ string, runnable func(args mock.Arguments)) *mock.Call {
	getCall := mdb.On("Get", mock.AnythingOfType(typ), StringType, StringType)
	getCall.Return(nil)
	if runnable != nil {
		getCall.Run(runnable)
	}
	return getCall
}

// ExpectGetNotFound is a helper that expects a not found on a Get
func (mdb *DB) ExpectGetNotFound(typ string) *mock.Call {
	getCall := mdb.On("Get", mock.AnythingOfType(typ), StringType, StringType)
	getCall.Return(sql.ErrNoRows)
	return getCall
}

// ExpectDeletion is a helper that expects a deletion
func (mdb *DB) ExpectDeletion() *mock.Call {
	return mdb.On("Exec", StringType, StringType).Return(nil, nil)
}

// ExpectDeletionError is a helper that expects a deletion to fail
func (mdb *DB) ExpectDeletionError() *mock.Call {
	return mdb.On("Exec", StringType, StringType).Return(nil, serverError)
}

// ExpectInsert is a helper that expects an insertion
func (mdb *DB) ExpectInsert(typ string) *mock.Call {
	return mdb.On("NamedExec", StringType, mock.AnythingOfType(typ)).Return(nil, nil)
}

// ExpectInsertError is a helper that expects an insert to fail
func (mdb *DB) ExpectInsertError(typ string) *mock.Call {
	return mdb.On("NamedExec", StringType, mock.AnythingOfType(typ)).Return(nil, serverError)
}

// ExpectInsertConflict is a helper that expects a conflict on an insertion
func (mdb *DB) ExpectInsertConflict(typ string, fieldName string) *mock.Call {
	conflictError := newConflictError(fieldName)
	return mdb.On("NamedExec", StringType, mock.AnythingOfType(typ)).Return(nil, conflictError)
}

// ExpectUpdate is a helper that expects an update
func (mdb *DB) ExpectUpdate(typ string) *mock.Call {
	return mdb.On("NamedExec", StringType, mock.AnythingOfType(typ)).Return(nil, nil)
}

// ExpectUpdateConflict is a helper that expects a conflict on an update
func (mdb *DB) ExpectUpdateConflict(typ string, fieldName string) *mock.Call {
	conflictError := newConflictError(fieldName)
	return mdb.On("NamedExec", StringType, mock.AnythingOfType(typ)).Return(nil, conflictError)
}

// ExpectUpdateError is a helper that expects an update to fail
func (mdb *DB) ExpectUpdateError(typ string) *mock.Call {
	return mdb.On("NamedExec", StringType, mock.AnythingOfType(typ)).Return(nil, serverError)
}
<commit_msg>feat(mockdb): add more functions to simulate a db connection error<commit_after>package mockdb

import (
	"database/sql"
	"fmt"

	"github.com/Nivl/go-rest-tools/storage/db"
	"github.com/lib/pq"
	"github.com/stretchr/testify/mock"
)

var (
	// StringType represents a string argument
	StringType = mock.AnythingOfType("string")
	// InType represents an int argument
	InType = mock.AnythingOfType("int")

	// serverError represents a database connection error
	serverError = &pq.Error{
		Code:    "08006",
		Message: "error: connection failure",
		Detail:  "the connection to the database failed",
	}
)

func newConflictError(fieldName string) *pq.Error {
	return &pq.Error{
		Code:    db.ErrDup,
		Message: "error: duplicate field",
		Detail:  fmt.Sprintf("Key (%s)=(Google) already exists.", fieldName),
	}
}

// ExpectGet is a helper that expects a Get
func (mdb *DB) ExpectGet(typ string, runnable func(args mock.Arguments)) *mock.Call {
	getCall := mdb.On("Get", mock.AnythingOfType(typ), StringType, StringType)
	getCall.Return(nil)
	if runnable != nil {
		getCall.Run(runnable)
	}
	return getCall
}

// ExpectGetNotFound is a helper that expects a not found on a Get
func (mdb *DB) ExpectGetNotFound(typ string) *mock.Call {
	getCall := mdb.On("Get", mock.AnythingOfType(typ), StringType, StringType)
	getCall.Return(sql.ErrNoRows)
	return getCall
}

// ExpectGetError is a helper that expects a connection error on a Get
func (mdb *DB) ExpectGetError(typ string) *mock.Call {
	getCall := mdb.On("Get", mock.AnythingOfType(typ), StringType, StringType)
	getCall.Return(serverError)
	return getCall
}

// ExpectSelectError is an helper that expects a connection error on a Select
func (mdb *DB) ExpectSelectError(typ string) *mock.Call {
	getCall := mdb.On("Select", mock.AnythingOfType(typ), StringType, InType, InType)
	getCall.Return(serverError)
	return getCall
}

// ExpectDeletion is a helper that expects a deletion
func (mdb *DB) ExpectDeletion() *mock.Call {
	return mdb.On("Exec", StringType, StringType).Return(nil, nil)
}

// ExpectDeletionError is a helper that expects a deletion to fail
func (mdb *DB) ExpectDeletionError() *mock.Call {
	return mdb.On("Exec", StringType, StringType).Return(nil, serverError)
}

// ExpectInsert is a helper that expects an insertion
func (mdb *DB) ExpectInsert(typ string) *mock.Call {
	return mdb.On("NamedExec", StringType, mock.AnythingOfType(typ)).Return(nil, nil)
}

// ExpectInsertError is a helper that expects an insert to fail
func (mdb *DB) ExpectInsertError(typ string) *mock.Call {
	return mdb.On("NamedExec", StringType, mock.AnythingOfType(typ)).Return(nil, serverError)
}

// ExpectInsertConflict is a helper that expects a conflict on an insertion
func (mdb *DB) ExpectInsertConflict(typ string, fieldName string) *mock.Call {
	conflictError := newConflictError(fieldName)
	return mdb.On("NamedExec", StringType, mock.AnythingOfType(typ)).Return(nil, conflictError)
}

// ExpectUpdate is a helper that expects an update
func (mdb *DB) ExpectUpdate(typ string) *mock.Call {
	return mdb.On("NamedExec", StringType, mock.AnythingOfType(typ)).Return(nil, nil)
}

// ExpectUpdateConflict is a helper that expects a conflict on an update
func (mdb *DB) ExpectUpdateConflict(typ string, fieldName string) *mock.Call {
	conflictError := newConflictError(fieldName)
	return mdb.On("NamedExec", StringType, mock.AnythingOfType(typ)).Return(nil, conflictError)
}

// ExpectUpdateError is a helper that expects an update to fail
func (mdb *DB) ExpectUpdateError(typ string) *mock.Call {
	return mdb.On("NamedExec", StringType, mock.AnythingOfType(typ)).Return(nil, serverError)
}
<|endoftext|>


<commit_before>// Copyright ©2012 The bíogo.external 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 external

import (
	"bytes"
	check "launchpad.net/gocheck"
	"os/exec"
	"sort"
	"strings"
	"testing"
)

// Tests
func Test(t *testing.T) { check.TestingT(t) }

type S struct{}

var _ = check.Suite(&S{})

type Ls struct {
	Cmd  string   `buildarg:"{{if .}}{{.}}{{else}}ls{{end}}"` // ls
	Glob []string `buildarg:"{{if .}}{{args .}}{{end}}"`      // "<in>"...
}

func (l Ls) BuildCommand() (*exec.Cmd, error) {
	cl := Must(Build(l))
	return exec.Command(cl[0], cl[1:]...), nil
}

func (s *S) TestBuildLs(c *check.C) {
	_, err := Ls{}.BuildCommand()
	c.Check(err, check.Equals, nil)
}

func (s *S) TestLs(c *check.C) {
	_, err := exec.LookPath("ls")
	if err != nil {
		c.Skip("ls not present")
	}
	files := []string{"external.go", "external_test.go", "external_example_test.go"}
	sort.Strings(files)
	ls, err := Ls{
		Glob: files,
	}.BuildCommand()
	if err != nil {
		c.Check(err, check.Equals, nil) // Build Ls command.
	}
	ls.Stdout = &bytes.Buffer{}
	ls.Stderr = &bytes.Buffer{}
	err = ls.Run()
	if err != nil {
		c.Check(err, check.Equals, nil) // Run Ls command.
	}
	list := strings.Fields(ls.Stdout.(*bytes.Buffer).String())
	sort.Strings(list)
	c.Check(files, check.DeepEquals, list)
	c.Check(ls.Stderr.(*bytes.Buffer).String(), check.Equals, "")
}

type Du struct {
	Cmd     string   `buildarg:"{{if .}}{{.}}{{else}}du{{end}}"`                       // du
	Exclude []string `buildarg:"{{if .}}{{mprintf \"--exclude=%s\" . | args}}{{end}}"` // --exclude="file"...
}

func (d Du) BuildCommand() (*exec.Cmd, error) {
	cl := Must(Build(d))
	return exec.Command(cl[0], cl[1:]...), nil
}

func (s *S) TestBuildDu(c *check.C) {
	_, err := Du{}.BuildCommand()
	c.Check(err, check.Equals, nil)
}

func (s *S) TestDu(c *check.C) {
	_, err := exec.LookPath("du")
	if err != nil {
		c.Skip("du not present")
	}
	files := []string{
		"external.go",
		"external_test.go",
		"external_example_test.go",
		".git",
	}
	du, err := Du{
		Exclude: files,
	}.BuildCommand()
	if err != nil {
		c.Check(err, check.Equals, nil) // Build Du command.
	}
	c.Check(du.Args, check.DeepEquals, []string{
		"du",
		"--exclude=external.go",
		"--exclude=external_test.go",
		"--exclude=external_example_test.go",
		"--exclude=.git",
	})
	du.Stdout = &bytes.Buffer{}
	du.Stderr = &bytes.Buffer{}
	err = du.Run()
	if err != nil {
		c.Check(err, check.Equals, nil) // Run Du command.
	}
	list := strings.Fields(du.Stdout.(*bytes.Buffer).String())
	c.Check(list, check.DeepEquals, []string{
		"16", "./last",
		"20", "./muscle",
		"128", "./mafft",
		"164", ".",
	})
	c.Check(du.Stderr.(*bytes.Buffer).String(), check.Equals, "")
}
<commit_msg>Generalise du test for future fs changes<commit_after>// Copyright ©2012 The bíogo.external 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 external

import (
	"bytes"
	check "launchpad.net/gocheck"
	"os/exec"
	"sort"
	"strconv"
	"strings"
	"testing"
)

// Tests
func Test(t *testing.T) { check.TestingT(t) }

type S struct{}

var _ = check.Suite(&S{})

type Ls struct {
	Cmd  string   `buildarg:"{{if .}}{{.}}{{else}}ls{{end}}"` // ls
	Glob []string `buildarg:"{{if .}}{{args .}}{{end}}"`      // "<in>"...
}

func (l Ls) BuildCommand() (*exec.Cmd, error) {
	cl := Must(Build(l))
	return exec.Command(cl[0], cl[1:]...), nil
}

func (s *S) TestBuildLs(c *check.C) {
	_, err := Ls{}.BuildCommand()
	c.Check(err, check.Equals, nil)
}

func (s *S) TestLs(c *check.C) {
	_, err := exec.LookPath("ls")
	if err != nil {
		c.Skip("ls not present")
	}
	files := []string{"external.go", "external_test.go", "external_example_test.go"}
	sort.Strings(files)
	ls, err := Ls{
		Glob: files,
	}.BuildCommand()
	if err != nil {
		c.Check(err, check.Equals, nil) // Build Ls command.
	}
	ls.Stdout = &bytes.Buffer{}
	ls.Stderr = &bytes.Buffer{}
	err = ls.Run()
	if err != nil {
		c.Check(err, check.Equals, nil) // Run Ls command.
	}
	list := strings.Fields(ls.Stdout.(*bytes.Buffer).String())
	sort.Strings(list)
	c.Check(files, check.DeepEquals, list)
	c.Check(ls.Stderr.(*bytes.Buffer).String(), check.Equals, "")
}

type Du struct {
	Cmd     string   `buildarg:"{{if .}}{{.}}{{else}}du{{end}}"`                       // du
	Exclude []string `buildarg:"{{if .}}{{mprintf \"--exclude=%s\" . | args}}{{end}}"` // --exclude="file"...
}

func (d Du) BuildCommand() (*exec.Cmd, error) {
	cl := Must(Build(d))
	return exec.Command(cl[0], cl[1:]...), nil
}

func (s *S) TestBuildDu(c *check.C) {
	_, err := Du{}.BuildCommand()
	c.Check(err, check.Equals, nil)
}

func (s *S) TestDu(c *check.C) {
	_, err := exec.LookPath("du")
	if err != nil {
		c.Skip("du not present")
	}
	files := []string{
		"external.go",
		"external_test.go",
		"external_example_test.go",
		".git",
	}
	du, err := Du{
		Exclude: files,
	}.BuildCommand()
	if err != nil {
		c.Check(err, check.Equals, nil) // Build Du command.
	}
	c.Check(du.Args, check.DeepEquals, []string{
		"du",
		"--exclude=external.go",
		"--exclude=external_test.go",
		"--exclude=external_example_test.go",
		"--exclude=.git",
	})
	du.Stdout = &bytes.Buffer{}
	du.Stderr = &bytes.Buffer{}
	err = du.Run()
	if err != nil {
		c.Check(err, check.Equals, nil) // Run Du command.
	}

	// Expected behaviour of du on a directory is that all first elements of a line
	// be a positive integer and the last line's value be the sum of previous lines.
	// In the biogo hierarchy, all dir names are in [a-z]+.
	list := strings.Fields(du.Stdout.(*bytes.Buffer).String())
	var sum int
	for i := 0; i < len(list)-3; i += 2 {
		size, err := strconv.Atoi(list[i])
		c.Check(err, check.Equals, nil)
		c.Check(size >= 0, check.Equals, true)
		if i != len(list)-2 {
			sum += size
			c.Check(list[i+1], check.Matches, "./[a-z]+")
		} else {
			c.Check(list[i], check.Equals, sum)
			c.Check(list[i+1], check.Equals, ".")
		}
	}
	c.Check(du.Stderr.(*bytes.Buffer).String(), check.Equals, "")
}
<|endoftext|>


<commit_before>package internal_models

import (
	"log"
	"sync"
	"time"

	"github.com/influxdata/telegraf"
)

const (

	// Default size of metrics batch size.
	DEFAULT_METRIC_BATCH_SIZE = 1000

	// Default number of metrics kept. It should be a multiple of batch size.
	DEFAULT_METRIC_BUFFER_LIMIT = 10000
)

// tmpmetrics point to batch of metrics ready to be wrote to output.
// readI point to the oldest batch of metrics (the first to sent to output). It
// may point to nil value if tmpmetrics is empty.
// writeI point to the next slot to buffer a batch of metrics is output fail to
// write.
type RunningOutput struct {
	Name                string
	Output              telegraf.Output
	Config              *OutputConfig
	Quiet               bool
	MetricBufferLimit   int
	MetricBatchSize     int
	FlushBufferWhenFull bool

	metrics    []telegraf.Metric
	tmpmetrics []([]telegraf.Metric)
	writeI     int
	readI      int

	sync.Mutex
}

func NewRunningOutput(
	name string,
	output telegraf.Output,
	conf *OutputConfig,
) *RunningOutput {
	ro := &RunningOutput{
		Name:              name,
		metrics:           make([]telegraf.Metric, 0),
		Output:            output,
		Config:            conf,
		MetricBufferLimit: DEFAULT_METRIC_BUFFER_LIMIT,
		MetricBatchSize:   DEFAULT_METRIC_BATCH_SIZE,
	}
	return ro
}

// AddMetric adds a metric to the output. This function can also write cached
// points if FlushBufferWhenFull is true.
func (ro *RunningOutput) AddMetric(metric telegraf.Metric) {
	if ro.Config.Filter.IsActive {
		if !ro.Config.Filter.ShouldMetricPass(metric) {
			return
		}
	}
	ro.Lock()
	defer ro.Unlock()

	if ro.tmpmetrics == nil {
		size := ro.MetricBufferLimit / ro.MetricBatchSize
		// ro.metrics already contains one batch
		size = size - 1

		if size < 1 {
			size = 1
		}
		ro.tmpmetrics = make([]([]telegraf.Metric), size)
	}

	// Filter any tagexclude/taginclude parameters before adding metric
	if len(ro.Config.Filter.TagExclude) != 0 || len(ro.Config.Filter.TagInclude) != 0 {
		// In order to filter out tags, we need to create a new metric, since
		// metrics are immutable once created.
		tags := metric.Tags()
		fields := metric.Fields()
		t := metric.Time()
		name := metric.Name()
		ro.Config.Filter.FilterTags(tags)
		// error is not possible if creating from another metric, so ignore.
		metric, _ = telegraf.NewMetric(name, tags, fields, t)
	}

	if len(ro.metrics) < ro.MetricBatchSize {
		ro.metrics = append(ro.metrics, metric)
	} else {
		flushSuccess := true
		if ro.FlushBufferWhenFull {
			err := ro.write(ro.metrics)
			if err != nil {
				log.Printf("ERROR writing full metric buffer to output %s, %s",
					ro.Name, err)
				flushSuccess = false
			}
		} else {
			flushSuccess = false
		}
		if !flushSuccess {
			if ro.tmpmetrics[ro.writeI] != nil && ro.writeI == ro.readI {
				log.Printf("WARNING: overwriting cached metrics, you may want to " +
					"increase the metric_buffer_limit setting in your [agent] " +
					"config if you do not wish to overwrite metrics.\n")
				ro.readI = (ro.readI + 1) % cap(ro.tmpmetrics)
			}
			ro.tmpmetrics[ro.writeI] = ro.metrics
			ro.writeI = (ro.writeI + 1) % cap(ro.tmpmetrics)
		}
		ro.metrics = make([]telegraf.Metric, 0)
		ro.metrics = append(ro.metrics, metric)
	}
}

// Write writes all cached points to this output.
func (ro *RunningOutput) Write() error {
	ro.Lock()
	defer ro.Unlock()

	// Write any cached metric buffers before, as those metrics are the
	// oldest
	for ro.tmpmetrics[ro.readI] != nil {
		if err := ro.write(ro.tmpmetrics[ro.readI]); err != nil {
			return err
		} else {
			ro.tmpmetrics[ro.readI] = nil
			ro.readI = (ro.readI + 1) % cap(ro.tmpmetrics)
		}
	}

	err := ro.write(ro.metrics)
	if err != nil {
		return err
	} else {
		ro.metrics = make([]telegraf.Metric, 0)
	}

	return nil
}

func (ro *RunningOutput) write(metrics []telegraf.Metric) error {
	if len(metrics) == 0 {
		return nil
	}
	start := time.Now()
	err := ro.Output.Write(metrics)
	elapsed := time.Since(start)
	if err == nil {
		if !ro.Quiet {
			log.Printf("Wrote %d metrics to output %s in %s\n",
				len(metrics), ro.Name, elapsed)
		}
	}
	return err
}

// OutputConfig containing name and filter
type OutputConfig struct {
	Name   string
	Filter Filter
}
<commit_msg>buffers: fix bug when Write called before AddMetric<commit_after>package internal_models

import (
	"log"
	"sync"
	"time"

	"github.com/influxdata/telegraf"
)

const (

	// Default size of metrics batch size.
	DEFAULT_METRIC_BATCH_SIZE = 1000

	// Default number of metrics kept. It should be a multiple of batch size.
	DEFAULT_METRIC_BUFFER_LIMIT = 10000
)

// tmpmetrics point to batch of metrics ready to be wrote to output.
// readI point to the oldest batch of metrics (the first to sent to output). It
// may point to nil value if tmpmetrics is empty.
// writeI point to the next slot to buffer a batch of metrics is output fail to
// write.
type RunningOutput struct {
	Name                string
	Output              telegraf.Output
	Config              *OutputConfig
	Quiet               bool
	MetricBufferLimit   int
	MetricBatchSize     int
	FlushBufferWhenFull bool

	metrics    []telegraf.Metric
	tmpmetrics []([]telegraf.Metric)
	writeI     int
	readI      int

	sync.Mutex
}

func NewRunningOutput(
	name string,
	output telegraf.Output,
	conf *OutputConfig,
) *RunningOutput {
	ro := &RunningOutput{
		Name:              name,
		metrics:           make([]telegraf.Metric, 0),
		Output:            output,
		Config:            conf,
		MetricBufferLimit: DEFAULT_METRIC_BUFFER_LIMIT,
		MetricBatchSize:   DEFAULT_METRIC_BATCH_SIZE,
	}
	return ro
}

// AddMetric adds a metric to the output. This function can also write cached
// points if FlushBufferWhenFull is true.
func (ro *RunningOutput) AddMetric(metric telegraf.Metric) {
	if ro.Config.Filter.IsActive {
		if !ro.Config.Filter.ShouldMetricPass(metric) {
			return
		}
	}
	ro.Lock()
	defer ro.Unlock()

	if ro.tmpmetrics == nil {
		size := ro.MetricBufferLimit / ro.MetricBatchSize
		// ro.metrics already contains one batch
		size = size - 1

		if size < 1 {
			size = 1
		}
		ro.tmpmetrics = make([]([]telegraf.Metric), size)
	}

	// Filter any tagexclude/taginclude parameters before adding metric
	if len(ro.Config.Filter.TagExclude) != 0 || len(ro.Config.Filter.TagInclude) != 0 {
		// In order to filter out tags, we need to create a new metric, since
		// metrics are immutable once created.
		tags := metric.Tags()
		fields := metric.Fields()
		t := metric.Time()
		name := metric.Name()
		ro.Config.Filter.FilterTags(tags)
		// error is not possible if creating from another metric, so ignore.
		metric, _ = telegraf.NewMetric(name, tags, fields, t)
	}

	if len(ro.metrics) < ro.MetricBatchSize {
		ro.metrics = append(ro.metrics, metric)
	} else {
		flushSuccess := true
		if ro.FlushBufferWhenFull {
			err := ro.write(ro.metrics)
			if err != nil {
				log.Printf("ERROR writing full metric buffer to output %s, %s",
					ro.Name, err)
				flushSuccess = false
			}
		} else {
			flushSuccess = false
		}
		if !flushSuccess {
			if ro.tmpmetrics[ro.writeI] != nil && ro.writeI == ro.readI {
				log.Printf("WARNING: overwriting cached metrics, you may want to " +
					"increase the metric_buffer_limit setting in your [agent] " +
					"config if you do not wish to overwrite metrics.\n")
				ro.readI = (ro.readI + 1) % cap(ro.tmpmetrics)
			}
			ro.tmpmetrics[ro.writeI] = ro.metrics
			ro.writeI = (ro.writeI + 1) % cap(ro.tmpmetrics)
		}
		ro.metrics = make([]telegraf.Metric, 0)
		ro.metrics = append(ro.metrics, metric)
	}
}

// Write writes all cached points to this output.
func (ro *RunningOutput) Write() error {
	ro.Lock()
	defer ro.Unlock()

	if ro.tmpmetrics == nil {
		size := ro.MetricBufferLimit / ro.MetricBatchSize
		// ro.metrics already contains one batch
		size = size - 1

		if size < 1 {
			size = 1
		}
		ro.tmpmetrics = make([]([]telegraf.Metric), size)
	}

	// Write any cached metric buffers before, as those metrics are the
	// oldest
	for ro.tmpmetrics[ro.readI] != nil {
		if err := ro.write(ro.tmpmetrics[ro.readI]); err != nil {
			return err
		} else {
			ro.tmpmetrics[ro.readI] = nil
			ro.readI = (ro.readI + 1) % cap(ro.tmpmetrics)
		}
	}

	err := ro.write(ro.metrics)
	if err != nil {
		return err
	} else {
		ro.metrics = make([]telegraf.Metric, 0)
	}

	return nil
}

func (ro *RunningOutput) write(metrics []telegraf.Metric) error {
	if len(metrics) == 0 {
		return nil
	}
	start := time.Now()
	err := ro.Output.Write(metrics)
	elapsed := time.Since(start)
	if err == nil {
		if !ro.Quiet {
			log.Printf("Wrote %d metrics to output %s in %s\n",
				len(metrics), ro.Name, elapsed)
		}
	}
	return err
}

// OutputConfig containing name and filter
type OutputConfig struct {
	Name   string
	Filter Filter
}
<|endoftext|>


<commit_before>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package symbolizer provides a routine to populate a profile with
// symbol, file and line number information. It relies on the
// addr2liner and demangle packages to do the actual work.
package symbolizer

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"path/filepath"
	"strings"

	"github.com/google/pprof/internal/binutils"
	"github.com/google/pprof/internal/plugin"
	"github.com/google/pprof/internal/symbolz"
	"github.com/google/pprof/profile"
	"github.com/ianlancetaylor/demangle"
)

// Symbolizer implements the plugin.Symbolize interface.
type Symbolizer struct {
	Obj plugin.ObjTool
	UI  plugin.UI
}

// test taps for dependency injection
var symbolzSymbolize = symbolz.Symbolize
var localSymbolize = doLocalSymbolize

// Symbolize attempts to symbolize profile p. First uses binutils on
// local binaries; if the source is a URL it attempts to get any
// missed entries using symbolz.
func (s *Symbolizer) Symbolize(mode string, sources plugin.MappingSources, p *profile.Profile) error {
	remote, local, force, demanglerMode := true, true, false, ""
	for _, o := range strings.Split(strings.ToLower(mode), ":") {
		switch o {
		case "none", "no":
			return nil
		case "local", "fastlocal":
			remote, local = false, true
		case "remote":
			remote, local = true, false
		case "", "force":
			force = true
		default:
			switch d := strings.TrimPrefix(o, "demangle="); d {
			case "full", "none", "templates":
				demanglerMode = d
				force = true
				continue
			case "default":
				continue
			}
			s.UI.PrintErr("ignoring unrecognized symbolization option: " + mode)
			s.UI.PrintErr("expecting -symbolize=[local|fastlocal|remote|none][:force][:demangle=[none|full|templates|default]")
		}
	}

	var err error
	if local {
		// Symbolize locally using binutils.
		if err = localSymbolize(mode, p, s.Obj, s.UI); err != nil {
			s.UI.PrintErr("local symbolization: " + err.Error())
		}
	}
	if remote {
		if err = symbolzSymbolize(sources, postURL, p, s.UI); err != nil {
			return err // Ran out of options.
		}
	}

	Demangle(p, force, demanglerMode)
	return nil
}

// postURL issues a POST to a URL over HTTP.
func postURL(source, post string) ([]byte, error) {
	resp, err := http.Post(source, "application/octet-stream", strings.NewReader(post))
	if err != nil {
		return nil, fmt.Errorf("http post %s: %v", source, err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("server response: %s", resp.Status)
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

// doLocalSymbolize adds symbol and line number information to all locations
// in a profile. mode enables some options to control
// symbolization.
func doLocalSymbolize(mode string, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error {
	force := false
	// Disable some mechanisms based on mode string.
	for _, o := range strings.Split(strings.ToLower(mode), ":") {
		switch {
		case o == "force":
			force = true
		case o == "fastlocal":
			if bu, ok := obj.(*binutils.Binutils); ok {
				bu.SetFastSymbolization(true)
			}
		default:
		}
	}

	mt, err := newMapping(prof, obj, ui, force)
	if err != nil {
		return err
	}
	defer mt.close()

	functions := make(map[profile.Function]*profile.Function)
	for _, l := range mt.prof.Location {
		m := l.Mapping
		segment := mt.segments[m]
		if segment == nil {
			// Nothing to do.
			continue
		}

		stack, err := segment.SourceLine(l.Address)
		if err != nil || len(stack) == 0 {
			// No answers from addr2line.
			continue
		}

		l.Line = make([]profile.Line, len(stack))
		for i, frame := range stack {
			if frame.Func != "" {
				m.HasFunctions = true
			}
			if frame.File != "" {
				m.HasFilenames = true
			}
			if frame.Line != 0 {
				m.HasLineNumbers = true
			}
			f := &profile.Function{
				Name:       frame.Func,
				SystemName: frame.Func,
				Filename:   frame.File,
			}
			if fp := functions[*f]; fp != nil {
				f = fp
			} else {
				functions[*f] = f
				f.ID = uint64(len(mt.prof.Function)) + 1
				mt.prof.Function = append(mt.prof.Function, f)
			}
			l.Line[i] = profile.Line{
				Function: f,
				Line:     int64(frame.Line),
			}
		}

		if len(stack) > 0 {
			m.HasInlineFrames = true
		}
	}

	return nil
}

// Demangle updates the function names in a profile with demangled C++
// names, simplified according to demanglerMode. If force is set,
// overwrite any names that appear already demangled.
func Demangle(prof *profile.Profile, force bool, demanglerMode string) {
	if force {
		// Remove the current demangled names to force demangling
		for _, f := range prof.Function {
			if f.Name != "" && f.SystemName != "" {
				f.Name = f.SystemName
			}
		}
	}

	var options []demangle.Option
	switch demanglerMode {
	case "": // demangled, simplified: no parameters, no templates, no return type
		options = []demangle.Option{demangle.NoParams, demangle.NoTemplateParams}
	case "templates": // demangled, simplified: no parameters, no return type
		options = []demangle.Option{demangle.NoParams}
	case "full":
		options = []demangle.Option{demangle.NoClones}
	case "none": // no demangling
		return
	}

	// Copy the options because they may be updated by the call.
	o := make([]demangle.Option, len(options))
	for _, fn := range prof.Function {
		if fn.Name != "" && fn.SystemName != fn.Name {
			continue // Already demangled.
		}
		copy(o, options)
		if demangled := demangle.Filter(fn.SystemName, o...); demangled != fn.SystemName {
			fn.Name = demangled
			continue
		}
		// Could not demangle. Apply heuristics in case the name is
		// already demangled.
		name := fn.SystemName
		if looksLikeDemangledCPlusPlus(name) {
			if demanglerMode == "" || demanglerMode == "templates" {
				name = removeMatching(name, '(', ')')
			}
			if demanglerMode == "" {
				name = removeMatching(name, '<', '>')
			}
		}
		fn.Name = name
	}
}

// looksLikeDemangledCPlusPlus is a heuristic to decide if a name is
// the result of demangling C++. If so, further heuristics will be
// applied to simplify the name.
func looksLikeDemangledCPlusPlus(demangled string) bool {
	if strings.Contains(demangled, ".<") { // Skip java names of the form "class.<init>"
		return false
	}
	return strings.ContainsAny(demangled, "<>[]") || strings.Contains(demangled, "::")
}

// removeMatching removes nested instances of start..end from name.
func removeMatching(name string, start, end byte) string {
	s := string(start) + string(end)
	var nesting, first, current int
	for index := strings.IndexAny(name[current:], s); index != -1; index = strings.IndexAny(name[current:], s) {
		switch current += index; name[current] {
		case start:
			nesting++
			if nesting == 1 {
				first = current
			}
		case end:
			nesting--
			switch {
			case nesting < 0:
				return name // Mismatch, abort
			case nesting == 0:
				name = name[:first] + name[current+1:]
				current = first - 1
			}
		}
		current++
	}
	return name
}

// newMapping creates a mappingTable for a profile.
func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) {
	mt := &mappingTable{
		prof:     prof,
		segments: make(map[*profile.Mapping]plugin.ObjFile),
	}

	// Identify used mappings
	mappings := make(map[*profile.Mapping]bool)
	for _, l := range prof.Location {
		mappings[l.Mapping] = true
	}

	missingBinaries := false
	for midx, m := range prof.Mapping {
		if !mappings[m] {
			continue
		}

		// Do not attempt to re-symbolize a mapping that has already been symbolized.
		if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) {
			continue
		}

		if m.File == "" {
			if midx == 0 {
				ui.PrintErr("Main binary filename not available.\n" +
					"Try passing the path to the main binary before the profile.")
				continue
			}
			missingBinaries = true
			continue
		}

		// Skip well-known system mappings
		name := filepath.Base(m.File)
		if name == "[vdso]" || strings.HasPrefix(name, "linux-vdso") {
			continue
		}

		// Skip mappings pointing to a source URL
		if m.BuildID == "" {
			if u, err := url.Parse(m.File); err == nil && u.IsAbs() {
				continue
			}
		}

		f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset)
		if err != nil {
			ui.PrintErr("Local symbolization failed for ", name, ": ", err)
			continue
		}
		if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID {
			ui.PrintErr("Local symbolization failed for ", name, ": build ID mismatch")
			f.Close()
			continue
		}

		mt.segments[m] = f
	}
	if missingBinaries {
		ui.PrintErr("Some binary filenames not available. Symbolization may be incomplete.")
	}
	return mt, nil
}

// mappingTable contains the mechanisms for symbolization of a
// profile.
type mappingTable struct {
	prof     *profile.Profile
	segments map[*profile.Mapping]plugin.ObjFile
}

// Close releases any external processes being used for the mapping.
func (mt *mappingTable) close() {
	for _, segment := range mt.segments {
		segment.Close()
	}
}
<commit_msg>Add a message recommending the use of PPROF_BINARY_PATH<commit_after>// Copyright 2014 Google Inc. All Rights Reserved.
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
//     http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

// Package symbolizer provides a routine to populate a profile with
// symbol, file and line number information. It relies on the
// addr2liner and demangle packages to do the actual work.
package symbolizer

import (
	"fmt"
	"io/ioutil"
	"net/http"
	"net/url"
	"path/filepath"
	"strings"

	"github.com/google/pprof/internal/binutils"
	"github.com/google/pprof/internal/plugin"
	"github.com/google/pprof/internal/symbolz"
	"github.com/google/pprof/profile"
	"github.com/ianlancetaylor/demangle"
)

// Symbolizer implements the plugin.Symbolize interface.
type Symbolizer struct {
	Obj plugin.ObjTool
	UI  plugin.UI
}

// test taps for dependency injection
var symbolzSymbolize = symbolz.Symbolize
var localSymbolize = doLocalSymbolize

// Symbolize attempts to symbolize profile p. First uses binutils on
// local binaries; if the source is a URL it attempts to get any
// missed entries using symbolz.
func (s *Symbolizer) Symbolize(mode string, sources plugin.MappingSources, p *profile.Profile) error {
	remote, local, force, demanglerMode := true, true, false, ""
	for _, o := range strings.Split(strings.ToLower(mode), ":") {
		switch o {
		case "none", "no":
			return nil
		case "local", "fastlocal":
			remote, local = false, true
		case "remote":
			remote, local = true, false
		case "", "force":
			force = true
		default:
			switch d := strings.TrimPrefix(o, "demangle="); d {
			case "full", "none", "templates":
				demanglerMode = d
				force = true
				continue
			case "default":
				continue
			}
			s.UI.PrintErr("ignoring unrecognized symbolization option: " + mode)
			s.UI.PrintErr("expecting -symbolize=[local|fastlocal|remote|none][:force][:demangle=[none|full|templates|default]")
		}
	}

	var err error
	if local {
		// Symbolize locally using binutils.
		if err = localSymbolize(mode, p, s.Obj, s.UI); err != nil {
			s.UI.PrintErr("local symbolization: " + err.Error())
		}
	}
	if remote {
		if err = symbolzSymbolize(sources, postURL, p, s.UI); err != nil {
			return err // Ran out of options.
		}
	}

	Demangle(p, force, demanglerMode)
	return nil
}

// postURL issues a POST to a URL over HTTP.
func postURL(source, post string) ([]byte, error) {
	resp, err := http.Post(source, "application/octet-stream", strings.NewReader(post))
	if err != nil {
		return nil, fmt.Errorf("http post %s: %v", source, err)
	}
	if resp.StatusCode != http.StatusOK {
		return nil, fmt.Errorf("server response: %s", resp.Status)
	}
	defer resp.Body.Close()
	return ioutil.ReadAll(resp.Body)
}

// doLocalSymbolize adds symbol and line number information to all locations
// in a profile. mode enables some options to control
// symbolization.
func doLocalSymbolize(mode string, prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI) error {
	force := false
	// Disable some mechanisms based on mode string.
	for _, o := range strings.Split(strings.ToLower(mode), ":") {
		switch {
		case o == "force":
			force = true
		case o == "fastlocal":
			if bu, ok := obj.(*binutils.Binutils); ok {
				bu.SetFastSymbolization(true)
			}
		default:
		}
	}

	mt, err := newMapping(prof, obj, ui, force)
	if err != nil {
		return err
	}
	defer mt.close()

	functions := make(map[profile.Function]*profile.Function)
	for _, l := range mt.prof.Location {
		m := l.Mapping
		segment := mt.segments[m]
		if segment == nil {
			// Nothing to do.
			continue
		}

		stack, err := segment.SourceLine(l.Address)
		if err != nil || len(stack) == 0 {
			// No answers from addr2line.
			continue
		}

		l.Line = make([]profile.Line, len(stack))
		for i, frame := range stack {
			if frame.Func != "" {
				m.HasFunctions = true
			}
			if frame.File != "" {
				m.HasFilenames = true
			}
			if frame.Line != 0 {
				m.HasLineNumbers = true
			}
			f := &profile.Function{
				Name:       frame.Func,
				SystemName: frame.Func,
				Filename:   frame.File,
			}
			if fp := functions[*f]; fp != nil {
				f = fp
			} else {
				functions[*f] = f
				f.ID = uint64(len(mt.prof.Function)) + 1
				mt.prof.Function = append(mt.prof.Function, f)
			}
			l.Line[i] = profile.Line{
				Function: f,
				Line:     int64(frame.Line),
			}
		}

		if len(stack) > 0 {
			m.HasInlineFrames = true
		}
	}

	return nil
}

// Demangle updates the function names in a profile with demangled C++
// names, simplified according to demanglerMode. If force is set,
// overwrite any names that appear already demangled.
func Demangle(prof *profile.Profile, force bool, demanglerMode string) {
	if force {
		// Remove the current demangled names to force demangling
		for _, f := range prof.Function {
			if f.Name != "" && f.SystemName != "" {
				f.Name = f.SystemName
			}
		}
	}

	var options []demangle.Option
	switch demanglerMode {
	case "": // demangled, simplified: no parameters, no templates, no return type
		options = []demangle.Option{demangle.NoParams, demangle.NoTemplateParams}
	case "templates": // demangled, simplified: no parameters, no return type
		options = []demangle.Option{demangle.NoParams}
	case "full":
		options = []demangle.Option{demangle.NoClones}
	case "none": // no demangling
		return
	}

	// Copy the options because they may be updated by the call.
	o := make([]demangle.Option, len(options))
	for _, fn := range prof.Function {
		if fn.Name != "" && fn.SystemName != fn.Name {
			continue // Already demangled.
		}
		copy(o, options)
		if demangled := demangle.Filter(fn.SystemName, o...); demangled != fn.SystemName {
			fn.Name = demangled
			continue
		}
		// Could not demangle. Apply heuristics in case the name is
		// already demangled.
		name := fn.SystemName
		if looksLikeDemangledCPlusPlus(name) {
			if demanglerMode == "" || demanglerMode == "templates" {
				name = removeMatching(name, '(', ')')
			}
			if demanglerMode == "" {
				name = removeMatching(name, '<', '>')
			}
		}
		fn.Name = name
	}
}

// looksLikeDemangledCPlusPlus is a heuristic to decide if a name is
// the result of demangling C++. If so, further heuristics will be
// applied to simplify the name.
func looksLikeDemangledCPlusPlus(demangled string) bool {
	if strings.Contains(demangled, ".<") { // Skip java names of the form "class.<init>"
		return false
	}
	return strings.ContainsAny(demangled, "<>[]") || strings.Contains(demangled, "::")
}

// removeMatching removes nested instances of start..end from name.
func removeMatching(name string, start, end byte) string {
	s := string(start) + string(end)
	var nesting, first, current int
	for index := strings.IndexAny(name[current:], s); index != -1; index = strings.IndexAny(name[current:], s) {
		switch current += index; name[current] {
		case start:
			nesting++
			if nesting == 1 {
				first = current
			}
		case end:
			nesting--
			switch {
			case nesting < 0:
				return name // Mismatch, abort
			case nesting == 0:
				name = name[:first] + name[current+1:]
				current = first - 1
			}
		}
		current++
	}
	return name
}

// newMapping creates a mappingTable for a profile.
func newMapping(prof *profile.Profile, obj plugin.ObjTool, ui plugin.UI, force bool) (*mappingTable, error) {
	mt := &mappingTable{
		prof:     prof,
		segments: make(map[*profile.Mapping]plugin.ObjFile),
	}

	// Identify used mappings
	mappings := make(map[*profile.Mapping]bool)
	for _, l := range prof.Location {
		mappings[l.Mapping] = true
	}

	missingBinaries := false
	for midx, m := range prof.Mapping {
		if !mappings[m] {
			continue
		}

		// Do not attempt to re-symbolize a mapping that has already been symbolized.
		if !force && (m.HasFunctions || m.HasFilenames || m.HasLineNumbers) {
			continue
		}

		if m.File == "" {
			if midx == 0 {
				ui.PrintErr("Main binary filename not available.")
				continue
			}
			missingBinaries = true
			continue
		}

		// Skip well-known system mappings
		name := filepath.Base(m.File)
		if name == "[vdso]" || strings.HasPrefix(name, "linux-vdso") {
			continue
		}

		// Skip mappings pointing to a source URL
		if m.BuildID == "" {
			if u, err := url.Parse(m.File); err == nil && u.IsAbs() {
				continue
			}
		}

		f, err := obj.Open(m.File, m.Start, m.Limit, m.Offset)
		if err != nil {
			ui.PrintErr("Local symbolization failed for ", name, ": ", err)
			missingBinaries = true
			continue
		}
		if fid := f.BuildID(); m.BuildID != "" && fid != "" && fid != m.BuildID {
			ui.PrintErr("Local symbolization failed for ", name, ": build ID mismatch")
			f.Close()
			continue
		}

		mt.segments[m] = f
	}
	if missingBinaries {
		ui.PrintErr("Some binary filenames not available. Symbolization may be incomplete.\n" +
			"Try setting PPROF_BINARY_PATH to the search path for local binaries.")
	}
	return mt, nil
}

// mappingTable contains the mechanisms for symbolization of a
// profile.
type mappingTable struct {
	prof     *profile.Profile
	segments map[*profile.Mapping]plugin.ObjFile
}

// Close releases any external processes being used for the mapping.
func (mt *mappingTable) close() {
	for _, segment := range mt.segments {
		segment.Close()
	}
}
<|endoftext|>