excpt.nim 22 KB

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