June 17, 2007
Jascha Wetzel wrote:
> Tom S wrote:
>> Uh... I just don't feel like teaching all my potential testers what a debugger is, and that they should always run the game from within the debugger, or I won't be able to help when something goes wrong :/
> 
> you don't need to. just write a batch file that runs your program in the debugger and make it the default way to start the game for the testers. you'll have
> 
> Unhandled Exception: EXCEPTION_ACCESS_VIOLATION(0xc0000005) at _Dmain debugees\debuggee.d:237 (0x00402768)
> 
> instead of
> 
> Error: Access Violation
> 
> The problem with access violations is that the CPU raises them, the OS handles them and throws the exception. In order to have the same info in those exceptions as we have in D exceptions, they need to be intercepted and decorated with filenames and source line numbers - data that is available at runtime only from debug symbols. That means, that there needs to be a runtime that can interpret debug symbols and that basically is shipping half a debugger with the executable.

Ok, point. Thanks! :) But now a tougher question.. What happens if the crash occurs inside a dynamically (DDL) loaded module? Can I somehow tell the debugger where to look for symbols? Right now I'm using the famous Phobos hack for backtraces. I modified it so it provides an interface for feeding new symbols, e.g. from DDLs.


-- 
Tomasz Stachowiak
http://h3.team0xf.com/
h3/h3r3tic on #D freenode
June 17, 2007
Tom S wrote:
> Ok, point. Thanks! :) But now a tougher question.. What happens if the crash occurs inside a dynamically (DDL) loaded module? Can I somehow tell the debugger where to look for symbols? Right now I'm using the famous Phobos hack for backtraces. I modified it so it provides an interface for feeding new symbols, e.g. from DDLs.

generally the debugger loads symbols from DLL's when they are loaded into the process' address space.
i don't know enough about DDL's loading mechanism, but i'm sure it can be handled similarly. besides that, as far as Ddbg is concerned, it doesn't support loading debug symbols from DLL's, yet, but that'll change...
June 17, 2007
The problem in question is that assert(someObject) doesn't check first that someObject is not null, so if it's null, calling it's invariant results in a segmantation fault. In code:

assert(someObject);

becomes

assert(invariantOf(someObject));

(where invariantOf is a ficticious function that is the invariant of the object)

You can avoid segmentation fault by making the compiler rewrite

assert(someObject);

to

assert(someObject !is null, "someObject is null");
assert(invariantOf(someObject));

Why not do it? In the first case you get a segfault, in the second you get a clear "Assert failed in line ..., someObject is null".

Jascha Wetzel escribió:
> Ary Manzana wrote:
>> You'll waste less time to fix the bug if you know which is the line that causes trouble, instead of doing these three steps: compiling with symbolic debug info, launching the program again and see where it crashes.
>>
>> You could even fix the bug by just looking at line 178, without debugging.
> 
> there is no way how access violations can have line numbers without adding debug symbols (read: line numbers) to the executable.
> imho, using -g should be default during development and testing.
June 17, 2007
Deewiant wrote:
> Walter Bright wrote:
>>sambeau wrote:
>>
>>>While I get your point completely I believe this style of debugging is
>>>only really useful to the hard-core professional.

Right.

>>>For the novice, where the debugger is a scary place of last resort, it
>>>would be more useful to have an assert too. All most of us want is a
>>>line number: and segfaults don't give us that.
>>
>>They do when combined with symbolic debug info - that is how the
>>debugger is able to display your source code with the highlighted line
>>where it seg faulted.
> 
> Yes, but you still need the debugger to see it. The fact remains that many
> novices and stubborn professionals don't use debuggers, no matter how useful
> they are and how many times they are praised by others. They're happy with their
> printf debugging, even though they would probably be happier using a debugger,
> at least in some cases.

Actually, the debugger and asserts (or even printf statements) all serve different purposes. The debugger is what I'd use when the situation is complex, that is, when there is much to inspect in the program state at one time. For example a tree structure, or other complex problem that I'm clueless about. (And a debugger is of course essential for the trial-and-error type of coder. :-) Sadly, that is the kind of coding that too easy access to [gui]debuggers tends to foster! :-( )

Printfs are real handy for much of the rest, since I usually have a pretty good guess of the one or two variables that might contain wrong values at some point. (Actually, even loop debuggin gets easier since I can put a few printfs inside the loop and then redirect the output to a file, so I have a hard copy of the entire situation, that I can refer to repeatedly, instead of only the momentaneous situation in the debugger. Lessing, grepping, heading or tailing the file are much faster than "creating the situation with single stepping" in the debugger.)

Asserts I mainly use in code that's ok at the time, but which might end up with wrong values when I'm debugging or developing another part of the routine or program. (Oh, and obviously and embarrassingly, I often use asserts instead of proper precondition tests.)

And, finally, asserts are, IMHO, the easiest way of ensuring I get reasonable info when the program is run by others and it misbehaves.

> In this case, either remove all asserts and force everyone to use a debugger in
> all cases, or add some asserts to make people generally happier and more
> productive when coding.

Exactly.

> Personally, I find it annoying that most crashing bugs I can catch easily with a
> normal run of the program, but when it's "Access Violation" or "Win32 Exception"
> or "Segmentation fault", I have to recompile with -g and fire up the debugger to
> find out exactly what happened and where.

Ditto.
June 17, 2007
Bill Baxter wrote:
> Georg Wrede wrote:
> 
>> Sure, we can learn to write if(o) and assert(o !is null). 
> 
>> Maybe this discussion should be deferred until nothing big is going on in D.
> 
> Aww.  But we have to keep ourselves entertained *somehow* while Walter is off implementing const.  :-)

:-)

Besides, it's not insurmountable to learn to "use this there and that otherwise".

My main problem was that we should strive to keep the language clean and consistent. And this was a prime example of frivolously destroying that for a truly third rate reason.

---

PS, if I ever start seeing code like

  if(o) assert(o);

then I will puke.
June 17, 2007
"Walter Bright" <newshound1@digitalmars.com> wrote in message news:f4s0i8$1vk8$1@digitalmars.com...
> Kristian Kilpi wrote:
>> The problem is that
>>
>>   assert(obj);
>>
>> does not first check if 'obj' is null.
>
> Yes it does, it's just that the hardware does the check, and gives you a seg fault exception if it is null.

There are two problems with this.

Firstly, this'll only work if the class actually has an invariant, and this invariant actually accesses object members (as opposed to merely checking static stuff).

Secondly, given a segfault or AV, how's one supposed to pinpoint the cause? An assert error, OTOH, has a filename and line number attached.  It's the same with the more general request of null reference checking - people want an error class that unambiguously indicates dereferencing a null pointer and which gives an immediate clue of where the problem is.

Stewart. 

June 17, 2007
But... thinking a little more, it may be the programmer's fault, actually, to think that someObject is not null. If you call assert(someObject.property) then you still get a segfault if someObject is null, and here it doesn't seam reasonable for the compiler to check if someObject is null.

Ary Manzana escribió:
> The problem in question is that assert(someObject) doesn't check first that someObject is not null, so if it's null, calling it's invariant results in a segmantation fault. In code:
> 
> assert(someObject);
> 
> becomes
> 
> assert(invariantOf(someObject));
> 
> (where invariantOf is a ficticious function that is the invariant of the object)
> 
> You can avoid segmentation fault by making the compiler rewrite
> 
> assert(someObject);
> 
> to
> 
> assert(someObject !is null, "someObject is null");
> assert(invariantOf(someObject));
> 
> Why not do it? In the first case you get a segfault, in the second you get a clear "Assert failed in line ..., someObject is null".
> 
> Jascha Wetzel escribió:
>> Ary Manzana wrote:
>>> You'll waste less time to fix the bug if you know which is the line that causes trouble, instead of doing these three steps: compiling with symbolic debug info, launching the program again and see where it crashes.
>>>
>>> You could even fix the bug by just looking at line 178, without debugging.
>>
>> there is no way how access violations can have line numbers without adding debug symbols (read: line numbers) to the executable.
>> imho, using -g should be default during development and testing.
June 17, 2007
Ary Manzana wrote:
>  If you call
> assert(someObject.property) then you still get a segfault if someObject is null, and here it doesn't seam reasonable for the compiler to check if someObject is null.

yep, the assert can wrap an expression and therefore an arbitrary amount of code. it's the same problem as with checking for AVs in the whole program in general.
June 17, 2007
Stewart Gordon wrote:
> "Walter Bright" <newshound1@digitalmars.com> wrote in message news:f4s0i8$1vk8$1@digitalmars.com...
>> Kristian Kilpi wrote:
>>> The problem is that
>>>
>>>   assert(obj);
>>>
>>> does not first check if 'obj' is null.
>>
>> Yes it does, it's just that the hardware does the check, and gives you a seg fault exception if it is null.
> 
> There are two problems with this.
> 
> Firstly, this'll only work if the class actually has an invariant, and this invariant actually accesses object members (as opposed to merely checking static stuff).

Not true. The mere act of looking up if an objects class has an invariant requires access to the .classinfo, causing a segfault. (I posted the code earlier in this thread)

> Secondly, given a segfault or AV, how's one supposed to pinpoint the cause? An assert error, OTOH, has a filename and line number attached.  It's the same with the more general request of null reference checking - people want an error class that unambiguously indicates dereferencing a null pointer and which gives an immediate clue of where the problem is.

According to Walter: compile with debug info and run in a debugger. However, I dislike this option.

The first reason I have for disliking this is that this requires the person seeing the error to have a debug build and run it in a debugger (requiring a debugger be installed). I can see this being troublesome in situations where the person experiencing the error isn't the developer (or even a tester). I'd say segfaults in deployed applications would be the worst kind, yet those are least likely to be run as a debug build in a debugger...

Secondly, what about applications where the segfault doesn't occur deterministically? (e.g. a multithreaded application)
Or if the exact situation is just hard to repeat? (e.g. interaction with other systems like databases whose state changes between runs[1])

I'd very much prefer it if the first time something like this went wrong the application would display as much information as possible without requiring debug info + debugger and without requiring the fault be duplicated in a new run of the application.


[1]: And might be hard to roll back to the exact state that caused the error, for instance because they have multiple clients (that must perhaps continue to run during fault isolation?) and/or are multithreaded themselves.
June 19, 2007
Walter Bright wrote:
> Georg Wrede wrote:
>> Walter Bright wrote:
>>> Kristian Kilpi wrote:
>>>
>>>> The problem is that
>>>>
>>>>   assert(obj);
>>>>
>>>> does not first check if 'obj' is null.
>>>
>>>
>>> Yes it does, it's just that the hardware does the check, and gives you a  seg fault exception if it is null.
>>
>> Asserts were INVENTED to *avoid segfaults*.
> 
> I don't know when assert first appeared. But I first encountered them in the 80's, when the most popular machine for programming was the x86. The x86 had no hardware protection. When you wrote through a NULL pointer, you scrambled the operating systems, and all kinds of terrible, unpredictable things ensued. Asserts were used a lot to try and head off these problems.
> 
> Enter the 286. What a godsend it was to develop in protected mode, when if you accessed a NULL pointer you got a seg fault instead of a scrambled system. Nirvana! What was even better, was the debugger would pop you right to where the problem was. It's not only asserts done in hardware, it's asserts with:
> 
> 1) zero code size cost
> 2) zero runtime cost
> 3) they're there for every pointer dereference
> 4) they work with the debugger to let you know exactly where the problem is
> 
> Seg faults are not an evil thing, they're there to help you. In fact, I'll often *deliberately* code them in so the debugger will pop up when it hits them.

True, but forgetting to 'new' a class is an extremely common mistake. The first time I ever used classes in D, I didn't 'new' it (I bet this will happen to almost everyone from a C++ background!). Getting an AV with no line number is pretty off-putting. This remains the #1 situation where I use a debugger. And I hate using debuggers to find silly typos. Getting an assert failure with a line number would be enormously more productive.

BTW, the same 'it segfaults anyway' argument could be used to some extent for array bounds checking.