Added 'ansic' os support for minimal (embedded) targets (#13088)

* os:any implementation
* os:asny: omit flock/funlock calls in echoBinSafe
* Disabled default "unhandled expection" reporting for `--os:any` to reduce
code size. Added unhandledExceptionHook instead which can be used to get
a notification from Nim and handle it from the application.
This commit is contained in:
Ico Doornekamp 2020-01-15 11:06:41 +01:00 • committed by Andreas Rumpf
commit 79a326759a
6 changed files with 87 additions and 73 deletions

View file

@ -22,7 +22,7 @@ type
osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris, osNone, osDos, osWindows, osOs2, osLinux, osMorphos, osSkyos, osSolaris,
osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx, osIrix, osNetbsd, osFreebsd, osOpenbsd, osDragonfly, osAix, osPalmos, osQnx,
osAmiga, osAtari, osNetware, osMacos, osMacosx, osIos, osHaiku, osAndroid, osVxWorks osAmiga, osAtari, osNetware, osMacos, osMacosx, osIos, osHaiku, osAndroid, osVxWorks
osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch osGenode, osJS, osNimVM, osStandalone, osNintendoSwitch, osAny
type type
TInfoOSProp* = enum TInfoOSProp* = enum
@ -177,6 +177,10 @@ const
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/", objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: ".elf", extSep: ".", scriptExt: ".sh", curDir: ".", exeExt: ".elf", extSep: ".",
props: {ospNeedsPIC, ospPosix}), props: {ospNeedsPIC, ospPosix}),
(name: "Any", parDir: "..", dllFrmt: "lib$1.so", altDirSep: "/",
objExt: ".o", newLine: "\x0A", pathSep: ":", dirSep: "/",
scriptExt: ".sh", curDir: ".", exeExt: "", extSep: ".",
props: {}),
] ]
type type

View file

@ -2096,7 +2096,7 @@ template sysAssert(cond: bool, msg: string) =
const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(nimscript) const hasAlloc = (hostOS != "standalone" or not defined(nogc)) and not defined(nimscript)
when notJSnotNims and hostOS != "standalone": when notJSnotNims and hostOS != "standalone" and hostOS != "any":
include "system/cgprocs" include "system/cgprocs"
when notJSnotNims and hasAlloc and not defined(nimSeqsV2): when notJSnotNims and hasAlloc and not defined(nimSeqsV2):
proc addChar(s: NimString, c: char): NimString {.compilerproc, benign.} proc addChar(s: NimString, c: char): NimString {.compilerproc, benign.}
@ -3395,6 +3395,11 @@ var
## ##
## If the handler does not raise an exception, ordinary control flow ## If the handler does not raise an exception, ordinary control flow
## continues and the program is terminated. ## continues and the program is terminated.
unhandledExceptionHook*: proc (e: ref Exception) {.nimcall, tags: [], benign, raises: [].}
## Set this variable to provide a procedure that should be called
## in case of an `unhandle exception` event. The standard handler
## writes an error message and terminates the program, except when
## using `--os:any`
type type
PFrame* = ptr TFrame ## Represents a runtime frame of the call stack; PFrame* = ptr TFrame ## Represents a runtime frame of the call stack;
@ -3745,7 +3750,7 @@ when not defined(JS):
when notJSnotNims: when notJSnotNims:
when hostOS != "standalone": when hostOS != "standalone" and hostOS != "any":
include "system/dyncalls" include "system/dyncalls"
include "system/sets" include "system/sets"

View file

@ -210,7 +210,7 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
# interested in # interested in
enabled = true enabled = true
when not hasThreadSupport: when hasSomeStackTrace and not hasThreadSupport:
var var
tempFrames: array[0..127, PFrame] # should not be alloc'd on stack tempFrames: array[0..127, PFrame] # should not be alloc'd on stack
@ -261,52 +261,53 @@ proc `$`(s: seq[StackTraceEntry]): string =
elif s[i].line == reraisedFromEnd: result.add "]]\n" elif s[i].line == reraisedFromEnd: result.add "]]\n"
else: addFrameEntry(result, s[i]) else: addFrameEntry(result, s[i])
proc auxWriteStackTrace(f: PFrame, s: var string) =
when hasThreadSupport:
var
tempFrames: array[0..127, 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
when hasSomeStackTrace: when hasSomeStackTrace:
proc auxWriteStackTrace(f: PFrame, s: var string) =
when hasThreadSupport:
var
tempFrames: array[0..127, 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) = proc rawWriteStackTrace(s: var string) =
when defined(nimStackTraceOverride): when defined(nimStackTraceOverride):
add(s, "Traceback (most recent call last, using override)\n") add(s, "Traceback (most recent call last, using override)\n")
@ -351,7 +352,7 @@ var onUnhandledException*: (proc (errorMsg: string) {.
## The default is to write a stacktrace to ``stderr`` and then call ``quit(1)``. ## The default is to write a stacktrace to ``stderr`` and then call ``quit(1)``.
## Unstable API. ## Unstable API.
proc reportUnhandledError(e: ref Exception) {.nodestroy.} = proc reportUnhandledErrorAux(e: ref Exception) {.nodestroy.} =
when hasSomeStackTrace: when hasSomeStackTrace:
var buf = newStringOfCap(2000) var buf = newStringOfCap(2000)
if e.trace.len == 0: if e.trace.len == 0:
@ -400,6 +401,14 @@ proc reportUnhandledError(e: ref Exception) {.nodestroy.} =
else: else:
showErrorMessage(tbuf()) showErrorMessage(tbuf())
proc reportUnhandledError(e: ref Exception) {.nodestroy.} =
if unhandledExceptionHook != nil:
unhandledExceptionHook(e)
when hostOS != "any":
reportUnhandledErrorAux(e)
else:
discard()
proc nimLeaveFinally() {.compilerRtl.} = proc nimLeaveFinally() {.compilerRtl.} =
when defined(cpp) and not defined(noCppExceptions): when defined(cpp) and not defined(noCppExceptions):
{.emit: "throw;".} {.emit: "throw;".}
@ -425,14 +434,6 @@ when gotoBasedExceptions:
currException = nil currException = nil
quit(1) quit(1)
addQuitProc(proc () {.noconv.} =
if currException != nil:
reportUnhandledError(currException)
# emulate: ``programResult = 1`` via abort() and a nop signal handler.
c_signal(SIGABRT, (proc (sign: cint) {.noconv, benign.} = discard))
c_abort()
)
proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} = proc raiseExceptionAux(e: sink(ref Exception)) {.nodestroy.} =
if localRaiseHook != nil: if localRaiseHook != nil:
if not localRaiseHook(e): return if not localRaiseHook(e): return

View file

@ -615,7 +615,7 @@ when declared(stdout):
android_log_print(ANDROID_LOG_VERBOSE, "nim", s) android_log_print(ANDROID_LOG_VERBOSE, "nim", s)
else: else:
# flockfile deadlocks some versions of Android 5.x.x # flockfile deadlocks some versions of Android 5.x.x
when not defined(windows) and not defined(android) and not defined(nintendoswitch): when not defined(windows) and not defined(android) and not defined(nintendoswitch) and hostOS != "any":
proc flockfile(f: File) {.importc, nodecl.} proc flockfile(f: File) {.importc, nodecl.}
proc funlockfile(f: File) {.importc, nodecl.} proc funlockfile(f: File) {.importc, nodecl.}
flockfile(stdout) flockfile(stdout)
@ -629,7 +629,7 @@ when declared(stdout):
const linefeed = "\n" const linefeed = "\n"
discard c_fwrite(linefeed.cstring, linefeed.len, 1, stdout) discard c_fwrite(linefeed.cstring, linefeed.len, 1, stdout)
discard c_fflush(stdout) discard c_fflush(stdout)
when not defined(windows) and not defined(android) and not defined(nintendoswitch): when not defined(windows) and not defined(android) and not defined(nintendoswitch) and hostOS != "any":
funlockfile(stdout) funlockfile(stdout)
when defined(windows) and compileOption("threads"): when defined(windows) and compileOption("threads"):
releaseSys echoLock releaseSys echoLock

View file

@ -354,11 +354,11 @@ elif defined(gogc):
proc deallocOsPages(r: var MemRegion) {.inline.} = discard proc deallocOsPages(r: var MemRegion) {.inline.} = discard
proc deallocOsPages() {.inline.} = discard proc deallocOsPages() {.inline.} = discard
elif defined(nogc) and defined(useMalloc): elif (defined(nogc) or defined(gcDestructors)) and defined(useMalloc):
when not defined(useNimRtl): when not defined(useNimRtl):
proc alloc(size: Natural): pointer = proc alloc(size: Natural): pointer =
var x = c_malloc(size + sizeof(size)) var x = c_malloc (size + sizeof(size)).csize_t
if x == nil: raiseOutOfMem() if x == nil: raiseOutOfMem()
cast[ptr int](x)[] = size cast[ptr int](x)[] = size
@ -371,7 +371,7 @@ elif defined(nogc) and defined(useMalloc):
var x = cast[pointer](cast[int](p) - sizeof(newsize)) var x = cast[pointer](cast[int](p) - sizeof(newsize))
let oldsize = cast[ptr int](x)[] let oldsize = cast[ptr int](x)[]
x = c_realloc(x, newsize + sizeof(newsize)) x = c_realloc(x, (newsize + sizeof(newsize)).csize_t)
if x == nil: raiseOutOfMem() if x == nil: raiseOutOfMem()
@ -384,13 +384,13 @@ elif defined(nogc) and defined(useMalloc):
proc dealloc(p: pointer) = c_free(cast[pointer](cast[int](p) - sizeof(int))) proc dealloc(p: pointer) = c_free(cast[pointer](cast[int](p) - sizeof(int)))
proc allocShared(size: Natural): pointer = proc allocShared(size: Natural): pointer =
result = c_malloc(size) result = c_malloc(size.csize_t)
if result == nil: raiseOutOfMem() if result == nil: raiseOutOfMem()
proc allocShared0(size: Natural): pointer = proc allocShared0(size: Natural): pointer =
result = alloc(size) result = alloc(size)
zeroMem(result, size) zeroMem(result, size)
proc reallocShared(p: pointer, newsize: Natural): pointer = proc reallocShared(p: pointer, newsize: Natural): pointer =
result = c_realloc(p, newsize) result = c_realloc(p, newsize.csize_t)
if result == nil: raiseOutOfMem() if result == nil: raiseOutOfMem()
proc deallocShared(p: pointer) = c_free(p) proc deallocShared(p: pointer) = c_free(p)
@ -400,7 +400,7 @@ elif defined(nogc) and defined(useMalloc):
proc GC_setStrategy(strategy: GC_Strategy) = discard proc GC_setStrategy(strategy: GC_Strategy) = discard
proc GC_enableMarkAndSweep() = discard proc GC_enableMarkAndSweep() = discard
proc GC_disableMarkAndSweep() = discard proc GC_disableMarkAndSweep() = discard
proc GC_getStatistics(): string = return "" #proc GC_getStatistics(): string = return ""
proc getOccupiedMem(): int = discard proc getOccupiedMem(): int = discard
proc getFreeMem(): int = discard proc getFreeMem(): int = discard
@ -410,13 +410,6 @@ elif defined(nogc) and defined(useMalloc):
proc initGC() = discard proc initGC() = discard
proc newObj(typ: PNimType, size: int): pointer {.compilerproc.} =
result = alloc0(size)
proc newSeq(typ: PNimType, len: int): pointer {.compilerproc.} =
result = newObj(typ, addInt(mulInt(len, typ.base.size), GenericSeqSize))
cast[PGenericSeq](result).len = len
cast[PGenericSeq](result).reserved = len
proc newObjNoInit(typ: PNimType, size: int): pointer = proc newObjNoInit(typ: PNimType, size: int): pointer =
result = alloc(size) result = alloc(size)

View file

@ -295,5 +295,16 @@ elif hostOS == "standalone" or defined(StandaloneHeapSize):
proc osDeallocPages(p: pointer, size: int) {.inline.} = proc osDeallocPages(p: pointer, size: int) {.inline.} =
if bumpPointer-size == cast[int](p): if bumpPointer-size == cast[int](p):
dec bumpPointer, size dec bumpPointer, size
elif hostOS == "any":
proc osAllocPages(size: int): pointer {.inline.} =
result = c_malloc(size.csize_t)
proc osTryAllocPages(size: int): pointer {.inline.} =
result = c_malloc(size.csize_t)
proc osDeallocPages(p: pointer, size: int) {.inline.} =
c_free(p)
else: else:
{.error: "Port memory manager to your platform".} {.error: "Port memory manager to your platform".}