Discussion:
Program logic bugs vs input/environmental errors
(too old to reply)
Walter Bright via Digitalmars-d
2014-09-27 23:15:40 UTC
Permalink
This issue comes up over and over, in various guises. I feel like Yosemite Sam here:

https://www.youtube.com/watch?v=hBhlQgvHmQ0

In that vein, Exceptions are for either being able to recover from
input/environmental errors, or report them to the user of the application.

When I say "They are NOT for debugging programs", I mean they are NOT for
debugging programs.

assert()s and contracts are for debugging programs.
dmd test.d
test.d(15) Error: missing } thrown from dmd/src/parse.c(283)

?

See:

https://issues.dlang.org/show_bug.cgi?id=13543

As for the programmer wanting to know where the message "missing }" came from,

grep -r dmd/src/*.c "missing }"

works nicely. I do that sort of thing all the time. It really isn't a problem.
bearophile via Digitalmars-d
2014-09-27 23:33:50 UTC
Permalink
Post by Walter Bright via Digitalmars-d
As for the programmer wanting to know where the message
"missing }" came from,
grep -r dmd/src/*.c "missing }"
works nicely. I do that sort of thing all the time. It really
isn't a problem.
grep is not useful for the purposes explained in issue 13543
because the file name is often inside a string variable, that is
initialized elsewhere or generated in some way. So the exception
is useful to know where's the instruction in user code that has
tried the failed I/O action, as I've explained in that issue.

Bye,
bearophile
Walter Bright via Digitalmars-d
2014-09-27 23:42:18 UTC
Permalink
Post by Walter Bright via Digitalmars-d
As for the programmer wanting to know where the message "missing }" came from,
grep -r dmd/src/*.c "missing }"
works nicely. I do that sort of thing all the time. It really isn't a problem.
grep is not useful for the purposes explained in issue 13543 because the file
name is often inside a string variable, that is initialized elsewhere or
generated in some way. So the exception is useful to know where's the
instruction in user code that has tried the failed I/O action, as I've explained
in that issue.
Even if that is what you wanted, you won't get that from FileException, as it
will only show file/lines emanating from calls inside std.file, not from higher
level callers.

Besides, take a bit of care when formulating a string for exceptions, and you
won't have any trouble grepping for it. This isn't rocket science.

Presenting internal debugging data to users for input/environmental errors is
just bad programming practice. We shouldn't be enshrining it in Phobos and
presenting it as a professional way to code.
H. S. Teoh via Digitalmars-d
2014-09-27 23:55:53 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Post by Walter Bright via Digitalmars-d
As for the programmer wanting to know where the message "missing }" came from,
grep -r dmd/src/*.c "missing }"
works nicely. I do that sort of thing all the time. It really isn't a problem.
grep is not useful for the purposes explained in issue 13543 because
the file name is often inside a string variable, that is initialized
elsewhere or generated in some way. So the exception is useful to
know where's the instruction in user code that has tried the failed
I/O action, as I've explained in that issue.
Even if that is what you wanted, you won't get that from
FileException, as it will only show file/lines emanating from calls
inside std.file, not from higher level callers.
Besides, take a bit of care when formulating a string for exceptions,
and you won't have any trouble grepping for it. This isn't rocket
science.
Presenting internal debugging data to users for input/environmental
errors is just bad programming practice. We shouldn't be enshrining it
in Phobos and presenting it as a professional way to code.
My take on this, is that uncaught exceptions are a program bug. Any
messages displayed to the user ought to come from a catch block that not
only prints the exception message (*without* things like line numbers
and stack traces, btw), but also provides context (e.g., "Error in
configuration file section 'abc': illegal field value" instead of just
"illegal field value" with no context of where it might have been
triggered).

Uncaught exceptions (which ideally should only be Errors, not
Exceptions) are a program bug that ought to be fixed. In the case that
somehow one managed to elude your catch blocks, the full debug infodump
(source file, line number, stack trace) is useful for users to hand back
to you in a bug report, so that you can track down the problem. The user
should not be expected to understand the infodump from an uncaught
exception, whereas a message printed from a catch block ought to be
user-understandable (like "can't open 'myphoto.jpg': file not found",
not "internal error on line 12345" which makes no sense to a user).


T
--
Laissez-faire is a French term commonly interpreted by Conservatives to mean 'lazy fairy,' which is the belief that if governments are lazy enough, the Good Fairy will come down from heaven and do all their work for them.
bearophile via Digitalmars-d
2014-09-27 23:59:43 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Even if that is what you wanted, you won't get that from
FileException, as it will only show file/lines emanating from
calls inside std.file, not from higher level callers.
Can't we use the template arguments like __LINE__ to offer the
line of code in the IO function in user code?
Post by Walter Bright via Digitalmars-d
Besides, take a bit of care when formulating a string for
exceptions, and you won't have any trouble grepping for it.
This isn't rocket science.
The exception is generated inside library code, not in user code.
There's nothing to grep in the user code. The D script often
looks like this:

void main() {
import std.stdio;
auto file_name = "some_file;
// some code here
auto file_name1 = file_name ~ "1.txt";
auto f1 = File(file_name1);
// some code here
auto file_name2 = file_name ~ "2.txt";
auto f2 = File(file_name2, "w");
// some code here
}
Post by Walter Bright via Digitalmars-d
Presenting internal debugging data to users for
input/environmental errors is just bad programming practice.7
We shouldn't be enshrining it in Phobos and presenting it as a
professional way to code.
The file/line of code is not meant to replace serious practices
for dealing with I/O failures in serious D programs.

Bye,
bearophile
Walter Bright via Digitalmars-d
2014-09-28 00:54:09 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Even if that is what you wanted, you won't get that from FileException, as it
will only show file/lines emanating from calls inside std.file, not from
higher level callers.
Can't we use the template arguments like __LINE__ to offer the line of code in
the IO function in user code?
We could, but again, WHOA CAMEL!

Also, one should be careful in using __LINE__ in a template, as it will be
forced to generate a new instantiation with every use.
Post by Walter Bright via Digitalmars-d
Besides, take a bit of care when formulating a string for exceptions, and you
won't have any trouble grepping for it. This isn't rocket science.
The exception is generated inside library code, not in user code. There's
void main() {
import std.stdio;
auto file_name = "some_file;
// some code here
auto file_name1 = file_name ~ "1.txt";
auto f1 = File(file_name1);
// some code here
auto file_name2 = file_name ~ "2.txt";
auto f2 = File(file_name2, "w");
// some code here
}
Come on. What is hard about "Cannot open file `xxxx` in mode `xxxx`", then you
grep for "Cannot open file" ?
grep "Cannot open file" *.d
stdio.d: text("Cannot open file `", name, "' in mode `",
stdio.d:// new StdioException("Cannot open file `"~fName~"' for reading"));
stdio.d:// new StdioException("Cannot open file `"~fName~"' for reading"));

Besides, File doesn't throw a FileException and doesn't add caller's __FILE__
and __LINE__.

If you're seriously proposing adding __FILE__ and __LINE__ to every function
call, then D is the wrong language. This would have disastrous bloat and
performance problems. You mentioned Python and Ruby, both of which are well
known for miserable performance.

Building the equivalent of a symbolic debugger into RELEASED D code is just not
acceptable.
Post by Walter Bright via Digitalmars-d
Presenting internal debugging data to users for input/environmental errors is
just bad programming practice.7 We shouldn't be enshrining it in Phobos and
presenting it as a professional way to code.
The file/line of code is not meant to replace serious practices for dealing with
I/O failures in serious D programs.
That's exactly what you're doing with it.
Walter Bright via Digitalmars-d
2014-09-28 00:40:22 UTC
Permalink
Post by H. S. Teoh via Digitalmars-d
My take on this, is that uncaught exceptions are a program bug.
Not to me. Custom messages would be better, but the exception message should be
serviceable.
Post by H. S. Teoh via Digitalmars-d
Uncaught exceptions (which ideally should only be Errors, not
Exceptions) are a program bug that ought to be fixed. In the case that
somehow one managed to elude your catch blocks, the full debug infodump
(source file, line number, stack trace) is useful for users to hand back
to you in a bug report, so that you can track down the problem. The user
should not be expected to understand the infodump from an uncaught
exception, whereas a message printed from a catch block ought to be
user-understandable (like "can't open 'myphoto.jpg': file not found",
not "internal error on line 12345" which makes no sense to a user).
Whoa, Camel! You're again thinking of Exceptions as a debugging tool.
Timon Gehr via Digitalmars-d
2014-09-28 00:54:34 UTC
Permalink
Post by H. S. Teoh via Digitalmars-d
My take on this, is that uncaught exceptions are a program bug.
Not to me. ...
It is not worth fixing if a program terminates with a stack trace?
Walter Bright via Digitalmars-d
2014-09-28 01:16:50 UTC
Permalink
Post by Timon Gehr via Digitalmars-d
Post by H. S. Teoh via Digitalmars-d
My take on this, is that uncaught exceptions are a program bug.
Not to me. ...
It is not worth fixing if a program terminates with a stack trace?
I never was in favor of adding the stack trace output, either, for the same
reason. Exceptions are not programming bugs.

For Errors, a case can be made for them.
Sean Kelly via Digitalmars-d
2014-09-28 16:16:07 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Whoa, Camel! You're again thinking of Exceptions as a debugging tool.
They can be. What if an API you're using throws an exception you
didn't expect, and therefore don't handle? This might be
considered a logic error if the exception is recoverable and you
don't intend the program to abort from that operation.
Sean Kelly via Digitalmars-d
2014-09-28 16:23:40 UTC
Permalink
On Sunday, 28 September 2014 at 00:40:26 UTC, Walter Bright
Post by Walter Bright via Digitalmars-d
Whoa, Camel! You're again thinking of Exceptions as a
debugging tool.
They can be. What if an API you're using throws an exception
you didn't expect, and therefore don't handle? This might be
considered a logic error if the exception is recoverable and
you don't intend the program to abort from that operation.
Also, I think the idea that a program is created and shipped to
an end user is overly simplistic. In the server/cloud
programming world, when an error occurs, the client who submitted
the request will get a response appropriate for them and the
system will also generate log information intended for people
working on the system. So things like stack traces and assertion
failure information is useful even for production software. Same
with any critical system, as I'm sure you're aware. The systems
are designed to handle failures in specific ways, but they also
have to leave a breadcrumb trail so the underlying problem can be
diagnosed and fixed. Internal testing is never perfect, and
achieving a high coverage percentage is nearly impossible if the
system wasn't designed from the ground up to be testable in such
a way (mock frameworks and such).
Walter Bright via Digitalmars-d
2014-09-28 17:33:35 UTC
Permalink
Also, I think the idea that a program is created and shipped to an end user is
overly simplistic. In the server/cloud programming world, when an error occurs,
the client who submitted the request will get a response appropriate for them
and the system will also generate log information intended for people working on
the system. So things like stack traces and assertion failure information is
useful even for production software. Same with any critical system, as I'm sure
you're aware. The systems are designed to handle failures in specific ways, but
they also have to leave a breadcrumb trail so the underlying problem can be
diagnosed and fixed. Internal testing is never perfect, and achieving a high
coverage percentage is nearly impossible if the system wasn't designed from the
ground up to be testable in such a way (mock frameworks and such).
Then use assert(). That's just what it's for.
Sean Kelly via Digitalmars-d
2014-09-28 19:33:35 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Post by Sean Kelly via Digitalmars-d
Also, I think the idea that a program is created and shipped
to an end user is overly simplistic. In the server/cloud
programming world, when an error occurs, the client who
submitted the request will get a response appropriate for them
and the system will also generate log information intended for
people working on the system. So things like stack traces and
assertion failure information is useful even for production
software. Same with any critical system, as I'm sure you're
aware. The systems are designed to handle failures in
specific ways, but they also have to leave a breadcrumb trail
so the underlying problem can be diagnosed and fixed.
Internal testing is never perfect, and achieving a high
coverage percentage is nearly impossible if the system wasn't
designed from the ground up to be testable in such a way (mock
frameworks and such).
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?
Walter Bright via Digitalmars-d
2014-09-28 20:13:49 UTC
Permalink
Post by Walter Bright via Digitalmars-d
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 :-)
Walter Bright via Digitalmars-d
2014-09-28 17:32:14 UTC
Permalink
Post by Sean Kelly via Digitalmars-d
Post by Walter Bright via Digitalmars-d
Whoa, Camel! You're again thinking of Exceptions as a debugging tool.
They can be.
Of course they can be. But it's inappropriate to use them that way, and we
should not be eschewing such in the library.
Post by Sean Kelly via Digitalmars-d
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.
Post by Sean Kelly via Digitalmars-d
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.
H. S. Teoh via Digitalmars-d
2014-09-28 20:56:27 UTC
Permalink
[...]
Post by Walter Bright via Digitalmars-d
Post by Sean Kelly via Digitalmars-d
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*.)
Post by Walter Bright via Digitalmars-d
Post by Sean Kelly via Digitalmars-d
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
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
Sean Kelly via Digitalmars-d
2014-09-28 21:16:57 UTC
Permalink
On Sunday, 28 September 2014 at 20:58:20 UTC, H. S. Teoh via
Post by H. S. Teoh via Digitalmars-d
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.
Cliff via Digitalmars-d
2014-09-28 21:22:55 UTC
Permalink
On Sunday, 28 September 2014 at 20:58:20 UTC, H. S. Teoh via
Post by H. S. Teoh via Digitalmars-d
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
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.

Steven Schveighoffer via Digitalmars-d
2014-09-28 01:24:00 UTC
Permalink
Post by Walter Bright via Digitalmars-d
When I say "They are NOT for debugging programs", I mean they are NOT
for debugging programs.
Library code often cannot make that choice. The issue with exceptions
vs. errors is that often you don't know where the input comes from.

e.g.:

auto f = File(someInternalStringThatIsCorrupted) -> error
auto f = File(argv[1]) -> exception

How does File know what it's target file name came from?

-Steve
Walter Bright via Digitalmars-d
2014-09-28 01:52:11 UTC
Permalink
Post by Steven Schveighoffer via Digitalmars-d
Post by Walter Bright via Digitalmars-d
When I say "They are NOT for debugging programs", I mean they are NOT
for debugging programs.
Library code often cannot make that choice.
The issue with exceptions vs. errors
is that often you don't know where the input comes from.
auto f = File(someInternalStringThatIsCorrupted) -> error
auto f = File(argv[1]) -> exception
How does File know what it's target file name came from?
If the app is concerned about invalid filenames as bugs, you should scrub the
filenames first. I.e. the interface is defined improperly if the code confuses a
programming bug with input errors.
Jacob Carlborg via Digitalmars-d
2014-09-28 09:01:13 UTC
Permalink
Post by Steven Schveighoffer via Digitalmars-d
Library code often cannot make that choice. The issue with exceptions
vs. errors is that often you don't know where the input comes from.
auto f = File(someInternalStringThatIsCorrupted) -> error
auto f = File(argv[1]) -> exception
How does File know what it's target file name came from?
Both of theses should throw an exception. Most stuff related to file
operations should throw an exception, not an error.
--
/Jacob Carlborg
ketmar via Digitalmars-d
2014-09-28 01:30:58 UTC
Permalink
On Sat, 27 Sep 2014 16:15:40 -0700
After all, what would you think of a compiler that spewed out
dmd test.d
test.d(15) Error: missing } thrown from dmd/src/parse.c(283)
?
"wow, that's cool! one more pleasing feature!"
-------------- next part --------------
A non-text attachment was scrubbed...
Name: signature.asc
Type: application/pgp-signature
Size: 181 bytes
Desc: not available
URL: <http://lists.puremagic.com/pipermail/digitalmars-d/attachments/20140928/e12d80ff/attachment.sig>
luka8088 via Digitalmars-d
2014-09-28 09:20:25 UTC
Permalink
Post by Walter Bright via Digitalmars-d
https://www.youtube.com/watch?v=hBhlQgvHmQ0
In that vein, Exceptions are for either being able to recover from
input/environmental errors, or report them to the user of the application.
When I say "They are NOT for debugging programs", I mean they are NOT
for debugging programs.
assert()s and contracts are for debugging programs.
dmd test.d
test.d(15) Error: missing } thrown from dmd/src/parse.c(283)
?
https://issues.dlang.org/show_bug.cgi?id=13543
As for the programmer wanting to know where the message "missing }" came from,
li
grep -r dmd/src/*.c "missing }"
works nicely. I do that sort of thing all the time. It really isn't a problem.
We has this issue at work (we are working with php). We outputted a
stack trace for both exceptions and asserts but the lines that should be
addressed are not always so obvious.

I found a solution and it works great for us. All library code is marked
appropriately so when stack is outputted it is shadows out (with gray
color) all the lines in library code and point out first non-library
line from the top of the stack. In 95% of the time it is the line that
the programmer should look into. Other 5% is the time when it shows the
line where programmer is forwarding a call to the library but turns out
to be ok as it turns out to be much more comprehensible than the entire
stack. One note worth mentioning is that juniors have much easier time
understanding which lines concern them, and from that I can only
conclude that such approach is more intuitive.

Marking is done on namespace level so it can be easily disabled for
entire namespace.

I think outputting a stack trace for asserts is a must because of that
5%. And for exceptions I agree completely with your arguments and I
think that there is no need for stack.

From my experience this has been a good approach and I think is worth
considering.
bearophile via Digitalmars-d
2014-09-28 09:28:13 UTC
Permalink
All library code is marked appropriately so when stack is
outputted it is shadows out (with gray color) all the lines in
library code and point out first non-library line from the top
of the stack. In 95% of the time it is the line that the
programmer should look into. Other 5% is the time when it shows
the line where programmer is forwarding a call to the library
but turns out to be ok as it turns out to be much more
comprehensible than the entire stack. One note worth mentioning
is that juniors have much easier time understanding which lines
concern them, and from that I can only conclude that such
approach is more intuitive.
This looks like a little enhancement request, to colourize
differently the parts of the stack trace of the library
code/runtime from the user code.
And for exceptions I agree completely with your arguments and I
think that there is no need for stack.
I think Walter is not suggesting to remove the stack trace for
exceptions.

Bye,
bearophile
Walter Bright via Digitalmars-d
2014-09-28 17:36:12 UTC
Permalink
And for exceptions I agree completely with your arguments and I think that
there is no need for stack.
I think Walter is not suggesting to remove the stack trace for exceptions.
I suggest removal of stack trace for exceptions, but leaving them in for asserts.

Asserts are a deliberately designed debugging tool. Exceptions are not.
Andrei Alexandrescu via Digitalmars-d
2014-09-28 17:49:10 UTC
Permalink
Post by Walter Bright via Digitalmars-d
And for exceptions I agree completely with your arguments and I think that
there is no need for stack.
I think Walter is not suggesting to remove the stack trace for exceptions.
I suggest removal of stack trace for exceptions, but leaving them in for asserts.
Asserts are a deliberately designed debugging tool. Exceptions are not.
I'm fine with that philosophy, with the note that it's customary
nowadays to inflict things like the stack trace on the user. -- Andrei
bearophile via Digitalmars-d
2014-09-28 18:25:25 UTC
Permalink
Post by Walter Bright via Digitalmars-d
I suggest removal of stack trace for exceptions, but leaving
them in for asserts.
I suggest to keep stack trace for both cases, and improve it with
colors :-) Another possibility is to keep the stack trace for
exceptions in nonrelease mode only.
Post by Walter Bright via Digitalmars-d
Asserts are a deliberately designed debugging tool. Exceptions
are not.
Exceptions are often used to help debugging... We have even
allowed exceptions inside D contracts (but I don't know why).

Bye,
bearophile
Walter Bright via Digitalmars-d
2014-09-28 19:32:03 UTC
Permalink
Post by bearophile via Digitalmars-d
Exceptions are often used to help debugging...

luka8088 via Digitalmars-d
2014-09-28 20:02:35 UTC
Permalink
Post by Walter Bright via Digitalmars-d
Post by bearophile via Digitalmars-d
Exceptions are often used to help debugging...
http://youtu.be/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?
Jacob Carlborg via Digitalmars-d
2014-09-28 19:33:33 UTC
Permalink
Post by Walter Bright via Digitalmars-d
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.
--
/Jacob Carlborg
Walter Bright via Digitalmars-d
2014-09-28 20:14:43 UTC
Permalink
Post by Walter Bright via Digitalmars-d
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.
Sean Kelly via Digitalmars-d
2014-09-28 19:34:27 UTC
Permalink
Post by Walter Bright via Digitalmars-d
And for exceptions I agree completely with your arguments and I think that
there is no need for stack.
I think Walter is not suggesting to remove the stack trace for exceptions.
I suggest removal of stack trace for exceptions, but leaving
them in for asserts.
Asserts are a deliberately designed debugging tool. Exceptions
are not.
Fair. So we generate traces for Errors but not Exceptions.
Ary Borenszweig via Digitalmars-d
2014-09-28 15:10:24 UTC
Permalink
Post by Walter Bright via Digitalmars-d
https://www.youtube.com/watch?v=hBhlQgvHmQ0
In that vein, Exceptions are for either being able to recover from
input/environmental errors, or report them to the user of the application.
When I say "They are NOT for debugging programs", I mean they are NOT
for debugging programs.
assert()s and contracts are for debugging programs.
For me, assert is useless.

We are developing a language using LLVM as our backend. If you give LLVM
something it doesn't like, you get something this:

~~~
Assertion failed: (S1->getType() == S2->getType() && "Cannot create
binary operator with two operands of differing type!"), function Create,
file Instructions.cpp, line 1850.

Abort trap: 6
~~~

That is what the user gets when there is a bug in the compiler, at least
when we are generating invalid LLVM code. And that's one of the good
paths, if you compiled LLVM with assertions, because otherwise I guess
it's undefined behaviour.

What I'd like to do, as a compiler, is to catch those errors and tell
the user: "You've found a bug in the app, could you please report it in
this URL? Thank you.". We can't: the assert is there and we can't change it.

Now, this is when you interface with C++/C code. But inside our language
code we always use exceptions so that programmers can choose what to do
in case of an error. With assert you loose that possibility.

Raising an exception is costly, but that should happen in exceptional
cases. Installing an exception handler is cost-free, so I don't see why
there is a need for a less powerful construct like assert.
Xiao Xie via Digitalmars-d
2014-09-28 16:25:41 UTC
Permalink
On Sunday, 28 September 2014 at 15:10:26 UTC, Ary Borenszweig
Post by Ary Borenszweig via Digitalmars-d
What I'd like to do, as a compiler, is to catch those errors
and tell the user: "You've found a bug in the app, could you
please report it in this URL? Thank you.". We can't: the assert
is there and we can't change it.
Why is SIGABRT handler not working for your usecase? print and
exit?
Walter Bright via Digitalmars-d
2014-09-28 17:40:47 UTC
Permalink
Post by Ary Borenszweig via Digitalmars-d
For me, assert is useless.
We are developing a language using LLVM as our backend. If you give LLVM
~~~
Assertion failed: (S1->getType() == S2->getType() && "Cannot create binary
operator with two operands of differing type!"), function Create, file
Instructions.cpp, line 1850.
Abort trap: 6
~~~
That is what the user gets when there is a bug in the compiler, at least when we
are generating invalid LLVM code. And that's one of the good paths, if you
compiled LLVM with assertions, because otherwise I guess it's undefined behaviour.
"You've found a bug in the app, could you please report it in this URL? Thank
you.". We can't: the assert is there and we can't change it.
You can hook D's assert and do what you want with it.
Post by Ary Borenszweig via Digitalmars-d
Now, this is when you interface with C++/C code. But inside our language code we
always use exceptions so that programmers can choose what to do in case of an
error. With assert you loose that possibility.
If you want to use Exceptions for debugging in your code, I won't try and stop
you. But using them for debugging in official Phobos I strongly object to.
Post by Ary Borenszweig via Digitalmars-d
Installing an exception handler is cost-free,
Take a look at the assembler dump from std.file.copy() that I posted in the
other thread.
Post by Ary Borenszweig via Digitalmars-d
so I don't see why there is a need
for a less powerful construct like assert.
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.
Sean Kelly via Digitalmars-d
2014-09-28 19:38:09 UTC
Permalink
Post by Walter Bright via Digitalmars-d
You can hook D's assert and do what you want with it.
With the caveat that you must finish by either exiting the app or
throwing an exception, since the compiler doesn't generate a
stack frame that can be returned from.
Post by Walter Bright via Digitalmars-d
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.
Walter Bright via Digitalmars-d
2014-09-28 20:31:01 UTC
Permalink
Post by Walter Bright via Digitalmars-d
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".
Sean Kelly via Digitalmars-d
2014-09-28 20:50:29 UTC
Permalink
Post by Walter Bright via Digitalmars-d
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.
Dmitry Olshansky via Digitalmars-d
2014-09-28 21:16:20 UTC
Permalink
Post by Walter Bright via Digitalmars-d
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
Sean Kelly via Digitalmars-d
2014-09-28 21:21:25 UTC
Permalink
On Sunday, 28 September 2014 at 21:16:51 UTC, Dmitry Olshansky
Post by Dmitry Olshansky via Digitalmars-d
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.
Loading...