summary refs log blame commit diff stats
path: root/lib/system/excpt.nim
blob: 8f7e2cbc72ab18a37893eb4e012f86a9b086ba5d (plain) (tree)
1
2
3
4
5
6
7
8
9

 
                                  
                                         




                                                   

                                                                              
 
                              
                  
 

                                                              
   

                                                                          
                                   
                                                          
                    
 



                                                              
                                                 
                                                           

                                                 
     
                                                                                   
                                   
                                    
                                         

                                                 
 



                                                                                        
                                                                          
                    
                               





                               



                                                            
                                 
 


                                                 



                                                                       
 





                                         

                                        
   

                                      

                                                      
                                            
                                   
 
    

                                                                          

                                                        
                                                          

                                                             
                               



                                     

                                                        
                                    
                          
 











                                                  
                                                
              
 







                                                                                    
                                                        

                     

                                        
                              
 
                                                                        
                      
                   
                        

                                               
                                  
                        
 
                                                      
                                               
 
                                                                     
                   
 

                                                      
                                                                      
                                               


                                                                        
 
                                                             
      
                                                       
                                     
                        
                        
                        






                                                         

                            
                                                                                        
                          

                                                       

                          
                                                                                       
                            

                                                                          
                                                                












                                                               
                    





                                                                     
 
                                                
     
                                                                                  











                                                                 





                                          




                                                    






                                                                


                
                                                                  
                    
                                     

                                                    
                         
                                      



                                                              

              





                                                           





                                                                      




                                                     
                                                                                   

                     
       


































                                                         
 
                                   
 
                                          



                                                                   
                         
                                     
           
                                                     

                                                                 
                                                               
                                        
         
                                   

                                                        


                                        

                                     
             
 
                                    


                                        









                                                                 
 
                                                      
                                        
                                                                          
    
                                                                              

                  
                                                              
















                                            
                            
                   
       


                                           
                                                                            













                                            
                                   
                                     
         
                                   
 




                                                           
 
                                        
                                                                                 

                      


                                      



                                         
                                        
 
                                                      


                                           
                                                                    

                                                                     
                                               



                                         
                                                              




                             



                                     
                                                                                 



                             
                          



                                                                         

                             
       
                         

                                      
         

                             
 

                                                                                 
                                 
                         
                                        





                                                   






                                                   


                                                                                     

                      
                                                                             

                                         
                                         
                          
                                                      
       



                                      
 







                                                                   
                        


                         
       

                                                                                              
 




                              
                                  
 
                                              
                  
                     


               
                                                                    

                                                             
                              
                                

                          
 





                                                                 



                                            
                                                                  

                                                                        
                             
                        
         
 
                                                           





                                                                


                                                              
 
                                                                      
                                                  

                                                                                   

      
                                                                                
 

                                                                          

                                                            
 



                                                                    

                                                             
                                                              
           
                        






                                                                            
 
                                                          
                                                  
                      
 

                                                            
              
         

                              

          
                                                             
                                                                       
                                                          
                                                                
                        
                                                                                
                        


                                                              
                                                            
                                                                               

                                     
                                 



                                               

                                

                             
                           
                                            

                                    
                                                                   
                            
                                           

                      

                        
                               


                                                                                      
                                    










                                                                      






                                    

                                     
                           
                                      
 
                                                             
 
                                                
                                                    
                                                           
                                             
 
                                                             


                                                 
#
#
#            Nim's Runtime Library
#        (c) Copyright 2015 Andreas Rumpf
#
#    See the file "copying.txt", included in this
#    distribution, for details about the copyright.
#

# Exception handling code. Carefully coded so that tiny programs which do not
# use the heap (and nor exceptions) do not include the GC or memory allocator.

import std/private/miscdollars
import stacktraces

const noStacktraceAvailable = "No stack traceback available\n"

var
  errorMessageWriter*: (proc(msg: string) {.tags: [WriteIOEffect], benign,
                                            nimcall.})
    ## Function that will be called
    ## instead of `stdmsg.write` when printing stacktrace.
    ## Unstable API.

when defined(windows):
  proc GetLastError(): int32 {.header: "<windows.h>", nodecl.}
  const ERROR_BAD_EXE_FORMAT = 193

when not defined(windows) or not defined(guiapp):
  proc writeToStdErr(msg: cstring) = rawWrite(cstderr, msg)
  proc writeToStdErr(msg: cstring, length: int) =
    rawWriteString(cstderr, msg, length)
else:
  proc MessageBoxA(hWnd: pointer, lpText, lpCaption: cstring, uType: int): int32 {.
    header: "<windows.h>", nodecl.}
  proc writeToStdErr(msg: cstring) =
    discard MessageBoxA(nil, msg, nil, 0)
  proc writeToStdErr(msg: cstring, length: int) =
    discard MessageBoxA(nil, msg, nil, 0)

proc writeToStdErr(msg: string) {.inline.} =
  # fix bug #13115: handles correctly '\0' unlike default implicit conversion to cstring
  writeToStdErr(msg.cstring, msg.len)

proc showErrorMessage(data: cstring, length: int) {.gcsafe, raises: [].} =
  var toWrite = true
  if errorMessageWriter != nil:
    try:
      errorMessageWriter($data)
      toWrite = false
    except:
      discard
  if toWrite:
    when defined(genode):
      # stderr not available by default, use the LOG session
      echo data
    else:
      writeToStdErr(data, length)

proc showErrorMessage2(data: string) {.inline.} =
  showErrorMessage(data.cstring, data.len)

proc chckIndx(i, a, b: int): int {.inline, compilerproc, benign.}
proc chckRange(i, a, b: int): int {.inline, compilerproc, benign.}
proc chckRangeF(x, a, b: float): float {.inline, compilerproc, benign.}
proc chckNil(p: pointer) {.noinline, compilerproc, benign.}

type
  GcFrame = ptr GcFrameHeader
  GcFrameHeader {.compilerproc.} = object
    len: int
    prev: ptr GcFrameHeader

when NimStackTraceMsgs:
  var frameMsgBuf* {.threadvar.}: string
var
  framePtr {.threadvar.}: PFrame
  excHandler {.threadvar.}: PSafePoint
    # list of exception handlers
    # a global variable for the root of all try blocks
  currException {.threadvar.}: ref Exception
  gcFramePtr {.threadvar.}: GcFrame

type
  FrameState = tuple[gcFramePtr: GcFrame, framePtr: PFrame,
                     excHandler: PSafePoint, currException: ref Exception]

proc getFrameState*(): FrameState {.compilerRtl, inl.} =
  return (gcFramePtr, framePtr, excHandler, currException)

proc setFrameState*(state: FrameState) {.compilerRtl, inl.} =
  gcFramePtr = state.gcFramePtr
  framePtr = state.framePtr
  excHandler = state.excHandler
  currException = state.currException

proc getFrame*(): PFrame {.compilerRtl, inl.} = framePtr

proc popFrame {.compilerRtl, inl.} =
  framePtr = framePtr.prev

when false:
  proc popFrameOfAddr(s: PFrame) {.compilerRtl.} =
    var it = framePtr
    if it == s:
      framePtr = framePtr.prev
    else:
      while it != nil:
        if it == s:
          framePtr = it.prev
          break
        it = it.prev

proc setFrame*(s: PFrame) {.compilerRtl, inl.} =
  framePtr = s

proc getGcFrame*(): GcFrame {.compilerRtl, inl.} = gcFramePtr
proc popGcFrame*() {.compilerRtl, inl.} = gcFramePtr = gcFramePtr.prev
proc setGcFrame*(s: GcFrame) {.compilerRtl, inl.} = gcFramePtr = s
proc pushGcFrame*(s: GcFrame) {.compilerRtl, inl.} =
  s.prev = gcFramePtr
  zeroMem(cast[pointer](cast[int](s)+%sizeof(GcFrameHeader)), s.len*sizeof(pointer))
  gcFramePtr = s

proc pushSafePoint(s: PSafePoint) {.compilerRtl, inl.} =
  s.prev = excHandler
  excHandler = s

proc popSafePoint {.compilerRtl, inl.} =
  excHandler = excHandler.prev

proc pushCurrentException(e: sink(ref Exception)) {.compilerRtl, inl.} =
  e.up = currException
  currException = e
  #showErrorMessage2 "A"

proc popCurrentException {.compilerRtl, inl.} =
  currException = currException.up
  #showErrorMessage2 "B"

proc popCurrentExceptionEx(id: uint) {.compilerRtl.} =
  discard "only for bootstrapping compatbility"

proc closureIterSetupExc(e: ref Exception) {.compilerproc, inline.} =
  currException = e

# some platforms have native support for stack traces:
const
  nativeStackTraceSupported* = (defined(macosx) or defined(linux)) and
                              not NimStackTrace
  hasSomeStackTrace = NimStackTrace or defined(nimStackTraceOverride) or
    (defined(nativeStackTrace) and nativeStackTraceSupported)


when defined(nativeStacktrace) and nativeStackTraceSupported:
  type
    TDl_info {.importc: "Dl_info", header: "<dlfcn.h>",
               final, pure.} = object
      dli_fname: cstring
      dli_fbase: pointer
      dli_sname: cstring
      dli_saddr: pointer

  proc backtrace(symbols: ptr pointer, size: int): int {.
    importc: "backtrace", header: "<execinfo.h>".}
  proc dladdr(addr1: pointer, info: ptr TDl_info): int {.
    importc: "dladdr", header: "<dlfcn.h>".}

  when not hasThreadSupport:
    var
      tempAddresses: array[maxStackTraceLines, pointer] # should not be alloc'd on stack
      tempDlInfo: TDl_info

  proc auxWriteStackTraceWithBacktrace(s: var string) =
    when hasThreadSupport:
      var
        tempAddresses: array[maxStackTraceLines, pointer] # but better than a threadvar
        tempDlInfo: TDl_info
    # This is allowed to be expensive since it only happens during crashes
    # (but this way you don't need manual stack tracing)
    var size = backtrace(cast[ptr pointer](addr(tempAddresses)),
                         len(tempAddresses))
    var enabled = false
    for i in 0..size-1:
      var dlresult = dladdr(tempAddresses[i], addr(tempDlInfo))
      if enabled:
        if dlresult != 0:
          var oldLen = s.len
          add(s, tempDlInfo.dli_fname)
          if tempDlInfo.dli_sname != nil:
            for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
            add(s, tempDlInfo.dli_sname)
        else:
          add(s, '?')
        add(s, "\n")
      else:
        if dlresult != 0 and tempDlInfo.dli_sname != nil and
            c_strcmp(tempDlInfo.dli_sname, "signalHandler") == 0'i32:
          # Once we're past signalHandler, we're at what the user is
          # interested in
          enabled = true

when hasSomeStackTrace and not hasThreadSupport:
  var
    tempFrames: array[maxStackTraceLines, PFrame] # should not be alloc'd on stack

template reraisedFrom(z): untyped =
  StackTraceEntry(procname: nil, line: z, filename: nil)

proc auxWriteStackTrace(f: PFrame; s: var seq[StackTraceEntry]) =
  var
    it = f
    i = 0
  while it != nil:
    inc(i)
    it = it.prev
  var last = i-1
  when true: # not defined(gcDestructors):
    if s.len == 0:
      s = newSeq[StackTraceEntry](i)
    else:
      last = s.len + i - 1
      s.setLen(last+1)
  it = f
  while it != nil:
    s[last] = StackTraceEntry(procname: it.procname,
                              line: it.line,
                              filename: it.filename)
    when NimStackTraceMsgs:
      let first = if it.prev == nil: 0 else: it.prev.frameMsgLen
      if it.frameMsgLen > first:
        s[last].frameMsg.setLen(it.frameMsgLen - first)
        # somehow string slicing not available here
        for i in first .. it.frameMsgLen-1:
          s[last].frameMsg[i-first] = frameMsgBuf[i]
    it = it.prev
    dec last

template addFrameEntry(s: var string, f: StackTraceEntry|PFrame) =
  var oldLen = s.len
  s.toLocation(f.filename, f.line, 0)
  for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
  add(s, f.procname)
  when NimStackTraceMsgs:
    when typeof(f) is StackTraceEntry:
      add(s, f.frameMsg)
    else:
      var first = if f.prev == nil: 0 else: f.prev.frameMsgLen
      for i in first..<f.frameMsgLen: add(s, frameMsgBuf[i])
  add(s, "\n")

proc `$`(stackTraceEntries: seq[StackTraceEntry]): string =
  when defined(nimStackTraceOverride):
    let s = addDebuggingInfo(stackTraceEntries)
  else:
    let s = stackTraceEntries

  result = newStringOfCap(2000)
  for i in 0 .. s.len-1:
    if s[i].line == reraisedFromBegin: result.add "[[reraised from:\n"
    elif s[i].line == reraisedFromEnd: result.add "]]\n"
    else: addFrameEntry(result, s[i])

when hasSomeStackTrace:

  proc auxWriteStackTrace(f: PFrame, s: var string) =
    when hasThreadSupport:
      var
        tempFrames: array[maxStackTraceLines, PFrame] # but better than a threadvar
    const
      firstCalls = 32
    var
      it = f
      i = 0
      total = 0
    # setup long head:
    while it != nil and i <= high(tempFrames)-firstCalls:
      tempFrames[i] = it
      inc(i)
      inc(total)
      it = it.prev
    # go up the stack to count 'total':
    var b = it
    while it != nil:
      inc(total)
      it = it.prev
    var skipped = 0
    if total > len(tempFrames):
      # skip N
      skipped = total-i-firstCalls+1
      for j in 1..skipped:
        if b != nil: b = b.prev
      # create '...' entry:
      tempFrames[i] = nil
      inc(i)
    # setup short tail:
    while b != nil and i <= high(tempFrames):
      tempFrames[i] = b
      inc(i)
      b = b.prev
    for j in countdown(i-1, 0):
      if tempFrames[j] == nil:
        add(s, "(")
        add(s, $skipped)
        add(s, " calls omitted) ...\n")
      else:
        addFrameEntry(s, tempFrames[j])

  proc stackTraceAvailable*(): bool

  proc rawWriteStackTrace(s: var string) =
    when defined(nimStackTraceOverride):
      add(s, "Traceback (most recent call last, using override)\n")
      auxWriteStackTraceWithOverride(s)
    elif NimStackTrace:
      if framePtr == nil:
        add(s, noStacktraceAvailable)
      else:
        add(s, "Traceback (most recent call last)\n")
        auxWriteStackTrace(framePtr, s)
    elif defined(nativeStackTrace) and nativeStackTraceSupported:
      add(s, "Traceback from system (most recent call last)\n")
      auxWriteStackTraceWithBacktrace(s)
    else:
      add(s, noStacktraceAvailable)

  proc rawWriteStackTrace(s: var seq[StackTraceEntry]) =
    when defined(nimStackTraceOverride):
      auxWriteStackTraceWithOverride(s)
    elif NimStackTrace:
      auxWriteStackTrace(framePtr, s)
    else:
      s = @[]

  proc stackTraceAvailable(): bool =
    when defined(nimStackTraceOverride):
      result = true
    elif NimStackTrace:
      if framePtr == nil:
        result = false
      else:
        result = true
    elif defined(nativeStackTrace) and nativeStackTraceSupported:
      result = true
    else:
      result = false
else:
  proc stackTraceAvailable*(): bool = result = false

var onUnhandledException*: (proc (errorMsg: string) {.
  nimcall, gcsafe.}) ## Set this error \
  ## handler to override the existing behaviour on an unhandled exception.
  ##
  ## The default is to write a stacktrace to `stderr` and then call `quit(1)`.
  ## Unstable API.

proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} =
  when hasSomeStackTrace:
    var buf = newStringOfCap(2000)
    if e.trace.len == 0:
      rawWriteStackTrace(buf)
    else:
      var trace = $e.trace
      add(buf, trace)
      `=destroy`(trace)
    add(buf, "Error: unhandled exception: ")
    add(buf, e.msg)
    add(buf, " [")
    add(buf, $e.name)
    add(buf, "]\n")

    if onUnhandledException != nil:
      onUnhandledException(buf)
    else:
      showErrorMessage2(buf)
    `=destroy`(buf)
  else:
    # ugly, but avoids heap allocations :-)
    template xadd(buf, s, slen) =
      if L + slen < high(buf):
        copyMem(addr(buf[L]), (when s is cstring: s else: cstring(s)), slen)
        inc L, slen
    template add(buf, s) =
      xadd(buf, s, s.len)
    var buf: array[0..2000, char]
    var L = 0
    if e.trace.len != 0:
      var trace = $e.trace
      add(buf, trace)
      `=destroy`(trace)
    add(buf, "Error: unhandled exception: ")
    add(buf, e.msg)
    add(buf, " [")
    xadd(buf, e.name, e.name.len)
    add(buf, "]\n")
    if onUnhandledException != nil:
      onUnhandledException($buf.addr)
    else:
      showErrorMessage(buf.addr, L)

proc reportUnhandledError(e: ref Exception) {.nodestroy.} =
  if unhandledExceptionHook != nil:
    unhandledExceptionHook(e)
  when hostOS != "any":
    reportUnhandledErrorAux(e)

proc nimLeaveFinally() {.compilerRtl.} =
  when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions:
    {.emit: "throw;".}
  else:
    if excHandler != nil:
      c_longjmp(excHandler.context, 1)
    else:
      reportUnhandledError(currException)
      quit(1)

when gotoBasedExceptions:
  var nimInErrorMode {.threadvar.}: bool

  proc nimErrorFlag(): ptr bool {.compilerRtl, inl.} =
    result = addr(nimInErrorMode)

  proc nimTestErrorFlag() {.compilerRtl.} =
    ## This proc must be called before `currException` is destroyed.
    ## It also must be called at the end of every thread to ensure no
    ## error is swallowed.
    if nimInErrorMode and currException != nil:
      reportUnhandledError(currException)
      currException = nil
      quit(1)

proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} =
  when defined(nimPanics):
    if e of Defect:
      reportUnhandledError(e)
      quit(1)

  if localRaiseHook != nil:
    if not localRaiseHook(e): return
  if globalRaiseHook != nil:
    if not globalRaiseHook(e): return
  when defined(cpp) and not defined(noCppExceptions) and not gotoBasedExceptions:
    if e == currException:
      {.emit: "throw;".}
    else:
      pushCurrentException(e)
      {.emit: "throw e;".}
  elif defined(nimQuirky) or gotoBasedExceptions:
    # XXX This check should likely also be done in the setjmp case below.
    if e != currException:
      pushCurrentException(e)
    when gotoBasedExceptions:
      inc nimInErrorMode
  else:
    if excHandler != nil:
      pushCurrentException(e)
      c_longjmp(excHandler.context, 1)
    else:
      reportUnhandledError(e)
      quit(1)

proc raiseExceptionEx(e: sink(ref Exception), ename, procname, filename: cstring,
                      line: int) {.compilerRtl, nodestroy.} =
  if e.name.isNil: e.name = ename
  when hasSomeStackTrace:
    when defined(nimStackTraceOverride):
      if e.trace.len == 0:
        rawWriteStackTrace(e.trace)
      else:
        e.trace.add reraisedFrom(reraisedFromBegin)
        auxWriteStackTraceWithOverride(e.trace)
        e.trace.add reraisedFrom(reraisedFromEnd)
    elif NimStackTrace:
      if e.trace.len == 0:
        rawWriteStackTrace(e.trace)
      elif framePtr != nil:
        e.trace.add reraisedFrom(reraisedFromBegin)
        auxWriteStackTrace(framePtr, e.trace)
        e.trace.add reraisedFrom(reraisedFromEnd)
  else:
    if procname != nil and filename != nil:
      e.trace.add StackTraceEntry(procname: procname, filename: filename, line: line)
  raiseExceptionAux(e)

proc raiseException(e: sink(ref Exception), ename: cstring) {.compilerRtl.} =
  raiseExceptionEx(e, ename, nil, nil, 0)

proc reraiseException() {.compilerRtl.} =
  if currException == nil:
    sysFatal(ReraiseDefect, "no exception to reraise")
  else:
    when gotoBasedExceptions:
      inc nimInErrorMode
    else:
      raiseExceptionAux(currException)

proc threadTrouble() =
  # also forward declared, it is 'raises: []' hence the try-except.
  try:
    if currException != nil: reportUnhandledError(currException)
  except:
    discard
  quit 1

proc writeStackTrace() =
  when hasSomeStackTrace:
    var s = ""
    rawWriteStackTrace(s)
  else:
    let s = noStacktraceAvailable
  cast[proc (s: string) {.noSideEffect, tags: [], nimcall, raises: [].}](showErrorMessage2)(s)

proc getStackTrace(): string =
  when hasSomeStackTrace:
    result = ""
    rawWriteStackTrace(result)
  else:
    result = noStacktraceAvailable

proc getStackTrace(e: ref Exception): string =
  if not isNil(e):
    result = $e.trace
  else:
    result = ""

proc getStackTraceEntries*(e: ref Exception): seq[StackTraceEntry] =
  ## Returns the attached stack trace to the exception `e` as
  ## a `seq`. This is not yet available for the JS backend.
  when not defined(nimSeqsV2):
    shallowCopy(result, e.trace)
  else:
    result = move(e.trace)

proc getStackTraceEntries*(): seq[StackTraceEntry] =
  ## Returns the stack trace entries for the current stack trace.
  ## This is not yet available for the JS backend.
  when hasSomeStackTrace:
    rawWriteStackTrace(result)

const nimCallDepthLimit {.intdefine.} = 2000

proc callDepthLimitReached() {.noinline.} =
  writeStackTrace()
  let msg = "Error: call depth limit reached in a debug build (" &
      $nimCallDepthLimit & " function calls). You can change it with " &
      "-d:nimCallDepthLimit=<int> but really try to avoid deep " &
      "recursions instead.\n"
  showErrorMessage2(msg)
  quit(1)

proc nimFrame(s: PFrame) {.compilerRtl, inl, raises: [].} =
  if framePtr == nil:
    s.calldepth = 0
    when NimStackTraceMsgs: s.frameMsgLen = 0
  else:
    s.calldepth = framePtr.calldepth+1
    when NimStackTraceMsgs: s.frameMsgLen = framePtr.frameMsgLen
  s.prev = framePtr
  framePtr = s
  if s.calldepth == nimCallDepthLimit: callDepthLimitReached()

when defined(cpp) and appType != "lib" and not gotoBasedExceptions and
    not defined(js) and not defined(nimscript) and
    hostOS != "standalone" and hostOS != "any" and not defined(noCppExceptions) and
    not defined(nimQuirky):

  type
    StdException {.importcpp: "std::exception", header: "<exception>".} = object

  proc what(ex: StdException): cstring {.importcpp: "((char *)#.what())".}

  proc setTerminate(handler: proc() {.noconv.})
    {.importc: "std::set_terminate", header: "<exception>".}

  setTerminate proc() {.noconv.} =
    # Remove ourself as a handler, reinstalling the default handler.
    setTerminate(nil)

    var msg = "Unknown error in unexpected exception handler"
    try:
      {.emit: "#if !defined(_MSC_VER) || (_MSC_VER >= 1923)".}
      raise
      {.emit: "#endif".}
    except Exception:
      msg = currException.getStackTrace() & "Error: unhandled exception: " &
        currException.msg & " [" & $currException.name & "]"
    except StdException as e:
      msg = "Error: unhandled cpp exception: " & $e.what()
    except:
      msg = "Error: unhandled unknown cpp exception"

    {.emit: "#if defined(_MSC_VER) && (_MSC_VER < 1923)".}
    msg = "Error: unhandled unknown cpp exception"
    {.emit: "#endif".}

    when defined(genode):
      # stderr not available by default, use the LOG session
      echo msg
    else:
      writeToStdErr msg & "\n"

    quit 1

when not defined(noSignalHandler) and not defined(useNimRtl):
  proc signalHandler(sign: cint) {.exportc: "signalHandler", noconv.} =
    template processSignal(s, action: untyped) {.dirty.} =
      if s == SIGINT: action("SIGINT: Interrupted by Ctrl-C.\n")
      elif s == SIGSEGV:
        action("SIGSEGV: Illegal storage access. (Attempt to read from nil?)\n")
      elif s == SIGABRT:
        action("SIGABRT: Abnormal termination.\n")
      elif s == SIGFPE: action("SIGFPE: Arithmetic error.\n")
      elif s == SIGILL: action("SIGILL: Illegal operation.\n")
      elif (when declared(SIGBUS): s == SIGBUS else: false):
        action("SIGBUS: Illegal storage access. (Attempt to read from nil?)\n")
      else:
        block platformSpecificSignal:
          when declared(SIGPIPE):
            if s == SIGPIPE:
              action("SIGPIPE: Pipe closed.\n")
              break platformSpecificSignal
          action("unknown signal\n")

    # print stack trace and quit
    when defined(memtracker):
      logPendingOps()
    when hasSomeStackTrace:
      when not usesDestructors: GC_disable()
      var buf = newStringOfCap(2000)
      rawWriteStackTrace(buf)
      processSignal(sign, buf.add) # nice hu? currying a la Nim :-)
      showErrorMessage2(buf)
      when not usesDestructors: GC_enable()
    else:
      var msg: cstring
      template asgn(y) =
        msg = y
      processSignal(sign, asgn)
      # xxx use string for msg instead of cstring, and here use showErrorMessage2(msg)
      # unless there's a good reason to use cstring in signal handler to avoid
      # using gc?
      showErrorMessage(msg, msg.len)

    when defined(posix):
      # reset the signal handler to OS default
      c_signal(sign, SIG_DFL)

      # re-raise the signal, which will arrive once this handler exit.
      # this lets the OS perform actions like core dumping and will
      # also return the correct exit code to the shell.
      discard c_raise(sign)
    else:
      quit(1)

  proc registerSignalHandler() =
    c_signal(SIGINT, signalHandler)
    c_signal(SIGSEGV, signalHandler)
    c_signal(SIGABRT, signalHandler)
    c_signal(SIGFPE, signalHandler)
    c_signal(SIGILL, signalHandler)
    when declared(SIGBUS):
      c_signal(SIGBUS, signalHandler)
    when declared(SIGPIPE):
      c_signal(SIGPIPE, signalHandler)

  registerSignalHandler() # call it in initialization section

proc setControlCHook(hook: proc () {.noconv.}) =
  # ugly cast, but should work on all architectures:
  type SignalHandler = proc (sign: cint) {.noconv, benign.}
  c_signal(SIGINT, cast[SignalHandler](hook))

when not defined(noSignalHandler) and not defined(useNimRtl):
  proc unsetControlCHook() =
    # proc to unset a hook set by setControlCHook
    c_signal(SIGINT, signalHandler)