From 8cd2b8938956bc11d301321800b11412443f800b Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 29 Feb 2024 14:22:02 +0100 Subject: [PATCH 1/3] Feat: Inspection framework for easier debugging --- pkg/core/inspection/inspected_object.go | 135 ++++++++++++++++++++++++ pkg/core/inspection/session.go | 15 +++ pkg/core/inspection/utils.go | 29 +++++ pkg/protocol/inspection.go | 59 +++++++++++ pkg/tests/protocol_eviction_test.go | 3 + 5 files changed, 241 insertions(+) create mode 100644 pkg/core/inspection/inspected_object.go create mode 100644 pkg/core/inspection/session.go create mode 100644 pkg/core/inspection/utils.go create mode 100644 pkg/protocol/inspection.go diff --git a/pkg/core/inspection/inspected_object.go b/pkg/core/inspection/inspected_object.go new file mode 100644 index 000000000..959ede39a --- /dev/null +++ b/pkg/core/inspection/inspected_object.go @@ -0,0 +1,135 @@ +package inspection + +import ( + "fmt" + "regexp" + "strings" + "unicode" + + "github.com/iotaledger/hive.go/ds/orderedmap" + "github.com/iotaledger/hive.go/lo" + "github.com/iotaledger/hive.go/stringify" +) + +// InspectedObject is an interface that is used to represent an object that has been inspected. +type InspectedObject interface { + // Type returns the type of the object. + Type() string + + // InstanceID returns the instance identifier of the object. + InstanceID() string + + // AddChild adds a child object to the inspected object. + AddChild(name string, instance any, inspectManually ...func(object InspectedObject)) + + // String returns a string representation of the inspected object. + String() string +} + +// NewInspectedObject creates a new inspected object from the given instance and inspect function. +func NewInspectedObject(instance any, inspect func(InspectedObject), session ...Session) InspectedObject { + o := &inspectedObject{ + instance: instance, + childObjects: orderedmap.New[string, InspectedObject](), + session: lo.First(session, make(Session)), + } + + if o.inspected = o.session.FirstOccurrence(instance); o.inspected { + inspect(o) + } + + return o +} + +// inspectedObject is an implementation of the InspectedObject interface. +type inspectedObject struct { + instance any + childObjects *orderedmap.OrderedMap[string, InspectedObject] + session Session + inspected bool +} + +// Type returns the type of the object. +func (i *inspectedObject) Type() string { + runes := []rune(regexp.MustCompile(`[^.]+\.([^[]+).*`).ReplaceAllString(fmt.Sprintf("%T", i.instance), "${1}")) + runes[0] = unicode.ToUpper(runes[0]) + + return string(runes) +} + +// InstanceID returns the instance identifier of the object. +func (i *inspectedObject) InstanceID() string { + type named interface { + LogName() string + } + + if namedInstance, isNamed := i.instance.(named); isNamed { + return namedInstance.LogName() + } + + return fmt.Sprintf("%p", i.instance) +} + +// AddChild adds a child object to the inspected object. +func (i *inspectedObject) AddChild(name string, instance any, inspectManually ...func(object InspectedObject)) { + type inspectable interface { + Inspect(session ...Session) InspectedObject + } + + if stringify.IsInterfaceNil(instance) { + i.childObjects.Set(name, nil) + } else if len(inspectManually) >= 1 { + i.childObjects.Set(name, NewInspectedObject(instance, inspectManually[0], i.session)) + } else if inspectableInstance, isInspectable := instance.(inspectable); isInspectable { + i.childObjects.Set(name, inspectableInstance.Inspect(i.session)) + } else { + panic("added object does not have an 'Inspect(session ...Session) InspectedObject' method - please provide a manual inspection function") + } +} + +// String returns a string representation of the inspected object. +func (i *inspectedObject) String() string { + return i.indentedString(0) +} + +// indentedString returns a string representation of the inspected object with the given indentation. +func (i *inspectedObject) indentedString(indent int) string { + if i == nil { + return "nil" + } + + var typeString string + if instanceID, typeName := i.InstanceID(), i.Type(); typeName == instanceID { + typeString = typeName + } else { + typeString = typeName + "(" + instanceID + ")" + } + + if !i.inspected { + return typeString + " {...}" + } + + childOutputs := make([]string, 0) + i.childObjects.ForEach(func(key string, value InspectedObject) bool { + if value == nil { + childOutputs = append(childOutputs, strings.Repeat(" ", (indent+1)*indentationSize)+key+": nil") + } else if objectValue, ok := value.(*inspectedObject); !ok { + panic("this should never happen but linter requires type cast check") + } else if value.Type() == key || value.InstanceID() == key { + childOutputs = append(childOutputs, strings.Repeat(" ", (indent+1)*indentationSize)+objectValue.indentedString(indent+1)) + } else { + childOutputs = append(childOutputs, strings.Repeat(" ", (indent+1)*indentationSize)+key+": "+objectValue.indentedString(indent+1)) + } + + return true + }) + + if len(childOutputs) == 0 { + return typeString + " {}" + } + + return typeString + " {\n" + strings.Join(childOutputs, ",\n") + "\n" + strings.Repeat(" ", (indent)*indentationSize) + "}" +} + +// indentationSize defines the size of the indentation. +const indentationSize = 2 diff --git a/pkg/core/inspection/session.go b/pkg/core/inspection/session.go new file mode 100644 index 000000000..be03ae4f8 --- /dev/null +++ b/pkg/core/inspection/session.go @@ -0,0 +1,15 @@ +package inspection + +// Session is used to track instances of objects that have already been inspected. +type Session map[any]bool + +// FirstOccurrence checks if the given instance has already been inspected. +func (s Session) FirstOccurrence(instance any) bool { + if s[instance] { + return false + } + + s[instance] = true + + return true +} diff --git a/pkg/core/inspection/utils.go b/pkg/core/inspection/utils.go new file mode 100644 index 000000000..08fe8868a --- /dev/null +++ b/pkg/core/inspection/utils.go @@ -0,0 +1,29 @@ +package inspection + +type IterableSet[T comparable] interface { + ForEach(consumer func(element T) error) error +} + +func InspectSet[T comparable](setToInspect IterableSet[T], inspect func(inspectedSet InspectedObject, element T)) func(inspectedSet InspectedObject) { + return func(inspectedSet InspectedObject) { + _ = setToInspect.ForEach(func(key T) error { + inspect(inspectedSet, key) + + return nil + }) + } +} + +type IterableMap[K comparable, V any] interface { + ForEach(consumer func(key K, value V) bool) +} + +func InspectMap[K comparable, V any](mapToInspect IterableMap[K, V], inspect func(inspectedMap InspectedObject, key K, value V)) func(inspectedMap InspectedObject) { + return func(inspectedMap InspectedObject) { + mapToInspect.ForEach(func(key K, value V) bool { + inspect(inspectedMap, key, value) + + return true + }) + } +} diff --git a/pkg/protocol/inspection.go b/pkg/protocol/inspection.go new file mode 100644 index 000000000..f2c89554f --- /dev/null +++ b/pkg/protocol/inspection.go @@ -0,0 +1,59 @@ +package protocol + +import ( + "github.com/iotaledger/iota-core/pkg/core/inspection" + "github.com/iotaledger/iota-core/pkg/core/promise" + iotago "github.com/iotaledger/iota.go/v4" +) + +func (p *Protocol) Inspect(session ...inspection.Session) inspection.InspectedObject { + return inspection.NewInspectedObject(p, func(protocol inspection.InspectedObject) { + protocol.AddChild("Commitments", p.Commitments) + protocol.AddChild("Chains", p.Chains) + }, session...) +} + +func (c *Commitments) Inspect(session ...inspection.Session) inspection.InspectedObject { + return inspection.NewInspectedObject(c, func(commitments inspection.InspectedObject) { + commitments.AddChild("Set", c.Set, inspection.InspectSet[*Commitment](c.Set, func(inspectedSet inspection.InspectedObject, element *Commitment) { + inspectedSet.AddChild(element.LogName(), element) + })) + + commitments.AddChild("cachedRequests", c.cachedRequests, inspection.InspectMap(c.cachedRequests, func(cachedRequests inspection.InspectedObject, commitmentID iotago.CommitmentID, cachedRequest *promise.Promise[*Commitment]) { + cachedRequests.AddChild(commitmentID.String(), cachedRequest.Result()) + })) + }, session...) +} + +func (c *Commitment) Inspect(session ...inspection.Session) inspection.InspectedObject { + return inspection.NewInspectedObject(c, func(commitment inspection.InspectedObject) { + commitment.AddChild("Parent", c.Parent.Get()) + commitment.AddChild("MainChild", c.MainChild.Get()) + commitment.AddChild("Chain", c.Chain.Get()) + commitment.AddChild("Children", c.Children, inspection.InspectSet[*Commitment](c.Children, func(children inspection.InspectedObject, child *Commitment) { + children.AddChild(child.LogName(), child) + })) + }, session...) +} + +func (c *Chains) Inspect(session ...inspection.Session) inspection.InspectedObject { + return inspection.NewInspectedObject(c, func(chains inspection.InspectedObject) { + chains.AddChild("Main", c.Main.Get()) + chains.AddChild("HeaviestClaimedCandidate", c.HeaviestClaimedCandidate.Get()) + chains.AddChild("HeaviestAttestedCandidate", c.HeaviestAttestedCandidate.Get()) + chains.AddChild("HeaviestAttestedCandidate", c.HeaviestAttestedCandidate.Get()) + chains.AddChild("Set", c.Set, inspection.InspectSet[*Chain](c.Set, func(set inspection.InspectedObject, chain *Chain) { + set.AddChild(chain.LogName(), chain) + })) + }, session...) +} + +func (c *Chain) Inspect(session ...inspection.Session) inspection.InspectedObject { + return inspection.NewInspectedObject(c, func(chain inspection.InspectedObject) { + chain.AddChild("ForkingPoint", c.ForkingPoint.Get()) + chain.AddChild("ParentChain", c.ParentChain.Get()) + chain.AddChild("ChildChains", c.ChildChains, inspection.InspectSet[*Chain](c.ChildChains, func(childChains inspection.InspectedObject, chain *Chain) { + childChains.AddChild(chain.LogName(), chain) + })) + }, session...) +} diff --git a/pkg/tests/protocol_eviction_test.go b/pkg/tests/protocol_eviction_test.go index a5f0aac51..2088c75d5 100644 --- a/pkg/tests/protocol_eviction_test.go +++ b/pkg/tests/protocol_eviction_test.go @@ -1,6 +1,7 @@ package tests import ( + "fmt" "strconv" "testing" "time" @@ -187,4 +188,6 @@ func TestProtocol_Eviction(t *testing.T) { memConsumptionEnd := memsize.Scan(node.Protocol).Total require.Less(t, float64(lo.Return1(memConsumptionEnd)), 1.05*float64(memConsumptionStart), "memory consumption should not grow by more than 5%") + + fmt.Println(node.Protocol.Inspect()) } From 231596ca747d4d0f514a61f9fe498ff95a4106c5 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Thu, 29 Feb 2024 16:12:49 +0100 Subject: [PATCH 2/3] Feat: refactored framework a bit --- pkg/core/inspection/map_inspector.go | 17 ++++++++++++++++ pkg/core/inspection/set_inspector.go | 17 ++++++++++++++++ pkg/core/inspection/utils.go | 29 ---------------------------- pkg/protocol/inspection.go | 17 ++++++++++------ pkg/tests/protocol_eviction_test.go | 3 --- 5 files changed, 45 insertions(+), 38 deletions(-) create mode 100644 pkg/core/inspection/map_inspector.go create mode 100644 pkg/core/inspection/set_inspector.go delete mode 100644 pkg/core/inspection/utils.go diff --git a/pkg/core/inspection/map_inspector.go b/pkg/core/inspection/map_inspector.go new file mode 100644 index 000000000..876f080dd --- /dev/null +++ b/pkg/core/inspection/map_inspector.go @@ -0,0 +1,17 @@ +package inspection + +// MapInspector is a utility function that can be used to inspect a map of elements. +func MapInspector[K comparable, V any](mapToInspect mapInterface[K, V], inspect func(inspectedMap InspectedObject, key K, value V)) func(inspectedMap InspectedObject) { + return func(inspectedMap InspectedObject) { + mapToInspect.ForEach(func(key K, value V) bool { + inspect(inspectedMap, key, value) + + return true + }) + } +} + +// mapInterface is an interface that is used to iterate over a map of elements. +type mapInterface[K comparable, V any] interface { + ForEach(consumer func(key K, value V) bool) +} diff --git a/pkg/core/inspection/set_inspector.go b/pkg/core/inspection/set_inspector.go new file mode 100644 index 000000000..8dc29fed2 --- /dev/null +++ b/pkg/core/inspection/set_inspector.go @@ -0,0 +1,17 @@ +package inspection + +// SetInspector is a utility function that can be used to inspect a set of elements. +func SetInspector[T comparable](setToInspect setInterface[T], inspect func(inspectedSet InspectedObject, element T)) func(inspectedSet InspectedObject) { + return func(inspectedSet InspectedObject) { + _ = setToInspect.ForEach(func(key T) error { + inspect(inspectedSet, key) + + return nil + }) + } +} + +// setInterface is an interface that is used to iterate over a setInterface of elements. +type setInterface[T comparable] interface { + ForEach(consumer func(element T) error) error +} diff --git a/pkg/core/inspection/utils.go b/pkg/core/inspection/utils.go deleted file mode 100644 index 08fe8868a..000000000 --- a/pkg/core/inspection/utils.go +++ /dev/null @@ -1,29 +0,0 @@ -package inspection - -type IterableSet[T comparable] interface { - ForEach(consumer func(element T) error) error -} - -func InspectSet[T comparable](setToInspect IterableSet[T], inspect func(inspectedSet InspectedObject, element T)) func(inspectedSet InspectedObject) { - return func(inspectedSet InspectedObject) { - _ = setToInspect.ForEach(func(key T) error { - inspect(inspectedSet, key) - - return nil - }) - } -} - -type IterableMap[K comparable, V any] interface { - ForEach(consumer func(key K, value V) bool) -} - -func InspectMap[K comparable, V any](mapToInspect IterableMap[K, V], inspect func(inspectedMap InspectedObject, key K, value V)) func(inspectedMap InspectedObject) { - return func(inspectedMap InspectedObject) { - mapToInspect.ForEach(func(key K, value V) bool { - inspect(inspectedMap, key, value) - - return true - }) - } -} diff --git a/pkg/protocol/inspection.go b/pkg/protocol/inspection.go index f2c89554f..73cef9ba6 100644 --- a/pkg/protocol/inspection.go +++ b/pkg/protocol/inspection.go @@ -6,6 +6,7 @@ import ( iotago "github.com/iotaledger/iota.go/v4" ) +// Inspect inspects the protocol and its subcomponents. func (p *Protocol) Inspect(session ...inspection.Session) inspection.InspectedObject { return inspection.NewInspectedObject(p, func(protocol inspection.InspectedObject) { protocol.AddChild("Commitments", p.Commitments) @@ -13,46 +14,50 @@ func (p *Protocol) Inspect(session ...inspection.Session) inspection.InspectedOb }, session...) } +// Inspect inspects the Commitments and its subcomponents. func (c *Commitments) Inspect(session ...inspection.Session) inspection.InspectedObject { return inspection.NewInspectedObject(c, func(commitments inspection.InspectedObject) { - commitments.AddChild("Set", c.Set, inspection.InspectSet[*Commitment](c.Set, func(inspectedSet inspection.InspectedObject, element *Commitment) { + commitments.AddChild("Root", c.Root.Get()) + commitments.AddChild("Set", c.Set, inspection.SetInspector[*Commitment](c.Set, func(inspectedSet inspection.InspectedObject, element *Commitment) { inspectedSet.AddChild(element.LogName(), element) })) - - commitments.AddChild("cachedRequests", c.cachedRequests, inspection.InspectMap(c.cachedRequests, func(cachedRequests inspection.InspectedObject, commitmentID iotago.CommitmentID, cachedRequest *promise.Promise[*Commitment]) { + commitments.AddChild("cachedRequests", c.cachedRequests, inspection.MapInspector(c.cachedRequests, func(cachedRequests inspection.InspectedObject, commitmentID iotago.CommitmentID, cachedRequest *promise.Promise[*Commitment]) { cachedRequests.AddChild(commitmentID.String(), cachedRequest.Result()) })) }, session...) } +// Inspect inspects the Commitment and its subcomponents. func (c *Commitment) Inspect(session ...inspection.Session) inspection.InspectedObject { return inspection.NewInspectedObject(c, func(commitment inspection.InspectedObject) { commitment.AddChild("Parent", c.Parent.Get()) commitment.AddChild("MainChild", c.MainChild.Get()) commitment.AddChild("Chain", c.Chain.Get()) - commitment.AddChild("Children", c.Children, inspection.InspectSet[*Commitment](c.Children, func(children inspection.InspectedObject, child *Commitment) { + commitment.AddChild("Children", c.Children, inspection.SetInspector[*Commitment](c.Children, func(children inspection.InspectedObject, child *Commitment) { children.AddChild(child.LogName(), child) })) }, session...) } +// Inspect inspects the Chains and its subcomponents. func (c *Chains) Inspect(session ...inspection.Session) inspection.InspectedObject { return inspection.NewInspectedObject(c, func(chains inspection.InspectedObject) { chains.AddChild("Main", c.Main.Get()) chains.AddChild("HeaviestClaimedCandidate", c.HeaviestClaimedCandidate.Get()) chains.AddChild("HeaviestAttestedCandidate", c.HeaviestAttestedCandidate.Get()) chains.AddChild("HeaviestAttestedCandidate", c.HeaviestAttestedCandidate.Get()) - chains.AddChild("Set", c.Set, inspection.InspectSet[*Chain](c.Set, func(set inspection.InspectedObject, chain *Chain) { + chains.AddChild("Set", c.Set, inspection.SetInspector[*Chain](c.Set, func(set inspection.InspectedObject, chain *Chain) { set.AddChild(chain.LogName(), chain) })) }, session...) } +// Inspect inspects the Chain and its subcomponents. func (c *Chain) Inspect(session ...inspection.Session) inspection.InspectedObject { return inspection.NewInspectedObject(c, func(chain inspection.InspectedObject) { chain.AddChild("ForkingPoint", c.ForkingPoint.Get()) chain.AddChild("ParentChain", c.ParentChain.Get()) - chain.AddChild("ChildChains", c.ChildChains, inspection.InspectSet[*Chain](c.ChildChains, func(childChains inspection.InspectedObject, chain *Chain) { + chain.AddChild("ChildChains", c.ChildChains, inspection.SetInspector[*Chain](c.ChildChains, func(childChains inspection.InspectedObject, chain *Chain) { childChains.AddChild(chain.LogName(), chain) })) }, session...) diff --git a/pkg/tests/protocol_eviction_test.go b/pkg/tests/protocol_eviction_test.go index 2088c75d5..a5f0aac51 100644 --- a/pkg/tests/protocol_eviction_test.go +++ b/pkg/tests/protocol_eviction_test.go @@ -1,7 +1,6 @@ package tests import ( - "fmt" "strconv" "testing" "time" @@ -188,6 +187,4 @@ func TestProtocol_Eviction(t *testing.T) { memConsumptionEnd := memsize.Scan(node.Protocol).Total require.Less(t, float64(lo.Return1(memConsumptionEnd)), 1.05*float64(memConsumptionStart), "memory consumption should not grow by more than 5%") - - fmt.Println(node.Protocol.Inspect()) } From 1a802fbd09ffae2c93f695f7e972b79feeaa2705 Mon Sep 17 00:00:00 2001 From: Hans Moog <3293976+hmoog@users.noreply.github.com> Date: Fri, 1 Mar 2024 03:12:57 +0100 Subject: [PATCH 3/3] Feat: moved to hive.go --- go.mod | 2 +- go.sum | 2 + pkg/core/inspection/inspected_object.go | 135 ------------------------ pkg/core/inspection/map_inspector.go | 17 --- pkg/core/inspection/session.go | 15 --- pkg/core/inspection/set_inspector.go | 17 --- pkg/protocol/inspection.go | 46 ++++---- tools/gendoc/go.mod | 2 +- tools/gendoc/go.sum | 1 + 9 files changed, 28 insertions(+), 209 deletions(-) delete mode 100644 pkg/core/inspection/inspected_object.go delete mode 100644 pkg/core/inspection/map_inspector.go delete mode 100644 pkg/core/inspection/session.go delete mode 100644 pkg/core/inspection/set_inspector.go diff --git a/go.mod b/go.mod index a4809a6bf..224c1976b 100644 --- a/go.mod +++ b/go.mod @@ -21,7 +21,7 @@ require ( github.com/iotaledger/hive.go/kvstore v0.0.0-20240223142044-12ffcb37c413 github.com/iotaledger/hive.go/lo v0.0.0-20240223142044-12ffcb37c413 github.com/iotaledger/hive.go/log v0.0.0-20240223142044-12ffcb37c413 - github.com/iotaledger/hive.go/runtime v0.0.0-20240223142044-12ffcb37c413 + github.com/iotaledger/hive.go/runtime v0.0.0-20240301015124-eea9bb54a256 github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20240223142044-12ffcb37c413 github.com/iotaledger/hive.go/stringify v0.0.0-20240223142044-12ffcb37c413 github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240216141618-d7dfe94bdc1e diff --git a/go.sum b/go.sum index 3632f5de7..83dd05a45 100644 --- a/go.sum +++ b/go.sum @@ -301,6 +301,8 @@ github.com/iotaledger/hive.go/log v0.0.0-20240223142044-12ffcb37c413 h1:4xKFtHoO github.com/iotaledger/hive.go/log v0.0.0-20240223142044-12ffcb37c413/go.mod h1:H5tmswUbT3o5+QiM6UPtBv7VnPf+lJtlantgpp2lzUI= github.com/iotaledger/hive.go/runtime v0.0.0-20240223142044-12ffcb37c413 h1:7O3oSoXlwV5L3/+kt/a1MN3Kwb5oXaaSZaa+aabh7BM= github.com/iotaledger/hive.go/runtime v0.0.0-20240223142044-12ffcb37c413/go.mod h1:pueoYXud+HmTY2x9j/S6+ZX3M5ZyENFKPDrx3EtcwWs= +github.com/iotaledger/hive.go/runtime v0.0.0-20240301015124-eea9bb54a256 h1:x5h3XFa3Iuy7Fy3JjWUsqZym6WI0X9edRVhEoDQ9JTc= +github.com/iotaledger/hive.go/runtime v0.0.0-20240301015124-eea9bb54a256/go.mod h1:pueoYXud+HmTY2x9j/S6+ZX3M5ZyENFKPDrx3EtcwWs= github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20240223142044-12ffcb37c413 h1:tQW0K9Ogyfgm3V0wYhRPChOYgrADGlRWF6P4gMY8Zbg= github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20240223142044-12ffcb37c413/go.mod h1:NK05G4PxwZF1m4jGANJWLhAQ2hP1Nt0L8mgCTFLsSCw= github.com/iotaledger/hive.go/stringify v0.0.0-20240223142044-12ffcb37c413 h1:QCidFFczZH9NEJTWm0ZzZKMaQ0XUxYyU00dpaH+skqs= diff --git a/pkg/core/inspection/inspected_object.go b/pkg/core/inspection/inspected_object.go deleted file mode 100644 index 959ede39a..000000000 --- a/pkg/core/inspection/inspected_object.go +++ /dev/null @@ -1,135 +0,0 @@ -package inspection - -import ( - "fmt" - "regexp" - "strings" - "unicode" - - "github.com/iotaledger/hive.go/ds/orderedmap" - "github.com/iotaledger/hive.go/lo" - "github.com/iotaledger/hive.go/stringify" -) - -// InspectedObject is an interface that is used to represent an object that has been inspected. -type InspectedObject interface { - // Type returns the type of the object. - Type() string - - // InstanceID returns the instance identifier of the object. - InstanceID() string - - // AddChild adds a child object to the inspected object. - AddChild(name string, instance any, inspectManually ...func(object InspectedObject)) - - // String returns a string representation of the inspected object. - String() string -} - -// NewInspectedObject creates a new inspected object from the given instance and inspect function. -func NewInspectedObject(instance any, inspect func(InspectedObject), session ...Session) InspectedObject { - o := &inspectedObject{ - instance: instance, - childObjects: orderedmap.New[string, InspectedObject](), - session: lo.First(session, make(Session)), - } - - if o.inspected = o.session.FirstOccurrence(instance); o.inspected { - inspect(o) - } - - return o -} - -// inspectedObject is an implementation of the InspectedObject interface. -type inspectedObject struct { - instance any - childObjects *orderedmap.OrderedMap[string, InspectedObject] - session Session - inspected bool -} - -// Type returns the type of the object. -func (i *inspectedObject) Type() string { - runes := []rune(regexp.MustCompile(`[^.]+\.([^[]+).*`).ReplaceAllString(fmt.Sprintf("%T", i.instance), "${1}")) - runes[0] = unicode.ToUpper(runes[0]) - - return string(runes) -} - -// InstanceID returns the instance identifier of the object. -func (i *inspectedObject) InstanceID() string { - type named interface { - LogName() string - } - - if namedInstance, isNamed := i.instance.(named); isNamed { - return namedInstance.LogName() - } - - return fmt.Sprintf("%p", i.instance) -} - -// AddChild adds a child object to the inspected object. -func (i *inspectedObject) AddChild(name string, instance any, inspectManually ...func(object InspectedObject)) { - type inspectable interface { - Inspect(session ...Session) InspectedObject - } - - if stringify.IsInterfaceNil(instance) { - i.childObjects.Set(name, nil) - } else if len(inspectManually) >= 1 { - i.childObjects.Set(name, NewInspectedObject(instance, inspectManually[0], i.session)) - } else if inspectableInstance, isInspectable := instance.(inspectable); isInspectable { - i.childObjects.Set(name, inspectableInstance.Inspect(i.session)) - } else { - panic("added object does not have an 'Inspect(session ...Session) InspectedObject' method - please provide a manual inspection function") - } -} - -// String returns a string representation of the inspected object. -func (i *inspectedObject) String() string { - return i.indentedString(0) -} - -// indentedString returns a string representation of the inspected object with the given indentation. -func (i *inspectedObject) indentedString(indent int) string { - if i == nil { - return "nil" - } - - var typeString string - if instanceID, typeName := i.InstanceID(), i.Type(); typeName == instanceID { - typeString = typeName - } else { - typeString = typeName + "(" + instanceID + ")" - } - - if !i.inspected { - return typeString + " {...}" - } - - childOutputs := make([]string, 0) - i.childObjects.ForEach(func(key string, value InspectedObject) bool { - if value == nil { - childOutputs = append(childOutputs, strings.Repeat(" ", (indent+1)*indentationSize)+key+": nil") - } else if objectValue, ok := value.(*inspectedObject); !ok { - panic("this should never happen but linter requires type cast check") - } else if value.Type() == key || value.InstanceID() == key { - childOutputs = append(childOutputs, strings.Repeat(" ", (indent+1)*indentationSize)+objectValue.indentedString(indent+1)) - } else { - childOutputs = append(childOutputs, strings.Repeat(" ", (indent+1)*indentationSize)+key+": "+objectValue.indentedString(indent+1)) - } - - return true - }) - - if len(childOutputs) == 0 { - return typeString + " {}" - } - - return typeString + " {\n" + strings.Join(childOutputs, ",\n") + "\n" + strings.Repeat(" ", (indent)*indentationSize) + "}" -} - -// indentationSize defines the size of the indentation. -const indentationSize = 2 diff --git a/pkg/core/inspection/map_inspector.go b/pkg/core/inspection/map_inspector.go deleted file mode 100644 index 876f080dd..000000000 --- a/pkg/core/inspection/map_inspector.go +++ /dev/null @@ -1,17 +0,0 @@ -package inspection - -// MapInspector is a utility function that can be used to inspect a map of elements. -func MapInspector[K comparable, V any](mapToInspect mapInterface[K, V], inspect func(inspectedMap InspectedObject, key K, value V)) func(inspectedMap InspectedObject) { - return func(inspectedMap InspectedObject) { - mapToInspect.ForEach(func(key K, value V) bool { - inspect(inspectedMap, key, value) - - return true - }) - } -} - -// mapInterface is an interface that is used to iterate over a map of elements. -type mapInterface[K comparable, V any] interface { - ForEach(consumer func(key K, value V) bool) -} diff --git a/pkg/core/inspection/session.go b/pkg/core/inspection/session.go deleted file mode 100644 index be03ae4f8..000000000 --- a/pkg/core/inspection/session.go +++ /dev/null @@ -1,15 +0,0 @@ -package inspection - -// Session is used to track instances of objects that have already been inspected. -type Session map[any]bool - -// FirstOccurrence checks if the given instance has already been inspected. -func (s Session) FirstOccurrence(instance any) bool { - if s[instance] { - return false - } - - s[instance] = true - - return true -} diff --git a/pkg/core/inspection/set_inspector.go b/pkg/core/inspection/set_inspector.go deleted file mode 100644 index 8dc29fed2..000000000 --- a/pkg/core/inspection/set_inspector.go +++ /dev/null @@ -1,17 +0,0 @@ -package inspection - -// SetInspector is a utility function that can be used to inspect a set of elements. -func SetInspector[T comparable](setToInspect setInterface[T], inspect func(inspectedSet InspectedObject, element T)) func(inspectedSet InspectedObject) { - return func(inspectedSet InspectedObject) { - _ = setToInspect.ForEach(func(key T) error { - inspect(inspectedSet, key) - - return nil - }) - } -} - -// setInterface is an interface that is used to iterate over a setInterface of elements. -type setInterface[T comparable] interface { - ForEach(consumer func(element T) error) error -} diff --git a/pkg/protocol/inspection.go b/pkg/protocol/inspection.go index 73cef9ba6..c59e59df5 100644 --- a/pkg/protocol/inspection.go +++ b/pkg/protocol/inspection.go @@ -1,7 +1,7 @@ package protocol import ( - "github.com/iotaledger/iota-core/pkg/core/inspection" + "github.com/iotaledger/hive.go/runtime/inspection" "github.com/iotaledger/iota-core/pkg/core/promise" iotago "github.com/iotaledger/iota.go/v4" ) @@ -9,20 +9,20 @@ import ( // Inspect inspects the protocol and its subcomponents. func (p *Protocol) Inspect(session ...inspection.Session) inspection.InspectedObject { return inspection.NewInspectedObject(p, func(protocol inspection.InspectedObject) { - protocol.AddChild("Commitments", p.Commitments) - protocol.AddChild("Chains", p.Chains) + protocol.Add("Commitments", p.Commitments) + protocol.Add("Chains", p.Chains) }, session...) } // Inspect inspects the Commitments and its subcomponents. func (c *Commitments) Inspect(session ...inspection.Session) inspection.InspectedObject { return inspection.NewInspectedObject(c, func(commitments inspection.InspectedObject) { - commitments.AddChild("Root", c.Root.Get()) - commitments.AddChild("Set", c.Set, inspection.SetInspector[*Commitment](c.Set, func(inspectedSet inspection.InspectedObject, element *Commitment) { - inspectedSet.AddChild(element.LogName(), element) + commitments.Add("Root", c.Root.Get()) + commitments.Add("Set", c.Set, inspection.SetInspector[*Commitment](c.Set, func(inspectedSet inspection.InspectedObject, element *Commitment) { + inspectedSet.Add(element.LogName(), element) })) - commitments.AddChild("cachedRequests", c.cachedRequests, inspection.MapInspector(c.cachedRequests, func(cachedRequests inspection.InspectedObject, commitmentID iotago.CommitmentID, cachedRequest *promise.Promise[*Commitment]) { - cachedRequests.AddChild(commitmentID.String(), cachedRequest.Result()) + commitments.Add("cachedRequests", c.cachedRequests, inspection.MapInspector(c.cachedRequests, func(cachedRequests inspection.InspectedObject, commitmentID iotago.CommitmentID, cachedRequest *promise.Promise[*Commitment]) { + cachedRequests.Add(commitmentID.String(), cachedRequest.Result()) })) }, session...) } @@ -30,11 +30,11 @@ func (c *Commitments) Inspect(session ...inspection.Session) inspection.Inspecte // Inspect inspects the Commitment and its subcomponents. func (c *Commitment) Inspect(session ...inspection.Session) inspection.InspectedObject { return inspection.NewInspectedObject(c, func(commitment inspection.InspectedObject) { - commitment.AddChild("Parent", c.Parent.Get()) - commitment.AddChild("MainChild", c.MainChild.Get()) - commitment.AddChild("Chain", c.Chain.Get()) - commitment.AddChild("Children", c.Children, inspection.SetInspector[*Commitment](c.Children, func(children inspection.InspectedObject, child *Commitment) { - children.AddChild(child.LogName(), child) + commitment.Add("Parent", c.Parent.Get()) + commitment.Add("MainChild", c.MainChild.Get()) + commitment.Add("Chain", c.Chain.Get()) + commitment.Add("Children", c.Children, inspection.SetInspector[*Commitment](c.Children, func(children inspection.InspectedObject, child *Commitment) { + children.Add(child.LogName(), child) })) }, session...) } @@ -42,12 +42,12 @@ func (c *Commitment) Inspect(session ...inspection.Session) inspection.Inspected // Inspect inspects the Chains and its subcomponents. func (c *Chains) Inspect(session ...inspection.Session) inspection.InspectedObject { return inspection.NewInspectedObject(c, func(chains inspection.InspectedObject) { - chains.AddChild("Main", c.Main.Get()) - chains.AddChild("HeaviestClaimedCandidate", c.HeaviestClaimedCandidate.Get()) - chains.AddChild("HeaviestAttestedCandidate", c.HeaviestAttestedCandidate.Get()) - chains.AddChild("HeaviestAttestedCandidate", c.HeaviestAttestedCandidate.Get()) - chains.AddChild("Set", c.Set, inspection.SetInspector[*Chain](c.Set, func(set inspection.InspectedObject, chain *Chain) { - set.AddChild(chain.LogName(), chain) + chains.Add("Main", c.Main.Get()) + chains.Add("HeaviestClaimedCandidate", c.HeaviestClaimedCandidate.Get()) + chains.Add("HeaviestAttestedCandidate", c.HeaviestAttestedCandidate.Get()) + chains.Add("HeaviestAttestedCandidate", c.HeaviestAttestedCandidate.Get()) + chains.Add("Set", c.Set, inspection.SetInspector[*Chain](c.Set, func(set inspection.InspectedObject, chain *Chain) { + set.Add(chain.LogName(), chain) })) }, session...) } @@ -55,10 +55,10 @@ func (c *Chains) Inspect(session ...inspection.Session) inspection.InspectedObje // Inspect inspects the Chain and its subcomponents. func (c *Chain) Inspect(session ...inspection.Session) inspection.InspectedObject { return inspection.NewInspectedObject(c, func(chain inspection.InspectedObject) { - chain.AddChild("ForkingPoint", c.ForkingPoint.Get()) - chain.AddChild("ParentChain", c.ParentChain.Get()) - chain.AddChild("ChildChains", c.ChildChains, inspection.SetInspector[*Chain](c.ChildChains, func(childChains inspection.InspectedObject, chain *Chain) { - childChains.AddChild(chain.LogName(), chain) + chain.Add("ForkingPoint", c.ForkingPoint.Get()) + chain.Add("ParentChain", c.ParentChain.Get()) + chain.Add("ChildChains", c.ChildChains, inspection.SetInspector[*Chain](c.ChildChains, func(childChains inspection.InspectedObject, chain *Chain) { + childChains.Add(chain.LogName(), chain) })) }, session...) } diff --git a/tools/gendoc/go.mod b/tools/gendoc/go.mod index 8e0daa864..3e0d61055 100644 --- a/tools/gendoc/go.mod +++ b/tools/gendoc/go.mod @@ -66,7 +66,7 @@ require ( github.com/iotaledger/hive.go/kvstore v0.0.0-20240223142044-12ffcb37c413 // indirect github.com/iotaledger/hive.go/lo v0.0.0-20240223142044-12ffcb37c413 // indirect github.com/iotaledger/hive.go/log v0.0.0-20240223142044-12ffcb37c413 // indirect - github.com/iotaledger/hive.go/runtime v0.0.0-20240223142044-12ffcb37c413 // indirect + github.com/iotaledger/hive.go/runtime v0.0.0-20240301015124-eea9bb54a256 // indirect github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20240223142044-12ffcb37c413 // indirect github.com/iotaledger/hive.go/stringify v0.0.0-20240223142044-12ffcb37c413 // indirect github.com/iotaledger/inx-app v1.0.0-rc.3.0.20240216141618-d7dfe94bdc1e // indirect diff --git a/tools/gendoc/go.sum b/tools/gendoc/go.sum index 5e52a016d..f43082b79 100644 --- a/tools/gendoc/go.sum +++ b/tools/gendoc/go.sum @@ -305,6 +305,7 @@ github.com/iotaledger/hive.go/log v0.0.0-20240223142044-12ffcb37c413 h1:4xKFtHoO github.com/iotaledger/hive.go/log v0.0.0-20240223142044-12ffcb37c413/go.mod h1:H5tmswUbT3o5+QiM6UPtBv7VnPf+lJtlantgpp2lzUI= github.com/iotaledger/hive.go/runtime v0.0.0-20240223142044-12ffcb37c413 h1:7O3oSoXlwV5L3/+kt/a1MN3Kwb5oXaaSZaa+aabh7BM= github.com/iotaledger/hive.go/runtime v0.0.0-20240223142044-12ffcb37c413/go.mod h1:pueoYXud+HmTY2x9j/S6+ZX3M5ZyENFKPDrx3EtcwWs= +github.com/iotaledger/hive.go/runtime v0.0.0-20240301015124-eea9bb54a256/go.mod h1:pueoYXud+HmTY2x9j/S6+ZX3M5ZyENFKPDrx3EtcwWs= github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20240223142044-12ffcb37c413 h1:tQW0K9Ogyfgm3V0wYhRPChOYgrADGlRWF6P4gMY8Zbg= github.com/iotaledger/hive.go/serializer/v2 v2.0.0-rc.1.0.20240223142044-12ffcb37c413/go.mod h1:NK05G4PxwZF1m4jGANJWLhAQ2hP1Nt0L8mgCTFLsSCw= github.com/iotaledger/hive.go/stringify v0.0.0-20240223142044-12ffcb37c413 h1:QCidFFczZH9NEJTWm0ZzZKMaQ0XUxYyU00dpaH+skqs=