excpt.nim 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656
  1. #
  2. #
  3. # Nim's Runtime Library
  4. # (c) Copyright 2015 Andreas Rumpf
  5. #
  6. # See the file "copying.txt", included in this
  7. # distribution, for details about the copyright.
  8. #
  9. # Exception handling code. Carefully coded so that tiny programs which do not
  10. # use the heap (and nor exceptions) do not include the GC or memory allocator.
  11. import std/private/miscdollars
  12. import stacktraces
  13. var
  14. errorMessageWriter*: (proc(msg: string) {.tags: [WriteIOEffect], benign,
  15. nimcall.})
  16. ## Function that will be called
  17. ## instead of `stdmsg.write` when printing stacktrace.
  18. ## Unstable API.
  19. when defined(windows):
  20. proc GetLastError(): int32 {.header: "<windows.h>", nodecl.}
  21. const ERROR_BAD_EXE_FORMAT = 193
  22. when not defined(windows) or not defined(guiapp):
  23. proc writeToStdErr(msg: cstring) = rawWrite(cstderr, msg)
  24. else:
  25. proc MessageBoxA(hWnd: pointer, lpText, lpCaption: cstring, uType: int): int32 {.
  26. header: "<windows.h>", nodecl.}
  27. proc writeToStdErr(msg: cstring) =
  28. discard MessageBoxA(nil, msg, nil, 0)
  29. proc showErrorMessage(data: cstring) {.gcsafe, raises: [].} =
  30. var toWrite = true
  31. if errorMessageWriter != nil:
  32. try:
  33. errorMessageWriter($data)
  34. toWrite = false
  35. except:
  36. discard
  37. if toWrite:
  38. when defined(genode):
  39. # stderr not available by default, use the LOG session
  40. echo data
  41. else:
  42. writeToStdErr(data)
  43. proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.}
  44. proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.}
  45. proc chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.}
  46. proc chckNil(p: pointer) {.noinline, compilerproc, benign.}
  47. type
  48. GcFrame = ptr GcFrameHeader
  49. GcFrameHeader {.compilerproc.} = object
  50. len: int
  51. prev: ptr GcFrameHeader
  52. when NimStackTraceMsgs:
  53. var frameMsgBuf* {.threadvar.}: string
  54. var
  55. framePtr {.threadvar.}: PFrame
  56. excHandler {.threadvar.}: PSafePoint
  57. # list of exception handlers
  58. # a global variable for the root of all try blocks
  59. currException {.threadvar.}: ref Exception
  60. gcFramePtr {.threadvar.}: GcFrame
  61. type
  62. FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame,
  63. excHandler: PSafePoint, currException: ref Exception]
  64. proc getFrameState*(): FrameState {.compilerRtl, inl.} =
  65. return (gcFramePtr, framePtr, excHandler, currException)
  66. proc setFrameState*(state: FrameState) {.compilerRtl, inl.} =
  67. gcFramePtr = state.gcFramePtr
  68. framePtr = state.framePtr
  69. excHandler = state.excHandler
  70. currException = state.currException
  71. proc getFrame*(): PFrame {.compilerRtl, inl.} = framePtr
  72. proc popFrame {.compilerRtl, inl.} =
  73. framePtr = framePtr.prev
  74. when false:
  75. proc popFrameOfAddr(s: PFrame) {.compilerRtl.} =
  76. var it = framePtr
  77. if it == s:
  78. framePtr = framePtr.prev
  79. else:
  80. while it != nil:
  81. if it == s:
  82. framePtr = it.prev
  83. break
  84. it = it.prev
  85. proc setFrame*(s: PFrame) {.compilerRtl, inl.} =
  86. framePtr = s
  87. proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr
  88. proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev
  89. proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s
  90. proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} =
  91. s.prev = gcFramePtr
  92. zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer))
  93. gcFramePtr = s
  94. proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
  95. s.prev = excHandler
  96. excHandler = s
  97. proc popSafePoint {.compilerRtl, inl.} =
  98. excHandler = excHandler.prev
  99. proc pushCurrentException(e: sink(ref Exception)) {.compilerRtl, inl.} =
  100. e.up = currException
  101. currException = e
  102. #showErrorMessage "A"
  103. proc popCurrentException {.compilerRtl, inl.} =
  104. currException = currException.up
  105. #showErrorMessage "B"
  106. proc popCurrentExceptionEx(id: uint) {.compilerRtl.} =
  107. discard "only for bootstrapping compatbility"
  108. proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
  109. currException = e
  110. # some platforms have native support for stack traces:
  111. const
  112. nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and
  113. not NimStackTrace
  114. hasSomeStackTrace = NimStackTrace or defined(nimStackTraceOverride) or
  115. (defined(nativeStackTrace) and nativeStackTraceSupported)
  116. when defined(nativeStacktrace) and nativeStackTraceSupported:
  117. type
  118. TDl_info {.importc: "Dl_info", header: "<dlfcn.h>",
  119. final, pure.} = object
  120. dli_fname: cstring
  121. dli_fbase: pointer
  122. dli_sname: cstring
  123. dli_saddr: pointer
  124. proc backtrace(symbols: ptr pointer, size: int): int {.
  125. importc: "backtrace", header: "<execinfo.h>".}
  126. proc dladdr(addr1: pointer, info: ptr TDl_info): int {.
  127. importc: "dladdr", header: "<dlfcn.h>".}
  128. when not hasThreadSupport:
  129. var
  130. tempAddresses: array[maxStackTraceLines, pointer] # should not be alloc'd on stack
  131. tempDlInfo: TDl_info
  132. proc auxWriteStackTraceWithBacktrace(s: var string) =
  133. when hasThreadSupport:
  134. var
  135. tempAddresses: array[maxStackTraceLines, pointer] # but better than a threadvar
  136. tempDlInfo: TDl_info
  137. # This is allowed to be expensive since it only happens during crashes
  138. # (but this way you don't need manual stack tracing)
  139. var size = backtrace(cast[ptr pointer](addr(tempAddresses)),
  140. len(tempAddresses))
  141. var enabled = false
  142. for i in 0..size-1:
  143. var dlresult = dladdr(tempAddresses[i], addr(tempDlInfo))
  144. if enabled:
  145. if dlresult != 0:
  146. var oldLen = s.len
  147. add(s, tempDlInfo.dli_fname)
  148. if tempDlInfo.dli_sname != nil:
  149. for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
  150. add(s, tempDlInfo.dli_sname)
  151. else:
  152. add(s, '?')
  153. add(s, "\n")
  154. else:
  155. if dlresult != 0 and tempDlInfo.dli_sname != nil and
  156. c_strcmp(tempDlInfo.dli_sname, "signalHandler") == 0'i32:
  157. # Once we're past signalHandler, we're at what the user is
  158. # interested in
  159. enabled = true
  160. when hasSomeStackTrace and not hasThreadSupport:
  161. var
  162. tempFrames: array[maxStackTraceLines, PFrame] # should not be alloc'd on stack
  163. template reraisedFrom(z): untyped =
  164. StackTraceEntry(procname: nil, line: z, filename: nil)
  165. proc auxWriteStackTrace(f: PFrame; s: var seq[StackTraceEntry]) =
  166. var
  167. it = f
  168. i = 0
  169. while it != nil:
  170. inc(i)
  171. it = it.prev
  172. var last = i-1
  173. when true: # not defined(gcDestructors):
  174. if s.len == 0:
  175. s = newSeq[StackTraceEntry](i)
  176. else:
  177. last = s.len + i - 1
  178. s.setLen(last+1)
  179. it = f
  180. while it != nil:
  181. s[last] = StackTraceEntry(procname: it.procname,
  182. line: it.line,
  183. filename: it.filename)
  184. when NimStackTraceMsgs:
  185. let first = if it.prev == nil: 0 else: it.prev.frameMsgLen
  186. if it.frameMsgLen > first:
  187. s[last].frameMsg.setLen(it.frameMsgLen - first)
  188. # somehow string slicing not available here
  189. for i in first .. it.frameMsgLen-1:
  190. s[last].frameMsg[i-first] = frameMsgBuf[i]
  191. it = it.prev
  192. dec last
  193. template addFrameEntry(s: var string, f: StackTraceEntry|PFrame) =
  194. var oldLen = s.len
  195. s.toLocation(f.filename, f.line, 0)
  196. for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
  197. add(s, f.procname)
  198. when NimStackTraceMsgs:
  199. when type(f) is StackTraceEntry:
  200. add(s, f.frameMsg)
  201. else:
  202. var first = if f.prev == nil: 0 else: f.prev.frameMsgLen
  203. for i in first..<f.frameMsgLen: add(s, frameMsgBuf[i])
  204. add(s, "\n")
  205. proc `$`(stackTraceEntries: seq[StackTraceEntry]): string =
  206. when defined(nimStackTraceOverride):
  207. let s = addDebuggingInfo(stackTraceEntries)
  208. else:
  209. let s = stackTraceEntries
  210. result = newStringOfCap(2000)
  211. for i in 0 .. s.len-1:
  212. if s[i].line == reraisedFromBegin: result.add "[[reraised from:\n"
  213. elif s[i].line == reraisedFromEnd: result.add "]]\n"
  214. else: addFrameEntry(result, s[i])
  215. when hasSomeStackTrace:
  216. proc auxWriteStackTrace(f: PFrame, s: var string) =
  217. when hasThreadSupport:
  218. var
  219. tempFrames: array[maxStackTraceLines, PFrame] # but better than a threadvar
  220. const
  221. firstCalls = 32
  222. var
  223. it = f
  224. i = 0
  225. total = 0
  226. # setup long head:
  227. while it != nil and i <= high(tempFrames)-firstCalls:
  228. tempFrames[i] = it
  229. inc(i)
  230. inc(total)
  231. it = it.prev
  232. # go up the stack to count 'total':
  233. var b = it
  234. while it != nil:
  235. inc(total)
  236. it = it.prev
  237. var skipped = 0
  238. if total > len(tempFrames):
  239. # skip N
  240. skipped = total-i-firstCalls+1
  241. for j in 1..skipped:
  242. if b != nil: b = b.prev
  243. # create '...' entry:
  244. tempFrames[i] = nil
  245. inc(i)
  246. # setup short tail:
  247. while b != nil and i <= high(tempFrames):
  248. tempFrames[i] = b
  249. inc(i)
  250. b = b.prev
  251. for j in countdown(i-1, 0):
  252. if tempFrames[j] == nil:
  253. add(s, "(")
  254. add(s, $skipped)
  255. add(s, " calls omitted) ...\n")
  256. else:
  257. addFrameEntry(s, tempFrames[j])
  258. proc stackTraceAvailable*(): bool
  259. proc rawWriteStackTrace(s: var string) =
  260. when defined(nimStackTraceOverride):
  261. add(s, "Traceback (most recent call last, using override)\n")
  262. auxWriteStackTraceWithOverride(s)
  263. elif NimStackTrace:
  264. if framePtr == nil:
  265. add(s, "No stack traceback available\n")
  266. else:
  267. add(s, "Traceback (most recent call last)\n")
  268. auxWriteStackTrace(framePtr, s)
  269. elif defined(nativeStackTrace) and nativeStackTraceSupported:
  270. add(s, "Traceback from system (most recent call last)\n")
  271. auxWriteStackTraceWithBacktrace(s)
  272. else:
  273. add(s, "No stack traceback available\n")
  274. proc rawWriteStackTrace(s: var seq[StackTraceEntry]) =
  275. when defined(nimStackTraceOverride):
  276. auxWriteStackTraceWithOverride(s)
  277. elif NimStackTrace:
  278. auxWriteStackTrace(framePtr, s)
  279. else:
  280. s = @[]
  281. proc stackTraceAvailable(): bool =
  282. when defined(nimStackTraceOverride):
  283. result = true
  284. elif NimStackTrace:
  285. if framePtr == nil:
  286. result = false
  287. else:
  288. result = true
  289. elif defined(nativeStackTrace) and nativeStackTraceSupported:
  290. result = true
  291. else:
  292. result = false
  293. else:
  294. proc stackTraceAvailable*(): bool = result = false
  295. var onUnhandledException*: (proc (errorMsg: string) {.
  296. nimcall, gcsafe.}) ## Set this error \
  297. ## handler to override the existing behaviour on an unhandled exception.
  298. ##
  299. ## The default is to write a stacktrace to ``stderr`` and then call ``quit(1)``.
  300. ## Unstable API.
  301. proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} =
  302. when hasSomeStackTrace:
  303. var buf = newStringOfCap(2000)
  304. if e.trace.len == 0:
  305. rawWriteStackTrace(buf)
  306. else:
  307. var trace = $e.trace
  308. add(buf, trace)
  309. `=destroy`(trace)
  310. add(buf, "Error: unhandled exception: ")
  311. add(buf, e.msg)
  312. add(buf, " [")
  313. add(buf, $e.name)
  314. add(buf, "]\n")
  315. if onUnhandledException != nil:
  316. onUnhandledException(buf)
  317. else:
  318. showErrorMessage(buf)
  319. `=destroy`(buf)
  320. else:
  321. # ugly, but avoids heap allocations :-)
  322. template xadd(buf, s, slen) =
  323. if L + slen < high(buf):
  324. copyMem(addr(buf[L]), cstring(s), slen)
  325. inc L, slen
  326. template add(buf, s) =
  327. xadd(buf, s, s.len)
  328. var buf: array[0..2000, char]
  329. var L = 0
  330. if e.trace.len != 0:
  331. var trace = $e.trace
  332. add(buf, trace)
  333. `=destroy`(trace)
  334. add(buf, "Error: unhandled exception: ")
  335. add(buf, e.msg)
  336. add(buf, " [")
  337. xadd(buf, e.name, e.name.len)
  338. add(buf, "]\n")
  339. when defined(nimNoArrayToCstringConversion):
  340. template tbuf(): untyped = addr buf
  341. else:
  342. template tbuf(): untyped = buf
  343. if onUnhandledException != nil:
  344. onUnhandledException($tbuf())
  345. else:
  346. showErrorMessage(tbuf())
  347. proc reportUnhandledError(e: ref Exception) {.nodestroy.} =
  348. if unhandledExceptionHook != nil:
  349. unhandledExceptionHook(e)
  350. when hostOS != "any":
  351. reportUnhandledErrorAux(e)
  352. else:
  353. discard ()
  354. proc nimLeaveFinally() {.compilerRtl.} =
  355. when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions:
  356. {.emit: "throw;".}
  357. else:
  358. if excHandler != nil:
  359. c_longjmp(excHandler.context, 1)
  360. else:
  361. reportUnhandledError(currException)
  362. quit(1)
  363. when gotoBasedExceptions:
  364. var nimInErrorMode {.threadvar.}: bool
  365. proc nimErrorFlag(): ptr bool {.compilerRtl, inl.} =
  366. result = addr(nimInErrorMode)
  367. proc nimTestErrorFlag() {.compilerRtl.} =
  368. ## This proc must be called before ``currException`` is destroyed.
  369. ## It also must be called at the end of every thread to ensure no
  370. ## error is swallowed.
  371. if nimInErrorMode and currException != nil:
  372. reportUnhandledError(currException)
  373. currException = nil
  374. quit(1)
  375. proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} =
  376. when defined(nimPanics):
  377. if e of Defect:
  378. reportUnhandledError(e)
  379. quit(1)
  380. if localRaiseHook != nil:
  381. if not localRaiseHook(e): return
  382. if globalRaiseHook != nil:
  383. if not globalRaiseHook(e): return
  384. when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions:
  385. if e == currException:
  386. {.emit: "throw;".}
  387. else:
  388. pushCurrentException(e)
  389. {.emit: "throw e;".}
  390. elif defined(nimQuirky) or gotoBasedExceptions:
  391. # XXX This check should likely also be done in the setjmp case below.
  392. if e != currException:
  393. pushCurrentException(e)
  394. when gotoBasedExceptions:
  395. inc nimInErrorMode
  396. else:
  397. if excHandler != nil:
  398. pushCurrentException(e)
  399. c_longjmp(excHandler.context, 1)
  400. else:
  401. reportUnhandledError(e)
  402. quit(1)
  403. proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring,
  404. line: int) {.compilerRtl, nodestroy.} =
  405. if e.name.isNil: e.name = ename
  406. when hasSomeStackTrace:
  407. when defined(nimStackTraceOverride):
  408. if e.trace.len == 0:
  409. rawWriteStackTrace(e.trace)
  410. else:
  411. e.trace.add reraisedFrom(reraisedFromBegin)
  412. auxWriteStackTraceWithOverride(e.trace)
  413. e.trace.add reraisedFrom(reraisedFromEnd)
  414. elif NimStackTrace:
  415. if e.trace.len == 0:
  416. rawWriteStackTrace(e.trace)
  417. elif framePtr != nil:
  418. e.trace.add reraisedFrom(reraisedFromBegin)
  419. auxWriteStackTrace(framePtr, e.trace)
  420. e.trace.add reraisedFrom(reraisedFromEnd)
  421. else:
  422. if procname != nil and filename != nil:
  423. e.trace.add StackTraceEntry(procname: procname, filename: filename, line: line)
  424. raiseExceptionAux(e)
  425. proc raiseException(e: sink(ref Exception), ename: cstring) {.compilerRtl.} =
  426. raiseExceptionEx(e, ename, nil, nil, 0)
  427. proc reraiseException() {.compilerRtl.} =
  428. if currException == nil:
  429. sysFatal(ReraiseDefect, "no exception to reraise")
  430. else:
  431. when gotoBasedExceptions:
  432. inc nimInErrorMode
  433. else:
  434. raiseExceptionAux(currException)
  435. proc threadTrouble() =
  436. # also forward declared, it is 'raises: []' hence the try-except.
  437. try:
  438. if currException != nil: reportUnhandledError(currException)
  439. except:
  440. discard
  441. quit 1
  442. proc writeStackTrace() =
  443. when hasSomeStackTrace:
  444. var s = ""
  445. rawWriteStackTrace(s)
  446. cast[proc (s: cstring) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage)(s)
  447. else:
  448. cast[proc (s: cstring) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage)("No stack traceback available\n")
  449. proc getStackTrace(): string =
  450. when hasSomeStackTrace:
  451. result = ""
  452. rawWriteStackTrace(result)
  453. else:
  454. result = "No stack traceback available\n"
  455. proc getStackTrace(e: ref Exception): string =
  456. if not isNil(e):
  457. result = $e.trace
  458. else:
  459. result = ""
  460. proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
  461. ## Returns the attached stack trace to the exception ``e`` as
  462. ## a ``seq``. This is not yet available for the JS backend.
  463. when not defined(nimSeqsV2):
  464. shallowCopy(result, e.trace)
  465. else:
  466. result = move(e.trace)
  467. proc getStackTraceEntries*(): seq[StackTraceEntry] =
  468. ## Returns the stack trace entries for the current stack trace.
  469. ## This is not yet available for the JS backend.
  470. when hasSomeStackTrace:
  471. rawWriteStackTrace(result)
  472. const nimCallDepthLimit {.intdefine.} = 2000
  473. proc callDepthLimitReached() {.noinline.} =
  474. writeStackTrace()
  475. showErrorMessage("Error: call depth limit reached in a debug build (" &
  476. $nimCallDepthLimit & " function calls). You can change it with " &
  477. "-d:nimCallDepthLimit=<int> but really try to avoid deep " &
  478. "recursions instead.\n")
  479. quit(1)
  480. proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} =
  481. if framePtr == nil:
  482. s.calldepth = 0
  483. when NimStackTraceMsgs: s.frameMsgLen = 0
  484. else:
  485. s.calldepth = framePtr.calldepth+1
  486. when NimStackTraceMsgs: s.frameMsgLen = framePtr.frameMsgLen
  487. s.prev = framePtr
  488. framePtr = s
  489. if s.calldepth == nimCallDepthLimit: callDepthLimitReached()
  490. when defined(cpp) and appType != "lib" and not gotoBasedExceptions and
  491. not defined(js) and not defined(nimscript) and
  492. hostOS != "standalone" and not defined(noCppExceptions):
  493. type
  494. StdException {.importcpp: "std::exception", header: "<exception>".} = object
  495. proc what(ex: StdException): cstring {.importcpp: "((char *)#.what())".}
  496. proc setTerminate(handler: proc() {.noconv.})
  497. {.importc: "std::set_terminate", header: "<exception>".}
  498. setTerminate proc() {.noconv.} =
  499. # Remove ourself as a handler, reinstalling the default handler.
  500. setTerminate(nil)
  501. var msg = "Unknown error in unexpected exception handler"
  502. try:
  503. {.emit: "#if !defined(_MSC_VER) || (_MSC_VER >= 1923)".}
  504. raise
  505. {.emit: "#endif".}
  506. except Exception:
  507. msg = currException.getStackTrace() & "Error: unhandled exception: " &
  508. currException.msg & " [" & $currException.name & "]"
  509. except StdException as e:
  510. msg = "Error: unhandled cpp exception: " & $e.what()
  511. except:
  512. msg = "Error: unhandled unknown cpp exception"
  513. {.emit: "#if defined(_MSC_VER) && (_MSC_VER < 1923)".}
  514. msg = "Error: unhandled unknown cpp exception"
  515. {.emit: "#endif".}
  516. when defined(genode):
  517. # stderr not available by default, use the LOG session
  518. echo msg
  519. else:
  520. writeToStdErr msg & "\n"
  521. quit 1
  522. when not defined(noSignalHandler) and not defined(useNimRtl):
  523. proc signalHandler(sign: cint) {.exportc: "signalHandler", noconv.} =
  524. template processSignal(s, action: untyped) {.dirty.} =
  525. if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n")
  526. elif s == SIGSEGV:
  527. action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n")
  528. elif s == SIGABRT:
  529. action("SIGABRT: Abnormal termination.\n")
  530. elif s == SIGFPE: action("SIGFPE: Arithmetic error.\n")
  531. elif s == SIGILL: action("SIGILL: Illegal operation.\n")
  532. elif (when declared(SIGBUS): s == SIGBUS else: false):
  533. action("SIGBUS: Illegal storage access. (Attempt to read from nil?)\n")
  534. else:
  535. block platformSpecificSignal:
  536. when declared(SIGPIPE):
  537. if s == SIGPIPE:
  538. action("SIGPIPE: Pipe closed.\n")
  539. break platformSpecificSignal
  540. action("unknown signal\n")
  541. # print stack trace and quit
  542. when defined(memtracker):
  543. logPendingOps()
  544. when hasSomeStackTrace:
  545. when not usesDestructors: GC_disable()
  546. var buf = newStringOfCap(2000)
  547. rawWriteStackTrace(buf)
  548. processSignal(sign, buf.add) # nice hu? currying a la Nim :-)
  549. showErrorMessage(buf)
  550. when not usesDestructors: GC_enable()
  551. else:
  552. var msg: cstring
  553. template asgn(y) =
  554. msg = y
  555. processSignal(sign, asgn)
  556. showErrorMessage(msg)
  557. quit(1) # always quit when SIGABRT
  558. proc registerSignalHandler() =
  559. c_signal(SIGINT, signalHandler)
  560. c_signal(SIGSEGV, signalHandler)
  561. c_signal(SIGABRT, signalHandler)
  562. c_signal(SIGFPE, signalHandler)
  563. c_signal(SIGILL, signalHandler)
  564. when declared(SIGBUS):
  565. c_signal(SIGBUS, signalHandler)
  566. when declared(SIGPIPE):
  567. c_signal(SIGPIPE, signalHandler)
  568. registerSignalHandler() # call it in initialization section
  569. proc setControlCHook(hook: proc () {.noconv.}) =
  570. # ugly cast, but should work on all architectures:
  571. type SignalHandler = proc (sign: cint) {.noconv, benign.}
  572. c_signal(SIGINT, cast[SignalHandler](hook))
  573. when not defined(noSignalHandler) and not defined(useNimRtl):
  574. proc unsetControlCHook() =
  575. # proc to unset a hook set by setControlCHook
  576. c_signal(SIGINT, signalHandler)