excpt.nim 22 KB

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