September 28, 2014
On 28.9.2014. 21:32, Walter Bright wrote:
> On 9/28/2014 11:25 AM, bearophile wrote:
>> Exceptions are often used to help debugging...
>
>
> https://www.youtube.com/watch?v=hBhlQgvHmQ0

Example exception messages:

Unable to connect to database
Invalid argument count
Invalid network package format

All this messages do not require a stack trace as they do not require code fixes, they indicate an issue outside the program itself. If stack trace is required then assert should have been used instead.

Or to better put it: can anyone give an example of exception that would require stack trace?

September 28, 2014
On 9/28/2014 12:33 PM, Sean Kelly wrote:
>> Then use assert(). That's just what it's for.
> What if I don't want to be forced to abort the program in the event of such an
> error?

Then we are back to the discussion about can a program continue after a logic error is uncovered, or not.

In any program, the programmer must decide if an error is a bug or not, before shipping it. Trying to avoid making this decision leads to confusion and using the wrong techniques to deal with it.

A program bug is, by definition, unknown and unanticipated. The idea that one can "recover" from it is fundamentally wrong. Of course, in D one can try and recover from them anyway, but you're on your own trying that, just as you're on your own when casting integers to pointers.

On the other hand, input/environmental errors must be anticipated and can often be recovered from. But presenting debug traces to the users for these implies at the very least a sloppily engineered product, in my not so humble opinion :-)
September 28, 2014
On 9/28/2014 12:33 PM, Jacob Carlborg wrote:
> On 2014-09-28 19:36, Walter Bright wrote:
>
>> I suggest removal of stack trace for exceptions, but leaving them in for
>> asserts.
>
> If you don't like the stack track, just wrap the "main" function in a try-catch
> block, catch all exceptions and print the error message.

That's what the runtime that calls main() is supposed to do.

September 28, 2014
On 9/28/2014 12:38 PM, Sean Kelly wrote:
>> Exceptions are meant for RECOVERABLE errors. If you're using them instead of
>> assert for logic bugs, you're looking at undefined behavior. Logic bugs are
>> not recoverable.
>
> In a multithreaded program, does this mean that the thread must be terminated or
> the entire process?  In a multi-user system, does this mean the transaction or
> the entire process?  The scope of a logic bug can be known to be quite limited.
> Remember my earlier point about Erlang, where a "process" there is actually just
> a logical thread in the VM.

This has been asked many times before.

If the threads share memory, the only robust choice is to terminate all the threads and the application.

If the thread is in another process, where the memory is not shared, then terminating and possibly restarting that process is quite acceptable.

> The scope of a logic bug can be known to be quite limited.

If you know about the bug, then you'd have fixed it already instead of inserting recovery code for unknown problems. I can't really accept that one has "unknown bugs of known scope".
September 28, 2014
On Sunday, 28 September 2014 at 20:31:03 UTC, Walter Bright wrote:
>
> If the threads share memory, the only robust choice is to terminate all the threads and the application.
>
> If the thread is in another process, where the memory is not shared, then terminating and possibly restarting that process is quite acceptable.
>
> > The scope of a logic bug can be known to be quite limited.
>
> If you know about the bug, then you'd have fixed it already instead of inserting recovery code for unknown problems. I can't really accept that one has "unknown bugs of known scope".

Well, say you're using SafeD or some other system where you know that memory corruption is not possible (pure functional programming, for example).  In this case, if you know what data a particular execution flow touches, you know the scope of the potential damage.  And if the data touched is all either shared but read-only or generated during the processing of the request, you can be reasonably certain that nothing outside the scope of the transaction has been adversely affected at all.
September 28, 2014
On Sun, Sep 28, 2014 at 10:32:14AM -0700, Walter Bright via Digitalmars-d wrote:
> On 9/28/2014 9:16 AM, Sean Kelly wrote:
[...]
> >What if an API you're using throws an exception you didn't expect, and therefore don't handle?
> 
> Then the app user sees the error message. This is one of the cool things about D - I can write small apps with NO error handling logic in it, and I still get appropriate and friendly messages when things go wrong like missing files.
> 
> That is, until recently, when I get a bunch of debug stack traces and internal file/line messages, which are of no use at all to an app user and look awful.

It looks even more awful when the person who wrote the library code is Russian, and the user speaks English, and when an uncaught exception terminates the program, you get a completely incomprehensible message in a language you don't know. Not much different from a line number and filename that has no meaning for a user.

That's why I said, an uncaught exception is a BUG. The only place where user-readable messages can be output is in a catch block where you actually have the chance to localize the error string. But if no catch block catches it, then by definition it's a bug, and you might as while print some useful info with it that your users can send back to you, rather than unhelpful bug reports of the form "the program crashed with error message 'internal error'". Good luck finding where in the code that is. (And no, grepping does not work -- the string 'internal error' could have come from a system call or C library error code translated by a generic code-to-message function, which could've been called from *anywhere*.)


> >This might be considered a logic error if the exception is recoverable and you don't intend the program to abort from that operation.
> 
> Adding file/line to all exceptions implies that they are all bugs, and encourages them to be thought of as bugs and debugging tools, when they are NOT. Exceptions are for:
> 
> 1. enabling recovery from input/environmental errors
> 2. reporting input/environmental errors to the app user
> 3. making input/environmental errors not ignorable by default
> 
> They are not for detecting logic errors. Assert is designed for that.

I do not condone adding file/line to exception *messages*. Catch blocks can print / translate those messages, which can be made user-friendly, but if the program failed to catch an exception, you're already screwed anyway so why not provide more info rather than less?

Unless, of course, you're suggesting that we put this around every
main() function:

	void main() {
		try {
			...
		} catch(Exception e) {
			assert(0, "Unhandled exception: I screwed up");
		}
	}


T

-- 
I think Debian's doing something wrong, `apt-get install pesticide', doesn't seem to remove the bugs on my system! -- Mike Dresser
September 28, 2014
29-Sep-2014 00:50, Sean Kelly пишет:
> On Sunday, 28 September 2014 at 20:31:03 UTC, Walter Bright wrote:
>>
>> If the threads share memory, the only robust choice is to terminate
>> all the threads and the application.
>>
>> If the thread is in another process, where the memory is not shared,
>> then terminating and possibly restarting that process is quite
>> acceptable.
>>
>> > The scope of a logic bug can be known to be quite limited.
>>
>> If you know about the bug, then you'd have fixed it already instead of
>> inserting recovery code for unknown problems. I can't really accept
>> that one has "unknown bugs of known scope".
>
> Well, say you're using SafeD or some other system where you know that
> memory corruption is not possible (pure functional programming, for
> example).

> In this case, if you know what data a particular execution
> flow touches, you know the scope of the potential damage.  And if the
> data touched is all either shared but read-only or generated during the
> processing of the request, you can be reasonably certain that nothing
> outside the scope of the transaction has been adversely affected at all.


not possible / highly unlikely (i.e. bug in VM or said system)

But otherwise agreed, dropping the whole process is not always a good idea or it easily becomes a DoS attack vector in a public service.


-- 
Dmitry Olshansky
September 28, 2014
On Sunday, 28 September 2014 at 20:58:20 UTC, H. S. Teoh via Digitalmars-d wrote:
>
> That's why I said, an uncaught exception is a BUG. The only  place where
> user-readable messages can be output is in a catch block where  you
> actually have the chance to localize the error string. But if  no catch
> block catches it, then by definition it's a bug, and you might  as while
> print some useful info with it that your users can send back to  you,
> rather than unhelpful bug reports of the form "the program crashed with
> error message 'internal error'".

Pretty much every system should generate a localized error message for the user and a detailed log of the problem for the programmer.  Ideally, the user message will indicate how to provide the detailed information to the developer so the problem can be fixed.

The one case where uncaught exceptions aren't really a bug is with programs that aren't being used outside the group that developed them.  In these cases, the default behavior is pretty much exactly what's desired--a message, file/line info, and a stack trace.  Which is why it's there.  The vast bulk of today's shipping code doesn't run from the command line anyway, so the default exception handler should be practically irrelevant.
September 28, 2014
On Sunday, 28 September 2014 at 21:16:51 UTC, Dmitry Olshansky wrote:
>
> But otherwise agreed, dropping the whole process is not always a good idea or it easily becomes a DoS attack vector in a public service.

What I really want to work towards is the Erlang model where an app is a web of communicating processes (though Erlang processes are effectively equivalent to D objects).  Then, killing a process on an error is absolutely correct.  It doesn't affect the resilience of the system.  But if these processes are actually threads or fibers with memory protection, things get a lot more complicated.  I really need to spend some time investigating how modern Linux systems handle tons of processes running on them and try to find a happy medium.
September 28, 2014
On Sunday, 28 September 2014 at 20:58:20 UTC, H. S. Teoh via Digitalmars-d wrote:
> I do not condone adding file/line to exception *messages*. Catch blocks
> can print / translate those messages, which can be made user-friendly,
> but if the program failed to catch an exception, you're already screwed
> anyway so why not provide more info rather than less?
>
> Unless, of course, you're suggesting that we put this around every
> main() function:
>
> 	void main() {
> 		try {
> 			...
> 		} catch(Exception e) {
> 			assert(0, "Unhandled exception: I screwed up");
> 		}
> 	}
>

In our production C# code, we had a few practices which might be applicable here:

1. main() definitely had a top-level try/catch handler to produce useful output messages.  Because throwing an uncaught exception out to the user *is* a bug, we naturally want to not just toss out a stack trace but information on what to do with it should a user encounter it.  Even better if there is additional runtime information which can be provided for a bug report.

2. We also registered a top-level unhandled exception handler on the AppDomain (equivalent to a process in .NET, except that multiple AppDomains may exist within a single OS process), which allows the catching to exceptions which would otherwise escape background threads.  Depending on the nature of the application, these could be logged to some repository to which the user could be directed.  It's hard to strictly automate this because exactly what you can do with an exception which escapes a thread will be application dependent.  In our case, these exceptions were considered bugs, were considered to be unrecoverable and resulted in a program abort with a user message indicating where to find the relevant log outputs and how to contact us.

3. For some cases, throwing an exception would also trigger an application dump suitable for post-mortem debugging from the point the exception was about to be thrown.  This functionality is, of course, OS-specific, but helped us on more than a few occasions by eliminating the need to try to pre-determine which information was important and which was not so the exception could be usefully populated.

I'm not a fan of eliminating the stack from exceptions.  While exceptions should not be used to catch logic errors, an uncaught exception is itself a logic error (that is, one has omitted some required conditions in their code) and thus the context of the error needs to be made available somehow.