Skip to content

Instantly share code, notes, and snippets.

@Grazfather
Last active March 5, 2021 23:05
Show Gist options
  • Save Grazfather/be5d5d15057fc985e3c6f03e26dd1aa5 to your computer and use it in GitHub Desktop.
Save Grazfather/be5d5d15057fc985e3c6f03e26dd1aa5 to your computer and use it in GitHub Desktop.
#!/usr/bin/env bb
(ns context-times
(:require [clojure.data.csv :as csv]
[clojure.java.io :as io]
[clojure.java.shell :refer [sh]]
[clojure.string :as s]
[clojure.tools.cli :refer [parse-opts]]))
;; CLI
(def cli-options
[["-s" "--start COMMIT" "Start commit"
:default "@^^^"
]
["-e" "--end COMMIT" "End commit"
:default "@"
]
["-r" "--repetitions REP" "Number of times to run the command on each commit"
:default 5
:parse-fn #(Integer/parseInt %)
:validate [#(> % 0) "Must be a number greater than 0"]
]
["-o" "--output FILENAME" "Filename to write CSV output to"
:default "stats.csv"
]
["-c" "--cmd COMMAND" "Python invocation to profile from GDB"
:default "gdb.execute(\"context\")"]])
;; Git stuff
(defn get-revision-hash
[rev]
(-> (sh "git" "log" "-1" "--pretty=%h" rev) :out s/trim))
(defn get-revision-title
[rev]
(-> (sh "git" "log" "-1" "--pretty=%s" rev) :out s/trim))
(defn get-revisions-in-range
[start end]
(-> (sh "git" "rev-list" "--reverse" (str start ".." end)) :out s/split-lines))
(defn get-current-branch
[]
(let [[_ _ branch] (->> (sh "git" "branch")
:out
(re-find #"\* (\(HEAD detached at )?([0-9a-zA-Z_\-.]+)"))]
branch))
(defn checkout
[rev]
(sh "git" "checkout" "--quiet" rev))
(defn run-on-revision
([f rev] (run-on-revision f rev 1))
([f rev n]
(let [current (get-revision-hash "@")]
(try
(checkout rev)
(repeatedly n f)
(finally
(sh "git" "reset" "--hard" "--quiet")
(checkout current))))))
;; GDB profile invocations
(defn time-gdb-command
[command]
(-> (sh "gdb" "-q"
"-ex" "start"
"-ex" "pi import profile"
"-ex" (str "pi profile.run(" (pr-str command) ", sort=\"cumtime\")")
"-ex" "quit"
"/bin/cat")
:out))
(defn get-profile-time
[output]
(let [[_ t] (re-find #"\n\s+\d+\s+[0-9.]+\s+[0-9.]+\s+([0-9.]+)\s+[0-9.]+ profile:.+\n" output)]
(read-string t)))
(defn get-command-times
([cmd start end] (get-command-times cmd start end 1))
([cmd start end n]
(let [revisions (get-revisions-in-range start end)
profiles (map #(run-on-revision #(time-gdb-command cmd) % n) revisions)
times (map #(map get-profile-time %) profiles)]
(map vector revisions times))))
(defn write-stats
[stats filename]
(with-open [writer (io/writer filename)]
(->> stats
(map #(let [[rev times] %]
(apply vector rev (get-revision-title rev) times)))
(csv/write-csv writer))))
(def opts (:options (parse-opts *command-line-args* cli-options)))
(let [start (:start opts)
end (:end opts)
reps (:repetitions opts)
cmd (:cmd opts)
filename (:output opts)]
(println (str "Running '" cmd "' on each revision between " start " and " end " " (str reps)
" times and writing output to " filename))
(let [stats (get-command-times cmd start end reps)]
(write-stats stats filename)))
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment