Documentation ¶
Index ¶
- Constants
- Variables
- func EnumeratePathWithGoroutineHead(head Node, enumeConfigure *EnumeConfigure) map[string]*LocalPath
- func GenDMap(vecChan []*instinfo.Channel, vecLocker []*instinfo.Locker) (DMap map[interface{}]*DPrim)
- func TypeMsgForNode(node Node) string
- func Walk(node Node, cfg *WalkConfig)
- func WalkGenEdge(node Node, mapHeadOfFn map[Node]bool)
- type AllConstraints
- type Call
- func (n *Call) CallCtx() *CallCtx
- func (n *Call) Context() *CallCtx
- func (n *Call) GetId() int
- func (n *Call) GetString() string
- func (n *Call) In() []*NodeEdge
- func (n *Call) InAdd(e *NodeEdge)
- func (n *Call) InOverWrite(e *NodeEdge)
- func (n *Call) Instruction() ssa.Instruction
- func (n *Call) Out() []*NodeEdge
- func (n *Call) OutAdd(e *NodeEdge)
- func (n *Call) OutOverWrite(e *NodeEdge)
- func (n *Call) Parent() *SyncGraph
- func (n *Call) SetId(id int)
- func (n *Call) SetString(str string)
- type CallCtx
- type ChainsToReachOp
- type ChanMake
- type ChanOp
- type DEdge
- type DPrim
- type End
- func (n *End) CallCtx() *CallCtx
- func (n *End) Context() *CallCtx
- func (n *End) GetId() int
- func (n *End) GetString() string
- func (n *End) In() []*NodeEdge
- func (n *End) InAdd(e *NodeEdge)
- func (n *End) InOverWrite(e *NodeEdge)
- func (n *End) Instruction() ssa.Instruction
- func (n *End) Out() []*NodeEdge
- func (n *End) OutAdd(e *NodeEdge)
- func (n *End) OutOverWrite(e *NodeEdge)
- func (n *End) Parent() *SyncGraph
- func (n *End) SetId(id int)
- func (n *End) SetString(str string)
- type EnumeConfigure
- type Go
- func (n *Go) CallCtx() *CallCtx
- func (n *Go) Context() *CallCtx
- func (n *Go) GetId() int
- func (n *Go) GetString() string
- func (n *Go) In() []*NodeEdge
- func (n *Go) InAdd(e *NodeEdge)
- func (n *Go) InOverWrite(e *NodeEdge)
- func (n *Go) Instruction() ssa.Instruction
- func (n *Go) Out() []*NodeEdge
- func (n *Go) OutAdd(e *NodeEdge)
- func (n *Go) OutOverWrite(e *NodeEdge)
- func (n *Go) Parent() *SyncGraph
- func (n *Go) SetId(id int)
- func (n *Go) SetString(str string)
- type Goroutine
- type If
- func (n *If) CallCtx() *CallCtx
- func (n *If) Context() *CallCtx
- func (n *If) GetId() int
- func (n *If) GetString() string
- func (n *If) In() []*NodeEdge
- func (n *If) InAdd(e *NodeEdge)
- func (n *If) InOverWrite(e *NodeEdge)
- func (n *If) Instruction() ssa.Instruction
- func (n *If) Out() []*NodeEdge
- func (n *If) OutAdd(e *NodeEdge)
- func (n *If) OutOverWrite(e *NodeEdge)
- func (n *If) Parent() *SyncGraph
- func (n *If) SetId(id int)
- func (n *If) SetString(str string)
- type InstCtxKey
- type Jump
- func (n *Jump) CallCtx() *CallCtx
- func (n *Jump) Context() *CallCtx
- func (n *Jump) GetId() int
- func (n *Jump) GetString() string
- func (n *Jump) In() []*NodeEdge
- func (n *Jump) InAdd(e *NodeEdge)
- func (n *Jump) InOverWrite(e *NodeEdge)
- func (n *Jump) Instruction() ssa.Instruction
- func (n *Jump) Out() []*NodeEdge
- func (n *Jump) OutAdd(e *NodeEdge)
- func (n *Jump) OutOverWrite(e *NodeEdge)
- func (n *Jump) Parent() *SyncGraph
- func (n *Jump) SetId(id int)
- func (n *Jump) SetString(str string)
- type Kill
- func (n *Kill) CallCtx() *CallCtx
- func (n *Kill) Context() *CallCtx
- func (n *Kill) GetId() int
- func (n *Kill) GetString() string
- func (n *Kill) In() []*NodeEdge
- func (n *Kill) InAdd(e *NodeEdge)
- func (n *Kill) InOverWrite(e *NodeEdge)
- func (n *Kill) Instruction() ssa.Instruction
- func (n *Kill) Out() []*NodeEdge
- func (n *Kill) OutAdd(e *NodeEdge)
- func (n *Kill) OutOverWrite(e *NodeEdge)
- func (n *Kill) Parent() *SyncGraph
- func (n *Kill) SetId(id int)
- func (n *Kill) SetString(str string)
- type LocalPath
- type LockerOp
- type Node
- type NodeEdge
- type NormalInst
- func (n *NormalInst) CallCtx() *CallCtx
- func (n *NormalInst) Context() *CallCtx
- func (n *NormalInst) GetId() int
- func (n *NormalInst) GetString() string
- func (n *NormalInst) In() []*NodeEdge
- func (n *NormalInst) InAdd(e *NodeEdge)
- func (n *NormalInst) InOverWrite(e *NodeEdge)
- func (n *NormalInst) Instruction() ssa.Instruction
- func (n *NormalInst) Out() []*NodeEdge
- func (n *NormalInst) OutAdd(e *NodeEdge)
- func (n *NormalInst) OutOverWrite(e *NodeEdge)
- func (n *NormalInst) Parent() *SyncGraph
- func (n *NormalInst) SetId(id int)
- func (n *NormalInst) SetString(str string)
- type Overwrite
- func (n *Overwrite) CallCtx() *CallCtx
- func (n *Overwrite) Context() *CallCtx
- func (n *Overwrite) GetId() int
- func (n *Overwrite) GetString() string
- func (n *Overwrite) In() []*NodeEdge
- func (n *Overwrite) InAdd(e *NodeEdge)
- func (n *Overwrite) InOverWrite(e *NodeEdge)
- func (n *Overwrite) Instruction() ssa.Instruction
- func (n *Overwrite) Out() []*NodeEdge
- func (n *Overwrite) OutAdd(e *NodeEdge)
- func (n *Overwrite) OutOverWrite(e *NodeEdge)
- func (n *Overwrite) Parent() *SyncGraph
- func (n *Overwrite) SetId(id int)
- func (n *Overwrite) SetString(str string)
- type PNode
- type PPath
- type Return
- func (n *Return) CallCtx() *CallCtx
- func (n *Return) Context() *CallCtx
- func (n *Return) GetId() int
- func (n *Return) GetString() string
- func (n *Return) In() []*NodeEdge
- func (n *Return) InAdd(e *NodeEdge)
- func (n *Return) InOverWrite(e *NodeEdge)
- func (n *Return) Instruction() ssa.Instruction
- func (n *Return) Out() []*NodeEdge
- func (n *Return) OutAdd(e *NodeEdge)
- func (n *Return) OutOverWrite(e *NodeEdge)
- func (n *Return) Parent() *SyncGraph
- func (n *Return) SetId(id int)
- func (n *Return) SetString(str string)
- type Select
- func (n *Select) CallCtx() *CallCtx
- func (n *Select) Context() *CallCtx
- func (n *Select) GetId() int
- func (n *Select) GetString() string
- func (n *Select) In() []*NodeEdge
- func (n *Select) InAdd(e *NodeEdge)
- func (n *Select) InOverWrite(e *NodeEdge)
- func (n *Select) Instruction() ssa.Instruction
- func (n *Select) Out() []*NodeEdge
- func (n *Select) OutAdd(e *NodeEdge)
- func (n *Select) OutOverWrite(e *NodeEdge)
- func (n *Select) Parent() *SyncGraph
- func (n *Select) SetId(id int)
- func (n *Select) SetString(str string)
- type SelectCase
- type Status
- type SyncGraph
- func (g *SyncGraph) BuildNodeInOut()
- func (g SyncGraph) CheckWithZ3() bool
- func (g *SyncGraph) ComputeFnOnOpPath()
- func (g *SyncGraph) EnumerateAllPathCombinations()
- func (g *SyncGraph) NewCtx(goroutine *Goroutine, head *ssa.Function) *CallCtx
- func (g *SyncGraph) NewGoroutine(headFn *ssa.Function) *Goroutine
- func (g *SyncGraph) OptimizeBB_V1()
- func (g *SyncGraph) OptimizeBB_V2()
- func (g *SyncGraph) PrintAllPathCombinations()
- func (g *SyncGraph) PrintGraphAllNodesType()
- func (g *SyncGraph) SetEnumCfg(unfold int, flagIgnoreNoSyncFn bool, flagIgnoreNormal bool)
- type SyncOp
- type Task
- type TaskPrimitive
- type TupleAPIFunc
- type Unfinish
- type WalkConfig
- type Z3Cfg
- type Z3System
- type ZGoroutine
- type ZNode
- type ZNodeBRecv
- type ZNodeBSend
- type ZNodeBasic
- type ZNodeClose
- type ZNodeNbRecv
- type ZNodeNbSend
- type ZSendRecvPair
Constants ¶
const ( WARNING_met_chan_var_buf = "WARNING_met_chan_var_buf" WARNING_met_nil_chan = "WARNING_met_nil_chan" )
const Done = "Done"
const EndDefer = 2
const In_progress = "In_progress"
const MaxRecursive = 1
const NotInteresting = 0
const (
ZMode_GL = 0 // In this mode, only nodes of type *Go and SyncOp will be recorded
)
Variables ¶
var ( Z3One z3.Int Z3Zero z3.Int )
var DependMap map[interface{}]*DPrim
var MapMeetCircularDependPrims map[interface{}]struct{}
var PrintedBlockPosStr map[string]struct{} = make(map[string]struct{})
Functions ¶
func EnumeratePathWithGoroutineHead ¶
func EnumeratePathWithGoroutineHead(head Node, enumeConfigure *EnumeConfigure) map[string]*LocalPath
Enumerate all possible paths of the given goroutine. If goroutine starts at function A, A has 3 paths, and in 2 paths A calls B, and B has 4 paths. Then we should return (1 + 2 * 4) paths, where B's path is inserted into A's path.
func TypeMsgForNode ¶
func Walk ¶
func Walk(node Node, cfg *WalkConfig)
func WalkGenEdge ¶
Walk through node and its succs, to update node.In, node.Out and node.Num_Paths. Don't walk through backedge. Don't walk into callee, just append them to head_of_fn
Types ¶
type AllConstraints ¶
type Call ¶
type Call struct { Inst ssa.CallInstruction Calling map[*callgraph.Edge]Node NextLocal Node // contains filtered or unexported fields }
func (*Call) InOverWrite ¶
func (n *Call) InOverWrite(e *NodeEdge)
func (*Call) Instruction ¶
func (n *Call) Instruction() ssa.Instruction
func (*Call) OutOverWrite ¶
func (n *Call) OutOverWrite(e *NodeEdge)
type ChainsToReachOp ¶
type ChainsToReachOp struct { Op interface{} Inst ssa.Instruction Chains []*path.EdgeChain VecBoolIsChainFinished []bool Finished bool }
Operation of a primitive, context-sensitive E.g. func send() { ch <- 1}
func main() { send() // Line 3 send() // Line 4 }
There are 2 operations, called by two paths: main() ---Line3---> send(); main() ---Line4---> send()
type ChanMake ¶
type ChanMake struct { Inst *ssa.MakeChan Channel *instinfo.Channel MakeOp instinfo.ChanOp Next Node // contains filtered or unexported fields }
func (*ChanMake) MapAliasOp ¶
type ChanOp ¶
type ChanOp struct { Channel *instinfo.Channel Op instinfo.ChanOp Next Node // contains filtered or unexported fields }
Can be send/receive/close. Note that send and receive here must not be in select
func (*ChanOp) MapAliasOp ¶
type DPrim ¶
type DPrim struct { Primitive interface{} // *instinfo.Channel or *instinfo.Locker Out []*DEdge In []*DEdge Circular_depend []*DPrim // contains filtered or unexported fields }
func (*DPrim) AddCircularDepend ¶
func (*DPrim) AddOutEdge ¶
func (*DPrim) IsCircularDepend ¶
type End ¶
type End struct { Inst ssa.Instruction Reason int // contains filtered or unexported fields }
func (*End) InOverWrite ¶
func (n *End) InOverWrite(e *NodeEdge)
func (*End) Instruction ¶
func (n *End) Instruction() ssa.Instruction
func (*End) OutOverWrite ¶
func (n *End) OutOverWrite(e *NodeEdge)
type EnumeConfigure ¶
type Go ¶
type Go struct { Inst *ssa.Go MapCreateGoroutines map[*callgraph.Edge]*Goroutine MapCreateNodes map[*callgraph.Edge]Node NextLocal Node // contains filtered or unexported fields }
func (*Go) InOverWrite ¶
func (n *Go) InOverWrite(e *NodeEdge)
func (*Go) Instruction ¶
func (n *Go) Instruction() ssa.Instruction
func (*Go) OutOverWrite ¶
func (n *Go) OutOverWrite(e *NodeEdge)
type If ¶
type If struct { Inst *ssa.If Cond ssa.Value Then Node Else Node BoolIsThenBackedge bool BoolIsElseBackedge bool // contains filtered or unexported fields }
func (*If) InOverWrite ¶
func (n *If) InOverWrite(e *NodeEdge)
func (*If) Instruction ¶
func (n *If) Instruction() ssa.Instruction
func (*If) OutOverWrite ¶
func (n *If) OutOverWrite(e *NodeEdge)
type InstCtxKey ¶
type InstCtxKey struct { Inst ssa.Instruction Ctx *CallCtx }
type Jump ¶
type Jump struct { Inst *ssa.Jump Next Node BoolIsNextexist bool BoolIsBackedge bool // contains filtered or unexported fields }
func (*Jump) InOverWrite ¶
func (n *Jump) InOverWrite(e *NodeEdge)
func (*Jump) Instruction ¶
func (n *Jump) Instruction() ssa.Instruction
func (*Jump) OutOverWrite ¶
func (n *Jump) OutOverWrite(e *NodeEdge)
type Kill ¶
type Kill struct { Inst ssa.Instruction // can be *ssa.Panic or *ssa.Call (callee is t.Fatal/Fatalf/...) BoolIsPanic bool BoolIsFatal bool Next Node // contains filtered or unexported fields }
func (*Kill) InOverWrite ¶
func (n *Kill) InOverWrite(e *NodeEdge)
func (*Kill) Instruction ¶
func (n *Kill) Instruction() ssa.Instruction
func (*Kill) OutOverWrite ¶
func (n *Kill) OutOverWrite(e *NodeEdge)
type LocalPath ¶
func NewEmptyPath ¶
func NewEmptyPath() *LocalPath
type LockerOp ¶
type LockerOp struct { Locker *instinfo.Locker Op instinfo.LockerOp Next Node // contains filtered or unexported fields }
func (*LockerOp) MapAliasOp ¶
type Node ¶
type Node interface { Context() *CallCtx Instruction() ssa.Instruction Parent() *SyncGraph In() []*NodeEdge Out() []*NodeEdge CallCtx() *CallCtx InAdd(*NodeEdge) OutAdd(*NodeEdge) InOverWrite(*NodeEdge) OutOverWrite(*NodeEdge) SetId(int) GetId() int SetString(string) GetString() string }
func ProcessInstGetNode ¶
func ProcessInstGetNode(targetInst ssa.Instruction, ctx *CallCtx) Node
type NormalInst ¶
type NormalInst struct { Inst ssa.Instruction Next Node // contains filtered or unexported fields }
func (*NormalInst) InOverWrite ¶
func (n *NormalInst) InOverWrite(e *NodeEdge)
func (*NormalInst) Instruction ¶
func (n *NormalInst) Instruction() ssa.Instruction
func (*NormalInst) OutOverWrite ¶
func (n *NormalInst) OutOverWrite(e *NodeEdge)
type Overwrite ¶
type Overwrite struct { Inst *ssa.MakeChan Chan *instinfo.Channel // contains filtered or unexported fields }
func (*Overwrite) InOverWrite ¶
func (n *Overwrite) InOverWrite(e *NodeEdge)
func (*Overwrite) Instruction ¶
func (n *Overwrite) Instruction() ssa.Instruction
func (*Overwrite) OutOverWrite ¶
func (n *Overwrite) OutOverWrite(e *NodeEdge)
type PPath ¶
type PPath struct { Path []*PNode // contains filtered or unexported fields }
func (*PPath) PrintPPath ¶
func (p *PPath) PrintPPath()
func (PPath) SetAllReached ¶
func (p PPath) SetAllReached()
func (PPath) SetBlockAt ¶
type Return ¶
type Return struct { Inst ssa.Instruction BoolIsEndOfGoroutine bool Caller Node // contains filtered or unexported fields }
func (*Return) InOverWrite ¶
func (n *Return) InOverWrite(e *NodeEdge)
func (*Return) Instruction ¶
func (n *Return) Instruction() ssa.Instruction
func (*Return) OutOverWrite ¶
func (n *Return) OutOverWrite(e *NodeEdge)
type Select ¶
type Select struct { Inst *ssa.Select Cases map[int]*SelectCase BoolHasDefault bool DefaultCase *SelectCase // contains filtered or unexported fields }
func (*Select) InOverWrite ¶
func (n *Select) InOverWrite(e *NodeEdge)
func (*Select) Instruction ¶
func (n *Select) Instruction() ssa.Instruction
func (*Select) OutOverWrite ¶
func (n *Select) OutOverWrite(e *NodeEdge)
type SelectCase ¶
type SelectCase struct { Channel *instinfo.Channel Op instinfo.ChanOp Index int // -1 if this is default BoolIsDefault bool Next Node BoolIsBackedge bool Select *Select // contains filtered or unexported fields }
func (*SelectCase) MapAliasOp ¶
func (*SelectCase) Operation ¶
func (a *SelectCase) Operation() interface{}
type SyncGraph ¶
type SyncGraph struct { // Prepare MainGoroutine *Goroutine // MainGoroutine is the Goroutine that is both a head and it contains the MakeChan operation HeadGoroutines []*Goroutine // HeadGoroutines are the starting Goroutines that don't have creator in the graph Goroutines []*Goroutine MapFirstNodeOfFn map[Node]struct{} // All Nodes that are the first Node of a function in SyncGraph Task *Task // Build MapInstCtxKey2Node map[InstCtxKey]Node // Two kinds of Node is not in this map: SelectCase, rundefer's Nodes Select2Case map[*Select][]*SelectCase MapInstCtxKey2Defer map[InstCtxKey][]Node NodeStatus map[Node]*Status MapPrim2VecSyncOp map[interface{}][]SyncOp Visited []*path.EdgeChain Worklist []*Unfinish MapFnOnOpPath map[*ssa.Function]struct{} // a map of functions that are on a path to reach a sync operation // Enumerate path PathCombinations []*pathCombination EnumerateCfg *EnumeConfigure }
func BuildGraph ¶
func (*SyncGraph) BuildNodeInOut ¶
func (g *SyncGraph) BuildNodeInOut()
This function updates Node.Num_paths and NodeEdge.AddValue
func (SyncGraph) CheckWithZ3 ¶
func (*SyncGraph) ComputeFnOnOpPath ¶
func (g *SyncGraph) ComputeFnOnOpPath()
Update g.MapFnOnOpPath.
func (*SyncGraph) EnumerateAllPathCombinations ¶
func (g *SyncGraph) EnumerateAllPathCombinations()
Enumerate all path combinations. A pathCombination: a slice of {one goroutine and one path}. Path can be Not_execute, meaning empty path. Note that the number of goroutine_path may be greater than len(g.Goroutines). For example, if a *Go is visited 3 times, we create 3 goroutines
func (*SyncGraph) OptimizeBB_V1 ¶
func (g *SyncGraph) OptimizeBB_V1()
If BB X post-dominates BB Y, and all BBs on any path from Y to X don't contain important Nodes, and X and Y are in the same layer of loop, we can ignore these BBs between X and Y. We will link Y and X directly (let the first Node of X be the next Node of the last Node of Y) What are important Nodes: Call to functions on MapFnOnOpPath/ operation of dependent primitives/ Return/ Kill/ End
func (*SyncGraph) OptimizeBB_V2 ¶
func (g *SyncGraph) OptimizeBB_V2()
func (*SyncGraph) PrintAllPathCombinations ¶
func (g *SyncGraph) PrintAllPathCombinations()
func (*SyncGraph) PrintGraphAllNodesType ¶
func (g *SyncGraph) PrintGraphAllNodesType()
Walk the whole graph, print type of node on path
type Task ¶
type Task struct { VecTaskPrimitive []*TaskPrimitive // VecTaskPrimitive means these primitives satify: all its operations are in the graph MapValue2TaskPrimitive map[interface{}]*TaskPrimitive MapLCARoot2Op map[*ssa.Function][]*ChainsToReachOp BoolFinished bool BoolGiveupIfCallgraphInaccurate bool }
func (*Task) IsPrimATarget ¶
A primitive is a target when it is in Task.VecTaskPrimitive
func (*Task) Step1AddPrim ¶
func (t *Task) Step1AddPrim(newP interface{})
After this function, a new primitive is added to the task, but TaskPrimitive.Ops are not completed. Need to run Step2CompletePrims after adding all primitives
func (*Task) Step2CompletePrims ¶
After adding all primitives that we want, complete everything in each TaskPrimitive.Ops
func (*Task) WantedList ¶
type TaskPrimitive ¶
type TaskPrimitive struct { Primitive interface{} Ops map[interface{}]*ChainsToReachOp Finished bool Task *Task }
func (*TaskPrimitive) CompleteOps ¶
func (tp *TaskPrimitive) CompleteOps(LCA2paths map[*ssa.Function][]*path.EdgeChain)
Generates tp.Ops, and find all callchains from head to an operation
type TupleAPIFunc ¶
type Unfinish ¶
type Unfinish struct { UnfinishedFn *ssa.Function Unfinished Node IsGo bool Site *callgraph.Edge // Site.Callee has at least 1 bb, and this bb has at least 1 inst Dir bool // true if Call/Go (from caller to callee), false if Return (from callee to caller) Ctx *CallCtx // a new CallCtx used for the Site. This can be directly used }
type WalkConfig ¶
type Z3System ¶
type Z3System struct { Constraints *AllConstraints Warnings []string Config *Z3Cfg Z3Ctx *z3.Context Solver *z3.Solver // contains filtered or unexported fields }
func NewZ3ForGl ¶
func NewZ3ForGl() *Z3System
func (*Z3System) Assert ¶
func (z *Z3System) Assert()
Assert puts assertions stored in z.Contraints into the solver
func (*Z3System) Prepare ¶
Initialize z.vecZGoroutines; generate ZNodes on each ZGoroutine from p_paths; generate constraints
func (*Z3System) PrintAssert ¶
func (z *Z3System) PrintAssert()
type ZGoroutine ¶
type ZNodeBRecv ¶
type ZNodeBRecv struct { Buffer z3.Int Other_SR []ZNode Closes []*ZNodeClose From_close z3.Bool IsUnlock bool ZNodeBasic }
type ZNodeBSend ¶
type ZNodeBSend struct { Buffer z3.Int Other_SR []ZNode IsLock bool ZNodeBasic }
type ZNodeBasic ¶
type ZNodeBasic struct { Id int ZGoroutine *ZGoroutine PtrPNode *PNode Order z3.Int // contains filtered or unexported fields }
func (*ZNodeBasic) Goroutine ¶
func (b *ZNodeBasic) Goroutine() *ZGoroutine
func (*ZNodeBasic) ID ¶
func (b *ZNodeBasic) ID() int
func (*ZNodeBasic) IsBlocking ¶
func (b *ZNodeBasic) IsBlocking() bool
func (*ZNodeBasic) PNode ¶
func (b *ZNodeBasic) PNode() *PNode
func (*ZNodeBasic) SetId ¶
func (b *ZNodeBasic) SetId(id int)
func (*ZNodeBasic) TraceOrder ¶
func (b *ZNodeBasic) TraceOrder() z3.Int
func (*ZNodeBasic) UpdateOrder ¶
func (b *ZNodeBasic) UpdateOrder(i z3.Int)
type ZNodeClose ¶
type ZNodeClose struct {
ZNodeBasic
}
type ZNodeNbRecv ¶
type ZNodeNbRecv struct { ZNodeBasic Pairs []*ZSendRecvPair Closes []*ZNodeClose FromClose z3.Bool }
type ZNodeNbSend ¶
type ZNodeNbSend struct { ZNodeBasic Closes []*ZNodeClose Pairs []*ZSendRecvPair }
type ZSendRecvPair ¶
type ZSendRecvPair struct { Send *ZNodeNbSend Recv *ZNodeNbRecv // contains filtered or unexported fields }