Showing preview only (1,284K chars total). Download the full file or copy to clipboard to get everything.
Repository: jpmonettas/flow-storm-debugger
Branch: master
Commit: e64bd0cac4b3
Files: 112
Total size: 1.2 MB
Directory structure:
gitextract_xzq9jm4n/
├── .dir-locals.el
├── .github/
│ └── FUNDING.yml
├── .gitignore
├── CHANGELOG.md
├── Makefile
├── Readme.md
├── UNLICENSE
├── build.clj
├── deps.edn
├── docs/
│ ├── _config.yml
│ ├── dev_notes.md
│ ├── high_level_diagram.drawio
│ ├── index.html
│ ├── related-research-and-tools.md
│ ├── run_configs.drawio
│ ├── test-cases.org
│ ├── timeline.drawio
│ ├── user_guide.adoc
│ └── user_guide.html
├── examples/
│ └── plugins/
│ └── basic-plugin/
│ ├── deps.edn
│ └── src/
│ └── flow_storm/
│ └── plugins/
│ └── timelines_counters/
│ ├── all.clj
│ ├── runtime.cljc
│ └── ui.clj
├── llm-prompt.txt
├── package.json
├── resources/
│ └── flowstorm/
│ ├── fonts/
│ │ └── LICENSE.txt
│ └── styles/
│ ├── font-size-lg.css
│ ├── font-size-md.css
│ ├── font-size-sm.css
│ ├── font-size-xl.css
│ ├── styles.css
│ ├── theme_dark.css
│ └── theme_light.css
├── scripts/
│ ├── flow-clj
│ ├── gsettings
│ └── mock-gnome.sh
├── shadow-cljs.edn
├── src-dbg/
│ └── flow_storm/
│ └── debugger/
│ ├── docs.clj
│ ├── events_processor.clj
│ ├── events_queue.clj
│ ├── main.clj
│ ├── repl/
│ │ ├── core.clj
│ │ └── nrepl.clj
│ ├── runtime_api.clj
│ ├── state.clj
│ ├── tutorials/
│ │ └── basics.clj
│ ├── ui/
│ │ ├── browser/
│ │ │ └── screen.clj
│ │ ├── commons.clj
│ │ ├── components.clj
│ │ ├── data_windows/
│ │ │ ├── data_windows.clj
│ │ │ ├── visualizers/
│ │ │ │ └── oscilloscope.clj
│ │ │ └── visualizers.clj
│ │ ├── docs/
│ │ │ └── screen.clj
│ │ ├── flows/
│ │ │ ├── bookmarks.clj
│ │ │ ├── call_tree.clj
│ │ │ ├── code.clj
│ │ │ ├── components.clj
│ │ │ ├── functions.clj
│ │ │ ├── general.clj
│ │ │ ├── multi_thread_timeline.clj
│ │ │ ├── printer.clj
│ │ │ ├── screen.clj
│ │ │ └── search.clj
│ │ ├── main.clj
│ │ ├── outputs/
│ │ │ └── screen.clj
│ │ ├── plugins.clj
│ │ ├── tasks.clj
│ │ └── utils.clj
│ ├── user_guide.clj
│ └── websocket.clj
├── src-dev/
│ ├── dev.clj
│ ├── dev_tester.clj
│ ├── dev_tester.cljs
│ ├── dev_tester_12.clj
│ ├── logging.properties
│ └── user.clj
├── src-inst/
│ ├── data_readers.clj
│ └── flow_storm/
│ ├── api.clj
│ ├── api.cljs
│ ├── jobs.cljc
│ ├── nrepl/
│ │ └── middleware.clj
│ ├── ns_reload_utils.clj
│ ├── preload.cljs
│ ├── remote_websocket_client.clj
│ ├── remote_websocket_client.cljs
│ ├── runtime/
│ │ ├── debuggers_api.cljc
│ │ ├── events.cljc
│ │ ├── indexes/
│ │ │ ├── api.cljc
│ │ │ ├── form_registry.cljc
│ │ │ ├── protocols.cljc
│ │ │ ├── storm_form_registry.clj
│ │ │ ├── thread_registry.cljc
│ │ │ ├── timeline_index.cljc
│ │ │ ├── total_order_timeline.cljc
│ │ │ └── utils.cljc
│ │ ├── outputs.cljc
│ │ ├── types/
│ │ │ ├── bind_trace.cljc
│ │ │ ├── expr_trace.cljc
│ │ │ ├── fn_call_trace.cljc
│ │ │ └── fn_return_trace.cljc
│ │ └── values.cljc
│ ├── storm_api.clj
│ ├── storm_preload.cljs
│ └── tracer.cljc
├── src-shared/
│ └── flow_storm/
│ ├── eql.cljc
│ ├── form_pprinter.clj
│ ├── json_serializer.clj
│ ├── json_serializer.cljs
│ ├── state_management.cljc
│ ├── types.cljc
│ └── utils.cljc
└── tests.edn
================================================
FILE CONTENTS
================================================
================================================
FILE: .dir-locals.el
================================================
((clojure-mode . ((cider-clojure-cli-aliases . "dev:dev-tools:storm")
(clojure-dev-menu-name . "flow-storm-dev-menu")
(cider-jack-in-nrepl-middlewares . ("refactor-nrepl.middleware/wrap-refactor"
"cider.nrepl/cider-middleware"
"flow-storm.nrepl.middleware/wrap-flow-storm")))))
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: [jpmonettas]
# patreon: # Replace with a single Patreon username
# open_collective: # Replace with a single Open Collective username
# ko_fi: # Replace with a single Ko-fi username
# tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
# community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
# liberapay: # Replace with a single Liberapay username
# issuehunt: # Replace with a single IssueHunt username
# otechie: # Replace with a single Otechie username
# lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
# custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
================================================
FILE: .gitignore
================================================
.clj-kondo
target
.cpcache
.nrepl-port
.shadow-cljs
.cljs_node_repl
out/
node_modules
public/js/
/.calva/
/.lsp/
================================================
FILE: CHANGELOG.md
================================================
# Changelog
## master (unreleased)
### New Features
### Changes
### Bugs fixed
- Fix multi thread timeline viewer running out of colors
- Fix flow-storm.runtime.indexes/detailed-total-order-timeline
## 4.5.9 (28-10-2025)
### New Features
### Changes
- Improving flow-storm.api/prob-ref
### Bugs fixed
- Fixing oscilloscope
## 4.5.8 (13-10-2025)
### New Features
### Changes
### Bugs fixed
- Fix seqable visualizer when clicking on empty rows
## 4.5.7 (03-10-2025)
### New Features
### Changes
- Use java.util.logging to log, so it can be disabled with logging.properties
### Bugs fixed
## 4.5.6 (24-09-2025)
### New Features
### Changes
- Upgrading hansel to 0.1.90 to improve objects methods names tracing
### Bugs fixed
## 4.5.5 (10-09-2025)
### New Features
### Changes
### Bugs fixed
- Fix .nrepl-port not found exception
## 4.5.4 (9-09-2025)
### New Features
### Changes
### Bugs fixed
- Fix heap reporting for ClojureScript (where heap data is currently missing)
## 4.5.3 (02-09-2025)
### New Features
- When starting the ui for remote debugging, when port not provided check .nrepl-port
### Changes
- Update hansel to 0.1.87
### Bugs fixed
## 4.5.2 (01-09-2025)
### New Features
- Added JVM option flowstorm.heapLimit which can be set also from the UI to automatically stop recording when
the heap reaches the set size in megabytes
### Changes
- JVM option flowstorm.throwOnTraceLimit has been renamed to flowstorm.throwOnLimit
- Update hansel to 0.1.85
### Bugs fixed
- Fix exceptions instrumentation in vanilla for non #trace ones
## 4.5.1 (26-08-2025)
### New Features
- Data Windows on-create can be called with :preferred-size :small
- Adds oscilloscope support for DW :preferred-size :small
- Add :pre-require to flow-storm.debugger.main/start-debugger for easier data window visualizers in ClojureScript
### Changes
- Perf improvement: reduce garbage created by keep-timeline-sub-range
- Perf improvement: improve how we send navs-refs for datawindows which are nil most of the time
### Bugs fixed
## 4.5.0 (05-08-2025)
### New Features
- A better oscilloscope data window
### Changes
- Limit the exceptions we are tracking on the UI to 100 so we don't make the UI super slow when too many exceptions are fired
- Improve quick jump box for same function fired on many threads
### Bugs fixed
- Fixed transients inspection
## 4.4.6 (10-06-2026)
### New Features
- Added llm-prompt.txt that can be used to instruct LLMs on how to use FlowStorm
- Added utilities api functions useful for llm-prompt.txt
### Changes
### Bugs fixed
## 4.4.5 (06-06-2025)
### New Features
### Changes
### Bugs fixed
- Fix shallow-indexed aspect extractor
## 4.4.4 (26-05-2025)
### New Features
### Changes
### Bugs fixed
- Report interruptible task failed messages
- Don't swallow the exception on :dbg
## 4.4.3 (21-05-2025)
### New Features
### Changes
### Bugs fixed
- Fix data windows default visualizers system
## 4.4.2 (13-05-2025)
### New Features
### Changes
- Make :preview the default visualizer for numbers
- Improved thread breakpoints UX/UI
- All api calls from UI have now a timeout, configurable via flowstorm.uiTimeoutMillis
### Bugs fixed
- Fix fn calls list styling
- Fix Browser's function 'Break' button styling
## 4.4.1 (03-05-2025)
### New Features
### Changes
- Locals context menu changed "Define all frame vars" to "Define all" which only defines the visible ones.
This is to make the future less confusing, specially in the presence of loops.
- Sort locals by symbol name
### Bugs fixed
- Fix locals display inside loops
## 4.4.0 (17-04-2025)
### New Features
- Check for `:flow-storm.power-step/skip` value when power stepping
### Changes
- Internal refactor
### Bugs fixed
- Extra styles should be applied at the end
## 4.3.0 (29-03-2025)
### New Features
### Changes
- Reduced memory footprint by ~10% (jvm17) by removing this-idx from FnCall, ExprExec, FnReturn, FnUnwind entries
- Programmable API BREAKING:
- removed indexes-api/entry-idx now that each entry doesn't know its own index
- (indexes-api/get-fn-call timeline entry) -> (indexes-api/get-fn-call timeline idx)
- (indexes-api/get-sub-form timeline entry) -> (indexes-api/get-sub-form timeline idx)
### Bugs fixed
## 4.2.2 (18-03-2025)
### New Features
- Add flowstorm.autoUpdateUI jvm opt
- Add flowstorm.threadTraceLimit jvm opt
- Add flowstorm.throwOnThreadLimit jvm opt
- Add flowstorm.callTreeUpdate jvm opt
### Changes
- Disable eql-query-pprint by default
- Perf improvements. We were calling val-pprint in a bunch of places where v-ref :val-preview could be used
### Bugs fixed
- Catch exceptions on preview build when print fails
## 4.2.1 (11-03-2025)
### New Features
- Accept multiple flowstorm.plugins.namespaces.* jvm props that will get merged
### Changes
- Make the locals pane a table, so columns can be resized and searched
### Bugs fixed
## 4.2.0 (25-02-2025)
### New Features
- Basic plugins system
- Enable/disable instrumentation from the UI
### Changes
- Disable preview pprints by default for speed. You can still enable them via the Config menu.
### Bugs fixed
## 4.1.2 (11-02-2025)
### New Features
### Changes
- Make scope visualizer a "rolling scope"
- Show tree-view childs truncation message inside the tree-view instead of a popup alert
- Upgrade to hansel 0.1.84 which doesn't depend on core.async
### Bugs fixed
## 4.1.1 (29-01-2025)
### New Features
- Configurable pprint previews menu for slow to pprint values cases
- Very crude zoom-in/zoom-out on scope visualizer
### Changes
- Make *out* and *err* print panel font monospaced
- Make printer support printing multi-line strings when printing strings
### Bugs fixed
- Fix printer removing after flow cleanning
- Fix data-window eql extractor so it doesn't break on infinite sequences
## 4.1.0 (01-01-2025)
### New Features
- Add configurable auto jump to exceptions
- New eql-query-pprint visualizer
- New webpage visualizer
- Add support for setting the visualizer via flow-storm.api/data-window-push-val
- Add debugger/bookmark to set bookmarks from code and also quickly jump to the first one
- Enable multiple ClojureScript runtimes <> multiple debuggers via "flowstorm_ws_port"
### Changes
- Configurable thread tab auto-update every 1sec (on by default)
- Remove middleware dependency on cider-nrepl-middleware
- Aspect extractors signature deprecation. Now it recieves the object and a map with extras
### Bugs fixed
- Fix taps "search value on flows"
- Fix middleware not working with nrepl > 1.3.0-beta2
- Fix middleware for ClojureScript
- Fix identity and equality powerstepping for unwinds
## 4.0.2 (25-11-2024)
### New Features
### Changes
- Change flow exceptions combo to only show exception root instead of all unwinds
### Bugs fixed
## 4.0.1 (12-11-2024)
### New Features
### Changes
### Bugs fixed
- Fix middleware for Cider Storm
## 4.0.0 (11-11-2024)
### New Features
- DataWindow system
- Outputs tool (Clojure only)
### Changes
- Improved flow search for DataWindows support
- Update javafx to "21.0.4-ea+1"
### Bugs fixed
- Fix Quick Jump on multiple flows
- Fix UI displaying of multi line strings in varios places
- Fix printers enable state on printers screen open
## 3.17.4 (24-09-2024)
### New Features
### Changes
### Bugs fixed
- Fix thread trace count not updating on thread tab refresh
- Fix ctx menu showing on forms background with left click
- For ns reload functionality don't read forms with *read-eval* false
## 3.17.3 (27-08-2024)
### New Features
### Changes
- Use quoted-string-split on editor open commands
### Bugs fixed
## 3.17.2 (06-08-2024)
### New Features
### Changes
- Improved multi-thread timeline colors
- Do not deref all derefables automatically, just atoms, refs, agents and vars and pending realized ones. Leave the reast to snapshot-value system
### Bugs fixed
- Do not deref delays when tracing values
## 3.17.1 (26-07-2024)
### New Features
- Add before-reload and after-reload hooks for the namespace reloading utilities
### Changes
- Update tutorial
### Bugs fixed
- Improve ns auto reload for namespaces loaded with load-file
## 3.17.0 (23-07-2024)
### New Features
- Make Search, Printer and Multi-thread-timeline tools work per flow
- Pake Printer use the multi-thread timeline if available to allow for thread interleaving print debugging
- New fn-call power stepper
- Optional automatic namespaces reload after changing prefixes for storm
### Changes
- Flows UI refactor
- Make stack click jump to beg of the fn instead of prev step
### Bugs fixed
## 3.16.0 (03-07-2024)
### New Features
- Add printer support to print on all threads
- Add printers transform expression (Clojure only)
### Changes
- UI refactor for toolbars
- UI refactor for printers and multi-thread timeline
- Set a UI limit of 200 on the Exceptions menu entries to improve UI responsiveness under deep recursions exceptions.
### Bugs fixed
- Fix #181 close children windows if main window closed
- Fix out of boundaries on step-prev-over and step-next-over
## 3.15.8 (21-06-2024)
### New Features
### Changes
- Add recorded threads counter on the threads menu button
- Clean initialization console logs for ClojureScript
### Bugs fixed
- Fix remote debugging for vanilla
- Fix windows paths handling in middleware
## 3.15.7 (13-06-2024)
### New Features
### Changes
### Bugs fixed
- Fix FlowStorm not starting in OSX because of wrong taskbar icon url
## 3.15.6 (13-06-2024)
### New Features
- Add jump to first and last occurrences buttons to power stepping controls
### Changes
- #rtrace now clears the current recording flow before running again
- Upgrade ikonli-javafx to 12.3.1
- Add FlowStorm icon to the toolbar and taskbar
- Namespace FlowStorm resources so they don't collide with other resources
### Bugs fixed
- Fix thread-id lost from thread tab after tab refresh
- Fix running with nrepl >= 1.2
## 3.15.5 (11-05-2024)
### New Features
### Changes
### Bugs fixed
- Make the code stepper the default tab instead of the tree
## 3.15.4 (06-05-2024)
### New Features
### Changes
### Bugs fixed
- Fix auto tab switch on code jump
## 3.15.3 (02-05-2024)
### New Features
- Add Goto file:line on the menu
- Show open in editor for any form that contains line meta
### Changes
- Show value inspector `def` on the same screen
- Remove unnamed anonymous functions from Quick Jump
- Remove docs builder (now on dyna-spec)
- Remove tools.build as a dependency
- Make the coed stepper the default tab instead of the tree
### Bugs fixed
## 3.15.2 (18-04-2024)
### New Features
- Timelines notification updates and thread refresh button
### Changes
### Bugs fixed
## 3.15.1 (16-04-2024)
### New Features
### Changes
### Bugs fixed
- Don't run #rtrace when recording is paused
- Fix vanilla-instrument-var for clojurescript
- Fix #rtrace for clojurescript
## 3.15.0 (16-04-2024)
### New Features
- Implement storm instrumentation management in the browser
- Add "Copy qualified function symbol" and "Copy function calling form" to form menu
- Add only-functions? to multi-thread timeline
### Changes
- The record flow selector combo is now next to the tabs
- The threads selector is now a menu button instead of a list view to gain some UI space
- Automatically open first recorded thread
- Improved graphical tutorial
- Help menu with tutorial and user's guide
- Don't start recording by default
### Bugs fixed
## 3.14.0 (30-03-2024)
### New Features
- Add support to open forms in editors
- Add an option to set threads limit to throw
- There is a new, much more powerfull global search system instead of the old per timeline search.
- An improved Flows system that allows the user to record into multiple flows.
- Implement an improved loop navigation system
### Changes
- Improve pprint panes. Now all of the also show exceptions
- #rtrace0 ... #rtrace5 reader tags were removed since they aren't needed anymore with the new flow system
### Bugs fixed
- Theme dialog panes
- Fix inspector power stepper
## 3.13.1 (21-03-2024)
### New Features
### Changes
- Bring back the clear recordings button to the toolbar
- A faster search system for ClojureScript (for every functionality that searches on the timeline)
### Bugs fixed
## 3.13.0 (19-03-2024)
### New Features
- Implemented thread trace limit as a fuse for infinite loops/recursion
- All possibly expensive slow operations are now cancellable. This includes :
- List functions calls
- List prints with the printer
- Values search
- Multi-thread timeline
- All power stepping tools
- Quick jump
- All functions that collect from the timeline will report as they run, no need to wait to the end. This includes :
- List functions calls
- List prints with the printer
- Multi-thread timeline
- Add a menu bar to help with discoverability
- Add functions calls ret render checkbox that update on change
### Changes
- Change Exceptions from a ComboBox to MenuButton
- Functions calls auto update on selection
### Bugs fixed
## 3.12.2 (05-03-2024)
### New Features
### Changes
### Bugs fixed
- Fix functions lists for nil returns
## 3.12.1 (05-03-2024)
### New Features
### Changes
- Improved functions pane.
- Change stack double click behavior as per #150
- Display bookmarks and inspector windows centered on main window
- Display all dialogs centered on main window
### Bugs fixed
## 3.12.0 (15-02-2024)
### New Features
- Add identity-other-thread power stepper
### Changes
- Big refactor with a ton of improvements to the repl API
### Bugs fixed
## 3.11.2 (13-02-2024)
### New Features
### Changes
### Bugs fixed
- Fix quick jump
## 3.11.1 (08-02-2024)
### New Features
### Changes
- Better execution highlighting system
- Browser and functions calls list views update as they change with the keyboard
### Bugs fixed
- Fix multi-thread timeline search
- Close context menu if there is one already open
## 3.11.0 (01-02-2024)
### New Features
- Add exceptions display
### Changes
- :ex captured exception system removed - superseded by unwind tracing
### Bugs fixed
- [IMPORTANT!] Fix timeline structure when functions unwind (requires ClojureStorm >= 1.11.1-19)
- Fix value inspector stack showing val instead of key
## 3.10.0 (29-01-2024)
### New Features
- Add a timeline index column to the bookmarks table
- Add a thread timeline index column to the multi-thread timeline
- New same-coord and custom-same-coord power steppers
- New context menu to find recordings from unhighlighted text ("Jump forward here", etc)
### Changes
- Make tooltips have a 400ms delay instead of default 1000ms
- Display timeline indexes zero based (used to be 1 based) to be less confusing when using the repl api
- Add thread-ids everywhere thread-names show
- Show form line next to form namespace if known
- New stepper buttong layout
- Centralize bookmarks system (one bookmark system for all flows and threads)
- Step over doesn't stop at function boundaries anymore
- Improved value inspector performance on remote runtimes (via :val-preview)
### Bugs fixed
- Pasting in quick jump box doesn't fire autocomplete
- Quick jump should create and/or focus the tab
- Fix tree view freezes when a node contains too many childs
- Fix quick-jump on Clojure remote
## 3.9.1 (12-01-2024)
### New Features
- Add print-wrap controls on panes
### Changes
- Don't automatically switch to the Taps tab on new taps
### Bugs fixed
- Don't trace false as nil
- Update hansel to 0.1.81 to fix browser var instrumentation in cljc
## 3.9.0 (19-12-2023)
### New Features
- Add a bookmarking system
- Add navigation undo/redo system
### Changes
- Upgrade JavaFX to 21.0.1
- Push minimal supported JDK version to 17 with a path for using it with 11
- Improve keyboard event handling system to support different layouts
- Improve following current selected expression
- Remove double scrolling in code panes
- Make code stack pane jump a double-click
- Support multiple debugger instances running at the same time. Useful for debugging multiple build in cljs.
### Bugs fixed
- Fix "Add to prints" not showing on Vanilla
- Enter on time box focus the code
## 3.8.6 (17-11-2023)
### New Features
### Changes
- Don't print handled exception error messages on std-err since it messes up cider
### Bugs fixed
- Capture exceptions on cljs remote connect
- [Remote] Don't crash the debugger if there is an exception initializing the RT through the repl
## 3.8.5 (10-11-2023)
### New Features
### Changes
### Bugs fixed
- Patch for clojure.pprint bug https://ask.clojure.org/index.php/13455/clojure-pprint-pprint-bug-when-using-the-code-dispatch-table
## 3.8.4 (09-11-2023)
### New Features
- Ctrl-f copies the current qualified funcion symbol to the clipboard
- Ctrl-Shift-f copies the current function call form
- Right clicking on a tree node now shows "Copy qualified function symbol"
- Right clicking on a tree node now shows "Copy function calling form"
### Changes
- Big codebase refactor for make it cleaner
- Improved search functionality (faster and with better UX)
### Bugs fixed
- Fix functions list not showing entire functions names when they are large
## 3.8.3 (25-10-2023)
### New Features
- Add dynamic font inc/dec and theme rotation
### Changes
### Bugs fixed
## 3.8.2 (23-10-2023)
### New Features
### Changes
- Upgrading j-system-theme-detector to 3.8.1 to fix a NPE
- Downgrading JavaFX to 19.0.2 since >20 needs JDK>=17 and we still want JDK11
### Bugs fixed
## 3.8.1 (19-10-2023)
### New Features
### Changes
- Improved code highlighter. Replaces JavaFX standard TextFlow with RichTextFx CodeArea for improved performance.
- Change hansel to com.github.flow-storm/hansel 0.1.79 for the organization move
### Bugs fixed
- Fix #98 Stepping over big forms is very slow
## 3.7.5 (02-10-2023)
### New Features
- Add function call limits
### Changes
- Disable functionality that doesn't make sense under storm when working under ClojureStorm or ClojureScriptStorm
- Improve Printer thread selection so you don't need to constantly re-select thread
### Bugs fixed
## 3.7.4 (27-09-2023)
### New Features
- Add flow-storm.storm-preload for ClojureScriptStorm
### Changes
- Improved initialization system for remote debugging
- Reuduce callstack tree nodes args print level and depth for perf (specially on remotes)
### Bugs fixed
## 3.7.3 (10-09-2023)
### New Features
- Add multimethod dispatch-val to stack pane
### Changes
- Upgrade hansel to 0.1.78
### Bugs fixed
- Fix printer goto location without thread selection
## 3.7.1 (06-09-2023)
### New Features
### Changes
### Bugs fixed
- Fix ClojureScript double require issue
- Fix java.util.ConcurrentModificationException when building timeline
## 3.7.1 (21-08-2023)
### New Features
- Implement quickjump
- Unblock all breakpoint blocked threads with toolbar and keyboard
### Changes
### Bugs fixed
- Fix enabling/disabling of thread breakpoints
## 3.7.0 (15-08-2023)
### New Features
- Add "Search value on Flows" to taps
- Add "Timeline tool" implementation
- Add power stepping to stepping controls
- Add "Printer tool" implementation
### Changes
### Bugs fixed
- Fix NPE after closing thread tab
## 3.6.10 (19-07-2023)
### New Features
### Changes
- Upgrade to hansel 0.1.74 for a couple of bug fixes. Check hansel changelog.
### Bugs fixed
## 3.6.9 (06-07-2023)
### New Features
### Changes
### Bugs fixed
- Fix ability to capture what happens before the debugger connects on ClojureScript
## 3.6.8 (03-07-2023)
### New Features
- Code stepping follow value allows you to step to the next/prev expression that evaluates to the same value. Useful for understanding how values flow through programs
- Value inspector follow value, same as before.
- New code stepping search tool
### Changes
- The call stack tree search tool was removed since it was buggy and hard to fix because of how javaFx lazy TreeView works
### Bugs fixed
## 3.6.7 (30-06-2023)
### New Features
### Changes
- Upgrade to hansel 0.1.69 with improved coordinate system
### Bugs fixed
- Fix call tree highlight current frame
## 3.6.6 (29-06-2023)
### New Features
- Add debugger window title configurable via :title and flowstorm.title prop
- Add support for debugging litteral maps and sets of any size
### Changes
- Upgrades to hansel 0.1.65 and supports new coordinate system
- Reintroduced #rtrace ^{:thread-trace-limit N}
### Bugs fixed
- issues/65 GUI icon hover making the icon unreadable
## 3.6.5 (16-06-2023)
### New Features
### Changes
- Add flow-storm-find-flow-fn-call to nrepl middleware
### Bugs fixed
## 3.6.4 (13-06-2023)
### New Features
- Add flow-storm.nrepl.middleware for editors integration
### Changes
- Improve step out functionality
### Bugs fixed
## 3.6.3 (06-06-2023)
### New Features
### Changes
- Fix flow-storm.runtime.values/value-type for sorte-maps
- Upgrade hansel to 0.1.63
### Bugs fixed
- Fix step over for the (map some-fn) etc cases
## 3.6.2 (01-06-2023)
### New Features
### Changes
- Exclude org.slf4j/slf4j-nop
### Bugs fixed
## 3.6.1 (30-05-2023)
### New Features
### Changes
- Make pprint panes a TextArea so we can copy it's content
- Upgrade hansel to 0.1.60 to fix ClojureScript namespace instrumentation issue
### Bugs fixed
## 3.6.0 (19-05-2023)
### New Features
- Add functions list refresh button
- Add step-prev-over and step-next-over buttons and keybindings
- Pprint panel now display value type
- Add define all current frame bindings
- Improve auto-scrolling when stepping on the code tool
### Changes
- Keep functions and tree state when switching tabs
- Much improved value inspector
- Update hansel to 0.1.56 so #trace (deftest ...) works
- BREAKING! flow-storm.runtime.values/snapshot-value defmethod was replaced by flow-storm.runtime.values/SnapshotP protocol for performance
### Bugs fixed
- Fix goto-location
- Respect flowstorm.startRecording before :dbg on ClojureStorm
## 3.5.1 (01-05-2023)
### New Features
- Add "Highlight current frame" on the call tree tool
- Add stack tab to code stepping tool
- Add a result pane on the functions list tool
- Add double click on calls tree tool node steps code
- Control recording from the UI
- Add threads breakpoints
- Add basic keyboard support
- Allow DEF button functionality to specify a NS
### Changes
- Made search functionality faster and simplified it's code
- #rtrace automatically opens the code stepping tool in the last position
- Upgrade hansel to 0.1.54
- Signal error when trying to use #rtrace with ClojureStorm
### Bugs fixed
- Don't crash if tracer functions are called before the system is fully started
- Fix Clojure remote debugging race condition
- Keep the thread list split pane size correct after window resize
## 3.4.1 (17-04-2023)
### New Features
### Changes
### Bugs fixed
- [CRITICAL] For remote connections fix repl-watchdog spamming the repl
## 3.4.0 (16-04-2023)
### New Features
- Add support for ClojureStorm
- Add a separate threads list and thread tabs contain names
- Add step up button
- Redesign Flows tools UX
### Changes
- A bunch of performance improvements
### Bugs fixed
- Many bug fixes (sorry for the low detail, this is a big release)
## 3.3.325 (09-04-2023)
### New Features
### Changes
- Exclude guava as a transitive dep in tools.build since it breaks shadow-cljs 2.21.0
- Upgrade hansel to 0.1.50
### Bugs fixed
## 3.3.320 (16-02-2023)
### New Features
### Changes
- Upgrade openjfx to 19.0.2.1
- Upgrade hansel to 0.1.46
### Bugs fixed
## 3.3.315 (30-01-2023)
### New Features
### Changes
- Upgrade to hansel 0.1.42 which contains a couple of bug fixes
### Bugs fixed
## 3.3.313 (29-01-2023)
### New Features
### Changes
### Bugs fixed
- Update to hansel 0.1.38 which contains a couple of bug fiexs
## 3.3.309 (29-12-2022)
### New Features
### Changes
- Improve docs file generation. Generated docs will be put into flow-docs.edn instead of samples.edn since
it is less likely to collide. Also the data format has been improved for extensibility
### Bugs fixed
## 3.3.307 (26-12-2022)
### New Features
### Changes
- Update hansel dependency to 0.1.35
### Bugs fixed
## 3.3.303 (20-12-2022)
### New Features
### Changes
### Bugs fixed
- Handle OS theme detector exceptions
## 3.3.301 (16-12-2022)
### New Features
### Changes
- Update hansel to 0.1.31 which contains a critical bug
- Improve value inspector styles
### Bugs fixed
- Fix docs examples display
### 3.3.295 (14-12-2022)
## New Features
- Add Flow Docs - Generate projects functions documentation by sampling their executions.
### Changes
### Bugs fixed
- Fix a ConcurrentModificationException on debuggers_api/reference_frame_data!
## 3.2.283 (29-11-2022)
## New Features
- Add browser var recursive instrumentation
### Changes
### Bugs fixed
- Update to hansel 0.1.22 to fix ClojureScript go blocks instrumentation
## 3.2.271 (16-11-2022)
## New Features
- New tap button allows you to tap> any value from the UI (nice to integrate with other tooling like portal)
- New locals "tap value" allows you to tap> any locals
### Changes
- Migrate to hansel for instrumentation (should be much better than previous instrumentation system)
- Value inspector navigation bar now shows keys instead of val text
### Bugs fixed
- Fix automatic [un]instrumentation watcher
## 3.1.263 (18-10-2022)
## New Features
### Changes
### Bugs fixed
- Fix clojure instrument entire namespace for non libraries
## 3.1.261 (18-10-2022)
## New Features
### Changes
- Remove flow-storm hard dependency on org.clojure/clojurescript artifact. Will lazy require when needed for ClojureScript, assuming the dependency will be provided
### Bugs fixed
## 3.1.259 (17-10-2022)
## New Features
- Add #rtrace ^{:thread-trace-limit X} where X can be a integer. Execution will throw after tracing X times for a trace. Useful for debugging possibly infinite loops
- Add support for snapshoting mutable values via flow-storm.runtime.values/snapshot-value multimethod
- Add #tap-stack-trace, to tap the current stack trace wherever you add it
- Add support for core.async/go blocks instrumentation
- Add Ctrl+MouseWheel on forms to step prev/next
### Changes
- Immediately highlihgt the first trace when creating a flow in the debugger
- Remove unnecessary first-fn-call-event
### Bugs fixed
- Alt+Tab now works on MacOs for switching between the debugger and the repl windows (thanks to Lucy Wang @lucywang000)
- Fix extend-protocol and extend-type instrumentations for ClojureScript
- Fix instrumentation breaking variadic functions in ClojureScript
## 3.0.236 (7-10-2022)
## New Features
### Changes
- Improves theming
- Fix dynamic vars not being re-evaluated as dynamic in cljs
### Bugs fixed
- Fix dynamic vars not being re-evalueated as dynamic in ClojureScript
- Fix timeout issues on remote connection
## 3.0.231 (6-10-2022)
## New Features
- Add `Instrument form without bindings` to form context menu
- Add got to last trace on code loops context-menu
### Changes
- Full [un]instrumentation synchronization between the browser and #trace (Clojure only)
- Now you can [un]instrument single vars from the browser, even if they where defined at the repl (Clojure only)
- Improved Single var [un]instrumentation from the browser (Clojure and ClojureScript)
### Bugs fixed
- Fix show-error on the clojure local path
## 3.0.216 (29-9-2022)
## New Features
- flow-storm.api/cli-run now supports :flow-id key
### Changes
### Bugs fixed
- Windows pprint form problem
- Bunch of minor small bug fixes
## 3.0.208 (28-9-2022)
## New Features
### Changes
- Instrummented code should run much faster due to removed unnecessary runtime ctx rebinding
### Bugs fixed
- Fix browser navigation after instrumentation bug
## 3.0.198 (24-9-2022)
## New Features
- Automatic event retention. For ClojureScript or remote Clojure you don't need to remote-connect by hand
when you want to capture traces before the debugger is connected. Just need to require flow-storm.api on your main.
- Automatic ui cleaning on reconnection
### Changes
- Remote connection now accepts :debugger-host and :runtime-host keys (check out the documentation)
### Bugs fixed
- Fix the more button on value inspector
- Fix for infinite sequence handling
## 3.0.188 (22-9-2022)
## New Features
- Automatic remote connection management. The re-connect button was removed from the toolbar since it isn't needed anymore
### Changes
### Bugs fixed
- Fix javafx platform not initialized exception when there is a error connecting to a repl
## 3.0.184 (21-9-2022)
## New Features
### Changes
### Bugs fixed
- Add support for remote debugging without repl connection (clojure and clojurescript)
- Show nrepl errors on the UI
- Fix ClojureScript re-run flow
- Fix a deadlock caused by the event system
## 3.0.173 (15-9-2022)
## New Features
3.0 is a full redesign! So it is full of changes, and fixes. Most remarkable things are :
- ClojureScript is feature par with Clojure now, so every feature is available to both languages.
- Remote debugging can be accomplished by just connecting to nrepl server (socket repl support on the roadmap)
- A programable API (https://jpmonettas.github.io/flow-storm-debugger/user_guide.html#_programmable_debugging)
- Enables the posibility to integrate it with IDEs/editors
### Changes
### Bugs fixed
## 2.3.141 (15-08-2022)
## New Features
* Add inspect for locals
* Can jump to any index by typing the idx number in the thread controls
### Changes
* Locals print-length is now 20 and print-level 5
* Make the value inspector show more info on dig nodes
### Bugs fixed
## 2.3.131 (10-08-2022)
## New Features
* Add a proper lazy and recursive value inspector
* Add tap tool (support for tap>)
* New functions for shutting down the debugger and connections gracefully.
When starting with `flow-storm.api/local-connect` or `flow-storm.api/remote-connect` you can shut it down with `flow-storm.api/stop`
When starting a standalone debugger with `flow-storm.debugger.main/start-debugger` you can shutdown with `flow-storm.debugger.main/stop-debugger`
* Add support for light and dark themes, in selected or automatic mode. Checkout the user guide for more info. (thanks to Liverm0r!)
* Thread tabs can be closed and reordered
### Changes
* The entire debugger was refactored to manage state with mount
### Bugs fixed
* Fix #28 - Callstack tree args and ret listviews should expand to the bottom
* Fix #34 - instrument-forms-for-namespaces not instrumenting (def foo (fn [...] ...))
## 2.2.114 (07-07-2022)
## New Features
* Add bottom bar progress indicator when running commands
* Add reload tree button on callstack tree tabs
* Add search bar to functions list
* This release also contains big internal refactors to make the codebase cleaner and more efficient
### Changes
* Automatically deref all reference values in traces
* Automatically change to flow tab on new flow
### Bugs fixed
## 2.2.99 (10-06-2022)
## New Features
* Add jump to first and last traces on thread controls (useful for exceptions debugging)
* Add print-level and print-meta controls on pprint value panels
* Improve re-run flow UX
* Namespace instrumentation now accepts :verbose? to log known and unknown instrumentation errors details
* Add flow-storm.api/uninstrument-forms-for-namespaces to undo instrument-form-for-namespaces instrumentation
* Add ctx menu on locals to define vars from values
* Add browser namespaces instrumentation/uninstrumentation
* Add browser instrumentation synchronization (for everything but #trace)
* Add #rtrace0 ... #rtrace5, like #rtrace but with different flow-ids
* Add double clicking on flows functions window executes show function calls
### Changes
* Remove flow-storm.api/run since #rtrace(runi) is enough
* Flows functions window now have checkboxes for selecting fncall arguments to print
### Bugs fixed
* Fix re run flow for #rtrace case
* Fix local binding instrumentation and debugging
## 2.2.68 (10-06-2022)
## New Features
* Add def value button on every value panel to define the value so you can work with it at the repl
* Add namespaces browser with instrumentation capabilities
### Changes
### Bugs fixed
## 2.2.64 (09-06-2022)
## New Features
* Add conditional tracing via #ctrace and ^{:trace/when ...} meta
### Changes
### Bugs fixed
## 2.2.59 (06-06-2022)
## New Features
### Changes
### Bugs fixed
* Fix run-command for the local connection path
## 2.2.57 (06-06-2022)
## New Features
* Add Clojurescript support
* Remote debugging via `flow-storm.api/remote-connect` and `flow-storm.api/cli-run`
### Changes
* `flow-storm.api/cli-run` now accepts :host and :port
### Bugs fixed
## 2.0.38 (02-05-2022)
## New Features
* Add styles customization via a user provided styles file
* The debugger can instrument and debug itself
### Changes
### Bugs fixed
## 2.0.0 (18-04-2022)
================================================
FILE: Makefile
================================================
.PHONY: clean docs test lint-dbg lint-inst install-dbg install-inst deploy-dbg deploy-inst
docs: docs/user_guide.adoc
asciidoctorj -b html5 -o docs/user_guide.html docs/user_guide.adoc
clean:
clj -T:build clean
test:
clj -M:test:dev unit-clj
lint:
clj-kondo --config .clj-kondo/config.edn --lint src-dbg src-shared src-inst
flow-storm-dbg.jar:
clj -T:build jar-dbg
flow-storm-inst.jar:
clj -T:build jar-inst
install-dbg: flow-storm-dbg.jar
mvn install:install-file -Dfile=target/flow-storm-dbg.jar -DpomFile=target/classes/META-INF/maven/com.github.flow-storm/flow-storm-dbg/pom.xml
install-inst: flow-storm-inst.jar
mvn install:install-file -Dfile=target/flow-storm-inst.jar -DpomFile=target/classes/META-INF/maven/com.github.flow-storm/flow-storm-inst/pom.xml
deploy-dbg:
mvn deploy:deploy-file -Dfile=target/flow-storm-dbg.jar -DrepositoryId=clojars -DpomFile=target/classes/META-INF/maven/com.github.flow-storm/flow-storm-dbg/pom.xml -Durl=https://clojars.org/repo
deploy-inst:
mvn deploy:deploy-file -Dfile=target/flow-storm-inst.jar -DrepositoryId=clojars -DpomFile=target/classes/META-INF/maven/com.github.flow-storm/flow-storm-inst/pom.xml -Durl=https://clojars.org/repo
================================================
FILE: Readme.md
================================================
# Flow-storm debugger
<img src="./docs/images/icon.png" width="100">
This is the central repository for [FlowStorm](http://www.flow-storm.org/) an omniscient time travel debugger for Clojure and ClojureScript.

There are two ways of using it :
- [With ClojureStorm](https://flow-storm.github.io/flow-storm-debugger/user_guide.html#_clojurestorm) (recommended) : For dev, swap your Clojure compiler by ClojureStorm and get everything instrumented automatically
- [Vanilla FlowStorm](https://flow-storm.github.io/flow-storm-debugger/user_guide.html#_vanilla_flowstorm) : Just add FlowStorm to your dev classpath and instrument by re-evaluating forms
ClojureStorm is a fork of the official Clojure compiler that adds automatic instrumentation so you don't need to think about it (you can still disable it when you don't need it).
You use it by swapping the official Clojure compiler by ClojureStorm at dev time, using dev aliases or profiles.
# Artifacts
FlowStorm latest stable releases :
- The complete debugger (includes `flow-storm-inst`)
- `[com.github.flow-storm/flow-storm-dbg "4.5.9"]`
- A slimmer version with no GUI, to use it for Clojure or ClojureScript remote debugging
- `[com.github.flow-storm/flow-storm-inst "4.5.9"]`
ClojureStorm latest stable releases :
- Clojure 1.12
- `[com.github.flow-storm/clojure "1.12.4"]`
- Clojure 1.11
- `[com.github.flow-storm/clojure "1.11.4-10"]`
ClojureScriptStorm latest stable releases :
- ClojureScript 1.12.116
- `[com.github.flow-storm/clojurescript "1.12.134-3"]`
- ClojureScript 1.11.132
- `[com.github.flow-storm/clojurescript "1.11.132-9"]`
# Prerequisites
- jdk17+ (if you still need to run it with jdk11 take a look at [here](https://flow-storm.github.io/flow-storm-debugger/user_guide.html#_run_with_jdk_11))
- clojure 1.11.0+
- clojure 1.10.* only supported if you use it from source, like `{:git/url "https://github.com/flow-storm/flow-storm-debugger" :git/sha "..."}`
# QuickStart and Documentation
If you want to use it with Clojure checkout the [Clojure QuickStart guide](https://flow-storm.github.io/flow-storm-debugger/user_guide.html#_clojure)
or the [ClojureScript QuickStart Guide](https://flow-storm.github.io/flow-storm-debugger/user_guide.html#_clojurescript) if you are using ClojureScript.
Please refer to the [user's guide](https://flow-storm.github.io/flow-storm-debugger/user_guide.html) for a list of features and how to use them.
# ClojureStorm and ClojureScriptStorm
*ClojureStorm* is a dev compiler. It is a fork of the official Clojure compiler enhanced with automatic instrumentation.
To use it, you just swap it with your normal Clojure compiler at dev time (by using deps cli aliases or lein profiles) to improve
your development experience, while making sure you use your normal compiler for everything else (tests and production).
*ClojureScriptStorm* is the same as ClojureStorm but for ClojureScript, so a fork of the official ClojureScript compiler is enhanced with automatic instrumentation.
ClojureStorm sources are here : https://github.com/flow-storm/clojure
ClojureScriptStorm sources are here : https://github.com/flow-storm/clojurescript
# Features
Flow storm debugger is packed with a ton of features, which the [user's guide](https://flow-storm.github.io/flow-storm-debugger/user_guide.html)
covers in detail.
## Information for developers
If you want to enhance, fix, debug, or just learn about the internals of FlowStorm take a look at
[here](./docs/dev_notes.md)
## Some demo videos (newers at the top)
- [FlowStorm demo at Clojure Apropos](https://www.youtube.com/watch?v=a-PrBjlBdw8)
- [ClojureScript compiler fun with FlowStorm](https://www.youtube.com/watch?v=YYHRx3EnPmg)
- [Don't fear the storm](https://www.youtube.com/watch?v=CspQX_R0NbM)
- [Clojure visual-tools 29 - FlowStorm 4.1 workflows](https://www.youtube.com/watch?v=9nY25hwzWRc)
- [Clojure web apps with FlowStorm 3.17](https://www.youtube.com/watch?v=h8AFpZkAwPo)
- [Reifying execution, the interactive programming missing piece](https://www.youtube.com/watch?v=BuSpMvVU7j4&t=1394s)
- [FlowStorm printer demo](https://www.youtube.com/watch?v=06-MA4HSS24)
- [Smashing a real ClojureScript bug with FlowStorm](https://www.youtube.com/watch?v=4VXT-RHHuvI)
- [Debugging Clojure with FlowStorm 3.6](https://www.youtube.com/watch?v=Mmr1nO6uMzc)
- [Searching and following values](https://www.youtube.com/watch?v=CwXhy-QsZHw)
- [Show me your REPL episode](https://www.youtube.com/watch?v=2nH59edD5Uo)
- [Debugging Clojure with FlowStorm](https://www.youtube.com/watch?v=PbGVTVs1yiU)
- [Debugging ClojureScript with FlowStorm](https://www.youtube.com/watch?v=jMYl32lnMhI)
- [Presentation at London Clojurians](https://www.youtube.com/watch?v=A3AzlqNwUXc)
- [Flows basics](https://www.youtube.com/watch?v=YnpQMrkj4v8)
- [Instrumenting libraries](https://youtu.be/YnpQMrkj4v8?t=332)
- [Debugging the ClojureScript compiler](https://youtu.be/YnpQMrkj4v8?t=533)
- [Browser](https://www.youtube.com/watch?v=cnLwRzxrKDk)
- [Def button](https://youtu.be/cnLwRzxrKDk?t=103)
## FAQ
### Clojure has the repl, why does it need a debugger?
In [this talk](https://www.youtube.com/watch?v=A3AzlqNwUXc&t=934s) I tried to argue that even as amazing as it is to have a repl to poke around,
there are some inconveniences that I think can be greatly improved by a debugger.
- Defining function parameters and locals with def (for sub form execution) isn't easy for complex values
- When functions contains loops, maps, filters, etc with anonymous functions is hard to capture every value for further inspection.
- Being Clojure a dynamic lang, running the program in my head isn't easy if I'm not familiar with the code base
- Adding prints (or tap>) is inconvenient because you need to guess where the problem probably is first.
- Debugging statements needs to be constantly typed and removed which gets repetitive and annoying
- Exploring complex values at the console is tedious, that is why tools like portal, reveal, rebl, etc exist.
Some of the issues there can be alleviated by adding libraries like scope capture, and portal but it isn't straight forward to integrate them
and even if it was IMHO there is a lot to be gained from a proper integrated debugging system.
So I want to stop guessing and a tool that allows me to see what is happening when a Clojure program runs, be it a small expression or an entire codebase.
I want it to be useful for cases when I'm chasing a bug or when I just want to understand how something works.
I also think some Clojure constraints, like immutability and being expression based, allows us to go beyond repl poking and steppers.
We can record everything that happens when a program runs and then inspect the execution using multiple tools (being a stepper one of them) and fully
integrating it with the repl which also enhance the repl poking experience.
### What's that magic? How does it work?
The idea behind FlowStorm is pretty simple :
- Instrument Clojure code
- Run it
- Record everything that happens on each thread during execution into timelines
- Provide a GUI with multiple tools to explore the recorded values and execution flows
The interesting part here I guess are instrumentation and recording.
FlowStorm can instrument your code in two ways :
- The ClojureStorm way (recommended for Clojure) which swaps your official Clojure compiler with a patched one (only for dev) that emits extra JVM bytecode everytime you compile something
to record everything that is happening. This method provides automatic instrumentation everywhere, which is very practical. You still get to un-instrument things if you need to do
things like measure performance, which isn't going to be accurate with the extra bytecode.
- The vanilla way (can be used for Clojure and is the only option for ClojureScript), that just grabs the Clojure source expressions you are interested in, walks the AST, instruments it, and re-evals the
instrumented version through the repl. You do this by using reader tags (like `#trace (defn foo [...] ...)`) or by using the FlowStorm browser tab to instrument entire namespaces.
Doesn't matter which method instrumented the code, when it runs it will record every expression in a timeline.
Because Clojure is expression based and most data is immutable, recording is just retaining JVM references together with the source coordinate of each expression. This is pretty fast and a simple way of recording execution.
The timeline is a structure that provides efficient insertion, and efficient access sequentially and as a functions call tree.
You can see some diagrams here : https://flow-storm.github.io/flow-storm-debugger/user_guide.html#_internals_diagrams_and_documentation
### Isn't recording everything too expensive? Like, does it work with something like a game loop?
The answer here is it depends. First, not everything needs to be recorded all the time. FlowStorm provides easy controls for start/stop recording, instrumenting/un-instrumenting stuff,
freeing recordings, and for keeping an eye on the heap space which you can also control via JVM parameters.
For a lot of applications it is probably fine even if you keep recording everything. For applications that generate a lot of recordings you can keep the recording off and just enable it
with one click right before executing the action you want to record, and then turn it off again.
For things like game loops you can also use the thread breakpoints functionality which provides a way of pausing/resuming threads at specific points so you have more control on how they
execute. Combining that with start/stopping recording is probably enough to debug this kind of application.
### Aren't omniscient debuggers slow?
Omniscient debuggers have a reputation of being slow, and I think a bunch of them are. IMHO a lot of the slowness comes from how things need to be recorded and replayed on mutable languages.
For Clojure, just retaining references is enough, and looking into a value at any point in time is just looking at it. Since most references point to immutable values, they don't need to be
reconstructed by applying state changes for each step.
There are mutable language omniscient debuggers that implement stepping backwards fast enough to be useful, but trying to implement things like searching or following values is probably going to be
much slower since everything needs to be reconstructed at each step.
### How does it compare to other Clojure debuggers?
There are many comparison points with different Clojure debugging tools, I'll try to list some of them here.
If you feel something here is unfair or needs to be clarified please open an issue or submit a PR. Also if you would like to see here how it compares with any other debugging tool let me know.
The first big difference is that FlowStorm works for Clojure and ClojureScript while most debuggers like Cider/VSCode/Cursive only work for Clojure. So once you learn
to use FlowStorm you can use it with both languages.
The second imho big difference is that most debugging tools are designed to understand small pieces of execution you could be interested in, while FlowStorm
is designed to look at a program execution as a whole, to be used not only while chasing bugs but also as a development companion to help you reason about whatever
system you are running.
Most tools like Cider, Cursive or VScode debuggers are blocking steppers and require you to add breakpoints to specific places, which is okay if you have an idea
already of where the bug could be located, but they fall short if you aren't sure, or you just want to use the tool to understand an entire codebase execution.
#### FlowStorm VS Cursive (or Browser's for ClojureScript) debuggers
I think the main difference here is FlowStorm being expression and value oriented while the Cursive and Browser's one being line and memory poking oriented,
which I don't think is very useful in Clojure[Script]. If you want to experience what I mean, try to debug this exception using Cursive vs FlowStorm/Cider/VSCode :
```
(defn foo [n]
(->> (range n)
(filter odd?)
(partition-all 2)
(map second)
(drop 10)
(reduce +)))
(foo 70)
```
On the other side, the nice thing about the Cursive debugger is that you can use it to step over Java code which is useful if you have a mixed lang codebase. But for those cases
you can always use both.
#### FlowStorm VS Cider/VSCode debuggers
Cider and VSCode debuggers are steppers, so I'll only be comparing it against the stepping and value inspection capabilities of FlowStorm, but keep in mind that FlowStorm provides
many more features than stepping. Cider provide some tracing capabilities to trace entire namespaces but it relies on printing to the console which I haven't found useful outside of very specific situations, so
I don't think it is useful to understand an entire codebase.
For the steppers, the main difference is that Cider/VSCode are blocking debuggers, which are nice in some situations but only provide stepping forward, while FlowStorm allows you to
step in many different ways.
Another difference is that currently on Cider/VSCode you can't step into functions unless you have previously instrumented them, which for the most isn't a problem in FlowStorm if you are using ClojureStorm.
A nice thing about Cider/VSCode steppers is that you get to step over your editor source code, which can also be done in FlowStorm if you use an integration like CiderStorm.
There is also some code that currently can't be stepped with Cider/VSCode that can be with FlowStorm. This is code that happens to be inside literal sets of maps with more than 8 keys.
If you want to compare it try stepping code like this in both debuggers :
```
(defn bla []
{(+ 1 2) (first #{3})
2 (+ 1 1)
4 (+ 2 2)
6 (+ 3 3)
8 (+ 4 4)
10 (+ 5 5)
12 (+ 6 6)
14 (+ 7 7)
16 (+ 8 8)})
(bla)
```
## What to do when things don't work?
Please create a [issue](https://github.com/flow-storm/flow-storm-debugger/issues) if you think you found a bug.
If you are not sure you can ask in :
- [#flow-storm slack channel](https://clojurians.slack.com/archives/C03KZ3XT0CF)
- [github discussions](https://github.com/flow-storm/flow-storm-debugger/discussions)
## Acknowledgements
Big thanks to [Roam Research](https://roamresearch.com/), [Nubank](https://nubank.com.br/) and all previous and current sponsors.


Thanks to [Cider](https://github.com/clojure-emacs/cider/) debugger for inspiration and some clever ideas for code instrumentation.
================================================
FILE: UNLICENSE
================================================
This is free and unencumbered software released into the public domain.
Anyone is free to copy, modify, publish, use, compile, sell, or
distribute this software, either in source code form or as a compiled
binary, for any purpose, commercial or non-commercial, and by any
means.
In jurisdictions that recognize copyright laws, the author or authors
of this software dedicate any and all copyright interest in the
software to the public domain. We make this dedication for the benefit
of the public at large and to the detriment of our heirs and
successors. We intend this dedication to be an overt act of
relinquishment in perpetuity of all present and future rights to this
software under copyright law.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR
OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE,
ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
OTHER DEALINGS IN THE SOFTWARE.
For more information, please refer to <http://unlicense.org/>
================================================
FILE: build.clj
================================================
(ns build
(:require [clojure.tools.build.api :as b]
[clojure.string :as str]
[clojure.java.io :as io]
[clojure.spec.alpha :as s]))
(def version (or (System/getenv "VERSION")
"4.5.9"))
(def target-dir "target")
(def class-dir (str target-dir "/classes"))
(defn clean [_]
(b/delete {:path target-dir}))
(def aot-compile-nses
"Precompile this so clojure storm can call flow-storm.storm-api functions
at repl init and it doesn't slow down repl startup."
['flow-storm.storm-api
'flow-storm.utils
'flow-storm.api
'flow-storm.runtime.debuggers-api
'flow-storm.runtime.types.fn-call-trace
'flow-storm.runtime.indexes.timeline-index
'flow-storm.runtime.indexes.api
'flow-storm.runtime.indexes.protocols
'flow-storm.runtime.types.fn-return-trace
'flow-storm.runtime.events
'flow-storm.runtime.indexes.thread-registry
'flow-storm.runtime.indexes.form-registry
'flow-storm.runtime.values
'flow-storm.runtime.types.bind-trace
'flow-storm.runtime.types.expr-trace
'flow-storm.runtime.indexes.utils])
(def aot? (if-let [aot (System/getenv "AOT")]
(read-string aot)
;; if no AOT value provided we default to true
true))
(defn- check-jvm []
(let [jvm-version (-> (System/getProperty "java.specification.version")
Integer/parseInt)]
(when (>= jvm-version 21)
(throw (ex-info "Not building with JVM >= 21 because of the SequencedCollection issue. See https://aphyr.com/posts/369-classnotfoundexception-java-util-sequencedcollection" {})))
(println "Building with JVM " jvm-version)))
(defn jar-dbg [_]
(check-jvm)
(clean nil)
(println "AOT compiling dbg : " aot?)
(let [lib 'com.github.flow-storm/flow-storm-dbg
basis (b/create-basis {:project "deps.edn"})
jar-file (format "%s/%s.jar" target-dir (name lib))
src-dirs ["src-dbg" "src-shared" "src-inst"]]
(b/write-pom {:class-dir class-dir
:lib lib
:version version
:basis basis
:src-dirs src-dirs
:pom-data [[:licenses
[:license
[:name "Unlicense"]
[:url "http://unlicense.org/"]]]]})
(when aot?
(b/compile-clj {:basis basis
:src-dirs src-dirs
:class-dir class-dir
:compile-opts {:direct-linking false}
:ns-compile aot-compile-nses}))
(b/copy-dir {:src-dirs (into src-dirs ["resources"])
:target-dir class-dir})
(b/jar {:class-dir class-dir
:jar-file jar-file})))
(defn jar-inst [_]
(check-jvm)
(clean nil)
(println "AOT compiling inst : " aot?)
(let [lib 'com.github.flow-storm/flow-storm-inst
src-dirs ["src-inst" "src-shared"]
basis (b/create-basis {:project nil
:extra {:deps {'org.java-websocket/Java-WebSocket {:mvn/version "1.5.3"}
'com.cognitect/transit-clj {:mvn/version "1.0.333"}
'com.cognitect/transit-cljs {:mvn/version "0.8.280"}
'com.github.flow-storm/hansel {:mvn/version "0.1.90"}
'org.clojure/data.int-map {:mvn/version "1.2.1"}
'amalloy/ring-buffer {:mvn/version "1.3.1"}}
:paths src-dirs}})
jar-file (format "%s/%s.jar" target-dir (name lib))]
(b/write-pom {:class-dir class-dir
:lib lib
:version version
:basis basis
:src-dirs src-dirs
:pom-data [[:licenses
[:license
[:name "Unlicense"]
[:url "http://unlicense.org/"]]]]})
(when aot?
(b/compile-clj {:basis basis
:src-dirs src-dirs
:class-dir class-dir
:compile-opts {:direct-linking false}
:ns-compile aot-compile-nses}))
(b/copy-dir {:src-dirs src-dirs
:target-dir class-dir})
(b/jar {:class-dir class-dir
:jar-file jar-file})))
================================================
FILE: deps.edn
================================================
{:paths ["src-inst" "src-dbg" "src-shared" "resources"]
:deps {;; IMPORTANT !!
;; If adding any dependency for the `inst` part also add it on
;; build.clj jar-inst
org.java-websocket/Java-WebSocket {:mvn/version "1.5.3"}
com.cognitect/transit-clj {:mvn/version "1.0.333"}
com.cognitect/transit-cljs {:mvn/version "0.8.280"}
com.github.flow-storm/hansel {:mvn/version "0.1.90"}
org.openjfx/javafx-controls {:mvn/version "21.0.4-ea+1"}
org.openjfx/javafx-base {:mvn/version "21.0.4-ea+1"}
org.openjfx/javafx-graphics {:mvn/version "21.0.4-ea+1"}
org.openjfx/javafx-web {:mvn/version "21.0.4-ea+1"}
org.kordamp.ikonli/ikonli-javafx {:mvn/version "12.3.1"}
org.kordamp.ikonli/ikonli-materialdesign-pack {:mvn/version "12.3.1"}
com.github.jpmonettas/j-system-theme-detector {:mvn/version "3.8.1"}
nrepl/nrepl {:mvn/version "1.1.1"}
org.clojure/data.int-map {:mvn/version "1.2.1"}
org.fxmisc.richtext/richtextfx {:mvn/version "0.11.1"}
amalloy/ring-buffer {:mvn/version "1.3.1"}}
:aliases {:cljs-storm {:classpath-overrides {org.clojure/clojurescript nil} ;; disable the official compiler
:extra-deps {thheller/shadow-cljs {:mvn/version "2.27.4"
:exclusions [org.clojure/clojurescript]}
;; bring ClojureScriptStorm
com.github.flow-storm/clojurescript {:mvn/version "1.11.132-2"}
;; add FlowStorm runtime dep
com.github.flow-storm/flow-storm-inst {:local/root "." #_#_:mvn/version "RELEASE"}
cider/cider-nrepl {:mvn/version "0.28.3"}
refactor-nrepl/refactor-nrepl {:mvn/version "3.5.2"}
cider/piggieback {:mvn/version "0.5.2"}}
:jvm-opts ["-Dcljs.storm.instrumentOnlyPrefixes=dev"
"-Dcljs.storm.instrumentEnable=true"]}
:storm {:classpath-overrides {org.clojure/clojure nil}
:extra-deps {com.github.flow-storm/clojure {:mvn/version "1.12.4"}}
:jvm-opts ["-Dflowstorm.theme=dark"
"-Dclojure.storm.instrumentAutoPrefixes=false"
"-Dclojure.storm.instrumentOnlyPrefixes=dev-tester"
"-Dflowstorm.jarEditorCommand=emacsclient -n +<<LINE>>:0 <<JAR>>/<<FILE>>"
"-Dflowstorm.fileEditorCommand=emacsclient -n +<<LINE>>:0 <<FILE>>"]}
:fs-timelines-counters-plugin {:extra-deps {timelines-counters/timelines-counter {:local/root "./examples/plugins/basic-plugin/"}}
:jvm-opts ["-Dflowstorm.plugins.namespaces.timelines-counters=flow-storm.plugins.timelines-counters.all"]}
:dev {:extra-paths ["src-dev" "classes"]
:extra-deps {
org.openjfx/javafx-swing {:mvn/version "21.0.4-ea+1"} ;; for scenic view to run
io.github.tonsky/clj-reload {:mvn/version "0.7.1"}
}
:jvm-opts ["-Dvisualvm.display.name=FlowStorm"
;; for the profilers
"-Djdk.attach.allowAttachSelf" "-XX:+UnlockDiagnosticVMOptions" "-XX:+DebugNonSafepoints"]}
:build {:extra-deps {io.github.clojure/tools.build {:git/tag "v0.9.6" :git/sha "8e78bcc"}}
:ns-default build
:jvm-opts ["-Dcljfx.skip-javafx-initialization=true"] }
:test {:extra-paths ["test"]
:extra-deps {lambdaisland/kaocha {:mvn/version "1.70.1086"}
org.clojure/clojurescript {:mvn/version "1.11.60"}}
:jvm-opts ["-Xmx10500m"]
:main-opts ["-m" "kaocha.runner"]}}}
================================================
FILE: docs/_config.yml
================================================
theme: jekyll-theme-cayman
================================================
FILE: docs/dev_notes.md
================================================
# Developer notes
The purpose of this document is to collect information and tips for people wanting to enhance, fix, debug,
or just learn about the internals of FlowStorm.
## FlowStorm design
FlowStorm is made of three parts :
* An __instrumentation__ system
* A __runtime__ system
* A __debugger__ system
If you want some diagrams of how all this works together take a look at
[here](https://raw.githubusercontent.com/flow-storm/flow-storm-debugger/master/docs/high_level_diagram.svg)
### Instrumentation
The __instrumentation__ system is responsible for instrumenting your code. This means interleaving extra code to trace
what your program is doing. There are currently 3 ways of instrumenting :
* [Hansel](https://github.com/flow-storm/hansel) a library to add instrumentation by re-writing forms at macroexpansion time
* [ClojureStorm](https://github.com/flow-storm/clojure) a Clojure dev compiler that will instrument by emitting extra bytecode
* [ClojureScriptStorm](https://github.com/flow-storm/clojurescript) a ClojureScript dev compiler that will instrument by emitting extra javascript
They are all independent from FlowStorm but you need to choose one of them to use FlowStorm with your programs.
When starting, FlowStorm will setup callbacks to them, which they will use when generating instrumentation.
You can see how it hooks with each of them in :
`flow-storm.tracer/[hansel-config | hook-clojure-storm | hook-clojurescript-storm]`
Whatever instrumentation system the user chooses, when instrumented code runs it will hit :
* `flow-storm.tracer/trace-fn-call`
* `flow-storm.tracer/trace-fn-return`
* `flow-storm.tracer/trace-fn-unwind`
* `flow-storm.tracer/trace-expr-exec`
* `flow-storm.tracer/trace-bind`
See the __runtime__ for what happens next.
### Runtime
The __runtime__ is FlowStorm's subsystem that runs inside the debuggee process.
Its responsibility is to record all the traces that arrive via the `flow-storm.tracer/trace-*` set of functions while
`flow-storm.tracer/recording` is `true`.
It will record everything in two registries `flow-storm.runtime.indexes.api/forms-registry` and
`flow-storm.runtime.indexes.api/flow-thread-registry`. Take a look at their docs strings for more info.
The forms registry will store all the instrumented forms by form-id, which is a hash of the form.
The threads registry on the other side will be storing one timeline per thread plus a multi-thread timeline when
its recording is on.
The timeline is the main recording structure, and what every FlowStorm functionality is build upon.
You can see a diagram of the timeline internal structure
[here](https://raw.githubusercontent.com/flow-storm/flow-storm-debugger/master/docs/timeline.svg).
It is currently implemented as `flow-storm.runtime.indexes.timeline-index/ExecutionTimelineTree` type, which internally
uses a mutable list. It would be simpler if this could be an immutable list but the decision was made because it needs
to be fast to build, and without too much garbage generation, so we don't make the debuggee threads too slow.
With the current architecture transients can't be used because there isn't a trace that indicates that a thread is done,
so it can't be persisted. Maybe it can be done in the future if some kind of batching by time is implemented.
All objects that represent traces are defined by types in `flow-storm.runtime.types.*` instead of maps. This is to
reduce memory footprint.
All the timelines can be explored from the repl by using the functions defined in `flow-storm.runtime.indexes.api`.
The __runtime__ exposes all the indexes functionality to debuggers via `flow-storm.runtime.debuggers-api`. The main
difference between this and `flow-storm.runtime.indexes.api` is that the former will return value ids instead of actual
values, since not all values can leave the debuggee process (think of infinite sequences), and also because of
performance, since most of the time, for big nested values, the user is interested in a small part of them and
serializing is expensive.
When debugging locally, the functions in `flow-storm.runtime.debuggers-api` will be called directly, while they will be
called through a websocket in the remote case.
The __runtime__ part is packaged into `com.github.flow-storm/flow-storm-inst` as well as in
`com.github.flow-storm/flow-storm-dbg` artifacts.
### Debugger
The __debugger__ is the part of FlowStorm that implements all the tools to explore the recordings with a GUI.
Its main entry point is `flow-storm.debugger.main/start-debugger`.
It has a bunch of subsystems that implement different aspects of it :
* `flow-storm.debugger.state/state`
* `flow-storm.debugger.runtime-api/rt-api`
* `flow-storm.debugger.ui.main/ui`
* `flow-storm.debugger.events-queue/events-queue`
* `flow-storm.debugger.websocket/websocket-server`
* `flow-storm.debugger.repl.core/repl`
All namespaces have doc strings explaining they purpose and how they work.
Not all subsystems will be running in all modes. For example the `websocket-server` and the `repl` client will be only
running in remote debugging mode, since they are not needed for local debugging.
The __debugger__ uses a custom component system defined in `flow-storm.state-management`, which is a very simple `mount`
like component system. It doesn't use `mount` since we don't want to drag too many dependencies to the user classpath.
The __debugger__ GUI is implemented as a JavaFX application, with all screens implemented in `flow-storm.debugger.ui.*`.
### The coordinate system
All expressions traces will contain a `:coord` field, which specifies the coordinate inside the form with id `:form-id`
a value refers to.
The coordinates are stored as a string, like `"3,2"`. They are stored as strings for performance reasons. This is
because on ClojureStorm, they can be compiled to the strings constant pool, which will be interned when the class is loaded and
they can thereof be referenced by traces.
As an example, the coordinate `"3,2"` for the form :
```clojure
(defn foo [a b] (+ a b))
```
refers to the second symbol `b` in the `(+ a b)` expression, which is under coordinate `3`.
The coordinates are a zero based collection of positional indexes from the root, for all but for maps
and sets.
For them instead of an index it will be, for a :
map key : a string K followed by the hash of the key form
map value: a string V followed by the hash of the key form for the val
For sets it will also be a string K followed by the hash of the set
element form.
As an example :
(defn foo [a b]
(assoc {1 10
(+ 42 43) 100}
:x #{(+ 1 2) (+ 3 4) (+ 4 5) (+ 1 1 (* 2 2))}))
some examples coordinates :
[3 1 "K-240379483"] => (+ 42 43)
[3 2 "K1305480196" 3] => (* 2 2)
You can see an implementation of this hash function in `hansel.utils/clojure-form-source-hash`.
The reason we can't use indices for maps and sets is because the order is not guaranteed.
### Values
As described in the __runtime__ section, recorded values never leave `flow-storm.runtime.debuggers-api`.
They are stored into a registry, and a reference to them is returned. This references are defined in
`flow-storm.types/ValueRef`, which acts as a reified pointer.
Whatever is using `flow-storm.runtime.debuggers-api` will deal with this `ValueRef`s instead of actual values.
Functions are also exposed by `flow-storm.runtime.debuggers-api` to work with `ValueRef`s :
* `val-pprint` for printing a value into a string representation with provided `print-level` and `print-length`
The values registry implementation is a little more involved than just a map from (hash value) -> value because not all
values can be hashed, specially infinite sequences. For that, every value is wrapped in a
`flow-storm.runtime.values/HashableObjWrap` which will use `flow-storm.utils/object-id` for the hash.
### Events
Events are a way for the __runtime__ to communicate information to the __debugger__.
All possible events are defined in `flow-storm.runtime.events`.
If there is nothing subscribed to runtime events the events will accumulate inside
`flow-storm.runtime.events/pending-events`, and as soon as there is a subscription they will be dispatched.
This is to capture events fired by recording when the __debugger__ is still not connected.
On the __debugger__ side they will accumulate on `flow-storm.debugger.events-queue` and will be dispatched by a
specialized thread. Most events are processed by `flow-storm.debugger.events-processor/process-event` but any part
of the __debugger__ can listen to __runtime__ events by adding a callback with `flow-storm.debugger.events-queue/add-dispatch-fn`.
### Tasks
Most of the __runtime__ functionality the __debugger__ calls is called synchronously, but there are some functions where doing
like this leads to suboptimal UX. This are most functionality that needs to search or collect on the timeline. For big recordings
this could take a long time.
For this reason, functions that loop on the timeline run under tasks. This are looping process that run async, can report progress
and can be interrupted.
All the functions that run tasks ends up in `-task`, like `search-collect-timelines-entries-task`.
From the __debugger__, for calling a task function `flow-storm.debugger.ui.tasks/submit-task` can be used.
On the __runtime__ side, there are a couple of utilities that make implementing this interruptible loopings easies :
- `flow-storm.runtime.debuggers-api/submit-batched-collect-interruptible-task` (for collecting functionality that traverse the entire timeline)
- `flow-storm.runtime.debuggers-api/submit-find-interruptible-task` (for looping through first match)
There
### Remote debugging
Remote debugging means that the __debugger__ and the __runtime__ run in different processes.
This is optional in Clojure but the only way of debugging in ClojureScript.
The difference with local debugging is that the interaction happens over a websocket and a repl connection instead of
direct function calls. Interaction here refers to api calls from the __debugger__ to the __runtime__ and events flowing
the other way around.
The debuggee can start a repl server which the __debugger__ can then connect to, while and the __debugger__ will start a
websocket server that the __runtime__ will connect to by running `flow-storm.runtime.debuggers-api/remote-connect`.
The reason there are two different kinds of connections between the same processes are capabilities and performance.
The repl connection is the only way of accomplishing some functionality in ClojureScript, while the websocket is
better suited for events dispatches.
Most of the api calls travel trough the websocket connection, you can see which goes through which connection in
`flow-storm.debugger.runtime-api/rt-api`.
## FlowStorm dev tips
The easiest way in my opinion to work with the FlowStorm codebase is by starting a repl with the `:dev` and `:storm`
aliases (unless one needs to specifically try Vanilla FlowStorm).
The FlowStorm codebase includes a dev namespace inside `src-dev/dev.clj`, which contains some handy code for development,
and is designed so you can work without having to restart the repl.
Specially :
* `start-local` to start everything locally, with spec checking the `flow-storm.debugger.state/state`
* `start-remote` to start only the __debugger__ and connect to a different process, with spec checking the `flow-storm.debugger.state/state`
* `stop` to shutdown the system gracefully
* `refresh` which will use tools.namespace to refresh namespaces
After the UI is running, everything you eval under the `dev-tester` namespace will be recorded. The `dev-tester`
namespace contains a bunch of meaningless functions, with the only purpose of generating data for testing the system.
In most situations, you should be able to change and re-eval a function on the repl and retry it without restarting the UI.
## Working with the UI
When tweaking the UI [ScenicView](https://github.com/JonathanGiles/scenic-view) helps a lot, since it allows us to
explore the JavaFX Nodes tree, and also reload css on the fly.
## Using FlowStorm as a backend for other tooling
FlowStorm includes an nRepl middleware `flow-storm.nrepl.middleware` which exposes all its functionality as nRepl
operations.
For examples of tooling using this middleware, take a look at [cider-storm](https://github.com/flow-storm/cider-storm).
## Working with ClojureStorm
Since ClojureStorm is a fork of Clojure, any technique you use for working with the Clojure compiler will apply here.
For example if you start the repl with `:storm`, `:dev` and `:jvm-debugger` you should be able to connect a Java debugger
like IntelliJ's one and add breakpoints. Then run whatever expression at the repl and step with the debugger.
Another handy tool for troubleshooting instrumentation is
[clj-java-decompiler](https://github.com/clojure-goes-fast/clj-java-decompiler).
Let's say you have instrumented:
```clojure
(defn sum [a b] (+ a b))
```
You can then decompile it into :
```java
public final class dev_tester$sum extends AFunction
{
public static Object invokeStatic(final Object a, final Object b) {
Number add;
try {
Tracer.traceFnCall(new Object[] { a, b }, "user", "sum", -1340777963);
Tracer.traceBind(a, "", "a");
Tracer.traceBind(b, "", "b");
Tracer.traceExpr(a, "3,1", -1340777963);
Tracer.traceExpr(b, "3,2", -1340777963);
add = Numbers.add(a, b);
Tracer.traceExpr(add, "3", -1340777963);
Tracer.traceFnReturn(add, "", -1340777963);
}
catch (Throwable throwable) {
Tracer.traceFnUnwind(throwable, "", -1340777963);
throw throwable;
}
return add;
}
@Override
public Object invoke(final Object a, final Object b) {
return invokeStatic(a, b);
}
}
```
or its bytecode equivalent.
## Working with ClojureScriptStorm
For working with ClojureScript storm we can use FlowStorm! since it is a Clojure codebase.
Take a look at this for ideas : https://jpmonettas.github.io/my-blog/public/compilers-with-flow-storm.html
## Installing local versions
You can install both artifacts locally by running :
```bash
$ VERSION=3.8.4-SNAPSHOT make install-inst # for building and installing the inst artifact
$ VERSION=3.8.4-SNAPSHOT make install-dbg # for building and installing the dbg artifact
```
================================================
FILE: docs/high_level_diagram.drawio
================================================
<mxfile host="Electron" modified="2024-11-28T15:27:53.852Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.1 Chrome/112.0.5615.204 Electron/24.6.1 Safari/537.36" etag="_nN5NNCstrdBxJAHVi_g" version="21.6.1" type="device">
<diagram name="Page-1" id="KafUcJg5-WmZ6K7IxeHq">
<mxGraphModel dx="1430" dy="844" grid="0" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="4681" pageHeight="3300" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="op3-RK94J-eC3h-jjMCj-39" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=-0.036;entryY=0.533;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1195.6799999999998" y="1485.9900000000002" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-48" value="" style="group;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="1" vertex="1" connectable="0">
<mxGeometry x="1120" y="1170" width="160" height="140" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-43" value="<h1 style="font-size: 8px;"><font style="font-size: 8px;"><br></font></h1><h1 style="font-size: 8px;">Debuggers API</h1><div style="font-size: 8px;"><div style=""><div style=""><div>Exposes Indexes API with reified value pointers&nbsp;</div></div><div><br></div></div></div>" style="text;html=1;strokeColor=#9673a6;fillColor=#e1d5e7;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="op3-RK94J-eC3h-jjMCj-48" vertex="1">
<mxGeometry width="160" height="140" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-46" value="<h1 style="font-size: 8px;"><br></h1><div style="text-align: center;"><font style="font-size: 10px;"><b>Value references</b></font></div><div style="text-align: center;"><font style="font-size: 10px;">{vid value-ptr}</font></div>" style="text;html=1;strokeColor=#d6b656;fillColor=#fff2cc;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="op3-RK94J-eC3h-jjMCj-48" vertex="1">
<mxGeometry x="30" y="72.5" width="90" height="45" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-49" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" parent="1" source="op3-RK94J-eC3h-jjMCj-43" target="op3-RK94J-eC3h-jjMCj-29" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1370" y="1240" />
<mxPoint x="1370" y="1324" />
<mxPoint x="1865" y="1324" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-51" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" parent="1" source="op3-RK94J-eC3h-jjMCj-50" target="op3-RK94J-eC3h-jjMCj-43" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1170" y="1080" />
<mxPoint x="1160" y="1080" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-54" value="" style="endArrow=none;dashed=1;html=1;rounded=0;fontColor=#EA6B66;labelBorderColor=#EA6B66;labelBackgroundColor=#EA6B66;fillColor=#f8cecc;strokeColor=#b85450;" parent="1" edge="1">
<mxGeometry width="50" height="50" relative="1" as="geometry">
<mxPoint x="870" y="870" as="sourcePoint" />
<mxPoint x="2290" y="870" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-55" value="Runtime (JVM | Browser | NodeJS)" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;fontColor=#EA6B66;" parent="1" vertex="1">
<mxGeometry x="890" y="870" width="200" height="30" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-67" value="<font style="font-size: 11px;">call-fn</font>" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.723;entryY=0.006;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="op3-RK94J-eC3h-jjMCj-61" target="op3-RK94J-eC3h-jjMCj-43" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="1180" y="620" />
<mxPoint x="1236" y="620" />
<mxPoint x="1236" y="650" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-69" value="" style="swimlane;startSize=0;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="1" vertex="1">
<mxGeometry x="750" y="377" width="1690" height="1360" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-68" value="<b>High level overview of FlowStorm architecture</b>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="565" y="60" width="680" height="30" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-29" value="<h1 style="font-size: 8px;"><font style="font-size: 8px;"><br></font></h1><h1 style="font-size: 8px;"><font style="font-size: 8px;">Indexes API</font></h1><div style="font-size: 8px;"><div style=""><div style=""><font style="font-size: 8px;">- (add-fn-call ...)</font></div><div style=""><font style="font-size: 8px;">- ...</font></div><div style=""><font style="font-size: 8px;">- (get-form ...)</font></div><div style=""><font style="font-size: 8px;">- (all-threads ...)</font></div><div style=""><font style="font-size: 8px;">- (all-forms ...)</font></div><div style=""><font style="font-size: 8px;">- (timeline-count ...)</font></div><div style=""><font style="font-size: 8px;">- (timeline-entry ...)</font></div><div style=""><font style="font-size: 8px;">- ...</font></div></div></div>" style="text;html=1;strokeColor=#9673a6;fillColor=#e1d5e7;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="1115" y="920" width="160" height="105" as="geometry" />
</mxCell>
<mxCell id="SQkHDufhQtF8TV6C0DT0-6" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;" parent="op3-RK94J-eC3h-jjMCj-69" source="op3-RK94J-eC3h-jjMCj-32" target="SQkHDufhQtF8TV6C0DT0-3" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="SQkHDufhQtF8TV6C0DT0-13" value="push" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="SQkHDufhQtF8TV6C0DT0-6" vertex="1" connectable="0">
<mxGeometry x="0.1563" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-32" value="<h1 style="font-size: 8px;"><font style="font-size: 8px;"><br></font></h1><h1 style="font-size: 8px;">Events</h1><div style="font-size: 8px;"><div style=""><div style=""><div><span style="background-color: initial;">- [:flow-created]</span><br></div><div>- [:threads-updated]</div><div>- [:tap]</div><div>- ...</div><div>- ...</div><div><br></div></div><div><br></div></div></div>" style="text;html=1;strokeColor=#9673a6;fillColor=#e1d5e7;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="700" y="666" width="160" height="90" as="geometry" />
</mxCell>
<mxCell id="SQkHDufhQtF8TV6C0DT0-9" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;startArrow=classic;startFill=1;dashed=1;" parent="op3-RK94J-eC3h-jjMCj-69" source="op3-RK94J-eC3h-jjMCj-50" target="SQkHDufhQtF8TV6C0DT0-7" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-50" value="websocket-client" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="410" y="520" width="30" height="100" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-52" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.75;entryY=1;entryDx=0;entryDy=0;" parent="op3-RK94J-eC3h-jjMCj-69" source="op3-RK94J-eC3h-jjMCj-32" target="op3-RK94J-eC3h-jjMCj-50" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-53" value="push" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="op3-RK94J-eC3h-jjMCj-52" vertex="1" connectable="0">
<mxGeometry x="-0.3617" y="1" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-66" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0;entryDx=0;entryDy=0;startArrow=classic;startFill=1;exitX=0.75;exitY=1;exitDx=0;exitDy=0;" parent="op3-RK94J-eC3h-jjMCj-69" source="op3-RK94J-eC3h-jjMCj-60" target="SQkHDufhQtF8TV6C0DT0-7" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="385" y="340" />
<mxPoint x="418" y="340" />
<mxPoint x="418" y="370" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="SQkHDufhQtF8TV6C0DT0-5" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.75;exitY=0;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;" parent="op3-RK94J-eC3h-jjMCj-69" source="SQkHDufhQtF8TV6C0DT0-7" target="SQkHDufhQtF8TV6C0DT0-3" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="433" y="340" />
<mxPoint x="535" y="340" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-28" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.025;entryY=0.444;entryDx=0;entryDy=0;entryPerimeter=0;startArrow=classic;startFill=1;" edge="1" parent="op3-RK94J-eC3h-jjMCj-69" source="op3-RK94J-eC3h-jjMCj-56" target="3JVosarM1otrULn8aCVN-23">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-56" value="Debugger UI" style="shape=process;whiteSpace=wrap;html=1;backgroundOutline=1;fillColor=#cdeb8b;strokeColor=#36393d;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="400" y="90" width="120" height="60" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-57" value="runtime-api" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#cdeb8b;strokeColor=#36393d;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="340" y="180" width="120" height="20" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-60" value="remote" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#cdeb8b;strokeColor=#36393d;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="340" y="200" width="60" height="20" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-61" value="local" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#cdeb8b;strokeColor=#36393d;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="400" y="200" width="60" height="20" as="geometry" />
</mxCell>
<mxCell id="SQkHDufhQtF8TV6C0DT0-11" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;startArrow=classic;startFill=1;dashed=1;" parent="op3-RK94J-eC3h-jjMCj-69" source="op3-RK94J-eC3h-jjMCj-62" target="SQkHDufhQtF8TV6C0DT0-10" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-62" value="repl-client" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;fillColor=#CDEB8B;strokeColor=#82b366;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="355" y="390" width="30" height="80" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-64" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="op3-RK94J-eC3h-jjMCj-69" source="op3-RK94J-eC3h-jjMCj-60" target="op3-RK94J-eC3h-jjMCj-62" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-27" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.846;exitY=0.02;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;entryPerimeter=0;exitPerimeter=0;startArrow=classic;startFill=1;" edge="1" parent="op3-RK94J-eC3h-jjMCj-69" source="SQkHDufhQtF8TV6C0DT0-1" target="3JVosarM1otrULn8aCVN-23">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="582" y="175" />
<mxPoint x="581" y="175" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="SQkHDufhQtF8TV6C0DT0-1" value="events-procesor" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#cdeb8b;strokeColor=#36393d;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="480" y="180" width="120" height="20" as="geometry" />
</mxCell>
<mxCell id="SQkHDufhQtF8TV6C0DT0-4" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" parent="op3-RK94J-eC3h-jjMCj-69" source="3JVosarM1otrULn8aCVN-2" target="SQkHDufhQtF8TV6C0DT0-1" edge="1">
<mxGeometry relative="1" as="geometry">
<Array as="points" />
</mxGeometry>
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-63" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;fillColor=#cdeb8b;strokeColor=#36393d;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" parent="op3-RK94J-eC3h-jjMCj-69" source="op3-RK94J-eC3h-jjMCj-56" target="op3-RK94J-eC3h-jjMCj-57" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="400" y="160" as="sourcePoint" />
<Array as="points">
<mxPoint x="460" y="167" />
<mxPoint x="400" y="167" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="SQkHDufhQtF8TV6C0DT0-2" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.617;entryY=0.989;entryDx=0;entryDy=0;fillColor=#cdeb8b;strokeColor=#36393d;exitX=0.472;exitY=-0.017;exitDx=0;exitDy=0;exitPerimeter=0;entryPerimeter=0;" parent="op3-RK94J-eC3h-jjMCj-69" source="SQkHDufhQtF8TV6C0DT0-1" target="op3-RK94J-eC3h-jjMCj-56" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="470" y="160" as="sourcePoint" />
<mxPoint x="410" y="190" as="targetPoint" />
<Array as="points">
<mxPoint x="537" y="166" />
<mxPoint x="474" y="166" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="SQkHDufhQtF8TV6C0DT0-7" value="websocket-server" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;fillColor=#CDEB8B;strokeColor=#82b366;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="410" y="370" width="30" height="100" as="geometry" />
</mxCell>
<mxCell id="SQkHDufhQtF8TV6C0DT0-10" value="repl-server" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="355" y="520" width="30" height="80" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-38" value="REPL" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="380" y="1070" width="80" height="80" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-40" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.013;entryY=0.552;entryDx=0;entryDy=0;entryPerimeter=0;" parent="op3-RK94J-eC3h-jjMCj-69" source="op3-RK94J-eC3h-jjMCj-38" target="op3-RK94J-eC3h-jjMCj-29" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="445.67999999999984" y="1166.6800000000003" as="targetPoint" />
<Array as="points">
<mxPoint x="420" y="978" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-1" value="<font color="#ffffff"><b>Instrumented Code</b></font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#FF6666;strokeColor=#b85450;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="1130" y="530" width="130" height="60" as="geometry" />
</mxCell>
<mxCell id="SQkHDufhQtF8TV6C0DT0-14" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="op3-RK94J-eC3h-jjMCj-69" source="op3-RK94J-eC3h-jjMCj-3" target="op3-RK94J-eC3h-jjMCj-29" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-3" value="<h1><font style="font-size: 12px;">Tracer API</font></h1><div><div style="">- (trace-form-init …)</div><div style="">- (trace-fn-call …)</div><div style="">- (trace-fn-return …)<br></div><div style="">- (trace-fn-unwind …)</div><div style="">- (trace-expr-exec …)</div></div><div style="">- (trace-bind ...)</div>" style="text;html=1;strokeColor=#9673a6;fillColor=#e1d5e7;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="1115" y="690" width="160" height="150" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-5" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="op3-RK94J-eC3h-jjMCj-69" source="op3-RK94J-eC3h-jjMCj-1" target="op3-RK94J-eC3h-jjMCj-3" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-33" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;exitX=-0.001;exitY=0.096;exitDx=0;exitDy=0;exitPerimeter=0;" parent="op3-RK94J-eC3h-jjMCj-69" source="op3-RK94J-eC3h-jjMCj-29" target="op3-RK94J-eC3h-jjMCj-32" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-34" value="publish" style="edgeLabel;html=1;align=center;verticalAlign=middle;resizable=0;points=[];" parent="op3-RK94J-eC3h-jjMCj-33" vertex="1" connectable="0">
<mxGeometry x="-0.0141" y="2" relative="1" as="geometry">
<mxPoint as="offset" />
</mxGeometry>
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-22" value="" style="group;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1" connectable="0">
<mxGeometry x="1075" y="1061" width="200" height="270" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-7" value="" style="rounded=0;whiteSpace=wrap;html=1;" parent="op3-RK94J-eC3h-jjMCj-22" vertex="1">
<mxGeometry x="40" y="20" width="120" height="160" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-8" value="" style="rounded=0;whiteSpace=wrap;html=1;" parent="op3-RK94J-eC3h-jjMCj-22" vertex="1">
<mxGeometry x="50" y="30" width="120" height="160" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-9" value="" style="rounded=0;whiteSpace=wrap;html=1;" parent="op3-RK94J-eC3h-jjMCj-22" vertex="1">
<mxGeometry x="60" y="40" width="120" height="181" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-13" value="" style="group;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="op3-RK94J-eC3h-jjMCj-22" vertex="1" connectable="0">
<mxGeometry x="70" y="70" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-10" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="op3-RK94J-eC3h-jjMCj-13" vertex="1">
<mxGeometry width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-11" value="<font style="font-size: 8px;">Thread-0</font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="op3-RK94J-eC3h-jjMCj-13" vertex="1">
<mxGeometry width="40" height="10" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-12" value="timeline" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" parent="op3-RK94J-eC3h-jjMCj-13" vertex="1">
<mxGeometry x="7.5" y="20" width="85" height="20" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-14" value="" style="group;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="op3-RK94J-eC3h-jjMCj-22" vertex="1" connectable="0">
<mxGeometry x="70" y="130" width="100" height="50" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-16" value="<font style="font-size: 8px;">Thread-1</font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="op3-RK94J-eC3h-jjMCj-14" vertex="1">
<mxGeometry width="40" height="10" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-17" value="timeline" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" parent="op3-RK94J-eC3h-jjMCj-14" vertex="1">
<mxGeometry x="7.5" y="20" width="85" height="20" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-18" value="<font style="font-size: 7px;">Flow-0</font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="op3-RK94J-eC3h-jjMCj-22" vertex="1">
<mxGeometry x="30" y="20" width="60" height="10" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-19" value="<font style="font-size: 7px;">Flow-1</font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="op3-RK94J-eC3h-jjMCj-22" vertex="1">
<mxGeometry x="40" y="30" width="60" height="10" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-20" value="<font style="font-size: 7px;">Flow-2</font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="op3-RK94J-eC3h-jjMCj-22" vertex="1">
<mxGeometry x="50" y="40" width="60" height="10" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-21" value="flow-thread-registry" style="swimlane;horizontal=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="op3-RK94J-eC3h-jjMCj-22" vertex="1">
<mxGeometry width="200" height="240" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-26" value="mt-timeline" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="op3-RK94J-eC3h-jjMCj-22">
<mxGeometry x="70" y="192" width="99" height="20" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-24" value="forms-registry" style="swimlane;horizontal=0;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="op3-RK94J-eC3h-jjMCj-69" vertex="1">
<mxGeometry x="863" y="1059" width="200" height="244" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-25" value="<font style="font-size: 8px;">(defn foo [...])</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="op3-RK94J-eC3h-jjMCj-24" vertex="1">
<mxGeometry x="50" y="20" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-26" value="<font style="font-size: 8px;">(defn bar [...])</font>" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" parent="op3-RK94J-eC3h-jjMCj-24" vertex="1">
<mxGeometry x="50" y="60" width="120" height="30" as="geometry" />
</mxCell>
<mxCell id="bpvOFd_PEQqJzHh414_2-1" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" parent="op3-RK94J-eC3h-jjMCj-69" source="op3-RK94J-eC3h-jjMCj-29" target="op3-RK94J-eC3h-jjMCj-12" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-4" value="" style="group" vertex="1" connectable="0" parent="op3-RK94J-eC3h-jjMCj-69">
<mxGeometry x="522.5" y="230" width="35" height="125" as="geometry" />
</mxCell>
<mxCell id="SQkHDufhQtF8TV6C0DT0-3" value="events-queue" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#cdeb8b;strokeColor=#36393d;flipV=0;rotation=-90;" parent="3JVosarM1otrULn8aCVN-4" vertex="1">
<mxGeometry x="-27.5" y="70" width="90" height="20" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-2" value="Th" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="3JVosarM1otrULn8aCVN-4">
<mxGeometry width="35" height="35" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-3" value="" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="3JVosarM1otrULn8aCVN-4" source="SQkHDufhQtF8TV6C0DT0-3" target="3JVosarM1otrULn8aCVN-2">
<mxGeometry relative="1" as="geometry">
<mxPoint x="1290" y="635" as="sourcePoint" />
<mxPoint x="1290" y="570" as="targetPoint" />
</mxGeometry>
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-10" value="" style="group" vertex="1" connectable="0" parent="op3-RK94J-eC3h-jjMCj-69">
<mxGeometry x="720" y="550" width="110" height="70" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-5" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="3JVosarM1otrULn8aCVN-10">
<mxGeometry width="110" height="70" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-6" value="Jobs" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="3JVosarM1otrULn8aCVN-10">
<mxGeometry x="30" y="40" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-8" value="Th" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="3JVosarM1otrULn8aCVN-10">
<mxGeometry x="10" y="5" width="35" height="35" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-9" value="Th" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="3JVosarM1otrULn8aCVN-10">
<mxGeometry x="70" y="5" width="35" height="35" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="op3-RK94J-eC3h-jjMCj-69" source="3JVosarM1otrULn8aCVN-6" target="op3-RK94J-eC3h-jjMCj-32">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=0;exitDx=0;exitDy=0;entryX=0.5;entryY=1;entryDx=0;entryDy=0;" edge="1" parent="op3-RK94J-eC3h-jjMCj-69" source="3JVosarM1otrULn8aCVN-14" target="op3-RK94J-eC3h-jjMCj-32">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-13" value="" style="group" vertex="1" connectable="0" parent="op3-RK94J-eC3h-jjMCj-69">
<mxGeometry x="725" y="810" width="110" height="70" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-14" value="" style="rounded=1;whiteSpace=wrap;html=1;fillColor=#e1d5e7;strokeColor=#9673a6;" vertex="1" parent="3JVosarM1otrULn8aCVN-13">
<mxGeometry width="110" height="70" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-15" value="Interruptible<br>Async Tasks" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="3JVosarM1otrULn8aCVN-13">
<mxGeometry x="19" y="40" width="77" height="30" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-16" value="Th" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="3JVosarM1otrULn8aCVN-13">
<mxGeometry x="10" y="5" width="35" height="35" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-17" value="Th" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="3JVosarM1otrULn8aCVN-13">
<mxGeometry x="70" y="5" width="35" height="35" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-22" value="FXTh" style="ellipse;whiteSpace=wrap;html=1;aspect=fixed;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="op3-RK94J-eC3h-jjMCj-69">
<mxGeometry x="392" y="71" width="35" height="35" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-23" value="Dbg<br>State" style="shape=cylinder3;whiteSpace=wrap;html=1;boundedLbl=1;backgroundOutline=1;size=15;fillColor=#fff2cc;strokeColor=#d6b656;" vertex="1" parent="op3-RK94J-eC3h-jjMCj-69">
<mxGeometry x="557.5" y="90" width="47" height="66" as="geometry" />
</mxCell>
<mxCell id="SQkHDufhQtF8TV6C0DT0-12" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;entryX=0.085;entryY=0.003;entryDx=0;entryDy=0;entryPerimeter=0;" parent="1" source="SQkHDufhQtF8TV6C0DT0-10" target="op3-RK94J-eC3h-jjMCj-43" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="3JVosarM1otrULn8aCVN-18" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.994;exitY=0.378;exitDx=0;exitDy=0;entryX=0;entryY=0.5;entryDx=0;entryDy=0;startArrow=none;startFill=0;exitPerimeter=0;" edge="1" parent="1" source="op3-RK94J-eC3h-jjMCj-43" target="3JVosarM1otrULn8aCVN-14">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="op3-RK94J-eC3h-jjMCj-6" value="<font style="font-size: 7px;">[coord, value pointer, ...]</font>" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="1" vertex="1">
<mxGeometry x="1905" y="1020" width="80" height="20" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
================================================
FILE: docs/index.html
================================================
<h1>FlowStorm</h1>
<ul>
<li><a href="/user_guide.html">User Guide</a></li>
</ul>
================================================
FILE: docs/related-research-and-tools.md
================================================
- [Debugging Backwards in Time, Bil Lewis Paper](https://arxiv.org/pdf/cs/0310016.pdf)
- [Debugging Backwards in Time, Bil Lewis Debugger](https://github.com/OmniscientDebugger/LewisOmniscientDebugger)
- [Scalable Omniscient Debugging, Pothier, TOD, paper](https://pleiad.cl/papers/2007/pothierAl-oopsla2007.pdf)
- [Scalable Omniscient Debugging, Pothier, TOD, software](https://pleiad.cl/tod/)
- [NOD4J: Near-omniscient debugging tool for Java using size-limited execution trace](https://sel.ist.osaka-u.ac.jp/lab-db/betuzuri/archive/1199/1199.pdf)
- [Repeatedly-Executed-Method Viewer for Efficient Visualization of Execution Paths and States in Java](https://citeseerx.ist.psu.edu/document?repid=rep1&type=pdf&doi=233a50cdc9ba4ce0b322053cebbc380ec93a728c)
- [JIVE: Java Interactive Visualization Environment](https://cse.buffalo.edu/jive/tutorials.html)
- [Undo debugger for Java](https://undo.io/solutions/products/java/)
- [ChrononSystems debugger for Java](https://chrononsystems.com/products/chronon-time-travelling-debugger)
- [Wallabyjs](https://wallabyjs.com/docs/intro/time-travel-debugger.html)
- [ZStep common lisp, video](https://www.youtube.com/watch?v=VzGj59hg2io)
- [ZStep common lisp, paper](https://www.lirmm.fr/%7Educour/Doc-objets/ECOOP/papers/0276/02760011.pdf)
================================================
FILE: docs/run_configs.drawio
================================================
<mxfile host="Electron" modified="2023-07-21T18:53:27.851Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.1 Chrome/112.0.5615.204 Electron/24.6.1 Safari/537.36" etag="o80L2qysQee_8nPQAhx9" version="21.6.1" type="device">
<diagram name="Page-1" id="fo5acYU9oirqmaVJ-L3K">
<mxGraphModel dx="8138" dy="2040" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="4681" pageHeight="3300" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="LKMU_nn0iRrz3Z-2fcly-2" value="Clojure Local" style="swimlane;horizontal=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="170" y="430" width="400" height="550" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-5" value="JVM" style="swimlane;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-2">
<mxGeometry x="75" y="30" width="250" height="480" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-8" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="LKMU_nn0iRrz3Z-2fcly-5" source="LKMU_nn0iRrz3Z-2fcly-6" target="LKMU_nn0iRrz3Z-2fcly-7">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-6" value="Debugger UI" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-5">
<mxGeometry x="40" y="50" width="160" height="60" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-7" value="Runtime (timelines)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-5">
<mxGeometry x="40" y="390" width="160" height="60" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-3" value="Clojure Remote" style="swimlane;horizontal=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="640" y="430" width="400" height="550" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-9" value="JVM-1" style="swimlane;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-3">
<mxGeometry x="85" y="30" width="250" height="130" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-10" value="Debugger UI" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-9">
<mxGeometry x="40" y="40" width="160" height="60" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-11" value="JVM-2" style="swimlane;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-3">
<mxGeometry x="85" y="310" width="250" height="200" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-35" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="LKMU_nn0iRrz3Z-2fcly-11" source="LKMU_nn0iRrz3Z-2fcly-12" target="LKMU_nn0iRrz3Z-2fcly-19">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-12" value="REPL" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-11">
<mxGeometry x="20" y="40" width="100" height="60" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-36" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.75;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="LKMU_nn0iRrz3Z-2fcly-11" source="LKMU_nn0iRrz3Z-2fcly-18" target="LKMU_nn0iRrz3Z-2fcly-19">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-18" value="WebSocket" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-11">
<mxGeometry x="135" y="40" width="100" height="60" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-19" value="Runtime (timelines)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-11">
<mxGeometry x="20" y="120" width="210" height="60" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-20" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="LKMU_nn0iRrz3Z-2fcly-3" source="LKMU_nn0iRrz3Z-2fcly-10" target="LKMU_nn0iRrz3Z-2fcly-12">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-21" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.5;entryY=0;entryDx=0;entryDy=0;startArrow=classic;startFill=1;" edge="1" parent="LKMU_nn0iRrz3Z-2fcly-3" source="LKMU_nn0iRrz3Z-2fcly-10" target="LKMU_nn0iRrz3Z-2fcly-18">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="250" y="240" />
<mxPoint x="270" y="240" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-4" value="ClojureScript" style="swimlane;horizontal=0;whiteSpace=wrap;html=1;" vertex="1" parent="1">
<mxGeometry x="1110" y="430" width="400" height="560" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-22" value="JVM-1" style="swimlane;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-4">
<mxGeometry x="80" y="30" width="250" height="130" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-23" value="Debugger UI" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#d5e8d4;strokeColor=#82b366;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-22">
<mxGeometry x="40" y="40" width="160" height="60" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-24" value="JVM-2" style="swimlane;whiteSpace=wrap;html=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-4">
<mxGeometry x="80" y="190" width="250" height="130" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-25" value="Shadow CLJ REPL" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-24">
<mxGeometry x="10" y="40" width="160" height="60" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-26" value="Browser" style="swimlane;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-4">
<mxGeometry x="80" y="340" width="250" height="170" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-34" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.905;entryY=0;entryDx=0;entryDy=0;entryPerimeter=0;" edge="1" parent="LKMU_nn0iRrz3Z-2fcly-26" source="LKMU_nn0iRrz3Z-2fcly-28" target="LKMU_nn0iRrz3Z-2fcly-29">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-28" value="WebSocket" style="rounded=0;whiteSpace=wrap;html=1;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-26">
<mxGeometry x="140" y="30" width="100" height="40" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-29" value="Runtime (timelines)" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#f8cecc;strokeColor=#b85450;" vertex="1" parent="LKMU_nn0iRrz3Z-2fcly-26">
<mxGeometry x="20" y="100" width="190" height="60" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-30" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;" edge="1" parent="LKMU_nn0iRrz3Z-2fcly-4" source="LKMU_nn0iRrz3Z-2fcly-23" target="LKMU_nn0iRrz3Z-2fcly-25">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-31" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;startArrow=classic;startFill=1;" edge="1" parent="LKMU_nn0iRrz3Z-2fcly-4" source="LKMU_nn0iRrz3Z-2fcly-23" target="LKMU_nn0iRrz3Z-2fcly-28">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="270" y="210" />
<mxPoint x="270" y="210" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-32" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=0;entryDx=0;entryDy=0;" edge="1" parent="LKMU_nn0iRrz3Z-2fcly-4" source="LKMU_nn0iRrz3Z-2fcly-25" target="LKMU_nn0iRrz3Z-2fcly-29">
<mxGeometry relative="1" as="geometry">
<Array as="points">
<mxPoint x="150" y="380" />
<mxPoint x="148" y="380" />
</Array>
</mxGeometry>
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-33" value="<font size="1" style=""><b style="font-size: 24px;">Different configurations FlowStorm can be run in</b></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" vertex="1" parent="1">
<mxGeometry x="525" y="280" width="630" height="80" as="geometry" />
</mxCell>
<mxCell id="LKMU_nn0iRrz3Z-2fcly-37" value="" style="swimlane;startSize=0;" vertex="1" parent="1">
<mxGeometry x="-170" y="200" width="2060" height="980" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
================================================
FILE: docs/test-cases.org
================================================
* Test cases
| Name | Local Clj | Remote Clj | ClojureScript |
|---------------------------------+-----------+------------+----------------------|
| Basic #rtrace | OK | OK | OK |
| Basic #trace | OK | OK | OK |
| Def val | OK | OK | OK |
| Inspect val | OK | OK | OK |
| REPL [un]instrument var | OK | OK | OK / ~UN-FAIL-known~ |
| REPL [un]instrument namespaces | OK | OK | OK |
| Correct stepping | OK | OK | OK |
| Goto trace by typing idx | OK | OK | OK |
| Browser navigation | OK | OK | OK |
| Browser [un]instrument var | OK | OK | ~UN-FAIL-known~ |
| Browser [un]instrument ns | OK | OK | OK |
| Browser enable/disable instr | OK | OK | OK |
| Browser [un]instr sync | OK | OK | N/A |
| Cli-run | OK | N/A | N/A |
| Re-run flow | OK | OK | OK |
| Call tree | OK | OK | OK |
| Call tree goto trace | OK | OK | OK |
| Call search | OK | OK | OK |
| Functions list | OK | OK | OK |
| Functions list goto trace | OK | OK | OK |
| Fully instrument form | OK | OK | OK |
| UnInstrument forms from fn list | OK | OK | ~UN-FAIL-FAIL~ |
| Taps | OK | OK | OK |
| Themes | OK | OK | OK |
| Auto reconnect | N/A | OK | OK |
| | | | |
================================================
FILE: docs/timeline.drawio
================================================
<mxfile host="Electron" modified="2025-03-26T14:41:32.790Z" agent="Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) draw.io/21.6.1 Chrome/112.0.5615.204 Electron/24.6.1 Safari/537.36" etag="ip9CI5hasx3wubz7avyv" version="21.6.1" type="device">
<diagram name="Page-1" id="GOtxmTl-x517z7Cm9fr0">
<mxGraphModel dx="2074" dy="1197" grid="1" gridSize="10" guides="1" tooltips="1" connect="1" arrows="1" fold="1" page="1" pageScale="1" pageWidth="4681" pageHeight="3300" math="0" shadow="0">
<root>
<mxCell id="0" />
<mxCell id="1" parent="0" />
<mxCell id="MPshjHnfK2mcv_0NpmO8-31" value="" style="swimlane;startSize=0;" parent="1" vertex="1">
<mxGeometry x="130" y="290" width="1100" height="1200" as="geometry" />
</mxCell>
<mxCell id="yXxu-ckIG8VWti2M3FYv-1" value="" style="triangle;whiteSpace=wrap;html=1;rotation=-180;dashed=1;fillColor=#d5e8d4;strokeColor=#82b366;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="830" y="265" width="30" height="40" as="geometry" />
</mxCell>
<mxCell id="yXxu-ckIG8VWti2M3FYv-3" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0;exitY=0.5;exitDx=0;exitDy=0;entryX=0;entryY=0.75;entryDx=0;entryDy=0;dashed=1;fillColor=#d5e8d4;strokeColor=#82b366;endArrow=none;endFill=0;" parent="MPshjHnfK2mcv_0NpmO8-31" source="MPshjHnfK2mcv_0NpmO8-17" target="yXxu-ckIG8VWti2M3FYv-1" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-17" value="<h1><font style="font-size: 12px;">fn-return</font></h1><p style="line-height: 60%;">- coord&nbsp; &nbsp; &nbsp; (ref)</p><p style="line-height: 60%;">- retVal&nbsp; &nbsp; &nbsp; (ref)</p><p style="line-height: 60%;">- fnCallIdx (int)</p>" style="text;html=1;strokeColor=#82b366;fillColor=#d5e8d4;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="940" y="180" width="130" height="110" as="geometry" />
</mxCell>
<mxCell id="yXxu-ckIG8VWti2M3FYv-4" style="edgeStyle=orthogonalEdgeStyle;rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=-0.014;exitY=0.427;exitDx=0;exitDy=0;entryX=0;entryY=0.25;entryDx=0;entryDy=0;dashed=1;endArrow=none;endFill=0;fillColor=#d5e8d4;strokeColor=#82b366;exitPerimeter=0;" parent="MPshjHnfK2mcv_0NpmO8-31" source="yXxu-ckIG8VWti2M3FYv-2" target="yXxu-ckIG8VWti2M3FYv-1" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yXxu-ckIG8VWti2M3FYv-2" value="<h1><font style="font-size: 12px;">fn-unwind</font></h1><p style="line-height: 60%;">- coord&nbsp; &nbsp; &nbsp; &nbsp;(ref)</p><p style="line-height: 60%;">- throwable (ref)</p><p style="line-height: 60%;">- fnCallIdx&nbsp; &nbsp;(int)</p>" style="text;html=1;strokeColor=#82b366;fillColor=#d5e8d4;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="940" y="330" width="130" height="110" as="geometry" />
</mxCell>
<mxCell id="yXxu-ckIG8VWti2M3FYv-8" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.978;exitY=0.333;exitDx=0;exitDy=0;entryX=0.5;entryY=0.594;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=oval;endFill=1;exitPerimeter=0;dashed=1;fillColor=#d5e8d4;strokeColor=#82b366;" parent="MPshjHnfK2mcv_0NpmO8-31" source="yXxu-ckIG8VWti2M3FYv-7" target="yXxu-ckIG8VWti2M3FYv-5" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yXxu-ckIG8VWti2M3FYv-12" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.984;exitY=0.338;exitDx=0;exitDy=0;entryX=0.483;entryY=0.663;entryDx=0;entryDy=0;entryPerimeter=0;endArrow=oval;endFill=1;exitPerimeter=0;fillColor=#d5e8d4;strokeColor=#82b366;dashed=1;" parent="MPshjHnfK2mcv_0NpmO8-31" source="yXxu-ckIG8VWti2M3FYv-7" target="yXxu-ckIG8VWti2M3FYv-6" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-1" value="<p>fn-call</p>" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="335" y="245" width="40" height="80" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-2" value="<p>fn-end</p>" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="575" y="245" width="40" height="80" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-3" value="<p>expr-exec</p>" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="455" y="245" width="40" height="80" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-4" value="<p>fn-call</p>" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="415" y="245" width="40" height="80" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-5" value="<p>expr-exec</p>" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="495" y="245" width="40" height="80" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-6" value="<p>expr-exec</p>" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="535" y="245" width="40" height="80" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-7" value="<p>expr-exec</p>" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="615" y="245" width="40" height="80" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-8" value="<p>expr-exec</p>" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="655" y="245" width="40" height="80" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-9" value="<p>fn-end</p>" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;fillColor=#d5e8d4;strokeColor=#82b366;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="735" y="245" width="40" height="80" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-10" value="..." style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="685" y="270" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-11" value="<p>expr-exec</p>" style="rounded=0;whiteSpace=wrap;html=1;horizontal=0;fillColor=#f8cecc;strokeColor=#b85450;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="375" y="245" width="40" height="80" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-15" value="A thread timeline" style="swimlane;whiteSpace=wrap;html=1;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="295" y="205" width="510" height="200" as="geometry" />
</mxCell>
<mxCell id="yXxu-ckIG8VWti2M3FYv-7" value="Functions frames" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="MPshjHnfK2mcv_0NpmO8-15" vertex="1">
<mxGeometry x="55" y="160" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-20" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;endArrow=none;endFill=0;fillColor=#d5e8d4;strokeColor=#82b366;dashed=1;entryX=1;entryY=0.5;entryDx=0;entryDy=0;" parent="MPshjHnfK2mcv_0NpmO8-31" source="MPshjHnfK2mcv_0NpmO8-9" target="yXxu-ckIG8VWti2M3FYv-1" edge="1">
<mxGeometry relative="1" as="geometry">
<mxPoint x="690" y="310" as="sourcePoint" />
</mxGeometry>
</mxCell>
<mxCell id="yXxu-ckIG8VWti2M3FYv-5" value="" style="shape=curlyBracket;whiteSpace=wrap;html=1;rounded=1;flipH=1;labelPosition=right;verticalLabelPosition=middle;align=left;verticalAlign=middle;rotation=90;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="500" y="255" width="20" height="170" as="geometry" />
</mxCell>
<mxCell id="yXxu-ckIG8VWti2M3FYv-6" value="" style="shape=curlyBracket;whiteSpace=wrap;html=1;rounded=1;flipH=1;labelPosition=right;verticalLabelPosition=middle;align=left;verticalAlign=middle;rotation=90;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="550" y="155" width="20" height="400" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-16" value="<h1><font style="font-size: 12px;">fn-call&nbsp;</font></h1><p style="line-height: 60%;">- fnName&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (String)</p><p style="line-height: 60%;">- fnNs&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(String)</p><p style="line-height: 60%;">- formId&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (int)</p><p style="line-height: 60%;">- fnArgs&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (ref)</p><p style="line-height: 60%;">- parentIdx&nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(int)</p><p style="line-height: 60%;"><span style="background-color: initial;">- retIdx&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(int)</span></p><p style="line-height: 60%;">- frameBindings (array)<span style="background-color: initial;"><br></span></p>" style="text;html=1;strokeColor=#82b366;fillColor=#d5e8d4;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="30" y="180" width="190" height="210" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-21" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;fillColor=#d5e8d4;strokeColor=#82b366;dashed=1;endArrow=none;endFill=0;" parent="MPshjHnfK2mcv_0NpmO8-31" source="MPshjHnfK2mcv_0NpmO8-1" target="MPshjHnfK2mcv_0NpmO8-16" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-26" value="" style="group" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1" connectable="0">
<mxGeometry x="170" y="360" width="30" height="20" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-23" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="MPshjHnfK2mcv_0NpmO8-26" vertex="1">
<mxGeometry width="10" height="20" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-24" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="MPshjHnfK2mcv_0NpmO8-26" vertex="1">
<mxGeometry x="10" width="10" height="20" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-25" value="" style="rounded=0;whiteSpace=wrap;html=1;fillColor=#ffe6cc;strokeColor=#d79b00;" parent="MPshjHnfK2mcv_0NpmO8-26" vertex="1">
<mxGeometry x="20" width="10" height="20" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-22" value="<h1><font style="font-size: 12px;">bind&nbsp;</font></h1><p style="line-height: 60%;">- symName&nbsp; &nbsp; &nbsp; &nbsp;(String)</p><p style="line-height: 60%;">- val&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp;(ref)</p><p style="line-height: 60%;">- coord&nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; &nbsp; (ref)</p><p style="line-height: 60%;">- visibleAfterIdx (int)</p>" style="text;html=1;strokeColor=#d79b00;fillColor=#ffe6cc;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="100" y="410" width="150" height="130" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-29" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;entryX=0.25;entryY=1;entryDx=0;entryDy=0;dashed=1;fillColor=#ffe6cc;strokeColor=#d79b00;endArrow=none;endFill=0;" parent="MPshjHnfK2mcv_0NpmO8-31" source="MPshjHnfK2mcv_0NpmO8-22" target="MPshjHnfK2mcv_0NpmO8-23" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="yXxu-ckIG8VWti2M3FYv-17" value="" style="html=1;shadow=0;dashed=0;align=center;verticalAlign=middle;shape=mxgraph.arrows2.arrow;dy=0.6;dx=40;notch=0;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="482.5" y="185" width="135" height="10" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-18" value="<h1><font style="font-size: 12px;">expr-exec&nbsp;</font></h1><p style="line-height: 60%;">- coord&nbsp; &nbsp; &nbsp; (ref)</p><p style="line-height: 60%;">- exprVal&nbsp; &nbsp;(ref)</p><p style="line-height: 60%;">- fnCallIdx (int)</p>" style="text;html=1;strokeColor=#b85450;fillColor=#f8cecc;spacing=5;spacingTop=-20;whiteSpace=wrap;overflow=hidden;rounded=0;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="570" y="440" width="130" height="110" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-19" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=0.5;exitY=1;exitDx=0;exitDy=0;endArrow=none;endFill=0;dashed=1;fillColor=#f8cecc;strokeColor=#b85450;" parent="MPshjHnfK2mcv_0NpmO8-31" source="MPshjHnfK2mcv_0NpmO8-7" target="MPshjHnfK2mcv_0NpmO8-18" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="MPshjHnfK2mcv_0NpmO8-30" value="<font style="font-size: 21px;"><b>Schema of a flow main storage structures</b></font>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="300" y="10" width="520" height="30" as="geometry" />
</mxCell>
<mxCell id="cvbeNvJm6m_hZsHkrufq-1" value="<span style="font-size: 21px;">A thread timeline</span>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="300" y="100" width="520" height="30" as="geometry" />
</mxCell>
<mxCell id="cvbeNvJm6m_hZsHkrufq-2" value="<span style="font-size: 21px;">The multi-thread timeline</span>" style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="295" y="640" width="520" height="30" as="geometry" />
</mxCell>
<mxCell id="cvbeNvJm6m_hZsHkrufq-16" value="A multi-thread timeline" style="swimlane;whiteSpace=wrap;html=1;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="442.5" y="720" width="215" height="180" as="geometry" />
</mxCell>
<mxCell id="cvbeNvJm6m_hZsHkrufq-20" value="TOTE-0" style="rounded=0;whiteSpace=wrap;html=1;flipV=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="cvbeNvJm6m_hZsHkrufq-16" vertex="1">
<mxGeometry y="85" width="85" height="30" as="geometry" />
</mxCell>
<mxCell id="cvbeNvJm6m_hZsHkrufq-23" value="TOTE-1" style="rounded=0;whiteSpace=wrap;html=1;flipV=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="cvbeNvJm6m_hZsHkrufq-16" vertex="1">
<mxGeometry x="30" y="85" width="85" height="30" as="geometry" />
</mxCell>
<mxCell id="cvbeNvJm6m_hZsHkrufq-24" value="TOTE-N" style="rounded=0;whiteSpace=wrap;html=1;flipV=1;rotation=-90;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="cvbeNvJm6m_hZsHkrufq-16" vertex="1">
<mxGeometry x="115" y="85" width="85" height="30" as="geometry" />
</mxCell>
<mxCell id="cvbeNvJm6m_hZsHkrufq-25" value="..." style="text;html=1;strokeColor=none;fillColor=none;align=center;verticalAlign=middle;whiteSpace=wrap;rounded=0;" parent="cvbeNvJm6m_hZsHkrufq-16" vertex="1">
<mxGeometry x="85" y="85" width="60" height="30" as="geometry" />
</mxCell>
<mxCell id="cvbeNvJm6m_hZsHkrufq-27" style="rounded=0;orthogonalLoop=1;jettySize=auto;html=1;exitX=1;exitY=0.5;exitDx=0;exitDy=0;entryX=0.5;entryY=0;entryDx=0;entryDy=0;endArrow=none;endFill=0;dashed=1;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="MPshjHnfK2mcv_0NpmO8-31" source="cvbeNvJm6m_hZsHkrufq-26" target="cvbeNvJm6m_hZsHkrufq-20" edge="1">
<mxGeometry relative="1" as="geometry" />
</mxCell>
<mxCell id="cvbeNvJm6m_hZsHkrufq-26" value="<b>Total Order Timeline Entry<br></b><br>- thread-timeline (ref)<br>- thread-timeline-idx (int)" style="rounded=0;whiteSpace=wrap;html=1;align=left;fillColor=#dae8fc;strokeColor=#6c8ebf;" parent="MPshjHnfK2mcv_0NpmO8-31" vertex="1">
<mxGeometry x="140" y="780" width="160" height="105" as="geometry" />
</mxCell>
</root>
</mxGraphModel>
</diagram>
</mxfile>
================================================
FILE: docs/user_guide.adoc
================================================
= FlowStorm debugger User's Guide
:source-highlighter: rouge
:author: By Juan Monetta
:lang: en
:encoding: UTF-8
:doctype: book
:toc: left
:toclevels: 3
:sectlinks:
:sectanchors:
:leveloffset: 1
:sectnums:
_FlowStorm_ is a tracing debugger for Clojure and ClojureScript.
image::user_guide_images/intro_screenshot.png[]
It can instrument any Clojure code and provides many tools to explore and analyze your programs executions.
= Quick start
Before you start check _FlowStorm_ minimum requirements.
[IMPORTANT]
.Minimum requirements
====
- jdk >= 17 (if you still need to run it with jdk11 take a look at <<#_run_with_jdk_11,here>>)
- Clojure >= 1.10.0
====
== Clojure
There are two ways of using _FlowStorm_ for Clojure :
- With <<#_clojurestorm,ClojureStorm>> (recommended) : Swap your Clojure compiler at dev time by ClojureStorm and get everything instrumented automatically
- <<#_vanilla_flowstorm,Vanilla FlowStorm>> : Just add FlowStorm to your dev classpath and instrument by tagging and re-evaluating forms
=== ClojureStorm
This is the newest and simplest way of using _FlowStorm_. It requires you to swap your official Clojure compiler by _ClojureStorm_ only at dev time.
Swapping compilers sounds like a lot, but don't worry, _ClojureStorm_ is just a patch applied over the official compiler with some
extra stuff for automatic instrumentation. You shouldn't encounter any differences, it is only for dev, and you can swap it back
and forth by starting your repl with a different alias or lein profile.
The easiest way to run and learn _FlowStorm_ with _ClojureStorm_ is by running the repl tutorial.
==== Try it with no project and no config
You can start a repl with FlowStorm with a single command like this :
[%nowrap,bash]
----
;; on Linux and OSX
clj -Sforce -Sdeps '{:deps {} :aliases {:dev {:classpath-overrides {org.clojure/clojure nil} :extra-deps {com.github.flow-storm/clojure {:mvn/version "1.12.4"} com.github.flow-storm/flow-storm-dbg {:mvn/version "4.5.9"}}}}}' -A:dev
;; on Windows
clj -Sforce -Sdeps '{:deps {} :aliases {:dev {:classpath-overrides {org.clojure/clojure nil} :extra-deps {com.github.flow-storm/clojure {:mvn/version """1.12.4"""} com.github.flow-storm/flow-storm-dbg {:mvn/version """4.5.9"""}}}}}' -A:dev
----
Pasting that command on your terminal will bring up a repl with _FlowStorm_ and the compiler swapped by _ClojureStorm_. When the repl comes up
evaluate the `:dbg` keyword to bring up the UI and then click on `Help->Tutorial` on the menu for a tour of the basics.
After the tutorial you may want to use it on your projects. You use it by adding a deps.edn alias or lein profile.
The simplest way is to setup it globally, so that is what we are going to do next. You can also add it only
to specific projects if they require special configurations.
==== Global setup as deps.edn aliases
You can setup your global `~/.clojure/deps.edn` (on linux and macOS) or `%USERPROFILE%\.clojure\deps.edn` (on windows) like this :
[%nowrap,clojure]
----
{...
:aliases
{:1.12-storm {:classpath-overrides {org.clojure/clojure nil}
:extra-deps {com.github.flow-storm/clojure {:mvn/version "1.12.4"}
com.github.flow-storm/flow-storm-dbg {:mvn/version "4.5.9"}}}
;; Optional plugins you find yourself using regularly
:fs-web-plugin {:extra-deps {com.github.flow-storm/flow-storm-web-plugin {:mvn/version "1.0.0-beta"}}
:jvm-opts ["-Dclojure.storm.instrumentOnlyPrefixes.webPlugin=org.httpkit.server,ring.adapter.jetty,next.jdbc.result-set"
"-Dflowstorm.plugins.namespaces.webPlugin=flow-storm.plugins.web.all"]}
...}}
----
Then you can start your repls with the `:1.12-storm` alias (like `clj -A:1.12-storm`). When the repl comes up evaluate the `:dbg` keyword to bring up the UI,
then click on `Help->Tutorial` on the menu for a tour of the basics.
==== Global setup as leiningen profiles
You can setup your global `~/.lein/profiles.clj` (on linux and macOS) or `%USERPROFILE%\.lein\profiles.clj` (on windows) like this :
[%nowrap,clojure]
----
{:1.12-storm
{:dependencies [[com.github.flow-storm/clojure "1.12.4"]
[com.github.flow-storm/flow-storm-dbg "4.5.9"]]
:exclusions [org.clojure/clojure]}
;; Optional plugins you find yourself using regularly
:fs-web-plugin
{:dependencies [[com.github.flow-storm/flow-storm-web-plugin "1.0.0-beta"]]
:jvm-opts ["-Dclojure.storm.instrumentOnlyPrefixes.webPlugin=org.httpkit.server,ring.adapter.jetty,next.jdbc.result-set"
"-Dflowstorm.plugins.namespaces.webPlugin=flow-storm.plugins.web.all"]}
...}
----
Then you can start your project repls with `+1.12-storm` profile (like `lein with-profile +1.12-storm repl`). When the repl comes up evaluate the `:dbg` keyword to bring up the UI,
then click on `Help->Tutorial` on the menu for a tour of the basics.
[NOTE]
.Running lein repl without a project
====
For some reason if you run `lein with-profile +1.12-storm repl` outside of a project it will not run with the profile
activated correctly.
====
==== Per project setup with deps.edn
If your project is using deps.edn, you can add an alias that looks like this :
[%nowrap,clojure]
----
{...
:aliases {:1.12-storm
{;; for disabling the official compiler
:classpath-overrides {org.clojure/clojure nil}
:extra-deps {com.github.flow-storm/clojure {:mvn/version "1.12.4"}
com.github.flow-storm/flow-storm-dbg {:mvn/version "4.5.9"}}}}}
----
Once you have setup your deps.edn, start your repl with the `:1.12-storm` alias and run the debugger by evaluating
the `:dbg` keyworkd on your repl (this means just type `:dbg` and hit return).
If it is your first time using FlowStorm, when the UI comes up click on `Help->Tutorial` on the menu for a tour of the basics.
If you need more fine control over instrumentation see <<#_controlling_instrumentation,controlling instrumentation>>.
==== Setup with leiningen
If your project uses lein, you can add a profile that looks like this :
[%nowrap,clojure]
----
(defproject my.project "1.0.0"
:profiles {:1.12-storm
{:dependencies [[com.github.flow-storm/clojure "1.12.4"]
[com.github.flow-storm/flow-storm-dbg "4.5.9"]]
:exclusions [org.clojure/clojure]}}
...)
----
Once you have setup your lein profile globally or per project, start your repl with the `1.12-storm` profile and run the debugger by evaluating
the `:dbg` keyworkd on your repl (this means just type `:dbg` and hit return).
Make sure you activate the profile with `lein with-profile +1.12-storm repl`.
If it is your first time using FlowStorm, when the UI comes up click on `Help->Tutorial` on the menu for a tour of the basics.
If you need more fine control over instrumentation see <<#_controlling_instrumentation,controlling instrumentation>>.
[NOTE]
.lein dependencies
====
If you are using lein < 2.11.0 make sure your global :dependencies don't include the official org.clojure/clojure dependency.
Moving to lein latest version should work ok even if your global :dependencies contains the Clojure dep.
====
=== Vanilla FlowStorm
If you use the https://clojure.org/guides/deps_and_cli[clojure cli] you can start a repl with the _FlowStorm_ dependency loaded like this :
[,bash]
----
;; on Linux and OSX
clj -Sforce -Sdeps '{:deps {com.github.flow-storm/flow-storm-dbg {:mvn/version "4.5.9"}}}'
;; on Windows
clj -Sforce -Sdeps '{:deps {com.github.flow-storm/flow-storm-dbg {:mvn/version """4.5.9"""}}}'
----
If you are a https://leiningen.org/[lein] user add the dependency to your project.clj `:dependencies` and run `lein repl`.
Then require the api namespace and start the debugger :
[%nowrap,clojure]
----
user> (require '[flow-storm.api :as fs-api]) ;; the only namespace you need to require
user> (fs-api/local-connect) ;; will run the debugger GUI and get everything ready
----
You should now see a empty debugger window. Click on the recording button to leave the debugger in
recording mode and the let's debug something:
[%nowrap,clojure]
----
user> #rtrace (reduce + (map inc (range 10))) ;; #rtrace will instrument and run some code
----
After running it, you should get the return value of the expression (as if #rtrace wasn't there),
but now you will also have the debugger UI showing your recordings.
From here you probably want to check out the <<#_flows_tool, Flows tool>> which contains a lot of information
about exploring your recordings.
== ClojureScript
Debugging ClojureScript is a case of remote debugging in _FlowStorm_. This means the debugger
will run in a separate process and connect to the debuggee (your browser or nodejs runtime) via a websocket and optionally
an nrepl server.
There are two ways of using _FlowStorm_ with ClojureScript :
- With <<#_clojurescriptstorm_with_shadow_cljs,ClojureScriptStorm>> (recommended) : Swap your ClojureScript compiler by ClojureScriptStorm at dev and get everything instrumented automatically
- <<#_clojurescript_vanilla_flowstorm,Vanilla FlowStorm>> : Just add FlowStorm to your dev classpath and instrument by tagging and re-evaluating forms
_ClojureScriptStorm_ is a fork of the official ClojureScript compiler that adds automatic instrumentation so you don't need to think about it (you can still disable it when you don't need it).
You use it by swapping the official ClojureScript compiler by _ClojureScriptStorm_ at dev time, using dev aliases or profiles.
[NOTE]
.Repl connection
====
For enabling every debugger feature, _FlowStorm_ needs to connect to a cljs repl.
Currently only shadow-cljs repl over nrepl is supported.
====
=== ClojureScriptStorm with shadow-cljs
[IMPORTANT]
.Minimum requirements
====
- For ClojureScript 1.11.* shadow-cljs >= 2.25.4, For ClojureScript 1.12.* shadow-cljs >= 3.1.1
- FlowStorm >= 3.7.4
====
For setting up _FlowStorm_ with shadow-cljs you need to modify two files, your `shadow-cljs.edn` and your `deps.edn`.
This is setup once and forget, so once you have configured _FlowStorm_ you can do everything from the UI, without
any other sources modifications.
If you want a shadow-cljs template to play with, take a look at https://github.com/jpmonettas/shadow-flow-storm-basic/[this repo].
[NOTE]
.shadow-cljs
====
Currently you can only use _ClojureScriptStorm_ with shadow-cljs if you are resolving your
dependencies with deps.edn. This means having `:deps true` or similar in your shadow-cljs.edn.
If you have your dependencies directly in your shadow-cljs.edn you will have to use <<#_clojurescript_vanilla_flowstorm,Vanilla FlowStorm>>
for now.
This is because there is currently no way to swap the ClojureScript compiler in shadow-cljs.edn.
====
First, make your shadow-cljs.edn looks something like this :
[%nowrap,clojure]
----
{:deps {:aliases [:1.12-cljs-storm]}
:nrepl {:port 9000}
...
:builds
{:my-app {...
:devtools {:preloads [flow-storm.storm-preload]
:http-port 8021}}}}
----
So, the important parts are: you need to tell shadow to apply your deps.edn :1.12-cljs-storm alias, set up a nrepl port,
and also add `flow-storm.storm-preload` to your preloads. If you have other preloads make sure `flow-storm.storm-preload`
is the first one.
Then, modify your `deps.edn` dev profile to look like this :
[%nowrap,clojure]
----
{...
:aliases
;; this alias can be defined globally in your ~/.clojure/deps.edn so you don't have to modify this file in your project
{:1.12-cljs-storm
{:classpath-overrides {org.clojure/clojurescript nil} ;; disable the official compiler
:extra-deps {thheller/shadow-cljs {:mvn/version "3.3.4"
:exclusions [org.clojure/clojurescript]}
;; bring ClojureScriptStorm
com.github.flow-storm/clojurescript {:mvn/version "1.12.134-3"}
;; add FlowStorm runtime dep
com.github.flow-storm/flow-storm-inst {:mvn/version "4.5.9"}}}}}
----
There are lots of things going on there, but the main ones are: disabling the official compiler, adding
_ClojureScriptStorm_ and _FlowStorm_ dependencies, and then configuring what you want _ClojureScriptStorm_ to automatically
instrument.
By default the JVM property `cljs.storm.instrumentAutoPrefixes` is true so all your project top level namespaces
will be instrumented automatically.
If you need to set that property to false it is important to configure what namespaces you want to instrument,
and you do this by setting the `cljs.storm.instrumentOnlyPrefixes` jvm property.
This is a comma separated list of namespaces prefixes, you normally want your app namespaces plus some libraries, like :
`cljs.storm.instrumentOnlyPrefixes=org.my-app,org.my-lib,hiccup`
And this is it. Once you have it configured, run your shadow watch as you normally do, load your app on the browser (or nodejs).
Whenever your need the debugger, on a terminal run the ui with your shadow-cljs.edn data :
[,bash]
----
clj -Sforce -Sdeps '{:deps {com.github.flow-storm/flow-storm-dbg {:mvn/version "4.5.9"}}}' -X flow-storm.debugger.main/start-debugger :port 9000 :repl-type :shadow :build-id :my-app
----
and then reload you page so it connects to it.
Since we started the app with `flowstorm.startRecording=false` you will have to click on the record button once to start recording.
Whenever recording is enable and something executes under an instrumented namespace you should see the recordings appear in the debugger
under the main thread.
[NOTE]
.recording expressions typed on the repl
====
If you type at the repl something like `(defn foo [a b] (+ a b))` under an instrumented ns, the `foo` function will get instrumented
automatically and you will able to explore the recordings after the function is called.
On the other side, typing a simple expression like `(+ 1 2)` will not show anything, this is currently a limitation but you can
still make that work by wrapping the expression on a fn and immediately calling it, like `((fn [] (+ 1 2)))`
====
=== ClojureScriptStorm with cljs.main
You can use _FlowStorm_ and _ClojureScriptStorm_ with cljs.main.
The easiest way to try it is just by starting a repl, like this :
[%nowrap,bash]
----
clj -Sforce -J-Dcljs.storm.instrumentOnlyPrefixes=cljs.user -Sdeps '{:deps {com.github.flow-storm/clojurescript {:mvn/version "1.12.134-3"} com.github.flow-storm/flow-storm-inst {:mvn/version "4.5.9"}}}' -M -m cljs.main -co '{:preloads [flow-storm.storm-preload]}' --repl
----
If you run the command above you are running cljs.main --repl which will start a ClojureScript repl on
your terminal and open a browser connected to it. You runtime will also start with FlowStorm preloaded and everything under `cljs.user`
is going to be instrumented.
Then on a different terminal run the _FlowStorm_ UI :
[%nowrap,bash]
----
clj -Sforce -Sdeps '{:deps {com.github.flow-storm/flow-storm-dbg {:mvn/version "4.5.9"}}}' -X flow-storm.debugger.main/start-debugger
----
And now refresh your browser page so your browser app connects to the UI.
[NOTE]
.Limitations
====
There are some small limitations like not being able to modify instrumentation from the UI without restarting the repl.
This is because the _FlowStorm_ UI needs to also connect via nrepl to JVM process running the compiler, which isn't available
when running cljs.main.
====
=== ClojureScript vanilla FlowStorm
Let's say you are using https://github.com/thheller/shadow-cljs[shadow-cljs] to start a ClojureScript repl.
First you need to add _FlowStorm_ dependency to your project dependencies, like this :
[%nowrap,clojure]
----
$ cat shadow-cljs.edn
{...
:dependencies [... [com.github.flow-storm/flow-storm-inst "4.5.9"]]
;; the next two lines aren't needed but pretty convenient
:nrepl {:port 9000}
:my-build-id {:devtools {:preloads [flow-storm.preload]}}
...}
----
Then let's say you start your repl like :
[,bash]
----
npx shadow-cljs watch :my-build-id
shadow-cljs - config: /home/jmonetta/demo/shadow-cljs.edn
shadow-cljs - server version: 2.19.0 running at http://localhost:9630
shadow-cljs - nREPL server started on port 9000
shadow-cljs - watching build :my-build-id
[:my-build-id] Configuring build.
[:my-build-id] Compiling ...
[:my-build-id] Build completed. (127 files, 0 compiled, 0 warnings, 6.19s)
cljs.user=>
----
As you can see from the output log shadow-cljs started a nrepl server on port 9000, this is the port _FlowStorm_ needs to connect to,
so to start the debugger and connect to it you run :
[,bash]
----
;; on linux and mac-os
clj -Sforce -Sdeps '{:deps {com.github.flow-storm/flow-storm-dbg {:mvn/version "4.5.9"}}}' -X flow-storm.debugger.main/start-debugger :port 9000 :repl-type :shadow :build-id :my-build-id
;; on windows
clj -Sforce -Sdeps '{:deps {com.github.flow-storm/flow-storm-dbg {:mvn/version """4.5.9"""}}}' -X flow-storm.debugger.main/start-debugger :port 9000 :repl-type :shadow :build-id :my-build-id
----
And that is all you need, the debugger GUI will pop up and everything will be ready.
Try tracing some code from the repl :
[%nowrap,clojure]
----
cljs.user> #rtrace (reduce + (map inc (range 10))) ;; #rtrace will instrument and run some code
----
After running it, you should get the return value of the expression (as if #rtrace wasn't there).
The debugger thread list (the one on the left) shows all the threads it has recordings for. Because we are
in javascript land there will always be just one thread, called `main`.
Double clicking it should open the "thread exploring tools" for that thread in a new tab.
This guide will cover all the tools in more detail but if you are interested in code stepping for example you will find
it in the `code stepping tool` at the bottom left corner of the thread tab, the one that has the `()` icon.
Click on it and use the stepping controls to step over the code.
Now that everything seems to be working move on and explore the many features _FlowStorm_ provides. There are many ways of instrumenting
your code, and many ways to explore its executions.
If you are not using a repl or the repl you are using isn't supported by _FlowStorm_ yet you can still use the debugger
but not all features will be supported (mainly the browser features).
For this you can start the debugger like before but without any parameters, like this :
[,bash]
----
clj -Sforce -Sdeps '{:deps {com.github.flow-storm/flow-storm-dbg {:mvn/version "4.5.9"}}}' -X flow-storm.debugger.main/start-debugger
----
And then go to your app code and call `(flow-storm.runtime.debuggers-api/remote-connect)` maybe on your main, so every time your program starts
will automatically connect to the repl.
[NOTE]
.ClojureScript environments
====
_FlowStorm_ is supported for ClojureScript in :
- Browsers
- NodeJS
- React native
====
[NOTE]
.NodeJs and react-native
====
On NodeJs and react-native you need to install the `websocket` library.
Do this by running `npm install websocket --save`
For react-native if your app is running inside a cellphone you will have to also provide the `:debugger-host` key
to `flow-storm.debugger.main/start-debugger` with your box ip address, unless you are using adb reverse with your ports for
which you will have to `adb reverse tcp:7722 tcp:7722` (the debugger websocket port)
====
[NOTE]
.App initialization debugging
====
If you need to debug some app initialization, for adding `#trace` tags before the debugger is connected you
will have to require flow-storm.api yourself, probably in your main. All the tracing will be replayed to the debugger
once it is connected.
====
Here is a repo you can use if you want to try _FlowStorm_ with shadow-cljs https://github.com/flow-storm/shadow-flow-storm-basic
=== Multiple ClojureScript builds
You can setup FlowStorm to debug multiple ClojureScript builds. This can be useful when your application is made up of multiple parts,
like when you have web workers.
Debugging multiple builds require multiple debugger instances, one per build.
The FlowStorm UI will start a websocket server, by default on 7722, so if you want to run multiple instances of it, you need
to run each instance under a different port. You can do this by providing a `:ws-port` to the startup command.
So let's say you want to run two debuggers, one for your page and one for a webworker, your can run them like this :
[,bash]
----
# on one terminal start your app debugger instance
clj -Sforce -Sdeps '{:deps {com.github.flow-storm/flow-storm-dbg {:mvn/version "4.5.9"}}}' -X flow-storm.debugger.main/start-debugger :port 9000 :repl-type :shadow :build-id :my-app :ws-port 7722
# on a second terminal start your webworker debugger instance
clj -Sforce -Sdeps '{:deps {com.github.flow-storm/flow-storm-dbg {:mvn/version "4.5.9"}}}' -X flow-storm.debugger.main/start-debugger :port 9000 :repl-type :shadow :build-id :my-web-worker :ws-port 7733
----
Now you also need to configure your builds to tell them what port they should connect to.
You do this by writing different preloads for each of your builds, and then using them instead of your `flow-storm.storm-preload`, like:
`my_app.main_storm_preload.cljs`
[%nowrap,clojure]
----
(ns my-app.main-storm-preload
(:require [cljs.storm.tracer]
[flow-storm.tracer :as tracer]
[flow-storm.runtime.debuggers-api :as dbg-api]))
(dbg-api/start-runtime)
(tracer/hook-clojurescript-storm)
(dbg-api/remote-connect {:debugger-host "localhost" :debugger-ws-port 7722})
----
`my_app.webworker_storm_preload.cljs`
[%nowrap,clojure]
----
(ns my-app.webworker-storm-preload
(:require [cljs.storm.tracer]
[flow-storm.tracer :as tracer]
[flow-storm.runtime.debuggers-api :as dbg-api]))
(dbg-api/start-runtime)
(tracer/hook-clojurescript-storm)
(dbg-api/remote-connect {:debugger-host "localhost" :debugger-ws-port 7733})
----
They are the same as `flow-storm.storm-preload` just with different port numbers.
Now you can configure your shadow-cljs.edn like this :
[%nowrap,clojure]
----
{...
:builds
{:app
{:target :browser
...
:modules
{:my-app {:init-fn my.app/init
:preloads [my-app.main-storm-preload]}
:my-webworker {:init-fn my.app.worker/init
:preloads [my-app.webworker-storm-preload]
:web-worker true}}}}}
----
[NOTE]
.Multiple debuggers tips
====
You can change the theme or customize the styles of different instances to make it easier to
know which debugger instance is connected to which application.
====
== Babashka
You can debug your babashka scripts with FlowStorm using the JVM. The process is quite simple.
Let's say we want to debug this example script https://raw.githubusercontent.com/babashka/babashka/master/examples/htmx_todoapp.clj
which runs a webserver with a basic todo app.
First we need to generate a deps.edn by running `bb print-deps > deps.edn`
Then modify the resulting deps.edn to add the FlowStorm alias like this :
[%nowrap,clojure]
----
{...
:aliases {:dev {:classpath-overrides {org.clojure/clojure nil} ;; for disabling the official compiler
:extra-deps {com.github.flow-storm/clojure {:mvn/version "1.12.4"}
com.github.flow-storm/flow-storm-dbg {:mvn/version "4.5.9"}}
:jvm-opts ["-Dclojure.storm.instrumentOnlyPrefixes=user"]}}}
----
With `clojure.storm.instrumentOnlyPrefixes=user` we are telling ClojureStorm to instrument everything inside
the `user` namespace since the script doesn't contain any namespace declaration.
And that is it, you can now start your clojure repl as usual, with `clj -A:dev` and then eval the `:dbg` keyword to
start the debugger UI.
Then eval the entire file to compile everything. To start the server in this example you will have to remove the wrapping
that is basically only allowing the server to run if we are running from babashka, like this :
[%nowrap,clojure]
----
(when true #_(= *file* (System/getProperty "babashka.file"))
...)
----
so we can also start it from Clojure.
After the server has started, you can use the app from the browser and everything will get recorded as usual.
= The tool bar
The toolbar as well as the menu provides quick access to some general commands.
image::user_guide_images/toolbar.png[]
From left to right:
- Cancel current running task. Whenever you a running a task that can take some time, this button will be red, and you can use it to cancel the task.
- The `Inst enable` button allows to enable/disable instrumentation when in a Storm environment. A change on instrumentation will only affect newly compiled code.
= Flows tool
The `Flows` vertical tab contains a bunch of tools for recording and analyzing your programs executions.
First of all, what are Flows?
A Flow is an "execution flow" recording unit. The only purpose of a flow is to group recording activity.
This grouping allows us for example to run some code and record it under `flow-0`, then modify our code, run it again, and
record this second run (or flow) under `flow-1`. Now we can access both recordings separately.
image::user_guide_images/recording_controls.png[]
When you first open FlowStorm UI you will see four things, from left to right :
- Clear your recordings if any.
- Start/Stop recording. You can keep your heap from growing by stopping recording when you don't need it.
- Start/Stop recording the multi-thread timeline. Check out the <<#_multi_thread_timeline, multi-thread timeline>> tool.
- The `Rec on` combo-box to select under what flow new recordings are going to be stored.
Whenever there is something recorded for a flow, a new tab with the flow name will appear.
Execution inside a flow will be grouped by threads. So the first thing you will see on a flow is a menu of threads
we have recordings for so far. This threads will be referred sometimes as timelines, since they are a sequence of
recorded execution steps.
Let's say for example we have selected to record under `flow-1` and run some multi threaded code.
We are going to see something like this :
image::user_guide_images/multi_flows_1.png[]
There is a lot going on in the screenshot above, but the most important are :
- we have configured FlowStorm to record new executions under `flow-1`
- we have recorded stuff under `flow-1` and there are also some previous recordings under `flow-0`
- we are currently looking at `flow-1`, we have opened to explore the thread with id `1` called `main` and we are exploring it in <<#_code_stepping,the code stepper>>
- `Threads [4]` indicates we have recorded activity in 4 threads, which we can access via this menu
Now for a different example :
image::user_guide_images/multi_flows_2.png[]
This second image shows us exploring the recordings of a thread with id `474`, called `pool-4-thread-4` on `flow-0`.
image::user_guide_images/flows_toolbar.png[]
The `Flows tool` also contains a toolbar that contains the Quick jump box.
Use it for quickly opening the first recording of a function in <<#_code_stepping,the code stepper>>.
Will autocomplete the first 25 matches.
In the screenshot above we see analyzing the recordings in <<#_code_stepping,the code stepper>> but there are many tools to explore the recorded timelines,
which we are going to describe next.
== Code tool
image::user_guide_images/code_tool_tab.png[]
The code tool is the first of the `Flows` tab. It provides most of the functionality found in a traditional debugger.
You can use it to step over each expression, visualize values, locals and more.
=== Code stepping
The code tool allows you to step and "travel throught time" in two ways:
- Use the controls at the top to step over your code in different ways.
- Click on the highlighted forms to position the debugger at that point in time.
image::user_guide_images/controls.png[]
For moving around using the controls we have two rows of buttons.
The second row of controls, the most important one, are the stepping controls.
From left to right they are :
- Step over backwards, will make one step backwards always staying on the same frame.
- Step backwards, will step backwards in time going into sub functions.
- Step out, will position the debugger in the next step after this function was called.
- 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.
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.
The buttons around the step counter are :
- Jump to the first step of the recording.
- Jump to the last step of the recording.
On the first row we have more controls, also for moving around in time.
From left to right we have :
- Undo navigation
- Redo navigation
- Add a <<#_bookmarks, bookmark>>
- The last stepping controls to the right are the <<#_power_stepping, power stepping>> controls.
[NOTE]
.Highlighting
====
Only the forms that were executed at least once for the current function frame will be highlighted.
====
This means that code can be un-highlighted for two reasons:
- there isn't any recording for that part of the code
- there is a recording but doesn't belong to this function frame.
image::user_guide_images/stepper_highlighting.png[]
In the contrived example above we see we are stepping the `foo` function. All inside this function
body is highlighted but the bodies of the two anonymous functions for mapping and reducing. This
will only get highlighted once you step into their bodies.
In this case you are sure there are recordings for these functions bodies because the reduce is
non lazy, so if you keep stepping eventually you will get into their bodies, but there is a faster way.
image::user_guide_images/stepper_highlighting_2.png[]
For this you can right click on any un-highlighted expression that you think there could be a recording for and
select `Jump forward here`.
image::user_guide_images/stepper_highlighting_3.png[]
This will make FlowStorm scan from the current point of the timeline searching forward for a value
recorded at that coordinate (if any) and move the stepper to that point in time.
You also have `Jump to first record here` which will scan from the beginning of the timeline and `Jump backwards here`
which will search backwards from the current position.
=== Power stepping
image::user_guide_images/controls_power_custom.png[]
The controls at the right are power stepping controls. They provide more powerfull ways of stepping through the code.
Clicking on the first, back, next or last buttons will navigate the timeline using the selected power stepping tool in the dropdown.
There are currently six power stepping tools :
- `identity`, will step to the prev/next value which identity is the same as the current value.
- 'equality', will step to the prev/next value which is equals (clojure equality) to the current value.
- `same-coord` will step to the prev/next value for the same coordinate. This means it will move to the next recording in
the timeline for this exact place in the code you are currently in. You can also see it as take me to all the situations
when the current expression executed doesn't matter how we got to it.
- `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.
- `custom-same-coord`, the same as `custom` but fixed on the current coordinate like `same-coord`.
- `identity-other-thread`, will step to a position which identity is the same as the current value in a different thread.
Here the prev and next arrows do the same thing, it will just jump to the first position that matches this value on a
different thread. This has some limitations. If there are more than two threads working with this identity there is no way
of choosing which thread to go. If you need more control, checkout the <<#_programmable_debugging,programmable debugging>>
section, specially the `find-expr-entry` function.
- `fn-call`, allows you to provide a function to step to.
[NOTE]
.Custom stepping
====
Custom power stepping is only supported in Clojure now.
====
Power stepping automatically skips all values equals to `:flow-storm.power-step/skip`. This can be useful when combined
with <<#_dealing_with_mutable_values, snapshot-value>> as a way of ignoring some of them, which provides a way of sampling
tight loops like in games.
=== Searching
image::user_guide_images/search_access.png[]
You can use the search tool to search over all your flow recorded expressions and then make the stepper jump to them.
You can find the search tool under `More tools -> Search`.
There are multiple ways of searching:
- By pr-str
- By data window current value
- By predicate
==== Searching by pr-str
image::user_guide_images/search_pr_str.png[]
This type of search will walk over the selected threads expressions, converting their values to strings with `pr-str` up to the selected level and depth
and then checking if the resulting string contains your provided query string.
==== Searching by DataWindow value
image::user_guide_images/search_data_window.png[]
Searching by data window value allows you to select any of the current data windows and will search for the current selected data window value
over the selected threads expressions values using identity.
==== Searching by predicate
image::user_guide_images/search_pred.png[]
Searching by predicate allows you to provide a Clojure predicate which will be used over all selected threads expressions values.
=== Loops
Whenever you click a highlighted form that has been executed multiple times inside the same function call (any kind of loop),
instead of immediately jumping into it, FlowStorm will popup a menu, like in the picture below :
image::user_guide_images/loops.png[]
This is the loops navigation menu. It allows you to quickly move around interesting iterations of the loop.
The menu will display slightly different options depending on you current position. The `[FIRST] ...` and `[LAST] ...`
entries will always show, which allows you to quickly jump to the first and last iteration of the loop.
If you are currently before the loop, clicking into any expression inside the loop will show the first 20
values for the clicked expression.
If instead you are currently in a expression after the loop, clicking back to an expression inside the loop,
will show the last 20 values for the clicked expression.
Now if you are currently stepping inside the loop, clicking any other expression inside it will show you 10 values
before and 10 values after of your current position.
Clicking on any of this entries will take you to that position in time.
If this is not enough, and you want to see all the values taken by some expression along the loop, you can always
use the <<#_printer, printer tool>>.
=== Exceptions debugging
`FlowStorm` will capture all functions that didn't return because an exception unwind the stack, even
when that exception was captured further and it didn't bubble up.
image::user_guide_images/exceptions.png[]
When an unwind situation is recorded a combobox will show up in the toolbar, containing the functions names
together with the exceptions types. If you hover the mouse over any of them, a tooltip will display the exception message.
Clicking on any of them will position the stepper at that point in time so you can explore what happened before.
You can configure FlowStorm to automatically jump to exceptions with the `Config` menu by checking `Auto jump to exception`
which is disabled by default.
=== Locals
The locals panel will show the locals visible for the current point in time and their values at binding time.
image::user_guide_images/locals.png[]
Right clicking on them will show a menu where you can :
- define all
- define the value with a name, so you can use it at the repl
- inspect the value with a <<#_data_windows,data window>>
- tap the value as with `tap>`
`Define all` will define all the bindings currently visible in the locals pane in the current form namespace.
This is useful for trying things at your editor as described here https://www.cognitect.com/blog/2017/6/5/repl-debugging-no-stacktrace-required
[NOTE]
.Locals and mutable values
====
The Locals pane will show the value of each binding for a symbol at binding time, which are the same thing
no matter where you are in the current block when working with immutable objects, but not when working with mutable ones.
If what was bound was muttable in any way, you will be seeing the value at binding time, and not at current time.
====
=== Stack
The stack panel will always show the current stacktrace. Be aware that the stacktrace
only include functions calls that had been recorded, so if you aren't recording everything
there will be gaps.
image::user_guide_images/stack.png[]
Double clicking on any of the stack entries will make the debugger jump to that point in time.
=== Value panels
Value panels show in many places in _FlowStorm_.
image::user_guide_images/value_panels2.png[]
The value panel in the code tool always display a pretty print of the current expression value.
You can configure the print-level and print-meta for the pretty printing by using the controls at the top.
The value panel showing the current expression in the code stepper is a little bit special since it also
contains a <<#_data_windows,data window>> tab which allows you to quickly navigate the value or give it custom
visualizations.
image::user_guide_images/value_panels1.png[]
==== Define value for repl
Use the `def` button to define a var pointing to the current inspector value.
You can use / to provide a namespace, otherwise will be defined under [cljs.]user
=== Goto to file:line
Clicking on the `Actions->Goto file:line` menu allows you to search and jump to the first recording of a expression
with a file and line, given that one exists.
It will ask you for a file and line in the format of `<class-path-file-path>:<line>`.
If you have a file like `src/org/my_app/core.clj` and you are interested in expressions evaluating on like 42
you should search like `org/my_app/core.clj:42`.
== Call Stack tree tool
The call stack tree tool is the second one of the `Flows` tab. It allows you to see the execution flow by expanding its call stack tree.
image::user_guide_images/callstack_tool_tab.png[]
The call stack tree is useful for a high level overview of a complex execution and also as a tool for quickly moving through time.
You can jump to any point in time by double clicking on a node or by right clicking and on the context menu selecting `Step code`.
image::user_guide_images/callstack_tree.png[]
Use the button at the top left corner of the tree tool to show the current frame of the debugger in the tree.
There are also two <<#_value_panels,value panels>> at the bottom that show the arguments and return value for the currently selected function call.
[NOTE]
.Disabling the call stack tree tool
====
The call stack tree tool can be enable/disable on the fly if you are not using it and performance is an issue,
since keeping it updated can be expensive.
You can disable it from the Config menu or via the `flowstorm.callTreeUpdate=false` JVM prop.
====
== Functions tool
The functions tool is the third one of the `Flows` tab.
image::user_guide_images/functions_tool_tab.png[]
It shows a list of all traced functions sort by how many times the have been called.
image::user_guide_images/functions_calls.png[]
Normal functions will be colored black, multimethods magenta and types/records protocols/interfaces implementations in green.
Together with the <<#_call_stack_tree_tool, call stack tree>> it provides a high level overview of a thread execution, and allows you to
jump through time much faster than single stepping.
You can search over the functions list by using the bar at the top.
=== Function calls
Clicking on the calls counter of any function will display all function calls on the right sorted by time.
Each line will show the arguments vector for each call, and their return value.
Use the check boxes at the top to hide some of the arguments.
image::user_guide_images/function_calls.png[]
Double clicking on any row in the functions call list will jump to the stepper at that point in time.
You can also use the `args` and `ret` buttons to open the values on the inspector.
== Multi-thread timeline
You can use this tool to record, display and navigate a total order of your recordings in a timeline.
This can be used, for example, to visualize how multiple threads expressions interleave, which is sometimes useful to debug race conditions.
You enable/disable the multi-thread timeline recording using its button on the toolbar. Recording on the multi-thread
timeline will make your program execution a little slower so it is recommended to have it paused unless you need it.
When you have something recorded on the multi-thread timeline you access the tool from the top right corner.
image::user_guide_images/multi_timeline_access.png[]
As an example, let's say you record the execution this function :
[,clojure]
----
(defn run-parallel []
(->> (range 4)
(pmap (fn [i] (factorial i)))
(reduce +)))
----
By opening the tool a window like this should pop up :
image::user_guide_images/timeline.png[]
As you can see the timeline tool displays a linear representation of your expressions. Times flows from top to bottom and
each thread gets assigned a different color. Every time a function is called or returns you will see it under the `Function`
column, and for each expression executed you will see a row with its `Expression` and `Value`.
Double clicking any row will make your code stepper (on the main window) jump to the code at that point in time.
[NOTE]
.Big recordings timeline
====
Rendering the timeline needs some processing to render each sub-form and print each value so be aware it could be slow
if you try it on big recordings.
====
There is also a `Only functions?` checkbox at the top that will retrieve only function calls and can be used to visualize
the threads interleaving at a higher level.
== Printer
_FlowStorm_ has a lot of functionality to replace printing to the console as a debugging method since most of the time it is pretty
inefficient. Nonetheless, sometimes adding a bunch of print lines to specific places in your code is a very powerful way
of understanding execution.
For this cases _FlowStorm_ has the `Printer tool`, which allows you to define, manage and visualize print points, without the need
of re running your code. It will work on your recordings as everything else.
You can add and re run print points over your recordings as many times as you need. To add a print point, just right click on any
recorded expression.
image::user_guide_images/printer_add.png[]
It will ask you for a couple optional fields.
image::user_guide_images/printer_add_box.png[]
The `Message format` is the "println text". A message to identify the print on the printer output. Here you can use any text, in which you can
optionally use `%s` for the printed value, same as you would use it with format.
The `Expression` field can be use to apply a transformer function over the value before printing it. Useful when you want to see a part of the value.
image::user_guide_images/printer_access.png[]
After you add them, you can access the `Printers tool` by navigating to `More tools -> Printers`.
The threads selector allows you to select the thread the prints are going to run on.
Leaving it blank will run prints over all threads recordings (checkout the notes for caveats).
Clicking the `refresh` button will [re]run the printing again over the current recordings.
image::user_guide_images/printer.png[]
You can tweak your prints at any time, like changing the print-length, print-level, message, transform-fn or just temporarily disable any of them.
When you are ok re-setting you prints, just click refresh and they will print again.
Double clicking on any printed line will jump to the Flows code tab, with the debugger pointed to the expression that generated the print.
[IMPORTANT]
.Multi-thread prints order
====
If you select `All` threads, and have a multi-thread timeline recording, then the printer will use it and you can use prints to debug threads
interleaving for example, but if you run your printers with `All` threads selected without a multi-thread timeline recording they will print
sorted by thread and not in the order they happened.
====
== Bookmarks
Bookmarks are another quick way of jumping around in code and they can be added from your code or the FlowStorm UI.
You can find you bookmarks on the top menu `View -> Bookmarks`.
image::user_guide_images/bookmarks.png[]
Double clicking on any bookmark will make the debugger jump back to its position.
=== Code bookmarks
You add code bookmarks by adding the `(bookmark)` statement to your code, which optionally accepts a label.
The first time a bookmark statement is executed it will make the FlowStorm UI jump to it. Since this behavior
is similar to a `debugger` statement in languages like Javascript, it is also aliased as `(debugger)` so you can
use whichever you prefer.
[NOTE]
.ClojureScript support
====
This is currently only supported when using ClojureScriptStorm >= 1.11.132-9
====
=== UI bookmarks
UI bookmarks are useful when you find yourself jumping around, trying to understand a complex execution. They enable
you to mark execution positions so you can come back to them later.
image::user_guide_images/bookmarks_add_btn.png[]
You can bookmark the current position by pressing the bookmark button in the code tool, next to your stepping controls.
It will ask you the bookmark description.
= Browser tool
The browser tool is pretty straight forward. It allows you to navigate your namespaces and vars,
and provides ways of <<#_controlling_instrumentation,managing what gets instrumented>>.
image::user_guide_images/browser.png[]
= Outputs tool
image::user_guide_images/outputs.png[]
The outputs tool can be used instead of your normal IDE/Editor panel to visualize your evaluations
results, your taps outputs and your `*out*` and `*err*` streams writes (like printlns).
The advantages being :
- Custom visualizations
- Quick nested values navigation
- Quick taps values navigation
- Datafy nav navigation
- Access to all previously tapped values
- Access to the last 10 evaluated values (instead of just `*1` and `*2`)
- Ability to search tapped values in Flows
The taps visualization system works out of the box while the evals result and printing capture currently
depends on you using nrepl and starting with the flow-storm middleware. Checkout the outputs setup
section for instructions.
[NOTE]
.ClojureScript support
====
Only the taps viewer is currently supported on ClojureScript. The last evaluations
and the out and err streams capture aren't supported yet.
====
== Middleware setup
For using all the features in the Outputs tool you need to be using nrepl and start your repl with
`flow-storm.nrepl.middleware/wrap-flow-storm` middleware.
If you use Cider for example you can add it to `cider-jack-in-nrepl-middlewares` via customizing the global
value or by using `.dir-locals.el`.
== Output data window
The top panel is a <<#_data_windows,data window>> for displaying evaluations and taps.
As soon as you evaluate or tap something it will be displayed here.
== Last evals
The last evals pane gives you access to the last 10 evaluation results, same as `*1` and `*2`.
Click on any value to display it on the top data window.
== Taps
Everytime _FlowStorm_ starts, it will add a tap, so whenever you `tap>` something
it will show on the taps list.
Click on any value to display it on the top data window.
If the tapped value has also been recorded as an expression in Flows, you can right click on it
and run `Search value on Flows` to move the debugger to that point in time.
[NOTE]
.Search value on Flows
====
Be aware that if the code that taps your value is something like `(tap> :a-key)` you won't be able to jump
to it using this, because `:a-key` isn't a value recorded by _FlowStorm_, while if the tapping
code is like `(tap> some-bind)` or `(tap> (+ 2 3))` or the tapping of any other expression
you should be able to jump to it.
So if you want to use this functionality as a "mark" so you can quickly jump to different parts of
the recordings from the Taps tool, you can do it like `(tap> (str :my-mark))`
====
A `#tap` tag will also be available, which will tap and return so you can use it like `(+ 1 2 #tap (* 3 4))`
Use the `clear` button to clear the list.
There is also `#tap-stack-trace`. It will tap the current stack trace.
== Out and Err streams
Everything written on `*out*` or `*err*` will be captured and displayed on the bottom panel.
You can copy anything from this area with normal tools.
= Data Windows
image::user_guide_images/data_window.png[]
Data Windows are a user extensible tool to visualize and explore your data. Their role is to support :
- a way to navigate nested structures in a lazy way
- visualize and navigate metadata
- multiple visualizations for each value
- lazy/infinite sequences navigation
- a way to define the current sub-values so you can use them at the repl
- a mechanism for realtime data visualization
- clojure.datafy navigation out of the box
- tools for the user to add custom visualizations on the fly
The next sections will explore each of them.
== Data navigation
image::user_guide_images/data_window_dig.png[]
You can navigate into any key or value by clicking on it.
Use the breadcrumbs at the top to navigate back.
== Metadata navigation
image::user_guide_images/data_window_meta.png[]
If any value contains metadata, it will be shown at the top. Clicking on it will make the data window
navigate into it.
== Multiple visualizers
image::user_guide_images/data_window_multiple_viz.png[]
You can change how to display your current value by using the visualizers selector dropdown at the top.
== Sequences
image::user_guide_images/data_window_seqable.png[]
The seqable visualizer allows you to navigate all kind of sequences (even infinite ones) by bringing more pages on demand.
Click on `More` to bring the next page in.
== Defining values
You can always define a var for the current value being shown on any data window by clicking the `def` button.
Clicking on it will raise a popup asking for a symbol name. If you don't provide a fully qualified symbol
it will define the var under `user` or `cljs.user` if you are in ClojureScript.
A quick way to use it is to provide a short name, let's say `foo`, and then access it from your
code like `user/foo`.
== Realtime visualizations
image::user_guide_images/data_window_realtime.png[]
DataWindows not only support displaying and navigating values, but also updating them in real time from
your application.
From your program's code you can always create a data window with :
[,clojure]
----
(flow-storm.api/data-window-push-val :changing-long-dw-id 0 "a-long")
----
by providing a data window id, a value, and optionally the initial breadcrumb label.
But you can also update it (given that the selected visualizer supports updating like :scope for numbers) with :
[,clojure]
----
(flow-storm.api/data-window-val-update :changing-long-dw-id 0.5)
----
This `data-window-val-update` is pretty useful when called from loops or refs watches, specially paired
with a custom visualizer.
== Clojure datafy/nav
image::user_guide_images/data_window_datafy_nav.png[]
Data Windows support datafy nav out of the box. The data window will always be showing the result of
`clojure.datafy/datafy` of a value. For maps or vectors where keys provide navigation it will automatically
add a blue arrow next to the value.
Clicking on the value will just dig the data, while clicking on the blue arrow will navigate as with
`clojure.datafy/nav` applied to that collection on that key.
== EQL pprint visualizer
image::user_guide_images/eql_visualizer_0.png[]
image::user_guide_images/eql_visualizer_1.png[]
The `eql-query-pprint` visualizer allows you to explore your data "entities" by looking at subsets of it
using queries similar to datomic pull queries like in the screenshots above.
[NOTE]
.Disable by default
====
The EQL query pprint is disable by default. To enable it call `(flow-storm.runtime.values/register-eql-query-pprint-extractor)`.
====
By entities it means maps which contains only keywords as their keys. Every other collection
is just traversed.
This are all valid queries :
- `[*]`
- `[:name]`
- `[:name :age :vehicles]`
- `[:name :age {:vehicles [:type]}]`
- `[:name :age {:vehicles [?]}]`
- `[:name {:vehicles [*]}]`
- `[:name :age {:vehicles [:type {:seats [?]}]}]`
- `[:name :age {:vehicles [:type {:seats [:kind]}]}]`
- `[:name {:houses [:rooms]}]`
The `*` symbol means include all keys, while the `?` symbol means just list the keys, which helps
exploring big nested maps with many keys.
== Custom visualizers
An important aspect of Data Windows is to be able to provide custom visualizers on the fly.
Let's say we have model a chess board as a set of maps which represent our pieces.
[,clojure]
----
(def chess-board
#{{:kind :king :player :white :pos [0 5]}
{:kind :rook :player :white :pos [5 1]}
{:kind :p
gitextract_xzq9jm4n/ ├── .dir-locals.el ├── .github/ │ └── FUNDING.yml ├── .gitignore ├── CHANGELOG.md ├── Makefile ├── Readme.md ├── UNLICENSE ├── build.clj ├── deps.edn ├── docs/ │ ├── _config.yml │ ├── dev_notes.md │ ├── high_level_diagram.drawio │ ├── index.html │ ├── related-research-and-tools.md │ ├── run_configs.drawio │ ├── test-cases.org │ ├── timeline.drawio │ ├── user_guide.adoc │ └── user_guide.html ├── examples/ │ └── plugins/ │ └── basic-plugin/ │ ├── deps.edn │ └── src/ │ └── flow_storm/ │ └── plugins/ │ └── timelines_counters/ │ ├── all.clj │ ├── runtime.cljc │ └── ui.clj ├── llm-prompt.txt ├── package.json ├── resources/ │ └── flowstorm/ │ ├── fonts/ │ │ └── LICENSE.txt │ └── styles/ │ ├── font-size-lg.css │ ├── font-size-md.css │ ├── font-size-sm.css │ ├── font-size-xl.css │ ├── styles.css │ ├── theme_dark.css │ └── theme_light.css ├── scripts/ │ ├── flow-clj │ ├── gsettings │ └── mock-gnome.sh ├── shadow-cljs.edn ├── src-dbg/ │ └── flow_storm/ │ └── debugger/ │ ├── docs.clj │ ├── events_processor.clj │ ├── events_queue.clj │ ├── main.clj │ ├── repl/ │ │ ├── core.clj │ │ └── nrepl.clj │ ├── runtime_api.clj │ ├── state.clj │ ├── tutorials/ │ │ └── basics.clj │ ├── ui/ │ │ ├── browser/ │ │ │ └── screen.clj │ │ ├── commons.clj │ │ ├── components.clj │ │ ├── data_windows/ │ │ │ ├── data_windows.clj │ │ │ ├── visualizers/ │ │ │ │ └── oscilloscope.clj │ │ │ └── visualizers.clj │ │ ├── docs/ │ │ │ └── screen.clj │ │ ├── flows/ │ │ │ ├── bookmarks.clj │ │ │ ├── call_tree.clj │ │ │ ├── code.clj │ │ │ ├── components.clj │ │ │ ├── functions.clj │ │ │ ├── general.clj │ │ │ ├── multi_thread_timeline.clj │ │ │ ├── printer.clj │ │ │ ├── screen.clj │ │ │ └── search.clj │ │ ├── main.clj │ │ ├── outputs/ │ │ │ └── screen.clj │ │ ├── plugins.clj │ │ ├── tasks.clj │ │ └── utils.clj │ ├── user_guide.clj │ └── websocket.clj ├── src-dev/ │ ├── dev.clj │ ├── dev_tester.clj │ ├── dev_tester.cljs │ ├── dev_tester_12.clj │ ├── logging.properties │ └── user.clj ├── src-inst/ │ ├── data_readers.clj │ └── flow_storm/ │ ├── api.clj │ ├── api.cljs │ ├── jobs.cljc │ ├── nrepl/ │ │ └── middleware.clj │ ├── ns_reload_utils.clj │ ├── preload.cljs │ ├── remote_websocket_client.clj │ ├── remote_websocket_client.cljs │ ├── runtime/ │ │ ├── debuggers_api.cljc │ │ ├── events.cljc │ │ ├── indexes/ │ │ │ ├── api.cljc │ │ │ ├── form_registry.cljc │ │ │ ├── protocols.cljc │ │ │ ├── storm_form_registry.clj │ │ │ ├── thread_registry.cljc │ │ │ ├── timeline_index.cljc │ │ │ ├── total_order_timeline.cljc │ │ │ └── utils.cljc │ │ ├── outputs.cljc │ │ ├── types/ │ │ │ ├── bind_trace.cljc │ │ │ ├── expr_trace.cljc │ │ │ ├── fn_call_trace.cljc │ │ │ └── fn_return_trace.cljc │ │ └── values.cljc │ ├── storm_api.clj │ ├── storm_preload.cljs │ └── tracer.cljc ├── src-shared/ │ └── flow_storm/ │ ├── eql.cljc │ ├── form_pprinter.clj │ ├── json_serializer.clj │ ├── json_serializer.cljs │ ├── state_management.cljc │ ├── types.cljc │ └── utils.cljc └── tests.edn
Condensed preview — 112 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (1,315K chars).
[
{
"path": ".dir-locals.el",
"chars": 428,
"preview": "((clojure-mode . ((cider-clojure-cli-aliases . \"dev:dev-tools:storm\")\n\t\t\t\t (clojure-dev-menu-name . \"flow-storm-dev-men"
},
{
"path": ".github/FUNDING.yml",
"chars": 758,
"preview": "# These are supported funding model platforms\n\ngithub: [jpmonettas]\n# patreon: # Replace with a single Patreon username\n"
},
{
"path": ".gitignore",
"chars": 113,
"preview": ".clj-kondo\ntarget\n.cpcache\n.nrepl-port\n.shadow-cljs\n.cljs_node_repl\nout/\nnode_modules\npublic/js/\n/.calva/\n/.lsp/\n"
},
{
"path": "CHANGELOG.md",
"chars": 35765,
"preview": "# Changelog\n\n## master (unreleased)\n\t\n### New Features\n \n### Changes\n\n### Bugs fixed\n\n - Fix multi thread time"
},
{
"path": "Makefile",
"chars": 1198,
"preview": ".PHONY: clean docs test lint-dbg lint-inst install-dbg install-inst deploy-dbg deploy-inst\n\ndocs: docs/user_guide.adoc\n\t"
},
{
"path": "Readme.md",
"chars": 14876,
"preview": "# Flow-storm debugger\n\n<img src=\"./docs/images/icon.png\" width=\"100\">\n\nThis is the central repository for [FlowStorm](ht"
},
{
"path": "UNLICENSE",
"chars": 1210,
"preview": "This is free and unencumbered software released into the public domain.\n\nAnyone is free to copy, modify, publish, use, c"
},
{
"path": "build.clj",
"chars": 4436,
"preview": "(ns build\n (:require [clojure.tools.build.api :as b]\n [clojure.string :as str]\n [clojure.java.io "
},
{
"path": "deps.edn",
"chars": 4081,
"preview": "{:paths [\"src-inst\" \"src-dbg\" \"src-shared\" \"resources\"]\n :deps {;; IMPORTANT !!\n ;; If adding any dependency for "
},
{
"path": "docs/_config.yml",
"chars": 26,
"preview": "theme: jekyll-theme-cayman"
},
{
"path": "docs/dev_notes.md",
"chars": 14604,
"preview": "# Developer notes\n\nThe purpose of this document is to collect information and tips for people wanting to enhance, fix, d"
},
{
"path": "docs/high_level_diagram.drawio",
"chars": 33888,
"preview": "<mxfile host=\"Electron\" modified=\"2024-11-28T15:27:53.852Z\" agent=\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (K"
},
{
"path": "docs/index.html",
"chars": 84,
"preview": "<h1>FlowStorm</h1>\n\n<ul>\n <li><a href=\"/user_guide.html\">User Guide</a></li>\n</ul>\n"
},
{
"path": "docs/related-research-and-tools.md",
"chars": 1285,
"preview": "- [Debugging Backwards in Time, Bil Lewis Paper](https://arxiv.org/pdf/cs/0310016.pdf)\n- [Debugging Backwards in Time, B"
},
{
"path": "docs/run_configs.drawio",
"chars": 10091,
"preview": "<mxfile host=\"Electron\" modified=\"2023-07-21T18:53:27.851Z\" agent=\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (K"
},
{
"path": "docs/test-cases.org",
"chars": 2366,
"preview": "* Test cases\n\n| Name | Local Clj | Remote Clj | ClojureScript |\n|---------------------"
},
{
"path": "docs/timeline.drawio",
"chars": 18728,
"preview": "<mxfile host=\"Electron\" modified=\"2025-03-26T14:41:32.790Z\" agent=\"Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (K"
},
{
"path": "docs/user_guide.adoc",
"chars": 102967,
"preview": "= FlowStorm debugger User's Guide\n:source-highlighter: rouge\n:author: By Juan Monetta\n:lang: en\n:encoding: UTF-8\n:doctyp"
},
{
"path": "docs/user_guide.html",
"chars": 267198,
"preview": "<!DOCTYPE html>\n<html lang=\"en\">\n<head>\n<meta charset=\"UTF-8\">\n<meta http-equiv=\"X-UA-Compatible\" content=\"IE=edge\">\n<me"
},
{
"path": "examples/plugins/basic-plugin/deps.edn",
"chars": 3,
"preview": "{}\n"
},
{
"path": "examples/plugins/basic-plugin/src/flow_storm/plugins/timelines_counters/all.clj",
"chars": 163,
"preview": "(ns flow-storm.plugins.timelines-counters.all\n (:require [flow-storm.plugins.timelines-counters.ui]\n [flow-s"
},
{
"path": "examples/plugins/basic-plugin/src/flow_storm/plugins/timelines_counters/runtime.cljc",
"chars": 526,
"preview": "(ns flow-storm.plugins.timelines-counters.runtime\n (:require [flow-storm.runtime.indexes.api :as ia]\n [flow-"
},
{
"path": "examples/plugins/basic-plugin/src/flow_storm/plugins/timelines_counters/ui.clj",
"chars": 1722,
"preview": "(ns flow-storm.plugins.timelines-counters.ui\n (:require [flow-storm.debugger.ui.plugins :as fs-plugins]\n [fl"
},
{
"path": "llm-prompt.txt",
"chars": 4044,
"preview": "You can use the FlowStorm debugger tools via the repl to analyze FlowStorm recordings which records Clojure systems exec"
},
{
"path": "package.json",
"chars": 83,
"preview": "{\n \"dependencies\": {\n \"websocket\": \"1.0.34\",\n \"shadow-cljs\": \"2.23.3\"\n }\n}\n"
},
{
"path": "resources/flowstorm/fonts/LICENSE.txt",
"chars": 11560,
"preview": "\r\n Apache License\r\n Version 2.0, January 2004\r\n "
},
{
"path": "resources/flowstorm/styles/font-size-lg.css",
"chars": 306,
"preview": ".root {-fx-font-size: 19;}\n.ikonli-font-icon {-fx-font-size: 21;}\n.btn-sm {-fx-font-size: 16;}\n.btn-xs {-fx-font-size: 1"
},
{
"path": "resources/flowstorm/styles/font-size-md.css",
"chars": 307,
"preview": ".root {-fx-font-size: 16;}\n.ikonli-font-icon {-fx-font-size: 17;}\n.btn-sm {-fx-font-size: 13;}\n.btn-xs {-fx-font-size: 1"
},
{
"path": "resources/flowstorm/styles/font-size-sm.css",
"chars": 307,
"preview": ".root {-fx-font-size: 13;}\n.ikonli-font-icon {-fx-font-size: 15;}\n.btn-sm {-fx-font-size: 10;}\n.btn-xs {-fx-font-size: 8"
},
{
"path": "resources/flowstorm/styles/font-size-xl.css",
"chars": 306,
"preview": ".root {-fx-font-size: 22;}\n.ikonli-font-icon {-fx-font-size: 23;}\n.btn-sm {-fx-font-size: 19;}\n.btn-xs {-fx-font-size: 1"
},
{
"path": "resources/flowstorm/styles/styles.css",
"chars": 6414,
"preview": "\n@font-face {\n src: url('../fonts/Roboto-Light.ttf');\n}\n\n@font-face {\n src: url('../fonts/Roboto-Medium.ttf');\n}\n\n"
},
{
"path": "resources/flowstorm/styles/theme_dark.css",
"chars": 2143,
"preview": "* {\n\t-fx-theme-base1: #323232;\t\n\t-fx-theme-base2: #3f474f; \n\t-"
},
{
"path": "resources/flowstorm/styles/theme_light.css",
"chars": 2142,
"preview": "* {\n\t-fx-theme-base1: #ddd;\t\n\t-fx-theme-base2: #eee;\n\t-fx-them"
},
{
"path": "scripts/flow-clj",
"chars": 364,
"preview": "# Make a babashka script or shell script to run trampoline like clj -x com.foo/fn args\n# clj -X:dbg:inst:dev:build flow-"
},
{
"path": "scripts/gsettings",
"chars": 624,
"preview": "#!/home/jmonetta/bin/bb\n\n(ns gsettings)\n\n(require '[clojure.tools.cli :refer [parse-opts]])\n\n\n(let [[first-arg] *command"
},
{
"path": "scripts/mock-gnome.sh",
"chars": 124,
"preview": "#/bin/bash\n\nexport XDG_CURRENT_DESKTOP=gnome\nexport PATH=/home/jmonetta/my-projects/flow-storm-debugger/scripts/:$PATH\nb"
},
{
"path": "shadow-cljs.edn",
"chars": 445,
"preview": ";; shadow-cljs configuration\n{:deps {:aliases [:cljs-storm :dev]}\n\n :builds\n {:dev-test {:target :node-script\n "
},
{
"path": "src-dbg/flow_storm/debugger/docs.clj",
"chars": 1734,
"preview": "(ns flow-storm.debugger.docs\n (:require [flow-storm.state-management :refer [defstate]]\n [flow-storm.utils :"
},
{
"path": "src-dbg/flow_storm/debugger/events_processor.clj",
"chars": 8348,
"preview": "(ns flow-storm.debugger.events-processor\n\n \"Processing events the debugger receives from the runtime\"\n\n (:require [flo"
},
{
"path": "src-dbg/flow_storm/debugger/events_queue.clj",
"chars": 3341,
"preview": "(ns flow-storm.debugger.events-queue\n\n \"Namespace for the sub-component that manages an events queue.\n\n This events ar"
},
{
"path": "src-dbg/flow_storm/debugger/main.clj",
"chars": 9157,
"preview": "(ns flow-storm.debugger.main\n\n \" This is the main namespace for the debugger itself, the graphical part of FlowStorm,\n "
},
{
"path": "src-dbg/flow_storm/debugger/repl/core.clj",
"chars": 9857,
"preview": "(ns flow-storm.debugger.repl.core\n\n \"Stateful component that handles debugger connection to repls.\n `start-repl` will "
},
{
"path": "src-dbg/flow_storm/debugger/repl/nrepl.clj",
"chars": 3163,
"preview": "(ns flow-storm.debugger.repl.nrepl\n\n \"Utilities to connect to nRepl servers\"\n\n (:require [nrepl.core :as nrepl]\n "
},
{
"path": "src-dbg/flow_storm/debugger/runtime_api.clj",
"chars": 23240,
"preview": "(ns flow-storm.debugger.runtime-api\n\n \"Component that implements the api that the debugger\n uses to call the runtime.\n"
},
{
"path": "src-dbg/flow_storm/debugger/state.clj",
"chars": 30333,
"preview": "(ns flow-storm.debugger.state\n\n \"Sub component that manages the state of the debugger.\n This is the state for supporti"
},
{
"path": "src-dbg/flow_storm/debugger/tutorials/basics.clj",
"chars": 21247,
"preview": "(ns flow-storm.debugger.tutorials.basics\n (:require [flow-storm.debugger.ui.components :as ui]\n [flow-storm."
},
{
"path": "src-dbg/flow_storm/debugger/ui/browser/screen.clj",
"chars": 31986,
"preview": "(ns flow-storm.debugger.ui.browser.screen\n (:require [flow-storm.debugger.ui.utils :as ui-utils :refer [add-class]]\n "
},
{
"path": "src-dbg/flow_storm/debugger/ui/commons.clj",
"chars": 1052,
"preview": "(ns flow-storm.debugger.ui.commons\n (:require [flow-storm.debugger.state :as dbg-state]\n [flow-storm.debugge"
},
{
"path": "src-dbg/flow_storm/debugger/ui/components.clj",
"chars": 32899,
"preview": "(ns flow-storm.debugger.ui.components\n (:require [flow-storm.debugger.ui.utils :as ui-utils :refer [event-handler]]\n "
},
{
"path": "src-dbg/flow_storm/debugger/ui/data_windows/data_windows.clj",
"chars": 12758,
"preview": "(ns flow-storm.debugger.ui.data-windows.data-windows\n (:require [flow-storm.debugger.ui.utils\n :as ui-utils"
},
{
"path": "src-dbg/flow_storm/debugger/ui/data_windows/visualizers/oscilloscope.clj",
"chars": 13074,
"preview": "(ns flow-storm.debugger.ui.data-windows.visualizers.oscilloscope\n (:require [flow-storm.debugger.ui.components :as ui]\n"
},
{
"path": "src-dbg/flow_storm/debugger/ui/data_windows/visualizers.clj",
"chars": 15277,
"preview": "(ns flow-storm.debugger.ui.data-windows.visualizers\n (:require [flow-storm.debugger.ui.components :as ui]\n ["
},
{
"path": "src-dbg/flow_storm/debugger/ui/docs/screen.clj",
"chars": 10608,
"preview": "(ns flow-storm.debugger.ui.docs.screen\n (:require [flow-storm.debugger.ui.utils :as ui-utils]\n [flow-storm.d"
},
{
"path": "src-dbg/flow_storm/debugger/ui/flows/bookmarks.clj",
"chars": 4833,
"preview": "(ns flow-storm.debugger.ui.flows.bookmarks\n (:require [flow-storm.debugger.state :as dbg-state :refer [store-obj obj-lo"
},
{
"path": "src-dbg/flow_storm/debugger/ui/flows/call_tree.clj",
"chars": 13231,
"preview": "(ns flow-storm.debugger.ui.flows.call-tree\n (:require [flow-storm.debugger.ui.flows.code :as flow-code]\n [fl"
},
{
"path": "src-dbg/flow_storm/debugger/ui/flows/code.clj",
"chars": 51147,
"preview": "(ns flow-storm.debugger.ui.flows.code\n (:require [clojure.pprint :as pp]\n [clojure.string :as str]\n "
},
{
"path": "src-dbg/flow_storm/debugger/ui/flows/components.clj",
"chars": 6015,
"preview": "(ns flow-storm.debugger.ui.flows.components\n (:require [flow-storm.debugger.ui.utils :as ui-utils]\n [flow-st"
},
{
"path": "src-dbg/flow_storm/debugger/ui/flows/functions.clj",
"chars": 11804,
"preview": "(ns flow-storm.debugger.ui.flows.functions\n (:require [flow-storm.debugger.state :refer [store-obj obj-lookup] :as dbg-"
},
{
"path": "src-dbg/flow_storm/debugger/ui/flows/general.clj",
"chars": 4710,
"preview": "(ns flow-storm.debugger.ui.flows.general\n (:require [flow-storm.debugger.state :as dbg-state :refer [obj-lookup]]\n "
},
{
"path": "src-dbg/flow_storm/debugger/ui/flows/multi_thread_timeline.clj",
"chars": 7993,
"preview": "(ns flow-storm.debugger.ui.flows.multi-thread-timeline\n (:require [flow-storm.debugger.ui.utils\n :as ui-uti"
},
{
"path": "src-dbg/flow_storm/debugger/ui/flows/printer.clj",
"chars": 12817,
"preview": "(ns flow-storm.debugger.ui.flows.printer\n (:require [flow-storm.debugger.ui.utils :as ui-utils :refer [event-handler]]\n"
},
{
"path": "src-dbg/flow_storm/debugger/ui/flows/screen.clj",
"chars": 22347,
"preview": "(ns flow-storm.debugger.ui.flows.screen\n (:require [flow-storm.debugger.ui.flows.code :as flow-code]\n [flow-"
},
{
"path": "src-dbg/flow_storm/debugger/ui/flows/search.clj",
"chars": 10486,
"preview": "(ns flow-storm.debugger.ui.flows.search\n (:require [flow-storm.debugger.ui.utils :as ui-utils :refer [event-handler]]\n "
},
{
"path": "src-dbg/flow_storm/debugger/ui/main.clj",
"chars": 24981,
"preview": "(ns flow-storm.debugger.ui.main\n\n \"Main UI sub-component which renders the GUI using JavaFX.\n\n Defines a pretty standa"
},
{
"path": "src-dbg/flow_storm/debugger/ui/outputs/screen.clj",
"chars": 8133,
"preview": "(ns flow-storm.debugger.ui.outputs.screen\n (:require [flow-storm.debugger.ui.utils :as ui-utils]\n [flow-stor"
},
{
"path": "src-dbg/flow_storm/debugger/ui/plugins.clj",
"chars": 1396,
"preview": "(ns flow-storm.debugger.ui.plugins\n (:require [flow-storm.utils :as utils :refer [log-error log]]))\n\n(defonce *plugins "
},
{
"path": "src-dbg/flow_storm/debugger/ui/tasks.clj",
"chars": 1022,
"preview": "(ns flow-storm.debugger.ui.tasks\n (:require [flow-storm.debugger.events-queue :as events-queue]\n [flow-storm"
},
{
"path": "src-dbg/flow_storm/debugger/ui/utils.clj",
"chars": 11973,
"preview": "(ns flow-storm.debugger.ui.utils\n\n \"Mostly javaFx Utilities for building the UI\"\n\n (:require [flow-storm.utils :as uti"
},
{
"path": "src-dbg/flow_storm/debugger/user_guide.clj",
"chars": 772,
"preview": "(ns flow-storm.debugger.user-guide\n (:require [flow-storm.debugger.ui.components :as ui]\n [flow-storm.debugg"
},
{
"path": "src-dbg/flow_storm/debugger/websocket.clj",
"chars": 6694,
"preview": "(ns flow-storm.debugger.websocket\n\n \"Component that manages the websocket server started by the debugger.\n\n It will :\n"
},
{
"path": "src-dev/dev.clj",
"chars": 8160,
"preview": "(ns dev\n\n \"A bunch of utilities to help with development.\n\n After loading this ns you can :\n\n - `start-local` to star"
},
{
"path": "src-dev/dev_tester.clj",
"chars": 2874,
"preview": "(ns dev-tester)\n\n;;;;;;;;;;;;;;;;;;;;;;;\n;; Some testing code ;;\n;;;;;;;;;;;;;;;;;;;;;;;\n\n(defn uncatched-throw []\n (le"
},
{
"path": "src-dev/dev_tester.cljs",
"chars": 1077,
"preview": "(ns dev-tester\n (:require-macros [dev-tester :refer [dummy-sum-macro]]))\n\n(defn factorial [n]\n (if (zero? n)\n 1\n "
},
{
"path": "src-dev/dev_tester_12.clj",
"chars": 1044,
"preview": "(ns dev-tester-12)\n\n(defn method-values []\n (let [parser ^[String] Integer/parseInt\n parsed (mapv parser [\"1\" \"2"
},
{
"path": "src-dev/logging.properties",
"chars": 91,
"preview": ".level=SEVERE\n\n# Increase logging level for javafx to suppress warnings\njavafx.level=SEVERE"
},
{
"path": "src-dev/user.clj",
"chars": 0,
"preview": ""
},
{
"path": "src-inst/data_readers.clj",
"chars": 267,
"preview": "{trace flow-storm.api/read-trace-tag\n ctrace flow-storm.api/read-ctrace-tag\n rtrace flow-storm.api/read-rtrace-tag\n tap"
},
{
"path": "src-inst/flow_storm/api.clj",
"chars": 15050,
"preview": "(ns flow-storm.api\n\n \"API intended for users.\n Provides functionality to start the debugger and instrument forms.\"\n\n "
},
{
"path": "src-inst/flow_storm/api.cljs",
"chars": 1805,
"preview": "(ns flow-storm.api\n (:require [flow-storm.remote-websocket-client :as remote-websocket-client]\n [flow-storm."
},
{
"path": "src-inst/flow_storm/jobs.cljc",
"chars": 2685,
"preview": "(ns flow-storm.jobs\n (:require [flow-storm.runtime.events :as rt-events]\n [flow-storm.utils :as utils :refer"
},
{
"path": "src-inst/flow_storm/nrepl/middleware.clj",
"chars": 17717,
"preview": "(ns flow-storm.nrepl.middleware\n (:require [flow-storm.runtime.debuggers-api :as debuggers-api]\n [flow-storm"
},
{
"path": "src-inst/flow_storm/ns_reload_utils.clj",
"chars": 11437,
"preview": ";; Most functions here were copied from the amazing clj-reload by Nikita Prokopov (Tonsky)\n;; https://github.com/tonsky/"
},
{
"path": "src-inst/flow_storm/preload.cljs",
"chars": 515,
"preview": "(ns flow-storm.preload\n (:require [flow-storm.runtime.debuggers-api :as dbg-api]))\n\n(def dbg-port (js/parseInt\n "
},
{
"path": "src-inst/flow_storm/remote_websocket_client.clj",
"chars": 3574,
"preview": "(ns flow-storm.remote-websocket-client\n (:refer-clojure :exclude [send])\n (:require [flow-storm.json-serializer :as se"
},
{
"path": "src-inst/flow_storm/remote_websocket_client.cljs",
"chars": 3865,
"preview": "(ns flow-storm.remote-websocket-client\n (:require [flow-storm.utils :refer [log log-error] :as utils]\n [flow"
},
{
"path": "src-inst/flow_storm/runtime/debuggers_api.cljc",
"chars": 39839,
"preview": "(ns flow-storm.runtime.debuggers-api\n (:require [flow-storm.runtime.indexes.api :as index-api]\n [flow-storm."
},
{
"path": "src-inst/flow_storm/runtime/events.cljc",
"chars": 3750,
"preview": "(ns flow-storm.runtime.events)\n\n(defonce *dispatch (atom nil))\n(defonce pending-events (atom []))\n\n(defn clear-dispatch-"
},
{
"path": "src-inst/flow_storm/runtime/indexes/api.cljc",
"chars": 59354,
"preview": "(ns flow-storm.runtime.indexes.api\n \n \"You can use this namespace to work with your recordings from the repl.\n\n Find "
},
{
"path": "src-inst/flow_storm/runtime/indexes/form_registry.cljc",
"chars": 479,
"preview": "(ns flow-storm.runtime.indexes.form-registry\n (:require [flow-storm.runtime.indexes.protocols :as index-protos]))\n\n(def"
},
{
"path": "src-inst/flow_storm/runtime/indexes/protocols.cljc",
"chars": 2536,
"preview": "(ns flow-storm.runtime.indexes.protocols)\n\n;;;;;;;;;;;;;;;;;;;;;;;;\n;; Timeline protocols ;;\n;;;;;;;;;;;;;;;;;;;;;;;;\n\n("
},
{
"path": "src-inst/flow_storm/runtime/indexes/storm_form_registry.clj",
"chars": 584,
"preview": "(ns flow-storm.runtime.indexes.storm-form-registry\n (:require [flow-storm.runtime.indexes.protocols :as index-protos]\n "
},
{
"path": "src-inst/flow_storm/runtime/indexes/thread_registry.cljc",
"chars": 3870,
"preview": "(ns flow-storm.runtime.indexes.thread-registry\n (:require [flow-storm.runtime.indexes.protocols :as index-protos]\n "
},
{
"path": "src-inst/flow_storm/runtime/indexes/timeline_index.cljc",
"chars": 19506,
"preview": "(ns flow-storm.runtime.indexes.timeline-index\n (:require [flow-storm.runtime.indexes.protocols :as index-protos]\n "
},
{
"path": "src-inst/flow_storm/runtime/indexes/total_order_timeline.cljc",
"chars": 5692,
"preview": "(ns flow-storm.runtime.indexes.total-order-timeline\n (:require [flow-storm.runtime.indexes.protocols :as index-protos]\n"
},
{
"path": "src-inst/flow_storm/runtime/indexes/utils.cljc",
"chars": 4904,
"preview": "(ns flow-storm.runtime.indexes.utils\n #?(:clj (:require [clojure.data.int-map :as int-map]))\n #?(:clj (:import [java.u"
},
{
"path": "src-inst/flow_storm/runtime/outputs.cljc",
"chars": 1299,
"preview": "(ns flow-storm.runtime.outputs\n (:require [flow-storm.runtime.events :as rt-events]\n [flow-storm.runtime.val"
},
{
"path": "src-inst/flow_storm/runtime/types/bind_trace.cljc",
"chars": 1423,
"preview": "(ns flow-storm.runtime.types.bind-trace\n (:require [flow-storm.runtime.indexes.protocols :as index-protos]\n "
},
{
"path": "src-inst/flow_storm/runtime/types/expr_trace.cljc",
"chars": 1411,
"preview": "(ns flow-storm.runtime.types.expr-trace\n (:require [flow-storm.runtime.indexes.protocols :as index-protos]\n "
},
{
"path": "src-inst/flow_storm/runtime/types/fn_call_trace.cljc",
"chars": 2133,
"preview": "(ns flow-storm.runtime.types.fn-call-trace\n (:require [flow-storm.runtime.indexes.protocols :as index-protos]\n "
},
{
"path": "src-inst/flow_storm/runtime/types/fn_return_trace.cljc",
"chars": 2919,
"preview": "(ns flow-storm.runtime.types.fn-return-trace\n (:require [flow-storm.runtime.indexes.protocols :as index-protos]\n "
},
{
"path": "src-inst/flow_storm/runtime/values.cljc",
"chars": 16355,
"preview": "(ns flow-storm.runtime.values\n (:require [clojure.pprint :as pp]\n [flow-storm.utils :as utils]\n ["
},
{
"path": "src-inst/flow_storm/storm_api.clj",
"chars": 3262,
"preview": "(ns flow-storm.storm-api\n (:require [flow-storm.api :as fs-api]\n [flow-storm.tracer :as tracer]\n "
},
{
"path": "src-inst/flow_storm/storm_preload.cljs",
"chars": 630,
"preview": "(ns flow-storm.storm-preload\n (:require [cljs.storm.tracer]\n [flow-storm.tracer :as tracer]\n [flo"
},
{
"path": "src-inst/flow_storm/tracer.cljc",
"chars": 10788,
"preview": "(ns flow-storm.tracer\n (:require [flow-storm.utils :as utils :refer [stringify-coord]] \n [flow-st"
},
{
"path": "src-shared/flow_storm/eql.cljc",
"chars": 2729,
"preview": "(ns flow-storm.eql\n (:require [flow-storm.utils :as utils]))\n\n(defn entity? [data]\n (and (map? data)\n (every? ke"
},
{
"path": "src-shared/flow_storm/form_pprinter.clj",
"chars": 11249,
"preview": "(ns flow-storm.form-pprinter\n (:require [clojure.pprint :as pp]\n [flow-storm.utils :as utils]\n [h"
},
{
"path": "src-shared/flow_storm/json_serializer.clj",
"chars": 2507,
"preview": "(ns flow-storm.json-serializer\n (:require [cognitect.transit :as transit]\n [flow-storm.utils :refer [log-err"
},
{
"path": "src-shared/flow_storm/json_serializer.cljs",
"chars": 1853,
"preview": "(ns flow-storm.json-serializer\n (:require [cognitect.transit :as transit :refer [write-handler]]\n [cljs.read"
},
{
"path": "src-shared/flow_storm/state_management.cljc",
"chars": 3024,
"preview": "(ns flow-storm.state-management\n\n \"Like a smaller simplified version of mount so\n we don't bring that dependency with "
},
{
"path": "src-shared/flow_storm/types.cljc",
"chars": 1560,
"preview": "(ns flow-storm.types\n (:require [flow-storm.utils :as utils :refer [log-error]]))\n\n(defrecord ValueRef [vid])\n\n(defn va"
},
{
"path": "src-shared/flow_storm/utils.cljc",
"chars": 13748,
"preview": "(ns flow-storm.utils\n #?(:cljs (:require [goog.string :as gstr]\n [clojure.string :as str]\n "
},
{
"path": "tests.edn",
"chars": 129,
"preview": "#kaocha/v1\n{:tests [{:id :unit-clj\n :source-paths [\"src-inst\" \"src-dbg\" \"src-shared\"]\n :test-paths [\"t"
}
]
About this extraction
This page contains the full source code of the jpmonettas/flow-storm-debugger GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 112 files (1.2 MB), approximately 321.9k tokens. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.