Example #1
0
// Run function analyses main.main() then all the goroutines collected, and
// finally output the analysis results.
func (extract *CFSMExtract) Run() {
	startTime := time.Now()
	mainPkg := ssabuilder.MainPkg(extract.SSA.Prog)
	if mainPkg == nil {
		fmt.Fprintf(os.Stderr, "Error: 'main' package not found\n")
		os.Exit(1)
	}
	init := mainPkg.Func("init")
	main := mainPkg.Func("main")
	fr := makeToplevelFrame(extract)
	for _, pkg := range extract.SSA.Prog.AllPackages() {
		for _, memb := range pkg.Members {
			switch val := memb.(type) {
			case *ssa.Global:
				switch derefAll(val.Type()).(type) {
				case *types.Array:
					vd := utils.NewDef(val)
					fr.env.globals[val] = vd
					fr.env.arrays[vd] = make(Elems)

				case *types.Struct:
					vd := utils.NewDef(val)
					fr.env.globals[val] = vd
					fr.env.structs[vd] = make(Fields)

				case *types.Chan:
					var c *types.Chan
					vd := utils.NewDef(utils.EmptyValue{T: c})
					fr.env.globals[val] = vd

				default:
					fr.env.globals[val] = utils.NewDef(val)
				}
			}
		}
	}

	fmt.Fprintf(os.Stderr, "++ call.toplevel %s()\n", orange("init"))
	visitFunc(init, fr)
	if main == nil {
		fmt.Fprintf(os.Stderr, "Error: 'main()' function not found in 'main' package\n")
		os.Exit(1)
	}
	fmt.Fprintf(os.Stderr, "++ call.toplevel %s()\n", orange("main"))
	visitFunc(main, fr)

	fr.env.session.Types[fr.gortn.role] = fr.gortn.root

	var goFrm *frame
	for len(extract.goQueue) > 0 {
		goFrm, extract.goQueue = extract.goQueue[0], extract.goQueue[1:]
		fmt.Fprintf(os.Stderr, "\n%s\nLOCATION: %s%s\n", goFrm.fn.Name(), goFrm.gortn.role.Name(), loc(goFrm, goFrm.fn.Pos()))
		visitFunc(goFrm.fn, goFrm)
		goFrm.env.session.Types[goFrm.gortn.role] = goFrm.gortn.root
	}

	extract.Time = time.Since(startTime)
	extract.Done <- struct{}{}
}
Example #2
0
func visitIndex(inst *ssa.Index, fr *frame) {
	elem := inst
	array := inst.X
	index := inst.Index
	_, isArray := array.Type().Underlying().(*types.Array)
	_, isSlice := array.Type().Underlying().(*types.Slice)

	if isArray || isSlice {
		switch vd, kind := fr.get(array); kind {
		case Array:
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s)[%d] of type %s\n", cyan(reg(elem)), array.Name(), vd.String(), index, elem.Type().String())
			if fr.env.arrays[vd][index] == nil { // First use
				vdelem := utils.NewDef(elem)
				fr.env.arrays[vd][index] = vdelem
				fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as elem definition\n", elem.Name())
			} else if fr.env.arrays[vd][index].Var != elem { // Previously defined
				fmt.Fprintf(os.Stderr, "     ^ elem %s previously defined as %s\n", elem.Name(), reg(fr.env.arrays[vd][index].Var))
			} // else Accessed before (and unchanged)
			fr.locals[elem] = fr.env.arrays[vd][index]

		case LocalArray:
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s)[%d] (local) of type %s\n", cyan(reg(elem)), array.Name(), vd.String(), index, elem.Type().String())
			if fr.arrays[vd][index] == nil { // First use
				vdElem := utils.NewDef(elem)
				fr.arrays[vd][index] = vdElem
				fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as elem definition\n", elem.Name())
			} else if fr.arrays[vd][index].Var != elem { // Previously defined
				fmt.Fprintf(os.Stderr, "     ^ elem %s previously defined as %s\n", elem.Name(), reg(fr.arrays[vd][index].Var))
			} // else Accessed before (and unchanged)
			fr.locals[elem] = fr.arrays[vd][index]

		case Nothing, Untracked:
			// Nothing: Very likely external struct.
			// Untracked: likely branches of return values (e.g. returning nil)
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s)[%d] (external) of type %s\n", cyan(reg(elem)), inst.X.Name(), vd.String(), index, elem.Type().String())
			vd := utils.NewDef(array) // New external array
			fr.locals[array] = vd
			fr.env.arrays[vd] = make(Elems)
			vdElem := utils.NewDef(elem) // New external elem
			fr.env.arrays[vd][index] = vdElem
			fr.locals[elem] = vdElem
			fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as elem definition of type %s\n", elem.Name(), inst.Type().(*types.Pointer).Elem().Underlying().String())

		default:
			panic(fmt.Sprintf("Index: Cannot access non-array %s", reg(array)))
		}
	} else {
		panic(fmt.Sprintf("Index: Cannot access element - %s not an array", reg(array)))
	}
}
Example #3
0
func visitExtract(e *ssa.Extract, fr *frame) {
	if recvCh, ok := fr.recvok[e.Tuple]; ok && e.Index == 1 { // 1 = ok (bool)
		fmt.Fprintf(os.Stderr, "  EXTRACT for %s\n", recvCh.Name())
		//fr.locals[e] = e
		fr.env.recvTest[e] = recvCh
		return
	}
	if tpl, ok := fr.tuples[e.Tuple]; ok {
		fmt.Fprintf(os.Stderr, "   %s = extract %s[#%d] == %s\n", reg(e), e.Tuple.Name(), e.Index, tpl[e.Index].String())
		fr.locals[e] = tpl[e.Index]
	} else {
		// Check if we are extracting select index
		if _, ok := fr.env.selNode[e.Tuple]; ok && e.Index == 0 {
			fmt.Fprintf(os.Stderr, "   | %s = select %s index\n", e.Name(), e.Tuple.Name())
			fr.env.selIdx[e] = e.Tuple
			return
		}
		// Check if value is an external tuple (return value)
		if extType, isExtern := fr.env.extern[e.Tuple]; isExtern {
			if extTpl, isTuple := extType.(*types.Tuple); isTuple {
				if extTpl.Len() < e.Index {
					panic(fmt.Sprintf("Extract: Cannot extract from tuple %s\n", e.Tuple.Name()))
				}
				// if extracted value is a chan create a new channel for it
				if _, ok := extTpl.At(e.Index).Type().(*types.Chan); ok {
					panic("Extract: Undefined channel")
				}
			}
			if e.Index < len(tpl) {
				fmt.Fprintf(os.Stderr, "  extract %s[#%d] == %s\n", e.Tuple.Name(), e.Index, tpl[e.Index].String())
			} else {
				fmt.Fprintf(os.Stderr, "  extract %s[#%d/%d]\n", e.Tuple.Name(), e.Index, len(tpl))
			}
		} else {
			fmt.Fprintf(os.Stderr, "   # %s = %s of type %s\n", e.Name(), red(e.String()), e.Type().String())
			switch derefAll(e.Type()).Underlying().(type) {
			case *types.Array:
				vd := utils.NewDef(e)
				fr.locals[e] = vd
				fr.arrays[vd] = make(Elems)
				fmt.Fprintf(os.Stderr, "     ^ local array (used as definition)\n")
			case *types.Struct:
				vd := utils.NewDef(e)
				fr.locals[e] = vd
				fr.structs[vd] = make(Fields)
				fmt.Fprintf(os.Stderr, "     ^ local struct (used as definition)\n")
			}
		}
	}
}
Example #4
0
func visitRecv(recv *ssa.UnOp, fr *frame) {
	locn := loc(fr, recv.X.Pos())
	if vd, kind := fr.get(recv.X); kind == Chan {
		ch := fr.env.chans[vd]
		if recv.CommaOk {
			// ReceiveOK test
			fr.recvok[recv] = ch
			// TODO(nickng) technically this should do receive (both branches)
		} else {
			// Normal receive
			fr.gortn.AddNode(sesstype.NewRecvNode(*ch, fr.gortn.role, recv.X.Type()))
			fmt.Fprintf(os.Stderr, "  %s\n", orange((*fr.gortn.leaf).String()))
		}
	} else if kind == Nothing {
		fr.locals[recv.X] = utils.NewDef(recv.X)
		ch := fr.env.session.MakeExtChan(fr.locals[recv.X], fr.gortn.role)
		fr.env.chans[fr.locals[recv.X]] = &ch
		fr.gortn.AddNode(sesstype.NewRecvNode(ch, fr.gortn.role, recv.X.Type()))
		fmt.Fprintf(os.Stderr, "  %s\n", orange((*fr.gortn.leaf).String()))
		fmt.Fprintf(os.Stderr, "   ^ Recv: Channel %s at %s is external\n", reg(recv.X), locn)
	} else {
		fr.printCallStack()
		panic(fmt.Sprintf("Recv: Channel %s at %s is of wrong kind", reg(recv.X), locn))
	}
}
Example #5
0
// Tests SendNode creation.
func TestSendNode(t *testing.T) {
	s := CreateSession()
	r := s.GetRole("main")
	c := s.MakeChan(utils.NewDef(utils.EmptyValue{T: nil}), r)
	n := NewSendNode(r, c, nil)
	if n.Kind() != SendOp {
		t.Errorf("Expecting node kind to be %s but got %s\n", SendOp, n.Kind())
	}
	if n.(*SendNode).nondet {
		t.Errorf("Expecting Send to be deterministic by default\n")
	}
	if len(n.Children()) != 0 {
		t.Errorf("Expecting node to have 0 children but got %d\n", len(n.Children()))
	}

	n2 := NewSelectSendNode(r, c, nil)
	if n2.Kind() != SendOp {
		t.Errorf("Expecting node kind to be %s but got %s\n", SendOp, n2.Kind())
	}
	if !n2.(*SendNode).nondet {
		t.Errorf("Expecting Select-Send to be non-deterministic by default\n")
	}
	if len(n2.Children()) != 0 {
		t.Errorf("Expecting node to have 0 children but got %d\n", len(n2.Children()))
	}

	if n2 != n.Append(n2) {
		t.Errorf("Appended node is not same as expected\n")
	}
	if len(n.Children()) != 1 {
		t.Errorf("Expecting node to have 1 children but got %d\n", len(n.Children()))
	}

}
Example #6
0
func visitMakeChan(inst *ssa.MakeChan, caller *frame) {
	locn := loc(caller, inst.Pos())
	role := caller.gortn.role

	vd := utils.NewDef(inst) // Unique identifier for inst
	ch := caller.env.session.MakeChan(vd, role)

	caller.env.chans[vd] = &ch
	caller.gortn.AddNode(sesstype.NewNewChanNode(ch))
	caller.locals[inst] = vd
	fmt.Fprintf(os.Stderr, "   New channel %s { type: %s } by %s at %s\n", green(ch.Name()), ch.Type(), vd.String(), locn)
	fmt.Fprintf(os.Stderr, "               ^ in role %s\n", role.Name())
}
Example #7
0
// visitAlloc is for variable allocation (usually by 'new')
// Everything allocated here are pointers
func visitAlloc(inst *ssa.Alloc, fr *frame) {
	locn := loc(fr, inst.Pos())
	allocType := inst.Type().(*types.Pointer).Elem()
	if allocType == nil {
		panic("Alloc: Cannot Alloc for non-pointer type")
	}
	var val ssa.Value = inst

	switch t := allocType.Underlying().(type) {
	case *types.Array:
		vd := utils.NewDef(val)
		fr.locals[val] = vd
		if inst.Heap {
			fr.env.arrays[vd] = make(Elems)
			fmt.Fprintf(os.Stderr, "   %s = Alloc (array@heap) of type %s (%d elems) at %s\n", cyan(reg(inst)), inst.Type().String(), t.Len(), locn)
		} else {
			fr.arrays[vd] = make(Elems)
			fmt.Fprintf(os.Stderr, "   %s = Alloc (array@local) of type %s (%d elems) at %s\n", cyan(reg(inst)), inst.Type().String(), t.Len(), locn)
		}

	case *types.Chan:
		// VD will be created in MakeChan so no need to allocate here.
		fmt.Fprintf(os.Stderr, "   %s = Alloc (chan) of type %s at %s\n", cyan(reg(inst)), inst.Type().String(), locn)

	case *types.Struct:
		vd := utils.NewDef(val)
		fr.locals[val] = vd
		if inst.Heap {
			fr.env.structs[vd] = make(Fields, t.NumFields())
			fmt.Fprintf(os.Stderr, "   %s = Alloc (struct@heap) of type %s (%d fields) at %s\n", cyan(reg(inst)), inst.Type().String(), t.NumFields(), locn)
		} else {
			fr.structs[vd] = make(Fields, t.NumFields())
			fmt.Fprintf(os.Stderr, "   %s = Alloc (struct@local) of type %s (%d fields) at %s\n", cyan(reg(inst)), inst.Type().String(), t.NumFields(), locn)
		}

	default:
		fmt.Fprintf(os.Stderr, "   # %s = "+red("Alloc %s")+" of type %s\n", inst.Name(), inst.String(), t.String())
	}
}
Example #8
0
func visitSend(send *ssa.Send, fr *frame) {
	locn := loc(fr, send.Chan.Pos())
	if vd, kind := fr.get(send.Chan); kind == Chan {
		ch := fr.env.chans[vd]
		fr.gortn.AddNode(sesstype.NewSendNode(fr.gortn.role, *ch, send.Chan.Type()))
		fmt.Fprintf(os.Stderr, "  %s\n", orange((*fr.gortn.leaf).String()))
	} else if kind == Nothing {
		fr.locals[send.Chan] = utils.NewDef(send.Chan)
		ch := fr.env.session.MakeExtChan(fr.locals[send.Chan], fr.gortn.role)
		fr.env.chans[fr.locals[send.Chan]] = &ch
		fr.gortn.AddNode(sesstype.NewSendNode(fr.gortn.role, ch, send.Chan.Type()))
		fmt.Fprintf(os.Stderr, "  %s\n", orange((*fr.gortn.leaf).String()))
		fmt.Fprintf(os.Stderr, "   ^ Send: Channel %s at %s is external\n", reg(send.Chan), locn)
	} else {
		fr.printCallStack()
		panic(fmt.Sprintf("Send: Channel %s at %s is of wrong kind", reg(send.Chan), locn))
	}
}
Example #9
0
// Tests NewEndNode creation.
func TestEndNode(t *testing.T) {
	s := CreateSession()
	r := s.GetRole("main")
	c := s.MakeChan(utils.NewDef(utils.EmptyValue{T: nil}), r)
	n := NewEndNode(c)
	if n.Kind() != EndOp {
		t.Errorf("Expecting node kind to be %s but got %s\n", EndOp, n.Kind())
	}
	if len(n.Children()) != 0 {
		t.Errorf("Expecting node to have 0 children but got %d\n", len(n.Children()))
	}
	n2 := NewEndNode(c)
	if n2 != n.Append(n2) {
		t.Errorf("Appended node is not same as expected\n")
	}
	if len(n.Children()) != 1 {
		t.Errorf("Expecting node to have 1 children but got %d\n", len(n.Children()))
	}
}
Example #10
0
// handleExtRetvals looks up and stores return value from (ext) function calls.
// Ext functions have no code (no body to analyse) and unlike normal values,
// the return values/tuples are stored until they are referenced.
func (caller *frame) handleExtRetvals(returned ssa.Value, callee *frame) {
	// Since there are no code for the function, we use the function
	// signature to see if any of these are channels.
	// XXX We don't know where these come from so we put them in extern.
	resultsLen := callee.fn.Signature.Results().Len()
	if resultsLen > 0 {
		caller.env.extern[returned] = callee.fn.Signature.Results()
		if resultsLen == 1 {
			fmt.Fprintf(os.Stderr, "-- Return from %s (builtin/ext) with a single value\n", callee.fn.String())
			if _, ok := callee.fn.Signature.Results().At(0).Type().(*types.Chan); ok {
				vardef := utils.NewDef(returned)
				ch := caller.env.session.MakeExtChan(vardef, caller.gortn.role)
				caller.env.chans[vardef] = &ch
				fmt.Fprintf(os.Stderr, "-- Return value from %s (builtin/ext) is a channel %s (ext)\n", callee.fn.String(), (*caller.env.chans[vardef]).Name())
			}
		} else {
			fmt.Fprintf(os.Stderr, "-- Return from %s (builtin/ext) with %d-tuple\n", callee.fn.String(), resultsLen)
		}
	}
}
Example #11
0
func visitField(inst *ssa.Field, fr *frame) {
	field := inst
	struc := inst.X
	index := inst.Field

	if stype, ok := struc.Type().Underlying().(*types.Struct); ok {
		switch vd, kind := fr.get(struc); kind {
		case Struct:
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s).[%d] of type %s\n", cyan(reg(field)), struc.Name(), vd.String(), index, field.Type().String())
			if fr.env.structs[vd][index] == nil { // First use
				vdField := utils.NewDef(field)
				fr.env.structs[vd][index] = vdField
				fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as field definition\n", field.Name())
				// If field is struct
				if fieldType, ok := field.Type().Underlying().(*types.Struct); ok {
					fr.env.structs[vdField] = make(Fields, fieldType.NumFields())
					fmt.Fprintf(os.Stderr, "     ^ field %s is a struct (allocating)\n", field.Name())
				}
			} else if fr.env.structs[vd][index].Var != field { // Previously defined
				fmt.Fprintf(os.Stderr, "     ^ field %s previously defined as %s\n", field.Name(), reg(fr.env.structs[vd][index].Var))
			} // else Accessed before (and unchanged)
			fr.locals[field] = fr.env.structs[vd][index]

		case LocalStruct:
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s).[%d] (local) of type %s\n", cyan(reg(field)), struc.Name(), vd.String(), index, field.Type().String())
			if fr.structs[vd][index] == nil { // First use
				vdField := utils.NewDef(field)
				fr.structs[vd][index] = vdField
				fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as field definition\n", field.Name())
				// If field is struct
				if fieldType, ok := field.Type().Underlying().(*types.Struct); ok {
					fr.structs[vdField] = make(Fields, fieldType.NumFields())
					fmt.Fprintf(os.Stderr, "     ^ field %s is a struct (allocating locally)\n", field.Name())
				}
			} else if fr.structs[vd][index].Var != field { // Previously defined
				fmt.Fprintf(os.Stderr, "     ^ field %s previously defined as %s\n", field.Name(), reg(fr.structs[vd][index].Var))
			} // else Accessed before (and unchanged)
			fr.locals[field] = fr.structs[vd][index]

		case Nothing, Untracked:
			// Nothing: Very likely external struct.
			// Untracked: likely branches of return values (e.g. returning nil)
			fmt.Fprintf(os.Stderr, "   %s = %s(=%s).[%d] (external) of type %s\n", cyan(reg(field)), inst.X.Name(), vd.String(), index, field.Type().String())
			vd := utils.NewDef(struc) // New external struct
			fr.locals[struc] = vd
			fr.env.structs[vd] = make(Fields, stype.NumFields())
			vdField := utils.NewDef(field) // New external field
			fr.env.structs[vd][index] = vdField
			fr.locals[field] = vdField
			fmt.Fprintf(os.Stderr, "     ^ accessed for the first time: use %s as field definition of type %s\n", field.Name(), inst.Type().Underlying().String())
			// If field is struct
			if fieldType, ok := field.Type().Underlying().(*types.Struct); ok {
				fr.env.structs[vdField] = make(Fields, fieldType.NumFields())
				fmt.Fprintf(os.Stderr, "     ^ field %s previously defined as %s\n", field.Name(), reg(fr.env.structs[vd][index].Var))
			}

		default:
			panic(fmt.Sprintf("Field: Cannot access non-struct %s %T %d", reg(struc), struc.Type(), kind))
		}
	} else {
		panic(fmt.Sprintf("Field: Cannot access field - %s not a struct\n", reg(struc)))
	}
}
Example #12
0
func visitMakeSlice(inst *ssa.MakeSlice, fr *frame) {
	fr.env.arrays[utils.NewDef(inst)] = make(Elems)
}