Skip to content

Commit

Permalink
treesteps: add depth limits
Browse files Browse the repository at this point in the history
In many cases we want to focus on what operations do at the top few
levels of the hierarchy. For example, we may want to look at the
`mergingIter`/`levelIter` interaction but don't care about the
mechanics of what happens under the `singleLevelIterator`.

This commit introduces two options for `StartRecording`:
 - `MaxOpDepth` limits the maximum op depth; any operations or node
   updates for nodes below that depth are ignored.
 - `MaxTreeDepth` limits how many levels of the trees are shown;
   subtrees that are below that depth are replaced with a "..." node.
   `MaxTreeDepth` must be at least `MaxOpDepth`.
  • Loading branch information
RaduBerinde committed Sep 5, 2024
1 parent 9095323 commit 729b6f7
Show file tree
Hide file tree
Showing 4 changed files with 77 additions and 10 deletions.
14 changes: 11 additions & 3 deletions internal/treesteps/node_state.go
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import (

type nodeState struct {
recording *Recording
depth int
node Node
name string

Expand Down Expand Up @@ -41,9 +42,16 @@ func buildTree(n *nodeState) TreeNode {
}
}
}
for i := range info.children {
c := nodeStateLocked(n.recording, info.children[i])
t.Children = append(t.Children, buildTree(c))
if n.depth < n.recording.maxTreeDepth {
for i := range info.children {
c := nodeStateLocked(n.recording, info.children[i])
c.depth = n.depth + 1
t.Children = append(t.Children, buildTree(c))
}
} else {
for range info.children {
t.Children = append(t.Children, TreeNode{Name: "..."})
}
}
return t
}
Expand Down
8 changes: 8 additions & 0 deletions internal/treesteps/testdata/segment_tree
Original file line number Diff line number Diff line change
Expand Up @@ -736,3 +736,11 @@ https://raduberinde.github.io/treesteps/decode.html#eJzs2r1u2zAQB_C9T3Hg1AIaREv-
sum x1=2 x2=7 url
----
https://raduberinde.github.io/treesteps/decode.html#eJzs2ztu20wQwPH-O8WA1ReABZ-SIsBXSON0NgsBYRwB0SYQpSoIYKRInUJlTqeTBHpYj-XMMkgQgJL-leGZ5Y52Z_grLPhL9GYyq6NxdF8_zWq3kLfzupZmOYvi6H5Rf95km2j8EE3ddDGdfIziqFnO5JMTl0izmMwX9buTWKrEciU2UmPvp27afPAeVoKF8nShLUy1YKY8Xaox5eGBdmjthKn6yQf6B1KCyTFY7VqxbYNLHp2sVz_Xq-f16lnmE_dUj-UhjWVUnWWa5WwsaXoWc7tfv4mIvkdRaQt2W2kZl59G7X2zKrBuu30SWOCGStIulmrF9j9WXTVfFrhRKyl6xUw9nnTVOxQqjlGjQn7eGAl3x0u7162UcXV5LHm7jHZx7WqH06SJlzPOVChnsu_ssH326Kwdy_0roF5P0k648hgzbqSMZVCZq1o7-2O7ffvkd6a2jKVsF7JmVu-MSzMvZ1zUQDmVdvWnRzsUGbzEjM2Hp13QWpEYSZfmXsa4q2EsQ79C8KZaI1qcZYyDjFoH0e4oeXRRHLlEZP39xyb0fxbL8BVGYzRGYzRGh4f0Yo3W9oJsyIZsyO4YV8i-TrID-yM4giM4giM4gksPBO86qNF5QAd0fYoBXbkwQAf0rmL_BnS5U9Yb3Qd1UNcnGdSVCwP1nqGuWnc1sKM4iqO4318Uv3jFIfuvyTZbZBREcARHcARH8J4KLncBgFEcxVH81hU_JwLJ-yI5bIffC9iG7VtiG6Mx-qKNbr8MRgHIhmzIhmzIvmKyza0RXJsBBEdwBEfw_goudyGAYRzGYRzGYbxvjEP2H5Bt9MOoheAIjuDq5CM4gvuduDLBw5UAHdABHdAB_VJB3_-7oHTPNaiDOqiDurklqIdRV6jlby0ojuIoHlDc-2YSyv128C1nX9zejGoK3uB9u3gDNVD3DOrq63-_AgAA__9MOM8F

sum x1=2 x2=7 url max-tree-depth=2
----
https://raduberinde.github.io/treesteps/decode.html#eJzs2c9q6kAUBvD9fYrDrO6FIElM4iXgK9yNd6dZBDq1gea0mLgqBemi6y6y7NPlSUr8UxM9MyjUVuVb9pvJyZzJ_GCoT-pfmmsVq5Ge5ppL-j_Tmop5rhw1KvVjM1qoeKwyzsosvVeOKuY5PTCxS0WZzkp908o8IeuL2W3GWXHXCQNhYiBN9KTQF54OxUx4OBImRvJbhNDdhslq25Zbxu6Eqa7e62pRVwuapTzVMY09h_4mnZFinsfkeZ2MV3--EJFcI0ikCatS0gj326m5rp9Y5i3Lu5YJvV5PGK3k0U3MwTYleWH9bsNk75osiyJ5SZ9r8SdsWkS4_nLi2939AQ63mWHDQ4eixDhrr7K422TZ651uOdpkhhYH7RalPl3DYOt10g4rR7FLVL--NWV--w4N_kAHdEDHSXRItYAFWIDlB7BY6sMO7MDO4XZoaD328AM_8HMhFzVj67ADO7BzjB0aWo4-_MDPIX66h-j6DQEMwOB_zpegY_8bAguwAMt3YzGWhh3YgZ1j7DSXbfPRByAAAqBzv6kZmoUd2IGd4-ysf9eDH_j5Kj87N6xrRHRGYprd9sDmAtmAyCmIJM-_PgIAAP__yqMz1A==

sum x1=2 x2=7 url max-op-depth=1
----
https://raduberinde.github.io/treesteps/decode.html#eJzsl7uu00AQhnueYrQVSFvs-hIHS7wCTegSF5ZYgiU8oNipEFJEQU3hkqfzkyDn4tjeGeeUjs5UR2f-0fyey35SfqqPeelUqjZuXzqs4dPBOaiOpdJqU7sfnVqpdKsKLOoi_6a0qo4lfEdAA1WdH2r3eRCzZOxLgUX1dRQMiMSASjT3YHb5pPPnoNkhtM2_tjm1zQkOOe5dClurYZ2NlOpYpmDtKIaXf38DAF0jyqiESylKwXAY5esG2UzeubyZScCEEHkzS5ld_zSPPG8JuPZEoB0Dsj145NcbRfco4xCOFwPz25nI-N6TmNGFGkLfhhqc79Z3Y81EY3qKiJ74mfXlgx1yFePrEyDHY3wB43uMmUisYZWxWV7l6dmeXx-85GpjDbFvxN0svRm0wURjBrUiuqJGP2ytN1ndYkzxZLgFahWGEdGGE4WZVaIhmTrMTso70WikMI2svUaoGZkdKq3QALR__naht4GG5J0wWhgtjBZGC6Pnj_RpGU3VEmQLsgXZguwH5yrIXgSy4QPNXsG2YFuwLdgWbC8D28JoYfRTM9p_DIyBIFuQLcgWZAuyF4bs7qciwV_htnB7adzuTtUKvAXerxfeAmoB9cJAnf168z8AAP__JXpIKQ==
54 changes: 49 additions & 5 deletions internal/treesteps/tree_steps.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,11 +28,18 @@ import (
// in-progress recording (this applies to all nodes in the hierarchy).
//
// See SegmentTree in tree_steps_test.go for an example.
func StartRecording(root Node, name string) *Recording {
func StartRecording(root Node, name string, opts ...RecordingOption) *Recording {
mu.Lock()
defer mu.Unlock()
mu.recordingInProgress.Store(true)
w := &Recording{name: name}
w := &Recording{
name: name,
maxTreeDepth: 20,
maxOpDepth: 10,
}
for _, o := range opts {
o(w)
}

if mu.nodeMap == nil {
mu.nodeMap = make(map[Node]*nodeState)
Expand All @@ -42,6 +49,31 @@ func StartRecording(root Node, name string) *Recording {
return w
}

// RecordingOption is an optional argument to StartRecording.
type RecordingOption func(*Recording)

// MaxTreeDepth configures a recording to only show trees up to a certain depth.
// Operations and node updates below that level are ignored.
func MaxTreeDepth(maxTreeDepth int) RecordingOption {
return func(r *Recording) {
r.maxTreeDepth = maxTreeDepth
if r.maxOpDepth > r.maxTreeDepth {
r.maxOpDepth = r.maxTreeDepth
}
}
}

// MaxOpDepth configures a recording to only show operations for nodes up to a
// certain depth. Operations and node updates below that level are ignored.
func MaxOpDepth(maxOpDepth int) RecordingOption {
return func(r *Recording) {
r.maxOpDepth = maxOpDepth
if r.maxTreeDepth < r.maxOpDepth {
r.maxTreeDepth = r.maxOpDepth
}
}
}

// NodeUpdated can be used to trigger a new step in any recording that involves
// the given node. It can be used when the state of the node changed in a
// significant way. The reason is optional and will show up in the step
Expand All @@ -53,6 +85,12 @@ func NodeUpdated(n Node, reason string) {
mu.Lock()
defer mu.Unlock()
if ns, ok := mu.nodeMap[n]; ok {
if ns.depth > ns.recording.maxOpDepth {
// Ignore node updates below the op depth (if we are not stepping through
// operations on these nodes, we should not step through their updates
// either).
return
}
if reason == "" {
reason = " updated"
} else {
Expand Down Expand Up @@ -103,9 +141,11 @@ func (ni *NodeInfo) AddChildren(nodes ...Node) {
// Recording captures the step-by-step propagation of operations. See
// StartRecording.
type Recording struct {
name string
root *nodeState
steps []Step
name string
maxTreeDepth int
maxOpDepth int
root *nodeState
steps []Step
}

// Finish completes the recording and returns the recorded steps.
Expand Down Expand Up @@ -167,6 +207,10 @@ func StartOpf(node Node, format string, args ...any) *Op {
if !ok {
return nil
}
if ns.depth > ns.recording.maxOpDepth {
// Ignore any ops for nodes below maxOpDepth.
return nil
}
op := &Op{
details: fmt.Sprintf(format, args...),
nodeState: ns,
Expand Down
11 changes: 9 additions & 2 deletions internal/treesteps/tree_steps_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,13 @@ func TestSegmentTree(t *testing.T) {
var tree *SegmentTree
datadriven.RunTest(t, "testdata/segment_tree", func(t *testing.T, td *datadriven.TestData) string {
var r *Recording
var opts []RecordingOption
if val := 0; td.MaybeScanArgs(t, "max-tree-depth", &val) {
opts = append(opts, MaxTreeDepth(val))
}
if val := 0; td.MaybeScanArgs(t, "max-op-depth", &val) {
opts = append(opts, MaxOpDepth(val))
}
switch td.Cmd {
case "init":
var xRange int
Expand All @@ -144,14 +151,14 @@ func TestSegmentTree(t *testing.T) {
var x, delta int
td.ScanArgs(t, "x", &x)
td.ScanArgs(t, "delta", &delta)
r = StartRecording(tree.Root(), "Segment Tree add")
r = StartRecording(tree.Root(), "Segment Tree add", opts...)
tree.Add(x, delta)

case "sum":
var x1, x2 int
td.ScanArgs(t, "x1", &x1)
td.ScanArgs(t, "x2", &x2)
r = StartRecording(tree.Root(), "Segment Tree sum")
r = StartRecording(tree.Root(), "Segment Tree sum", opts...)
tree.Sum(x1, x2)

default:
Expand Down

0 comments on commit 729b6f7

Please sign in to comment.