I'm debugging a heavily assert()'ed iPhone app (Xcode, Objective-C++, and device simulator). In some cases, the assert failure would just terminate the app, instead of breaking into the debugger as I'd expect.
I made a workaround by implementing my own kinda-assert to the effect of:
#define AssertLite(b) if(!(b)) {asm {int 3}}
(fluff omitted), but I wonder if anyone ever encountered this. I could not determine a pattern as to when does it break and when does it terminate. The code is not threaded; all it does is done in event handlers.
Why does this happen and how do I make vanilla assert() behave like a conditional breakpoint it should be?
First off, since you are working on an iPhone app, you should probably use NSAssert() instead of the vanilla BSD assert function.
e.g. NSAssert(the_object, #"NIL object encountered");
The NSAssert macro will throw an Objective-C exception (NSInternalInconsistencyException) if the assertion fails.
Since your goal is to break on the exception, the next step is to make the Xcode debugger break on Objective-C exceptions. This is probably a good thing to do anyway.
In the Breakpoints window (Run->Show->Breakpoints menu item), click where it says "Double-Click for Symbol" to enter the symbol -[NSException raise]
The last thing to be careful off is that NSAsserts do not compile out in a release build. That means that you have to either be prepared to handle the exception in your application, or you need to create your own macro that does compile out in release builds.
Here's the macro I use to compile out assertions in runtime code (note that I then use HMAssert in my code instead of NSAssert):
#ifdef DEBUG
# define HMAssert(A,B) NSAssert(A,B)
#else
# define HMAssert(A,B)
#endif
This requires a DEBUG preprocessor macro to be defined. Here's how to set that up:
Right-click on your project in Xcode. That will be the top item in the left panel where your projects files are listed
Select "Get Info" from the context menu that pops up.
Go to the "Build" tab.
Make sure the "Configuration" is set to "Debug".
Type DEBUG into the field next to "Preprocessor Macros" under "GCC 4.2 - Preprocessing".
First of all, if you "Add Exception Breakpoint..." in the Breakpoint Navigator (⌘6), the debugger will stop on NSAssert's failures, allowing you to look at the stack and understand what went wrong.
You should use the standard NSAssert. If you use it correctly, there is not a lot that you need to manually create -- everything Mike mention is similar to the default NSAssert implementation.
You should run you release configuration with NS_BLOCK_ASSERTIONS set in your precompiled headers (follow Mike's steps), to disable assertions. If you need more info on why to do so, check out: http://myok12.wordpress.com/2010/10/10/to-use-or-not-to-use-assertions/
In Xcode 4 and new iOS, NSAssert may actually take a variable list of parameters. This may be useful to log some values together with the assert. The compiling-out assert (see answer by Mike above) could be defined like this:
#ifdef DEBUG
# define DAssert(A, B, ...) NSAssert(A, B, ##__VA_ARGS__);
#else
# define DAssert(...);
#endif
Also, there is no longer Run → Show → Breakpoints menu item. See this post to set up Xcode 4 to break on an assert as defined above.
One time I saw a different behavior from the assert() calls once. It was caused by the compiler picking up different macro definitions at different portions of the build process.
Once the include paths were straightened out, they all worked the same.
Related
I made a breakpoint in Xcode with the jump commend to force passing some condition, but when it execute to line 168 it crash with message
"Thread 1: EXC_BAD_ACCESS (code=1, address=0x1)"
why did that happen?
the console logged:
warning: MoreMultitypeCollectionViewCell.swift:178 appears multiple times in this function, selecting the first location:
MoreMultitypeCollectionViewCell.(updateButtonStateCkeck in _9A12557DCAB30EEB52DC7C2EA09487CD)() -> () + 1580 at MoreMultitypeCollectionViewCell.swift:178
MoreMultitypeCollectionViewCell.(updateButtonStateCkeck in _9A12557DCAB30EEB52DC7C2EA09487CD)() -> () + 1600 at MoreMultitypeCollectionViewCell.swift:178
my questions are:
How should I type in lldb to select location?
Is there a better way to force passing into If Statement without change code and rebuild project?
sometimes when I type 'po' in lldb or click print description in variable view, it will show fail message, how is that?
1) In lldb, the equivalent command is thread jump and you can specify an address as well as a line number there.
2) thread jump or the Xcode equivalent is an inherently dangerous operation. If you jump over the initialization of some variable, you will be dealing with bad data now and will likely crash. That sort of thing you can sometimes spot by eye - though Swift is lazy about initialization so the actual initialization of a variable may not happen where you think it does in the source. There are more subtle problems as well. For instance, if you jump over some code that as a byproduct of its operation retains or releases an object, the object will end up under or over retained. The former will cause crashes, the latter memory leaks. These retains & releases are generated by the compiler, so you can't see them in your source code, though you could if you look at the disassembly of the code you are jumping over.
Without looking at the code in question, I can't tell why this particular jump caused a crash.
But you can't 100% safely skip some of the code the compiler choose to emit. Looking at the disassembly you might be able to spot either (a) a better place to stop before the jump - i.e. stop past some retain or release that is causing a problem or jump to an address in the middle of a line so you still call a retain that's needed. You'll have to figure this out by hand.
3) There's not enough info to answer this question.
BTW, your image links don't seem to resolve.
I'm trying to add a breakpoint in the line # gutter, but no breakpoint is added when I do this in the playground. Is this possible or is there another way to set breakpoints in the playground?
There's no debugger so you can't add any breakpoints.
Matt, I could not enter code in the comments so here is a better view of using a variable on a line by itself to "debug" it.
for index in 1...5 {
dosomething(foo);
foo;
}
Then you can click the eyeball on the right hand side to see a history of foo as it was modified in the loop.
If you want to pause execution of a playground to have a peek at what's going on, you can use sleep. The information you can get isn't nearly as granular as what you can get from lldb.
To do this, you'll need to add import Foundation at the top of your playground.
Then, wherever you want to pause execution, you can add this:
sleep(10) // 10 second pause...you can make the number whatever you want
I'm just getting my feet wet in Swift, but I think the playground idea is to show the changing state as if you ran in debug and recorded all the variable changes. There's no actual need for a breakpoint as you can see the state at any "point in time". I think it'll take me a while to get used to it, having used a debugger for > 30 years, but should be quite useful for small bits of isolated test code, especially while I'm learning the language.
http://dlang.org/expression.html#AssertExpression
Regarding assert(0): "The optimization and code generation phases of compilation may assume that it is unreachable code."
The same documentation claims assert(0) is a 'special case', but there are several reasons that follow.
Can the D compiler optimize based on general assert-ions made in contracts and elsewhere?
(as if I needed another reason to enjoy the in{} and out{} constructs, but it certainly would make me feel a little more giddy to know that writing them could make things go fwoosh-ier)
In theory, yes, in practice, I don't think it does, especially since the asserts are killed before even getting to the optimizer on dmd -release. I'm not sure about gdc and ldc, but I think they share this portion of the code.
The spec's special case reference btw is that assert(0) is still present, in some form, with the -release compile flag. It is translated into an illegal instruction there (asm {hlt;} - non-kernel programs on x86 aren't allowed to use that so it will segfault upon hitting it), whereas all other asserts are simply left out of the code entirely in -release mode.
GDC certainly does optimise based on asserts. The if conditions make for much better code, even causing unnecessary code to disappear. However, unfortunately at the moment the way it is implemented is that the entire assert can disappear in release build mode so then the compiler never sees the beneficial if-condition info and actually generates worse code in release than in debug mode! Ironic. I have to admit that I've only looked at this effect with if conditions in asserts in the body, I haven't checked what effect in and out blocks have. The in- and out- etc contract blocks can be turned off based on a command line switch iirc, so they are not even compiled, I think this possibly means the compiler doesn't even look at them. So this is another thing that might possibly affect code generation, I haven't looked at it. But there is a feature here that I would very much like to see, that the if condition truth values in the assert conditions (checking that there is no side-effect code in the expression for the assert cond) can always be injected into the compiler as an assumption, just as if there had been an if statement even in release mode. It would involve pretending you had just seen an if ( xxx ) but with the actual code generation for the test suppressed in release mode, and with subsequent code feeling the beneficial effects of say known truth values, value-range limits and so on.
In Eclipse, is there a way to disable a breakpoint until another breakpoint is hit first?
This is a big hack, but it is a functional workaround:
Call the 'trigger' location breakpoint 1 and the target location breakpoint 2. We want breakpoint 2 to fire if-and-only-if execution has passed breakpoint 1.
Set conditional breakpoints at each.
For breakpoint 1, set the condition as System.setProperty("breaknow", "breaknow") == "". This condition will never be true, but will set a system property which we can read at breakpoint 2.
For breakpoint 2, set the condition as System.clearProperty("breaknow") != null. This condition will trigger when the system property is set, and also clear it (so we can repeat if needed).
As I said, it's a hack, but it seems to work. I submitted an Eclipse enhancement request to implement linking or chaining breakpoints as a native feature (https://bugs.eclipse.org/bugs/show_bug.cgi?id=390590). Unfortunately, I don't have bandwidth to implement it myself, but perhaps we'll get support for a cleaner solution someday.
One caveat (which applies to all conditional breakpoints, not just to this trick): From my experience, it appears that a setting a conditional breakpoint prevents the JIT from compiling the method of interest, running it in interpreted mode instead. Or perhaps, it allows the first C1 JIT stage but prevents the second-stage C2 compiler from optimizing?
In either case, you should be aware that the method you’re debugging will run considerably slower with a conditional breakpoint in place. This isn’t usually a problem, but when debugging very tight inner loops, I’ve found it better to fall back to the (sloppy) if (x) { // Do somthing useless and set a breakpoint here} method.
No. But you can have conditional breakpoints. I guess that hitting the other breakpoint indicates some change of state.
So, Right click the breakpoint -> Breakpoint properties -> check "conditional"
If you know the condition at which the other break point will be hit, then you can add that condition to the new breakpoint.
Conditional breakpoints are one possibility, and you can also set a Hit Count so that the breakpoint is only triggered after being hit the specified number of times.
But no, there is no way to do what you are asking.
Another idea is to disable your breakpoint and enable it once the other breakpoint is hit.
I have a code in Visual Studio 2008 in C++ that works with files just by fopen and fclose.
Everything works perfect in Debug mode. and I have tested with several datasets.
But it doesn't work in release mode. It crashes all the time.
I have turned off all the optimizations, also there is no dependency to anything(in the linker), and also I have set these:
Optimization: Disabled(/Od)
Keep Unreferenced Data.
Do Not Remove Redundant
Optimize for Windows98: NO
I still keep wondering how it should not work under these circumstances.
What else should I turn off to let it work as in debug mode?
I think if it works in release mode but not in debug mode, it might be a coding fault but the other way looks weird. isn't it?
I appreciate any help.
--Nima
Debug modes often initialize heap data allocations. The program might be dependent on this behavior. Look for variables and buffers that are not getting initialized.
1) Double check any and all code that depends on preprocessor macros.
2) Use assert() for verify program state preconditions. These must not be expected to impact program flow (ie. removing the check would still allow the code to provide the same end result) because assert is a macro. Use regular run-time conditionals when an assert won't do.
3) Indeed, never leave a variable in an uninitialized state.
By far the most likely explanation is differing undefined behavior in the two modes caused by uninitialized memory. Lack of thread safety and problems with synchronization code can also exhibit this kind of behavior because of differing timing environments between debug and release, but if your program isn't multi-threaded then obviously this can't be it.
I had experienced this and in my case it was because of one of my array of struct which suppose to have only X index, but my looping which check this struct was over checking to X+1 index. Interesting is debugging mode was running fine though I was on Visual C++ 2005.
I spent a few hours by putting in printf into my coding line by line to catch the bug. Anyone has good way to debug this kind of error please let me know.