Skip to content

Commit

Permalink
Add power stepping to stepping controls
Browse files Browse the repository at this point in the history
  • Loading branch information
Juan Pedro Monetta Sanchez authored and Juan Pedro Monetta Sanchez committed Jul 27, 2023
1 parent ac3b599 commit b7151c9
Show file tree
Hide file tree
Showing 10 changed files with 172 additions and 53 deletions.
1 change: 1 addition & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@

- Add "Search value on Flows" to taps
- Add "Timeline tool" implementation
- Add power stepping to stepping controls

### Changes

Expand Down
30 changes: 25 additions & 5 deletions docs/user_guide.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -516,18 +516,20 @@ You can use it to step over each expression, visualize values, locals and more.

image::user_guide_images/controls.png[]

The numbers at the end show `current_step_index / total_steps`. This means that a total of `total_steps` has been recorded
The numbers at the center show `current_step_index / total_steps`. This means that a total of `total_steps` has been recorded
for this thread so far.

Write any number (less than total_steps) on the text box to jump into that position in time. When jumping around you can write down any interesting
positions you find and then use this text box to jump back to it if you need.

The code tool allows you to step and "travel thought time" in two ways:

- You can use the controls at the top or [Ctrl | Alt]+MouseWheel on the forms to move one step at a time.
- You can use the controls at the top to step over your code in different ways.

- Or you can click on the highlighted forms to position the debugger at that point in time.

Only the forms that were executed at least once for the flow and thread will be highlighted.

From left to right this are the controls you have available :

- Jump to the first step of the recording.
Expand All @@ -538,10 +540,28 @@ From left to right this are the controls you have available :
- Step forward, will step forward in time going into sub functions.
- Step over forward, will make one step forwards always staying on the same frame.
- Jump to the last step of the recording.
- Follow value backwards. Will step to the previous expression (if any) that evaluates to the current value.
- Follow value forward. Will step to the next expression (if any) that evaluates to the current value.

Only the forms that were executed at least once for the flow and thread will be highlighted.
==== Power stepping

The controls at the right are power stepping controls. They provide more powerfull ways of stepping through the code.

There are currently 3 power stepping tools :

- identity, will step to the next value which identity is the same as the current value
- equality, will step to the next value which is equals (clojure equality) to the current value
- custom, allows you to provide a predicate, which will be used to find the next step.
If you define it like `(fn [v] (map? v))` will make the power stepper step over all map values.

image::user_guide_images/controls_power_custom.png[]

Clicking on the back and forward button will use the selected power stepping tool.

[NOTE]
.Custom stepping
====
Custom power stepping is only supported in Clojure now.
====


==== Searching

Expand Down
53 changes: 47 additions & 6 deletions docs/user_guide.html
Original file line number Diff line number Diff line change
Expand Up @@ -704,6 +704,7 @@ <h1>FlowStorm debugger User&#8217;s Guide</h1>
<li><a href="#_code_tool">4.1. Code tool</a>
<ul class="sectlevel4">
<li><a href="#_code_stepping">Code stepping</a></li>
<li><a href="#_power_stepping">Power stepping</a></li>
<li><a href="#_searching">Searching</a></li>
<li><a href="#_loops">Loops</a></li>
<li><a href="#_exception_debugging">Exception debugging</a></li>
Expand Down Expand Up @@ -1569,7 +1570,7 @@ <h5 id="_code_stepping"><a class="anchor" href="#_code_stepping"></a><a class="l
</div>
</div>
<div class="paragraph">
<p>The numbers at the end show <code>current_step_index / total_steps</code>. This means that a total of <code>total_steps</code> has been recorded
<p>The numbers at the center show <code>current_step_index / total_steps</code>. This means that a total of <code>total_steps</code> has been recorded
for this thread so far.</p>
</div>
<div class="paragraph">
Expand All @@ -1582,14 +1583,17 @@ <h5 id="_code_stepping"><a class="anchor" href="#_code_stepping"></a><a class="l
<div class="ulist">
<ul>
<li>
<p>You can use the controls at the top or [Ctrl | Alt]+MouseWheel on the forms to move one step at a time.</p>
<p>You can use the controls at the top to step over your code in different ways.</p>
</li>
<li>
<p>Or you can click on the highlighted forms to position the debugger at that point in time.</p>
</li>
</ul>
</div>
<div class="paragraph">
<p>Only the forms that were executed at least once for the flow and thread will be highlighted.</p>
</div>
<div class="paragraph">
<p>From left to right this are the controls you have available :</p>
</div>
<div class="ulist">
Expand Down Expand Up @@ -1618,16 +1622,53 @@ <h5 id="_code_stepping"><a class="anchor" href="#_code_stepping"></a><a class="l
<li>
<p>Jump to the last step of the recording.</p>
</li>
</ul>
</div>
</div>
<div class="sect4">
<h5 id="_power_stepping"><a class="anchor" href="#_power_stepping"></a><a class="link" href="#_power_stepping">Power stepping</a></h5>
<div class="paragraph">
<p>The controls at the right are power stepping controls. They provide more powerfull ways of stepping through the code.</p>
</div>
<div class="paragraph">
<p>There are currently 3 power stepping tools :</p>
</div>
<div class="ulist">
<ul>
<li>
<p>Follow value backwards. Will step to the previous expression (if any) that evaluates to the current value.</p>
<p>identity, will step to the next value which identity is the same as the current value</p>
</li>
<li>
<p>Follow value forward. Will step to the next expression (if any) that evaluates to the current value.</p>
<p>equality, will step to the next value which is equals (clojure equality) to the current value</p>
</li>
<li>
<p>custom, allows you to provide a predicate, which will be used to find the next step.
If you define it like <code>(fn [v] (map? v))</code> will make the power stepper step over all map values.</p>
</li>
</ul>
</div>
<div class="imageblock">
<div class="content">
<img src="user_guide_images/controls_power_custom.png" alt="controls power custom">
</div>
</div>
<div class="paragraph">
<p>Only the forms that were executed at least once for the flow and thread will be highlighted.</p>
<p>Clicking on the back and forward button will use the selected power stepping tool.</p>
</div>
<div class="admonitionblock note">
<table>
<tr>
<td class="icon">
<div class="title">Note</div>
</td>
<td class="content">
<div class="title">Custom stepping</div>
<div class="paragraph">
<p>Custom power stepping is only supported in Clojure now.</p>
</div>
</td>
</tr>
</table>
</div>
</div>
<div class="sect4">
Expand Down Expand Up @@ -2769,7 +2810,7 @@ <h3 id="_internals_diagrams_and_documentation"><a class="anchor" href="#_interna
</div>
<div id="footer">
<div id="footer-text">
Last updated 2023-07-27 08:15:04 -0300
Last updated 2023-07-27 10:33:28 -0300
</div>
</div>
</body>
Expand Down
Binary file modified docs/user_guide_images/controls.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
Binary file added docs/user_guide_images/controls_power_custom.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
68 changes: 47 additions & 21 deletions src-dbg/flow_storm/debugger/ui/flows/code.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@
(:require [clojure.pprint :as pp]
[flow-storm.form-pprinter :as form-pprinter]
[flow-storm.debugger.ui.flows.components :as flow-cmp]
[flow-storm.debugger.ui.utils :as ui-utils :refer [event-handler v-box h-box label icon list-view text-field tab-pane tab]]
[flow-storm.debugger.ui.utils :as ui-utils :refer [event-handler v-box h-box label icon list-view text-field tab-pane tab combo-box border-pane]]
[flow-storm.debugger.ui.value-inspector :as value-inspector]
[flow-storm.utils :as utils]
[flow-storm.debugger.ui.state-vars :refer [store-obj obj-lookup] :as ui-vars]
Expand Down Expand Up @@ -382,23 +382,51 @@
last-tentry (runtime-api/timeline-entry rt-api flow-id thread-id last-idx :at)]
(jump-to-coord flow-id thread-id last-tentry)))

(defn find-and-jump-same-val [flow-id thread-id vref backward?]
(defn find-and-jump-same-val [flow-id thread-id curr-vref search-params backward?]
(let [{:keys [idx]} (state/current-timeline-entry flow-id thread-id)]
(when-let [next-tentry (runtime-api/find-timeline-entry rt-api {:flow-id flow-id
:thread-id thread-id
:from-idx (if backward?
(dec idx)
(inc idx))
:backward? backward?
:comp-fn-key :identity
:eq-val-ref vref})]
(when-let [next-tentry (runtime-api/find-timeline-entry rt-api (merge
{:flow-id flow-id
:thread-id thread-id
:from-idx (if backward?
(dec idx)
(inc idx))
:backward? backward?
:eq-val-ref curr-vref}
search-params))]
(jump-to-coord flow-id thread-id next-tentry))))

(defn step-same-val [flow-id thread-id backward?]
(defn step-same-val [flow-id thread-id search-params backward?]
(let [{:keys [type result]} (state/current-timeline-entry flow-id thread-id)]
(when (#{:expr :fn-return} type)
(find-and-jump-same-val flow-id thread-id result backward?))))
(find-and-jump-same-val flow-id thread-id result search-params backward?))))


(defn- power-stepping-pane [flow-id thread-id]
(let [custom-expression-txt (doto (text-field {:initial-text "(fn [v] v)"})
(.setVisible false))
step-type-combo (combo-box {:items ["identity" "equality" "custom"]
:on-change-fn (fn [_ new-val]
(case new-val
"identity" (.setVisible custom-expression-txt false)
"equality" (.setVisible custom-expression-txt false)
"custom" (.setVisible custom-expression-txt true)))})
search-params (fn []
(let [step-type-val (-> step-type-combo .getSelectionModel .getSelectedItem)]
(case step-type-val
"identity" {:comp-fn-key :identity}
"equality" {:comp-fn-key :equality}
"custom" {:comp-fn-key :custom
:comp-fn-code (.getText custom-expression-txt)})))
val-prev-btn (ui-utils/icon-button :icon-name "mdi-ray-end-arrow"
:on-click (fn [] (step-same-val flow-id thread-id (search-params) true))
:tooltip "Find the prev expression that contains this value")
val-next-btn (ui-utils/icon-button :icon-name "mdi-ray-start-arrow"
:on-click (fn [] (step-same-val flow-id thread-id (search-params) false))
:tooltip "Find the next expression that contains this value")

power-stepping-pane (doto (h-box [val-prev-btn val-next-btn step-type-combo custom-expression-txt])
(.setSpacing 3))]
power-stepping-pane))

(defn- create-thread-controls-pane [flow-id thread-id]
(let [first-btn (ui-utils/icon-button :icon-name "mdi-page-first"
Expand Down Expand Up @@ -442,12 +470,7 @@
last-btn (ui-utils/icon-button :icon-name "mdi-page-last"
:on-click (fn [] (step-last flow-id thread-id))
:tooltip "Step to the last recorded expression")
val-prev-btn (ui-utils/icon-button :icon-name "mdi-ray-end-arrow"
:on-click (fn [] (step-same-val flow-id thread-id true))
:tooltip "Find the prev expression that contains this value")
val-next-btn (ui-utils/icon-button :icon-name "mdi-ray-start-arrow"
:on-click (fn [] (step-same-val flow-id thread-id false))
:tooltip "Find the next expression that contains this value")


re-run-flow-btn (ui-utils/icon-button :icon-name "mdi-cached"
:on-click (fn []
Expand All @@ -459,11 +482,14 @@

trace-pos-box (doto (h-box [curr-trace-text-field separator-lbl thread-trace-count-lbl] "trace-position-box")
(.setSpacing 2.0))
controls-box (doto (h-box [first-btn prev-over-btn prev-btn out-btn re-run-flow-btn next-btn next-over-btn last-btn val-prev-btn val-next-btn])
controls-box (doto (h-box [first-btn prev-over-btn prev-btn out-btn re-run-flow-btn next-btn next-over-btn last-btn])
(.setSpacing 2.0))]

(doto (h-box [controls-box trace-pos-box] "thread-controls-pane")
(.setSpacing 2.0))))
(border-pane {:left controls-box
:center trace-pos-box
:right (power-stepping-pane flow-id thread-id)}
"thread-controls-pane")
))

(defn- create-search-pane [flow-id thread-id]
(let [search-txt (doto (TextField.)
Expand Down
2 changes: 1 addition & 1 deletion src-dbg/flow_storm/debugger/ui/timeline/screen.clj
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
[flow-storm.debugger.state :as dbg-state]
[flow-storm.debugger.ui.flows.screen :as flows-screen])
(:import [javafx.scene.layout Priority VBox]
[javafx.scene.control TableCell TableRow CheckBox]
[javafx.scene.control TableRow CheckBox]
[javafx.scene.input MouseButton]))

(def thread-colors ["#DAE8FC" "#D5E8D4" "#FFE6CC" "#F8CECC" "#E1D5E7" "#60A917" "#4C0099" "#CC00CC"])
Expand Down
19 changes: 18 additions & 1 deletion src-dbg/flow_storm/debugger/ui/utils.clj
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,8 @@
(:require [flow-storm.utils :refer [log-error]])
(:import [javafx.scene.control Button ContextMenu Label ListView SelectionMode ListCell MenuItem ScrollPane Tab
Alert ButtonType Alert$AlertType ProgressIndicator ProgressBar TextField TextArea TableView TableColumn TableCell TableRow
TabPane$TabClosingPolicy TabPane$TabDragPolicy TableColumn$CellDataFeatures TabPane Tooltip]
TabPane$TabClosingPolicy TabPane$TabDragPolicy TableColumn$CellDataFeatures TabPane Tooltip
ComboBox]
[javafx.scene.layout HBox VBox BorderPane]
[javafx.geometry Side Pos]
[javafx.collections.transformation FilteredList]
Expand Down Expand Up @@ -195,6 +196,22 @@

ta))

(defn combo-box [{:keys [items on-change-fn]}]
(let [observable-list (FXCollections/observableArrayList)
cb (doto (ComboBox.)
(.setItems observable-list))
sel-model (.getSelectionModel cb)]
(.addAll observable-list (into-array String items))
(.select sel-model (first items))

(when on-change-fn
(-> cb
.valueProperty
(.addListener (proxy [ChangeListener] []
(changed [_ prev-val new-val]
(on-change-fn prev-val new-val))))))
cb))

(defn set-min-size-wrap-content [node]
(doto node
(.setMinHeight (Region/USE_PREF_SIZE))))
Expand Down
2 changes: 1 addition & 1 deletion src-dbg/flow_storm/debugger/ui/value_inspector.clj
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@
:def-fn (fn [] (def-val vref))
:tap-fn (fn [] (runtime-api/tap-value rt-api vref))
:find-val-fn (when find-and-jump-same-val
(fn [backward?] (find-and-jump-same-val vref backward?)))
(fn [backward?] (find-and-jump-same-val vref {:comp-fn-key :identity} backward?)))
:shallow-val shallow-val}))

(defn make-item [stack-key v]
Expand Down
50 changes: 32 additions & 18 deletions src-inst/flow_storm/runtime/indexes/api.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
[flow-storm.runtime.indexes.thread-registry :as thread-registry]
[flow-storm.runtime.indexes.form-registry :as form-registry]
[flow-storm.runtime.types.fn-call-trace :as fn-call-trace]
[flow-storm.runtime.types.fn-return-trace :as fn-return-trace]
[flow-storm.runtime.types.expr-trace :as expr-trace]
[clojure.pprint :as pp]
[flow-storm.utils :as utils]))
Expand Down Expand Up @@ -349,24 +350,37 @@
(defn find-timeline-entry [{:keys [flow-id thread-id from-idx eq-val comp-fn-key backward?] :as criteria
:or {from-idx 0
comp-fn-key :equality}}]
(let [comp-fn (case comp-fn-key
:equality =
:identity identical?)
search-pred (fn [tl-entry]
(and (expr-trace/expr-trace? tl-entry)
(comp-fn (expr-trace/get-expr-val tl-entry) eq-val)))]
(some (fn [[fid tid]]
(when (and (or (not (contains? criteria :flow-id))
(= flow-id fid))
(or (not (contains? criteria :thread-id))
(= thread-id tid)))
(let [{:keys [timeline-index]} (get-thread-indexes fid tid)]
(when-let [entry (index-protos/timeline-find-entry timeline-index
from-idx
backward?
search-pred)]
(assoc entry :flow-id fid :thread-id tid)))))
(index-protos/all-threads flow-thread-registry))))
(try
(let [comp-fn (case comp-fn-key
:equality =
:identity identical?
:custom #?(:clj (let [custom-cmp-fn (eval (read-string (:comp-fn-code criteria)))]
(fn [v _]
(custom-cmp-fn v)))
:cljs (do
(utils/log "Custom stepping is not supported in ClojureScript yet")
(constantly true))))
search-pred (fn [tl-entry]
(cond
(expr-trace/expr-trace? tl-entry)
(comp-fn (expr-trace/get-expr-val tl-entry) eq-val)

(fn-return-trace/fn-return-trace? tl-entry)
(comp-fn (fn-return-trace/get-ret-val tl-entry) eq-val)))]
(some (fn [[fid tid]]
(when (and (or (not (contains? criteria :flow-id))
(= flow-id fid))
(or (not (contains? criteria :thread-id))
(= thread-id tid)))
(let [{:keys [timeline-index]} (get-thread-indexes fid tid)]
(when-let [entry (index-protos/timeline-find-entry timeline-index
from-idx
backward?
search-pred)]
(assoc entry :flow-id fid :thread-id tid)))))
(index-protos/all-threads flow-thread-registry)))
#?(:clj (catch Exception e (utils/log "Exception searching for timeline entry" (.getMessage e)))
:cljs (catch js/Error e (utils/log "Exception searching for timeline entry" (.-message e))))))

(defn total-order-timeline []
(index-protos/total-order-timeline flow-thread-registry forms-registry))
Expand Down

0 comments on commit b7151c9

Please sign in to comment.