-
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathMain.purs
More file actions
285 lines (246 loc) · 12.4 KB
/
Main.purs
File metadata and controls
285 lines (246 loc) · 12.4 KB
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
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
-- Parse the NuttX Exception and NuttX Stack Dump. Explain the NuttX Exception.
-- Based on https://github.com/purescript-contrib/purescript-string-parsers/blob/main/test/Examples.purs
-- Main Module will be started by `spago run`
module Main where
-- Import the Prelude (Common Functions for PureScript)
import Prelude hiding (between)
-- Import the PureScript Libraries
import Data.Either (Either(..))
import Data.Int (fromStringAs, hexadecimal)
import Data.Maybe (Maybe(..), fromMaybe, isJust)
import Data.String (stripPrefix)
import Data.String.Pattern (Pattern(..))
import Data.String.Regex (match)
import Data.String.Regex.Flags (noFlags)
import Data.String.Regex.Unsafe (unsafeRegex)
import Effect (Effect)
import Effect.Console (log, logShow)
import StringParser (Parser, optional, regex, runParser, skipSpaces, string)
-- Main Function that will run our Test Code.
-- `Effect` says that it will do Side Effects (printing to console)
-- `Unit` means that no value will be returned
-- The next line declares the Function Type. We can actually erase it, VSCode PureScript Extension will helpfully suggest it for us.
main :: Effect Unit
main = printResults -- Run our Test Code and print the results
-- Given this NuttX Exception: `riscv_exception: EXCEPTION: Load page fault. MCAUSE: 000000000000000d, EPC: 000000008000a0e4, MTVAL: 0000000880203b88`
-- Explain in friendly words: "We hit a Load Page Fault. Our code at Code Address 8000a0e4 tried to access the Data Address 0000000880203b88, which is Invalid."
-- The next line declares the Function Type. We can actually erase it, VSCode PureScript Extension will helpfully suggest it for us.
explainException ∷
Int -- MCAUSE: Cause of Exception
→ String -- EPC: Exception Program Counter
→ String -- MTVAL: Exception Value
→ String -- Returns the Exception Explanation
-- Explain the NuttX Exception with mcause 13
-- `<>` will concat 2 strings
-- "🎵 I never promised you a rose garden"
explainException 13 epc mtval =
"We hit a Load Page Fault."
<> " Our code at Code Address " <> epc
<> " tried to access the Data Address " <> mtval <> ", which is Invalid."
-- Explain the NuttX Exception with mcause 12
-- `<>` will concat 2 strings
explainException 12 epc mtval =
"Instruction Page Fault at " <> epc <> ", " <> mtval
-- Explain the Other NuttX Exceptions, that are not matched with the above
explainException mcause epc mtval =
"Unknown Exception: mcause=" <> show mcause <> ", epc=" <> epc <> ", mtval=" <> mtval
-- Given an Address, identify the Origin (NuttX Kernel or App) and Type (Code / Data / BSS / Heap)
identifyAddress ∷
String -- Address: `502198ac`
→ Maybe -- If Unknown Address: Return `Nothing`
{ -- Else return...
origin ∷ String -- Origin: `nuttx` or `qjs`
, type ∷ AddressType -- Type: Code / Data / BSS / Heap
}
-- Address 502xxxxx comes from NuttX Kernel Code
-- Address 800xxxxx comes from NuttX App Code (QuickJS)
-- `|` works like `if ... else if`
-- "a `matches` b" is same as "(matches a b)"
-- `Just` returns an OK Value. `Nothing` returns No Value.
identifyAddress addr
| "502....." `matches` addr = Just { origin: "nuttx", type: Code }
| "800....." `matches` addr = Just { origin: "qjs", type: Code }
| otherwise = Nothing -- Unknown Address
-- Address can point to Code, Data, BSS or Heap
data AddressType = Code | Data | BSS | Heap
-- How to display an Address Type
instance Show AddressType where
show Code = "Code"
show Data = "Data"
show BSS = "BSS"
show Heap = "Heap"
-- Return True if the Address matches the Regex Pattern.
-- Pattern is assumed to match the Entire Address.
matches ∷
String -- Pattern: `502.....`
→ String -- Address: `502198ac`
→ Boolean -- Return True if Address matches Regex `^502.....$`
-- Match the Begin `^` and End `$` of the Address
-- `<>` will concat 2 strings
-- "a `unsafeRegex` b" is same as "(unsafeRegex a b)"
matches pattern addr =
let
patternWrap = "^" <> pattern <> "$" -- Regex Pattern becomes `^502.....$`
in
isJust $ -- Is there a Match...
patternWrap `unsafeRegex` noFlags -- For our Regex Pattern (no special flags)
`match` addr -- Against the Address?
-- Test our code. Parse the NuttX Exception and NuttX Stack Dump. Explain the NuttX Exception.
-- `Effect` says that it will do Side Effects (printing to console)
-- `Unit` means that no value will be returned
-- The next line declares the Function Type. We can actually erase it, VSCode PureScript Extension will helpfully suggest it for us.
printResults :: Effect Unit
printResults = do
-- Explain the NuttX Exception.
-- `$ something something` is shortcut for `( something something )`
log $ explainException 13 "8000a0e4" "0000000880203b88" -- We hit a Load Page Fault. Our code at Code Address 8000a0e4 tried to access the Data Address 0000000880203b88, which is Invalid.
log $ explainException 12 "epc" "mtval" -- Instruction Page Fault at epc, mtval
log $ explainException 0 "epc" "mtval" -- Unknown Exception: mcause=0, epc=epc, mtval=mtval
-- NuttX Kernel: 0x5020_0000 to 0x5021_98ac
-- NuttX App (qjs): 0x8000_0000 to 0x8006_4a28
logShow $ identifyAddress "502198ac" -- (Just { origin: "nuttx", type: Code })
logShow $ identifyAddress "8000a0e4" -- (Just { origin: "qjs", type: Code })
logShow $ identifyAddress "0000000800203b88" -- Nothing
-- Parse the NuttX Exception
-- Result: { epc: "8000ad8a", exception: "Instruction page fault", mcause: 12, mtval: "8000ad8a" }
doRunParser "parseException" parseException
"riscv_exception: EXCEPTION: Instruction page fault. MCAUSE: 000000000000000c, EPC: 000000008000ad8a, MTVAL: 000000008000ad8a"
-- Parse the line of NuttX Stack Dump
-- Result: { addr: "c02027e0", v1: "c0202010", v2: "00000000", v3: "00000001", v4: "00000000", v5: "00000000", v6: "00000000", v7: "8000ad8a", v8: "00000000" }
doRunParser "parseStackDump" parseStackDump
"stack_dump: 0xc02027e0: c0202010 00000000 00000001 00000000 00000000 00000000 8000ad8a 00000000"
-- Parse the line of NuttX Stack Dump with Timestamp
-- Result: { addr: "c02027e0", v1: "c0202010", v2: "00000000", v3: "00000001", v4: "00000000", v5: "00000000", v6: "00000000", v7: "8000ad8a", v8: "00000000" }
doRunParser "parseStackDump" parseStackDump
"[ 6.242000] stack_dump: 0xc02027e0: c0202010 00000000 00000001 00000000 00000000 00000000 8000ad8a 00000000"
-- Parse the NuttX Exception.
-- Given this NuttX Exception: `riscv_exception: EXCEPTION: Instruction page fault. MCAUSE: 000000000000000c, EPC: 000000008000ad8a, MTVAL: 000000008000ad8a`
-- Result: { epc: "8000ad8a", exception: "Instruction page fault", mcause: 12, mtval: "8000ad8a" }
-- The next line declares the Function Type. We can actually erase it, VSCode PureScript Extension will helpfully suggest it for us.
parseException ∷ Parser -- We're creating a Parser...
{ -- That accepts a String and returns...
exception ∷ String -- Exception Text: `Instruction page fault`
, mcause :: Int -- MCAUSE: 12
, epc :: String -- EPC: `8000ad8a`
, mtval :: String -- MTVAL: `8000ad8a`
}
-- To parse the line: `riscv_exception: EXCEPTION: Instruction page fault. MCAUSE: 000000000000000c, EPC: 000000008000ad8a, MTVAL: 000000008000ad8a`
parseException = do
-- Skip `riscv_exception: EXCEPTION: `
-- `void` means ignore the Text Captured
-- `$ something something` is shortcut for `( something something )`
-- `<*` is the Delimiter between Patterns
void $
string "riscv_exception:" -- Match the string `riscv_exception:`
<* skipSpaces -- Skip the following spaces
<* string "EXCEPTION:" -- Match the string `EXCEPTION:`
<* skipSpaces -- Skip the following spaces
-- `exception` becomes `Instruction page fault`
-- `<*` says when we should stop the Text Capture
exception <- regex "[^.]+"
<* string "."
<* skipSpaces
-- Skip `MCAUSE: `
-- `void` means ignore the Text Captured
-- `$ something something` is shortcut for `( something something )`
-- `<*` is the Delimiter between Patterns
void $ string "MCAUSE:" <* skipSpaces
-- `mcauseStr` becomes `000000000000000c`
-- We'll convert to integer later
mcauseStr <- regex "[0-9a-f]+" <* string "," <* skipSpaces
-- Skip `EPC: `
-- `epcWithPrefix` becomes `000000008000ad8a`
-- We'll strip the prefix `00000000` later
void $ string "EPC:" <* skipSpaces
epcWithPrefix <- regex "[0-9a-f]+" <* string "," <* skipSpaces
-- Skip `MTVAL: `
-- `mtvalWithPrefix` becomes `000000008000ad8a`
-- We'll strip the prefix `00000000` later
void $ string "MTVAL:" <* skipSpaces
mtvalWithPrefix <- regex "[0-9a-f]+"
-- Return the parsed content
-- `pure` because we're in a `do` block that allows (Side) Effects
-- TODO: Return a ParseError instead of -1
pure
{
exception
, mcause:
-1 `fromMaybe` -- If `mcauseStr` is not a valid hex, return -1
fromStringAs hexadecimal mcauseStr -- Else return the hex value of `mcauseStr`
, epc:
epcWithPrefix `fromMaybe` -- If `epcWithPrefix` does not have prefix `00000000`, return it
stripPrefix (Pattern "00000000") epcWithPrefix -- Else strip prefix `00000000` from `epc`
, mtval:
mtvalWithPrefix `fromMaybe` -- If `mtvalWithPrefix` does not have prefix `00000000`, return it
stripPrefix (Pattern "00000000") mtvalWithPrefix -- Else strip prefix `00000000` from `mtval`
}
-- Parse a line of NuttX Stack Dump.
-- Given this line of NuttX Stack Dump: `stack_dump: 0xc02027e0: c0202010 00000000 00000001 00000000 00000000 00000000 8000ad8a 00000000`
-- Result: { addr: "c02027e0", timestamp: "6.242000", v1: "c0202010", v2: "00000000", v3: "00000001", v4: "00000000", v5: "00000000", v6: "00000000", v7: "8000ad8a", v8: "00000000" }
-- The next line declares the Function Type. We can actually erase it, VSCode PureScript Extension will helpfully suggest it for us.
parseStackDump ∷ Parser -- We're creating a Parser...
{ -- That accepts a String and returns...
addr ∷ String -- Address: `c02027e0`
, v1 ∷ String -- Value 1: `c0202010`
, v2 ∷ String -- Value 2: `00000000`
, v3 ∷ String -- Value 3: `00000001`
, v4 ∷ String -- Value 4: `00000000`
, v5 ∷ String -- Value 5: `00000000`
, v6 ∷ String -- Value 6: `00000000`
, v7 ∷ String -- Value 7: `8000ad8a`
, v8 ∷ String -- Value 8: `00000000`
}
-- To parse the line: `stack_dump: 0xc02027e0: c0202010 00000000 00000001 00000000 00000000 00000000 8000ad8a 00000000`
parseStackDump = do
-- If the line begins with a Timestamp: `[ 6.242000] `
-- Skip `[ `
-- `optional` means Timestamp may or may not appear
-- `$ something something` is shortcut for `( something something )`
-- `<*` is the Delimiter between Patterns
optional $ -- Timestamp may or may not appear
string "[" -- Match the string `[`
<* skipSpaces -- Skip the following spaces
<* regex "[.0-9]+" -- Skip the number
<* string "]" -- Match the string `]`
<* skipSpaces -- Skip the following spaces
-- Skip `stack_dump: `
-- `void` means ignore the Text Captured
-- `$ something something` is shortcut for `( something something )`
-- `<*` is the Delimiter between Patterns
void $ string "stack_dump:" <* skipSpaces
-- `addr` becomes `c02027e0`
void $ string "0x"
addr <- regex "[0-9a-f]+" <* string ":" <* skipSpaces
-- `v1` becomes `c0202010`
-- `v2` becomes `00000000` and so on
v1 <- regex "[0-9a-f]+" <* skipSpaces
v2 <- regex "[0-9a-f]+" <* skipSpaces
v3 <- regex "[0-9a-f]+" <* skipSpaces
v4 <- regex "[0-9a-f]+" <* skipSpaces
v5 <- regex "[0-9a-f]+" <* skipSpaces
v6 <- regex "[0-9a-f]+" <* skipSpaces
v7 <- regex "[0-9a-f]+" <* skipSpaces
v8 <- regex "[0-9a-f]+" <* skipSpaces
-- Return the parsed content
-- `pure` because we're in a `do` block that allows (Side) Effects
pure
{ addr
, v1
, v2
, v3
, v4
, v5
, v6
, v7
, v8
}
-- Shows the results of calling `runParser`. We typically don't want to use
-- this function when writing a parser because it doesn't help us debug
-- our code when we write it incorrectly.
doRunParser :: forall a. Show a => String -> Parser a -> String -> Effect Unit
doRunParser parserName parser content = do
log $ "(runParser) Parsing content with '" <> parserName <> "'"
case runParser parser content of
Left error -> logShow error
Right result -> log $ "Result: " <> show result
log "-----"