-
Notifications
You must be signed in to change notification settings - Fork 25
/
core.clj
124 lines (114 loc) · 4.67 KB
/
core.clj
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
(ns clj-stacktrace.core
(:require [clj-stacktrace.utils :as utils]))
(defn- clojure-code?
"Returns true if the filename is non-null and indicates a clj source file."
[class-name file]
(or (utils/re-match? #"^user" class-name)
(= file "NO_SOURCE_FILE")
(and file (utils/re-match? #"\.clj$" file))))
(defn- clojure-ns
"Returns the clojure namespace name implied by the bytecode class name."
[class-name]
(utils/re-gsub #"_" "-" (utils/re-get #"([^$]+)\$" class-name 1)))
; drop everything before and including the first $
; drop everything after and including and the second $
; drop any __xyz suffixes
; sub _PLACEHOLDER_ for the corresponding char
(def clojure-fn-subs
[[#"^[^$]*\$" ""]
[#"\$.*" ""]
[#"__\d+.*" ""]
[#"_QMARK_" "?"]
[#"_BANG_" "!"]
[#"_PLUS_" "+"]
[#"_GT_" ">"]
[#"_LT_" "<"]
[#"_EQ_" "="]
[#"_STAR_" "*"]
[#"_SLASH_" "/"]
[#"_" "-"]])
(defn- clojure-fn
"Returns the clojure function name implied by the bytecode class name."
[class-name]
(reduce
(fn [base-name [pattern sub]] (utils/re-gsub pattern sub base-name))
class-name
clojure-fn-subs))
(defn- clojure-annon-fn?
"Returns true if the bytecode class name implies an anonymous inner fn."
[class-name]
(utils/re-match? #"\$.*\$" class-name))
(defn parse-trace-elem
"Returns a map of information about the java trace element.
All returned maps have the keys:
:file String of source file name.
:line Number of source line number of the enclosing form.
Additionally for elements from Java code:
:java true, to indicate a Java elem.
:class String of the name of the class to which the method belongs.
Additionally for elements from Clojure code:
:clojure true, to inidcate a Clojure elem.
:ns String representing the namespace of the function.
:fn String representing the name of the enclosing var for the function.
:annon-fn true iff the function is an anonymous inner fn."
[elem]
(let [class-name (.getClassName elem)
file (.getFileName elem)
line (let [l (.getLineNumber elem)] (if (pos? l) l))
parsed {:file file :line line}]
(if (clojure-code? class-name file)
(assoc parsed
:clojure true
:ns (clojure-ns class-name)
:fn (clojure-fn class-name)
:annon-fn (clojure-annon-fn? class-name))
(assoc parsed
:java true
:class class-name
:method (.getMethodName elem)))))
(defn parse-trace-elems
"Returns a seq of maps providing usefull information about the java stack
trace elements. See parse-trace-elem."
[elems]
(map parse-trace-elem elems))
(defn- trim-redundant
"Returns the portion of the tail of causer-elems that is not duplicated in
the tail of caused-elems. This corresponds to the \"...26 more\" that you
see at the bottom of regular trace dumps."
[causer-parsed-elems caused-parsed-elems]
(loop [rcauser-parsed-elems (reverse causer-parsed-elems)
rcaused-parsed-elems (reverse caused-parsed-elems)]
(if-let [rcauser-bottom (first rcauser-parsed-elems)]
(if (= rcauser-bottom (first rcaused-parsed-elems))
(recur (next rcauser-parsed-elems) (next rcaused-parsed-elems))
(reverse rcauser-parsed-elems)))))
(defn- parse-cause-exception
"Like parse-exception, but for causing exceptions. The returned map has all
of the same keys as the map returned by parse-exception, and one added one:
:trimmed-elems A subset of :trace-elems representing the portion of the
top of the stacktrace not shared with that of the caused
exception."
[causer-e caused-parsed-elems]
(let [parsed-elems (parse-trace-elems (.getStackTrace causer-e))
base {:class (class causer-e)
:message (.getMessage causer-e)
:trace-elems parsed-elems
:trimmed-elems (trim-redundant parsed-elems caused-parsed-elems)}]
(if-let [cause (.getCause causer-e)]
(assoc base :cause (parse-cause-exception cause parsed-elems))
base)))
(defn parse-exception
"Returns a Clojure map providing usefull informaiton about the exception.
The map has keys
:class Class of the exception.
:message Regular exception message string.
:trace-elems Parsed stack trace elems, see parse-trace-elem.
:cause See parse-cause-exception."
[e]
(let [parsed-elems (parse-trace-elems (.getStackTrace e))
base {:class (class e)
:message (.getMessage e)
:trace-elems parsed-elems}]
(if-let [cause (.getCause e)]
(assoc base :cause (parse-cause-exception cause parsed-elems))
base)))