Skip to content

Commit

Permalink
added run macro for easier deep trace usage
Browse files Browse the repository at this point in the history
  • Loading branch information
Lukas Domagala committed Dec 23, 2021
1 parent a45d403 commit f2545a1
Show file tree
Hide file tree
Showing 11 changed files with 136 additions and 81 deletions.
12 changes: 5 additions & 7 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
# omni-trace
Omnipotent/omniscient tracing core for debugging clojure(script)

alpha, api is still unstable but its only for dev time so there shouldn't be any problems.

beta, api is still unstable but its only for dev time so there shouldn't be any problems.

[![Clojars Project](https://img.shields.io/clojars/v/org.clojars.cyrik/omni-trace.svg)](https://clojars.org/org.clojars.cyrik/omni-trace)

Expand Down Expand Up @@ -56,15 +55,15 @@ or just through github source:

[Example "real world" usage story](https://cyrik.github.io/day1.html) (Spoilers for Advent of Code 2021 Day1)

## experimental deeptrace
## Deeptrace

```clojure
(require '[cyrik.omni-trace :as o])

;; uncomment to also trace clojure.core, tested with testing-ns only
;; (require '[cyrik.omni-trace.instrument :as i])
;; (reset! i/ns-blacklist [])
(o/run-traced 'cyrik.omni-trace.testing-ns/run-machine)
(o/run (cyrik.omni-trace.testing-ns/run-machine))

;; run this for cljs
;; (o/run-traced-cljs 'cyrik.omni-trace.testing-ns/run-machine)
Expand All @@ -73,14 +72,14 @@ or just through github source:

This uses [clj-kondo](https://github.com/clj-kondo/clj-kondo) to find all transitive calls from the provided symbol.
It then runs the function with any supplied args and untraces everything.
This reaches all the way down into clojure.core.
This reaches all the way down into clojure.core if you remove the blacklist.

![Screenshot](docs/deep-trace.png)

If your deeptraced function only traced itself make sure it's namespace is required somewhere inside "src" or "dev".
I will add more options for choosing namespaces later.
Currently there are still a few problems with recursion, will have to rewrite the deps graph for it.
I'm guessing a lot of code will stil explode when allowing clojure.core trace, since the tracing code itself uses those.
I'm guessing a lot of code will still explode when allowing clojure.core trace, since the tracing code itself, uses those.
Will probably cleanup the blacklist and try to use local function copies for tracing.

## experimental inner-trace
Expand Down Expand Up @@ -188,7 +187,6 @@ works pretty well already, but:

## In the works

- loads of cleanup of api and code
- better trace output to the REPL
- performance
- callbacks from Portal so you can rerun an updated function with the old params by clicking on it in the Flamegraph
Expand Down
2 changes: 1 addition & 1 deletion deps.edn
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@
:main-opts ["-m" "kaocha.runner" "unit" "features"]}
:test-kaocha-debux {:extra-paths ["test"]
:main-opts ["-m" "kaocha.runner" "unit-debux"]}
:test-cljs {:extra-paths ["test/unit"]
:test-cljs {:extra-paths ["test/unit" "test/features"]
:main-opts ["-m" "kaocha.runner" "unit-cljs"]}
:build {:deps {io.github.seancorfield/build-clj
{:git/tag "v0.5.4" :git/sha "bc9c0cc"}}
Expand Down
4 changes: 2 additions & 2 deletions dev/user.clj
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@

;; run-traced
(o/reset-workspace!)
(o/run-traced 'cyrik.omni-trace.testing-ns/run-machine)
(o/run (cyrik.omni-trace.testing-ns/run-machine))
(tap> (o/flamegraph))
(o/reset-workspace!)

Expand All @@ -72,7 +72,7 @@

;; deep into core
(reset! i/ns-blacklist [])
(o/run-traced 'cyrik.omni-trace.testing-ns/run-machine)
(o/run (cyrik.omni-trace.testing-ns/run-machine))
(tap> (o/flamegraph))
(o/reset-workspace!)
(reset! i/ns-blacklist ['cljs.core 'clojure.core])
Expand Down
4 changes: 2 additions & 2 deletions dev/user.cljs
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,7 @@
(tap> ^{:portal.viewer/default :portal.viewer/hiccup} [:button {:onclick (str "alert('123')")} (str "alert('123')")])
[:button {:onclick "alert('123')"} "Test it!"]
;remove tracing from a namesapce
(o/uninstrument-ns 'omni-trace.testing-ns)
(o/uninstrument-ns 'cyrik.omni-trace.testing-ns)
(o/reset-workspace!)
i/instrumented-vars
(macroexpand '(o/instrument-ns 'cyrik.omni-trace.testing-ns))
Expand All @@ -57,7 +57,7 @@
(macro/cljs-macroexpand-all '(o/instrument-fn 'cyrik.omni-trace.testing-ns/insert-coin))
(macro/cljs-macroexpand-all '(o/instrument 'cyrik.omni-trace.testing-ns/insert-coin))
(macro/cljs-macroexpand-all '(i/uninstrument))
(i/uninstrument)
(o/untrace)
(alter-meta! (var cyrik.omni-trace.testing-ns/insert-coin) assoc-in [:stuffs] "yeah123")
(.log js/console (meta (var cyrik.omni-trace.testing-ns/insert-coin)))

Expand Down
62 changes: 44 additions & 18 deletions src/cyrik/omni_trace.cljc
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
(ns cyrik.omni-trace
(:require
#?(:clj [cyrik.omni-trace.deep-trace :as deep])
#?(:clj [cyrik.omni-trace.util :as util])
[cyrik.omni-trace.instrument :as i]
[cyrik.omni-trace.graph :as flame]
[cljs.test]
#?(:clj [cljs.analyzer.api :as ana-api])
[net.cgrand.macrovich :as macros])

#?(:cljs (:require-macros
[cyrik.omni-trace :refer [instrument-fn uninstrument-fn instrument-ns uninstrument-ns]])))
[cyrik.omni-trace :refer [instrument-fn uninstrument-fn instrument-ns uninstrument-ns
run]])))

(defmacro instrument-fn
"Instruments a function.
Expand Down Expand Up @@ -87,33 +90,56 @@
#?(:clj
(defn run-traced [s & args]
(apply #'deep/run-traced (into [s] args))))
(macros/deftime
#?(:clj
(defmacro run [form]
(macros/case :clj `(deep/run-traced (~util/->sym ~(first form)) ~@(rest form))
:cljs `(do
~(let [fun (ana-api/resolve &env (first form))
args (rest form)
n (:ns fun)
f (-> fun
:name
name
symbol)
dep-list (deep/transitive-deps (deep/deps (deep/analysis ["dev" "src"]) :cljs)
n f)
sym-list (mapv #(symbol (name (first %)) (name (second %))) (filter first dep-list)) ;;fix nil namespaces
instrumenters (mapv (fn [sym] `#(cyrik.omni-trace.instrument.cljs/cljs-instrument-fn '~sym {:cyrik.omni-trace/workspace cyrik.omni-trace.instrument/workspace} cyrik.omni-trace.instrument/instrumented)) sym-list)
deinstrumenters (mapv (fn [sym] `#(cyrik.omni-trace.instrument.cljs/cljs-instrument-fn '~sym {:cyrik.omni-trace/workspace cyrik.omni-trace.instrument/workspace} cyrik.omni-trace.instrument/uninstrumented)) sym-list)
]
`(let [_# (doseq [f# ~instrumenters]
(f#))
result# (apply ~(symbol (name n) (name f)) (list ~@args))
_# (doseq [g# ~deinstrumenters]
(g#))]
result#)))))))

#?(:clj
(defmacro run-traced-cljs [ns f & args]
(let [ns (symbol (name ns))
f (symbol (name f))
dep-list (deep/transitive-deps (deep/deps (deep/analysis ["dev" "src"]) :cljs)
ns f)
sym-list (mapv #(symbol (name (first %)) (name (second %))) (filter first dep-list)) ;;fix nil namespaces
instrumenters (mapv (fn [sym] `#(cyrik.omni-trace.instrument.cljs/cljs-instrument-fn '~sym {:cyrik.omni-trace/workspace cyrik.omni-trace.instrument/workspace} cyrik.omni-trace.instrument/instrumented)) sym-list)
deinstrumenters (mapv (fn [sym] `#(cyrik.omni-trace.instrument.cljs/cljs-instrument-fn '~sym {:cyrik.omni-trace/workspace cyrik.omni-trace.instrument/workspace} cyrik.omni-trace.instrument/uninstrumented)) sym-list)
runner `#(~(symbol (name ns) (name f)))
merged (into [] (concat instrumenters [runner] deinstrumenters))]
`(doseq [f# ~merged]
(f#)))))

(comment
(require '[portal.api :as p])
(def portal (p/open))
(add-tap #'p/submit)




(filter #(and (= (:lang %) :cljs) (= (:from %) 'cyrik.omni-trace.testing-ns))(:var-usages(deep/analysis ["dev" "src"])))

(filter #(and (= (:lang %) :cljs) (= (:from %) 'cyrik.omni-trace.testing-ns)) (:var-usages (deep/analysis ["dev" "src"])))



(require '[cyrik.cljs-macroexpand :as macro])
(clojure.walk/macroexpand-all '(run (+ 1 2)))
(macro/cljs-macroexpand-all '(run `(+ 1 2)))
(macroexpand '(run `(+ 1 2)))
(defn thing [a]
(inc a))
(defn thing2 [a b]
(+ a b))
(run `(thing (inc (inc 1))))
(macroexpand '(run `(thing (inc (inc 1)))))
(macro/cljs-macroexpand-all '(run `(thing (inc (inc 1)))))
(cyrik.omni-trace/run (thing2 1 2))
(macroexpand '(cyrik.omni-trace/run (thing2 1 2)))
(macro/cljs-macroexpand-all '(cyrik.omni-trace/run (thing2 1 2)))
.
)
42 changes: 9 additions & 33 deletions src/cyrik/omni_trace/instrument/clj.clj
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@
[clojure.tools.reader.reader-types :as rts]
[clojure.string :as string]
[clojure.java.io :as io]
[cyrik.omni-trace.util :as u]
[borkdude.dynaload :as dynaload]
clojure.repl))

Expand Down Expand Up @@ -55,44 +56,19 @@
(use 'clojure.core)
(eval form)))

(defn var->sym [v]
(let [m (meta v)]
(symbol (name (ns-name (:ns m))) (name (:name m)))))

(defn ->var [something]
(cond
(var? something) something
(symbol? something) (resolve something)
(string? something) (resolve (symbol something))
:else nil))

(defn ->sym [something]
(cond
(symbol? something) something
(var? something) (var->sym something)
(string? something) (symbol something)
:else nil))

(defn ->ns [something]
(cond
(instance? clojure.lang.Namespace something) something
(symbol? something) (find-ns something)
(string? something) (find-ns (symbol something))
:else nil))

(defn fully-qualified [s]
(var->sym (resolve s)))
(u/var->sym (resolve s)))

(defn vars-in-ns-clj [sym]
(if (find-ns sym)
(for [[_ v] (ns-interns sym)
:when (not (:macro (meta v)))]
(var->sym v))
(u/var->sym v))
(throw (Exception. (str "ns " sym " does not exist.")))))

(defn clj-instrument-fn [f opts instrumenter]
(when-let [v (->var f)]
(let [var-name (var->sym v)
(when-let [v (u/->var f)]
(let [var-name (u/var->sym v)
original @v
meta* (update (meta v) :file #(if-let [classpath-file (io/resource %)]
(.getPath classpath-file)
Expand All @@ -115,7 +91,7 @@

(defn clj-instrument-ns [ns-sym opts instrumenter]
(->> ns-sym
->sym
u/->sym
vars-in-ns-clj
(filter symbol?)
(distinct)
Expand All @@ -124,16 +100,16 @@

(defn instrument [s opts instrumenter]
(let [xs (if (coll? s) s [s])
syms (map #(->sym %) xs)]
syms (map #(u/->sym %) xs)]
(mapcat #(if (string/includes? (name %) ".")
(clj-instrument-ns % opts instrumenter)
(instrument-syms [%] opts instrumenter))
syms)))

(comment
(-> #'cyrik.omni-trace.instrument.clj/fully-qualified-sym str symbol)
(instance? clojure.lang.Namespace (->ns "cyrik.omni-trace.testing-ns"))
(->var "cyrik.omni-trace.testing-ns/insert-coin")
(instance? clojure.lang.Namespace (u/->ns "cyrik.omni-trace.testing-ns"))
(u/->var "cyrik.omni-trace.testing-ns/insert-coin")

(type cyrik.omni-trace.testing-ns/insert-coin)
)
5 changes: 3 additions & 2 deletions src/cyrik/omni_trace/instrument/cljs.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@
(let [var-name (:name v)
file (get-file &env (or (:file (:meta v))
(:file v)));;incase of jar?
meta* (assoc (:meta v) :file file)]
meta* (select-keys (assoc (:meta v) :file file) [:column :line :file])]
(when-not (:macro v)
(swap! instrumented-var-names conj var-name)
`(when-let [instrumented# (~instrumenter '~sym (var ~sym) ~meta* nil ~opts)]
Expand All @@ -65,7 +65,7 @@
(let [var-name (:name v)
file (get-file &env (or (:file (:meta v))
(:file v)));;incase of jar?
meta* (assoc (:meta v) :file file)]
meta* (select-keys (assoc (:meta v) :file file) [:column :line :file])]
(when-not (:macro v)
(swap! instrumented-var-names disj var-name)
`(when-let [instrumented# (~instrumenter '~sym (var ~sym) ~meta* nil ~opts)]
Expand Down Expand Up @@ -120,6 +120,7 @@

(comment
(require '[cyrik.cljs-macroexpand :as macro])
(reset! instrumented-var-names #{})
(cyrik.omni-trace.instrument.cljs/sym-test "cljs.core/inc")
(macro/cljs-macroexpand-all '(cyrik.omni-trace.instrument.cljs/instrument ["cyrik.omni-trace.testing-ns" #'cljs.core/inc] {} #()))
(instrument "cyrik.omni-trace.testing-ns" {} #())
Expand Down
14 changes: 12 additions & 2 deletions src/cyrik/omni_trace/testing_ns.cljc
Original file line number Diff line number Diff line change
Expand Up @@ -110,8 +110,18 @@
(throw #?(:cljs (js/Error. "Oops") :clj (Exception. "Oops")))
(dissoc machine :change-returned)])

(defn run-machine []
(-> machine-init
(defn run-machine
([]
(-> machine-init
(insert-coin :quarter)
(insert-coin :dime)
(insert-coin :nickel)
(insert-coin :penny)
(press-button :a1)))
)
(defn run-machine-with
[start]
(-> start
(insert-coin :quarter)
(insert-coin :dime)
(insert-coin :nickel)
Expand Down
32 changes: 32 additions & 0 deletions src/cyrik/omni_trace/util.clj
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
(ns cyrik.omni-trace.util
(:require [clojure.repl :as repl]
))

(defn var->sym [v]
(let [m (meta v)]
(symbol (name (ns-name (:ns m))) (name (:name m)))))

(defn ->var [something]
(cond
(var? something) something
(symbol? something) (resolve something)
(string? something) (resolve (symbol something))
:else nil))

(defn ->sym [something]
(cond
(fn? something) (-> something .getClass .getName repl/demunge symbol)
(symbol? something) something
(var? something) (var->sym something)
(string? something) (symbol something)
:else nil))

(defn ->ns [something]
(cond
(instance? clojure.lang.Namespace something) something
(symbol? something) (find-ns something)
(string? something) (find-ns (symbol something))
:else nil))

(defn fully-qualified [s]
(var->sym (resolve s)))
Loading

0 comments on commit f2545a1

Please sign in to comment.