views:

39

answers:

2

Is there a way to drop into the debugger when any panic occurs like if there were a breakpoint?

I'm using Carbide.c++ 2.3.0. I know about the Debug Configurations > x86 Exceptions, but it covers only a small fraction of what can actually happen in a real application. For instance, it does not trap user panics, or ALLOC panics when application exits with memory leaks.

+1  A: 

To the best of my knowledge it can't be done.

What I've done is use simple function tracing logic so when a panic happens I have a stack trace at the point of the panic in my the panic handling code (which I log out). This works well except for the fact that you have to remember to add your macro's at the beginning of every function.

e.g.

#ifndef NDEBUG
class __FTrace
{
    __FTrace(const char* function)
    {
        TraceManager::GetInstance().EnterFunction(function);
    }
    ~__FTrace()
    {
        TraceManager::GetInstance().LeaveFunction(function);
    }
};    

#define FTRACE() __FTrace(__PRETTY_FUNCTION__)
#else
#define FTRACE()
#endif

void Func()
{
    FTRACE();
    ...
}

For ALLOC's, I've had a lot of success with the Hook Logger under the emulator. It's a real pain to setup and use but it will make it real easy to track down ALLOC memory leaks.

UPDATE: As requested, here is what my panic handling code looks like. Note that my application has to run in the background all the time, so it's setup to restart the app when something bad happens. Also this code works for 3rd Edition SDK's, I haven't tried it on later versions of the SDK's.

The point is to run the main application in another thread and then wait for it to exit. Then check to see why the thread exits, it the thread as exited for unknown reasons, log stuff like my own stack trace and restart the application.

TInt StartMainThread(TAny*)
    {
    FTRACE();
    __LOGSTR_TOFILE("Main Thread Start");
    TInt result(KErrNone);

    TRAPD(err, result = EikStart::RunApplication(NewApplication));

    if(KErrNone != err || KErrNone != result )
        {
        __LOGSTR_TOFILE("EikStart::RunApplication error: trap(%d), %d", err, result);
        }

    __LOGSTR_TOFILE("Main Thread End");
    return result;
    }

const TInt KMainThreadToLiveInSeconds = 10;

} // namespace *unnamed*

LOCAL_C CApaApplication* NewApplication()
    {
    FTRACE();
    return new CMainApplication;
    }


GLDEF_C TInt E32Main()
    {
#ifdef NDEBUG
    __LOGSTR_TOFILE("Application Start (release)");
#else   
    __LOGSTR_TOFILE("Application Start (debug)");
#endif  

#ifndef NO_TRACING
    __TraceManager::NewL();
#endif // !NO_TRACING

    RHeap& heap(User::Heap());
    TInt heapsize=heap.MaxLength();

    TInt exitReason(KErrNone);

    TTime timeToLive;
    timeToLive.UniversalTime();
    timeToLive += TTimeIntervalSeconds(KMainThreadToLiveInSeconds);

    LManagedHandle<RThread> mainThread;
    TInt err = mainThread->Create(_L("Main Thread"), StartMainThread, KDefaultStackSize, KMinHeapSize, heapsize, NULL);
    if (KErrNone != err) 
        {
        __LOGSTR_TOFILE("MainThread failed : %d", err);
        return err;
        }

    mainThread->SetPriority(EPriorityNormal);
    TRequestStatus status; 
    mainThread->Logon(status);

    mainThread->Resume();

    User::WaitForRequest(status);

    exitReason = mainThread->ExitReason();
TExitCategoryName category(mainThread->ExitCategory());

switch(mainThread->ExitType())
    {
    case EExitKill:
        __LOGSTR_TOFILE("ExitKill : (%S) : %d", &category, exitReason);
        break;

    case EExitTerminate:
        __LOGSTR_TOFILE("ExitTerminate : (%S) : %d", &category, exitReason);
        break;

    case EExitPanic:
        __LOGSTR_TOFILE("ExitPanic : (%S) : %d", &category, exitReason);
        break;

    default:
        __LOGSTR_TOFILE("ExitUnknown : (%S) : %d", &category, exitReason);
        break;
    }

#ifndef NO_TRACING
    __TraceManager::GetInstance().LogStackTrace();
#endif // NO_TRACING


    if( KErrNone != status.Int() )
        {
        TTime now;
        now.UniversalTime();

        if (timeToLive > now)
            {
            TTimeIntervalMicroSeconds diff = timeToLive.MicroSecondsFrom(now);
            __LOGSTR_TOFILE("Exiting due to TTL : (%Lu)", diff.Int64());
            }
        else
            {
            RProcess current;
            RProcess restart;
            err = restart.Create(current.FileName(), _L(""));
            if( KErrNone == err )
                {
                __LOGSTR_TOFILE("Restarting...");
                restart.Resume();
                return KErrNone;
                }
            else
                {
                __LOGSTR_TOFILE("Failed to start app: %d", err);
                }
            }
        }

    __LOGSTR_TOFILE("Application End");
    return exitReason;
    }
Shane Powell
The most interesting part here is your "panic handling code" which you seemingly omitted. ;) That's if it really can do something useful when any panic in your code occurs.And thanks for the Hook Logger pointer. This might come in really handy.
SnakE
There is nothing special about the panic handling code. I'll update the answer with my panic handling code.
Shane Powell
Thank you for the update. The separate thread trick is very nice. I think your solution is indispensable in production environment where you want to insure against tough bugs.
SnakE
+1  A: 

If you are using the emulator, you can debug panics by enabling 'just-in-time debugging. This is done by adding the following line to epoc32\data\epoc.ini:

JustInTime debug

For more details, see the epoc.ini reference in the SDK documentation.

Gareth Stockwell
Amazing, it really works!I keep wondering how obscure Symbian tools and documentation are. It's such a vital task to determine where the panic happens, but nobody knows how to do it! Even now when you pointed me at the docs, there is really no indication there that the JustInTime option has to do with panic trapping.Thank you a lot!
SnakE
@SnakE: true, the docs for that epoc.ini keyword aren't very clear. Those for the user-library function which actually gets called as a result of `JustInTime debug`, `User::SetJustInTime(TBool)` are a bit better.
Gareth Stockwell