Symbian Panics Explained
Developers often refer to code that "crashes" or "dies", when they mean that their code has suffered a panic. Panics are something that every Symbian C++ developer is familiar with, but they can cause a few questions when you first get started.
In this article, we've decided to go back to basics and explain what a panic is, what they do, how to get the most information about them to diagnose what's causing them, and when to use them in your code.
Note: If you've come to this article because you have suffered sudden 'code death' and want to find out quickly how to get more information about the cause, first read "What Does a Panic Look Like?" section below and then find out what panic value your code is raising. You can then cross check it against common values in the System Panic Reference or here
What is a Panic For?
On Symbian platform, a panic is used to highlight a programming error in the most noticeable way possible.
When a thread is panicked, the code in it stops running to ensure that you, the developer, are made aware of the programming error. You can't continue running code until you fix the problem and stop the panic from occurring.
What is the Effect of a Panic?
When a panic occurs in the main thread of a process, the entire process in which the thread runs will terminate.
When a panic occurs in a secondary thread, it is only that thread that closes, unless the thread has been set as process critical or process permanent, in which case the process also panics (for more information about critical threads, see the Application Reference for User::SetCritical()).
What Does a Panic Look Like?
On phone hardware a panic in an application's main thread closes the application without warning.
On the emulator, in a debug build, you can choose to break into the code to debug the cause of the panic — this is known as just-in-time debugging. By default, just-in-time debugging is enabled and a panic will stop the emulator and enter the debugger to allow you to diagnose what caused the panic. The panic function launches the debugger within the context of the function that called the panic. You can use the debugger to look through the call stack to see where the panic arose and examine the code to find the cause.
You can disable just-in-time debugging so that the emulator doesn't stop running when a panic occurs, by calling User::SetJustInTime(EFalse) in your code (before a panic occurs).
With just-in-time debugging disabled, a panic terminates the thread as usual, and if it's the main thread of an application, it closes it, displaying a message box which indicates the panic category and reason (which are discussed further in #How to Cause a Panic below).
Note: You can also use the emulator's epoc.ini setting file to enable and disable just-in-time debugging. See here for further information.
If the category and reason aren't displayed in the message box, use the file system browser of your PC to browse to the c:\resource directory of the emulator, and create an empty file in it called errrd (don't give the file a file system extension). The presence of this file causes the panic category and reason to be displayed in a message box when a panic occurs, as shown above.
The errrd file is usually present by default for the Windows emulator, but isn't usually present on phone hardware, so if you want to see more information when a panic occurs in a thread running on the phone, you need to create the file in the c:\resource directory. To do this, either use a filesystem browser that is capable of accessing that directory and allowing you to create files in it, or create your own SIS file to install the file to that location, or use one that's already available (I've put one here: File:ErrRdInstaller.SIS).
Comparing Leaves and Panics
As we've already described, a panic can't be trapped and terminates at least the thread in which it occurs (and often the process too).
A leave should always be caught ('trapped') by code somewhere in the call stack, because there should always be a top-level TRAP. However, if a leave is not caught, this implies that the top-level TRAP is absent (a programming error) and the thread will be panicked and terminate. You can find out more about leaves from: A Comparison of Leaves and Exceptions.
When to Leave? When to Panic?
Let's first make a clear distinction between programming errors ("bugs") and exceptional conditions:
Bugs may be contradictory assumptions, unexpected design errors or genuine implementation errors, such as writing off the end of an array or trying to write to a file before opening it. These are persistent, unrecoverable errors which should be detected and corrected in your code at the earliest opportunity.
Exceptional conditions are different. They may legitimately arise, although it is rare (hence the term 'exceptional') and they are not consistent with typical or expected execution. A good example of an exceptional condition that may occur on Symbian platform is an out-of-memory failure. It is not possible to stop exceptions occurring, so your code should implement a graceful recovery strategy rather than simply crash and burn.
Consider the following questions. Can a scenario arise legitimately? If it can - is there anything you should or could do to handle it?
If your answer to both is 'yes', you're looking at an exceptional condition and should flag it using a leave.
If the answer to either (or both) is 'no', you should consider the situation to be caused by a bug which must be tracked down and fixed. Use a panic (within an assertion) to flag it up.
How to Cause a Panic
A call to the static function User::Panic() panics the currently running thread. A thread may panic any thread in the same process by calling RThread::Panic(), but cannot panic threads in any other process. If a thread tries to panic a thread in another process, it will itself be panicked with KERN-EXEC 46.
Tip: There is an exception to this rule, which is where a server thread uses the RMessagePtr2 API to panic a misbehaving client thread, for example, when a client passes a badly-formed request. Rather than go ahead and attempt to read or write to a bad descriptor, the server protects itself and flags the problem by causing a panic in the client thread. Generally, a malformed client request occurs because of a programming error in client code, but this strategy also protects a server against more malicious "denial of service" attacks in which a client may deliberately pass a badly-formed or unrecognized request to a server to try to crash it.
When calling a Panic() method, you are required to pass in a descriptor to specify the 'category' of the panic and a TInt value to identify the panic (known as the panic 'reason'). You can use any length of panic category string that you wish, but it'll be truncated to 16 characters when it's reported.
The panic category and reason can be useful for tracking down the cause of a panic, for example, if you don't have access to the code for debugging. Symbian publishes a set of panic values, listed by category, in the System Panic Reference.
When using panics, it's good style to make your panic category string descriptive and unique, so other developers using your APIs can locate the string in your header files, and with it, any associated panic error codes (which should also have suitably descriptive names).
Thus, you might have a general panic header file for your library which includes the following:
ECorrupt, // =0,
ENotInitialized, // =1,
EInvalidSetting // =2
static void ExamplePanic(TExampleEnginePanic aCategory);
#endif // __EXAMPLEPANIC_H__
Which defines the ExamplePanic() function separately as follows:
static void ExamplePanic(TExampleEnginePanic aCategory)
If the library code is passed invalid arguments, it may call ExamplePanic() with the appropriate error code, resulting in a panic and termination of the thread in which it is running. The category and error will be reported, and may then be traced back by searching the library's header files for 'EXAMPLE-ENGINE', located inside ExamplePanic.h. You'll see I've commented each error's enum value with its associated number, just to make the lookup easier. I've tried to give each a descriptive name, though obviously they could be further documented, using in-source comments, for clarity.
Of course, if client programmers have access to the source code, they can also search it for calls to ExamplePanic() which use a particular error value, to track down where a panic originated.
Common Symbian platform Panic Categories
The System Panic Reference (in the Application Reference) lists common panic categories and associated error values. Typical values you may encounter include:
KERN-EXEC 3 — raised by an unhandled exception such as an access violation caused, for example, by dereferencing NULL or an invalid pointer.
TInt* myIntegerPtr; // Not initialized
*myIntegerPtr = 5; // Panics with KERN-EXEC 3
Other reasons for this panic to occur include memory misalignment or execution of an invalid instruction inside a system call to the kernel executive.
E32USER-CBASE 46 — raised by the active scheduler as a result of a stray signal
E32USER-CBASE 90 — raised by the cleanup stack when the object specified to be popped off is not the next object on the stack.
USER 11 — raised when an operation to modify a 16-bit descriptor fails because the operation would cause the descriptor's data area to exceed its maximum allocated length.
ALLOC xxxxxxxx — raised in debug builds when the heap checking macros (e.g. __UHEAP_MARK/__UHEAP_MARKEND) detect that more heap cells have been allocated than freed. See this Eliminating Memory Leaks in Symbian C++ Code for further information about the macros.
When to Cause a Panic
Panics make for a poor user experience. Think of yourself as a user - do any of us like an application to suddenly close for no apparent reason?
Panics are only useful for developers, to track down programming errors during development. You'll use them in assertion statements, but there's no other valid reason for a user to ever see a panic, so don't use them except as a development tool.
Thank you to Mark Shackman, Juuso Vuorinen andfor reviewing this article.
© 2010 Symbian Foundation Limited. This document is licensed under the Creative Commons Attribution-Share Alike 2.0 license. See http://creativecommons.org/licenses/by-sa/2.0/legalcode for the full terms of the license.
Note that this content was originally hosted on the Symbian Foundation developer wiki.