Merge pull request #759 from zielmicha/stdmsg

Write tracebacks to stderr instead of stdout.
This commit is contained in:
Andreas Rumpf 2013-12-20 09:42:39 -08:00
commit 259d205ff6
8 changed files with 84 additions and 41 deletions

2
.gitignore vendored
View file

@ -63,6 +63,8 @@ examples/cross_calculator/android/tags
/examples/allany /examples/allany
/examples/cairoex /examples/cairoex
/examples/cgiex /examples/cgiex
/examples/cgi/cgi_stacktrace
/examples/cgi/example
/examples/curlex /examples/curlex
/examples/debugging /examples/debugging
/examples/docstrings /examples/docstrings

View file

@ -0,0 +1,11 @@
#!/usr/bin/env python
import BaseHTTPServer
import CGIHTTPServer
server = BaseHTTPServer.HTTPServer
handler = CGIHTTPServer.CGIHTTPRequestHandler
server_address = ('localhost', 8008)
handler.cgi_directories = ['/']
httpd = server(server_address, handler)
httpd.serve_forever()

View file

@ -0,0 +1,5 @@
import cgi
cgi.setStackTraceStdout()
var a: string = nil
a.add "foobar"

7
examples/cgi/example.nim Normal file
View file

@ -0,0 +1,7 @@
import cgi
write(stdout, "Content-type: text/html\n\n")
write(stdout, "<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01//EN\">\n")
write(stdout, "<html><head><title>Test</title></head><body>\n")
write(stdout, "Hello!")
writeln(stdout, "</body></html>")

View file

@ -342,16 +342,35 @@ proc writeContentType*() =
## ##
## .. code-block:: Nimrod ## .. code-block:: Nimrod
## write(stdout, "Content-type: text/html\n\n") ## write(stdout, "Content-type: text/html\n\n")
##
## It also modifies the debug stack traces so that they contain
## ``<br />`` and are easily readable in a browser.
write(stdout, "Content-type: text/html\n\n") write(stdout, "Content-type: text/html\n\n")
system.stackTraceNewLine = "<br />\n"
proc setStackTraceNewLine*() = proc resetForStacktrace() =
## Modifies the debug stack traces so that they contain stdout.write """<!--: spam
## ``<br />`` and are easily readable in a browser. Content-Type: text/html
system.stackTraceNewLine = "<br />\n"
<body bgcolor=#f0f0f8><font color=#f0f0f8 size=-5> -->
<body bgcolor=#f0f0f8><font color=#f0f0f8 size=-5> --> -->
</font> </font> </font> </script> </object> </blockquote> </pre>
</table> </table> </table> </table> </table> </font> </font> </font>
"""
proc writeErrorMessage*(data: string) =
## Tries to reset browser state and writes `data` to stdout in
## <plaintext> tag.
resetForStacktrace()
# We use <plaintext> here, instead of escaping, so stacktrace can
# be understood by human looking at source.
stdout.write("<plaintext>\n")
stdout.write(data)
proc setStackTraceStdout*() =
## Makes Nimrod output stacktraces to stdout, instead of server log.
errorMessageWriter = writeErrorMessage
proc setStackTraceNewLine*() {.deprecated.} =
## Makes Nimrod output stacktraces to stdout, instead of server log.
## Depracated alias for setStackTraceStdout.
setStackTraceStdout()
proc setCookie*(name, value: string) = proc setCookie*(name, value: string) =
## Sets a cookie. ## Sets a cookie.
@ -374,4 +393,3 @@ when isMainModule:
const test1 = "abc\L+def xyz" const test1 = "abc\L+def xyz"
assert UrlEncode(test1) == "abc%0A%2Bdef+xyz" assert UrlEncode(test1) == "abc%0A%2Bdef+xyz"
assert UrlDecode(UrlEncode(test1)) == test1 assert UrlDecode(UrlEncode(test1)) == test1

View file

@ -1951,15 +1951,13 @@ when not defined(JS): #and not defined(NimrodVM):
## The standard output stream. ## The standard output stream.
stderr* {.importc: "stderr", header: "<stdio.h>".}: TFile stderr* {.importc: "stderr", header: "<stdio.h>".}: TFile
## The standard error stream. ## The standard error stream.
##
## Note: In my opinion, this should not be used -- the concept of a when defined(useStdoutAsStdmsg):
## separate error stream is a design flaw of UNIX. A separate *message template stdmsg*: TFile = stdout
## stream* is a good idea, but since it is named ``stderr`` there are few else:
## programs out there that distinguish properly between ``stdout`` and template stdmsg*: TFile = stderr
## ``stderr``. So, that's what you get if you don't name your variables ## Template which expands to either stdout or stderr depending on
## appropriately. It also annoys people if redirection ## `useStdoutAsStdmsg` compile-time switch.
## via ``>output.txt`` does not work because the program writes
## to ``stderr``.
proc Open*(f: var TFile, filename: string, proc Open*(f: var TFile, filename: string,
mode: TFileMode = fmRead, bufSize: int = -1): Bool {.tags: [].} mode: TFileMode = fmRead, bufSize: int = -1): Bool {.tags: [].}

View file

@ -11,14 +11,13 @@
# use the heap (and nor exceptions) do not include the GC or memory allocator. # use the heap (and nor exceptions) do not include the GC or memory allocator.
var var
stackTraceNewLine*: string ## undocumented feature; it is replaced by ``<br>`` errorMessageWriter*: (proc(msg: string): void {.tags: [FWriteIO].})
## for CGI applications ## Function that will be called
## instead of stdmsg.write when printing stacktrace.
template stackTraceNL: expr = ## Unstable API.
(if IsNil(stackTraceNewLine): "\n" else: stackTraceNewLine)
when not defined(windows) or not defined(guiapp): when not defined(windows) or not defined(guiapp):
proc writeToStdErr(msg: CString) = write(stdout, msg) proc writeToStdErr(msg: CString) = write(stdmsg, msg)
else: else:
proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {. proc MessageBoxA(hWnd: cint, lpText, lpCaption: cstring, uType: int): int32 {.
@ -27,6 +26,12 @@ else:
proc writeToStdErr(msg: CString) = proc writeToStdErr(msg: CString) =
discard MessageBoxA(0, msg, nil, 0) discard MessageBoxA(0, msg, nil, 0)
proc showErrorMessage(data: cstring) =
if errorMessageWriter != nil:
errorMessageWriter($data)
else:
writeToStdErr(data)
proc chckIndx(i, a, b: int): int {.inline, compilerproc.} proc chckIndx(i, a, b: int): int {.inline, compilerproc.}
proc chckRange(i, a, b: int): int {.inline, compilerproc.} proc chckRange(i, a, b: int): int {.inline, compilerproc.}
proc chckRangeF(x, a, b: float): float {.inline, compilerproc.} proc chckRangeF(x, a, b: float): float {.inline, compilerproc.}
@ -111,7 +116,7 @@ when defined(nativeStacktrace) and nativeStackTraceSupported:
add(s, tempDlInfo.dli_sname) add(s, tempDlInfo.dli_sname)
else: else:
add(s, '?') add(s, '?')
add(s, stackTraceNL) add(s, "\n")
else: else:
if dlresult != 0 and tempDlInfo.dli_sname != nil and if dlresult != 0 and tempDlInfo.dli_sname != nil and
c_strcmp(tempDlInfo.dli_sname, "signalHandler") == 0'i32: c_strcmp(tempDlInfo.dli_sname, "signalHandler") == 0'i32:
@ -172,21 +177,18 @@ proc auxWriteStackTrace(f: PFrame, s: var string) =
add(s, ')') add(s, ')')
for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ') for k in 1..max(1, 25-(s.len-oldLen)): add(s, ' ')
add(s, tempFrames[j].procname) add(s, tempFrames[j].procname)
add(s, stackTraceNL) add(s, "\n")
when hasSomeStackTrace: when hasSomeStackTrace:
proc rawWriteStackTrace(s: var string) = proc rawWriteStackTrace(s: var string) =
when nimrodStackTrace: when nimrodStackTrace:
if framePtr == nil: if framePtr == nil:
add(s, "No stack traceback available") add(s, "No stack traceback available\n")
add(s, stackTraceNL)
else: else:
add(s, "Traceback (most recent call last)") add(s, "Traceback (most recent call last)\n")
add(s, stackTraceNL)
auxWriteStackTrace(framePtr, s) auxWriteStackTrace(framePtr, s)
elif defined(nativeStackTrace) and nativeStackTraceSupported: elif defined(nativeStackTrace) and nativeStackTraceSupported:
add(s, "Traceback from system (most recent call last)") add(s, "Traceback from system (most recent call last)\n")
add(s, stackTraceNL)
auxWriteStackTraceWithBacktrace(s) auxWriteStackTraceWithBacktrace(s)
else: else:
add(s, "No stack traceback available\n") add(s, "No stack traceback available\n")
@ -207,7 +209,7 @@ proc raiseExceptionAux(e: ref E_Base) =
pushCurrentException(e) pushCurrentException(e)
c_longjmp(excHandler.context, 1) c_longjmp(excHandler.context, 1)
elif e[] of EOutOfMemory: elif e[] of EOutOfMemory:
writeToStdErr(e.name) showErrorMessage(e.name)
quitOrDebug() quitOrDebug()
else: else:
when hasSomeStackTrace: when hasSomeStackTrace:
@ -219,7 +221,7 @@ proc raiseExceptionAux(e: ref E_Base) =
add(buf, " [") add(buf, " [")
add(buf, $e.name) add(buf, $e.name)
add(buf, "]\n") add(buf, "]\n")
writeToStdErr(buf) showErrorMessage(buf)
else: else:
# ugly, but avoids heap allocations :-) # ugly, but avoids heap allocations :-)
template xadd(buf, s, slen: expr) = template xadd(buf, s, slen: expr) =
@ -235,7 +237,7 @@ proc raiseExceptionAux(e: ref E_Base) =
add(buf, " [") add(buf, " [")
xadd(buf, e.name, c_strlen(e.name)) xadd(buf, e.name, c_strlen(e.name))
add(buf, "]\n") add(buf, "]\n")
writeToStdErr(buf) showErrorMessage(buf)
quitOrDebug() quitOrDebug()
proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} = proc raiseException(e: ref E_Base, ename: CString) {.compilerRtl.} =
@ -255,9 +257,9 @@ proc WriteStackTrace() =
when hasSomeStackTrace: when hasSomeStackTrace:
var s = "" var s = ""
rawWriteStackTrace(s) rawWriteStackTrace(s)
writeToStdErr(s) showErrorMessage(s)
else: else:
writeToStdErr("No stack traceback available\n") showErrorMessage("No stack traceback available\n")
proc getStackTrace(): string = proc getStackTrace(): string =
when hasSomeStackTrace: when hasSomeStackTrace:
@ -298,13 +300,13 @@ when not defined(noSignalHandler):
var buf = newStringOfCap(2000) var buf = newStringOfCap(2000)
rawWriteStackTrace(buf) rawWriteStackTrace(buf)
processSignal(sig, buf.add) # nice hu? currying a la nimrod :-) processSignal(sig, buf.add) # nice hu? currying a la nimrod :-)
writeToStdErr(buf) showErrorMessage(buf)
GC_enable() GC_enable()
else: else:
var msg: cstring var msg: cstring
template asgn(y: expr) = msg = y template asgn(y: expr) = msg = y
processSignal(sig, asgn) processSignal(sig, asgn)
writeToStdErr(msg) showErrorMessage(msg)
when defined(endb): dbgAborting = True when defined(endb): dbgAborting = True
quit(1) # always quit when SIGABRT quit(1) # always quit when SIGABRT

View file

@ -29,7 +29,6 @@ Changes affecting backwards compatibility
using the new ``OSLastError`` proc. using the new ``OSLastError`` proc.
- ``os.parentDir`` now returns "" if there is no parent dir. - ``os.parentDir`` now returns "" if there is no parent dir.
Compiler Additions Compiler Additions
------------------ ------------------
@ -41,7 +40,8 @@ Compiler Additions
over the generated code. over the generated code.
- The compiler now supports a ``computedGoto`` pragma to support very fast - The compiler now supports a ``computedGoto`` pragma to support very fast
dispatching for interpreters and the like. dispatching for interpreters and the like.
- In CGI scripts stacktraces are shown user only if cgi.setStackTraceStdout
is used.
Language Additions Language Additions
------------------ ------------------