ITQuants blog

Fusion Capital Sophis/Fusion Sophis: how to get more information on access violation?

Oct 11

Written by:
10/11/2016 10:24 PM  RssIcon

I guess that everybody who uses Fusion Capital Sophis or Fusion Sophis already got the classic "Internal error on MFC message" dialog box that announciates that a crash, access violation or whatever, occured.

In most cases, unfortunately, classic logs are not sufficient to investigate why the problem occurs. The goal of this post is to propose a method that permits to dump the callstack when such an event occurs.

As the software is able to detect that a crash occurs by displaying such a message, as toolkit developers, we are able to overload this behaviour too, by adding a SEH (MS Structured Exception Handling) callback on the process. The Windows SDK permits this by using either SetUnhandledExceptionFilter or AddVectoredExceptionHandler. I recommend to use AddVectorExceptionHandler, because it can be debugged using Visual Studio. SetUnhandledExceptionFilter will work only on a batch that uses the Sophis API, and if not debugged. Launching it with Visual Studio in debug mode,  VS uses AddVectoredExceptionHandler to capture exceptions and proposes to edit the code when an exception occurs. At the same time, it disables the behaviour of the SetUnhandledExceptionFilter.

In order to detect all exceptions, the best is to build a Sophis plugin, that adds such an handler in the UNIVERSAL_MAIN entry point function as below:

UNIVERSAL_MAIN
{
    InitLinks(indirectionPtr,TOOLKIT_VERSION);
 
    AddVectoredExceptionHandler(0,ITQExceptionHandler::CatchUnhandledException);
 
    INITIALISE_SCENARIO(ITQDumpStackScenario,"ITQ Dump Stack Test")
}

The code proposed in attachment (ITQDumpStack.zip) implements such a solution. The one who wants to use it, has just to add the Sophis plugin ITQDumpStack.dll in the list of dlls to load by Sophis, in the configuration file.

Once the exception is catched, even if the exception mechanism does not give necessary the right call stack, dumping it in a text file permits to detect in most cases where the access violation occurs. That's why the proposed method CatchUnhandledException is calling CaptureStackBackTrace, which is a MS SDK method and SymFromAddr (MS DbgHelp API), which permits to dump the name of method according to the memory address stored in the callstack frames.

int ITQExceptionHandler::printStackTrace()
{
    FILE* hFile = fopen("crashStackTrace.txt","a+");
    if(hFile!=NULL)
    {
        void *stack[TRACE_MAX_STACK_FRAMES];
        HANDLE process = GetCurrentProcess();
        SymInitialize(process, NULL, TRUE);
        WORD numberOfFrames = CaptureStackBackTrace(0, TRACE_MAX_STACK_FRAMES, stack, NULL);
        SYMBOL_INFO *symbol = (SYMBOL_INFO *)malloc(sizeof(SYMBOL_INFO)+(TRACE_MAX_FUNCTION_NAME_LENGTH - 1) * sizeof(TCHAR));
        symbol->MaxNameLen = TRACE_MAX_FUNCTION_NAME_LENGTH;
        symbol->SizeOfStruct = sizeof(SYMBOL_INFO);
        DWORD displacement;
        IMAGEHLP_LINE64 *line = (IMAGEHLP_LINE64 *)malloc(sizeof(IMAGEHLP_LINE64));
        line->SizeOfStruct = sizeof(IMAGEHLP_LINE64);
        for (int i = 2; i < numberOfFrames; i++)
        {
            DWORD64 address = (DWORD64)(stack[i]);
            if (SymFromAddr(process, address, NULL, symbol)==TRUE && SymGetLineFromAddr64(process, address, &displacement, line))
            {
                fprintf(hFile,"\tat %s in %s: line: %lu: address: 0x%0X\n", symbol->Name, line->FileName, line->LineNumber, symbol->Address);
            }
            else
            {
                fprintf(hFile,"\tSymGetLineFromAddr64 returned error code %lu.\n", GetLastError());
                fprintf(hFile,"\tat %s, address 0x%0X.\n", symbol->Name, symbol->Address);
            }
        }
        fclose(hFile);
    }
    return 0;
}
 
LONG WINAPI ITQExceptionHandler::CatchUnhandledException(PEXCEPTION_POINTERS pExceptionPtrs)
{
    printStackTrace();
  // Execute default exception handler next
  return EXCEPTION_EXECUTE_HANDLER;
}

Testing it by clicking on the ITQ Dump Stack Test scenario, this will give the following dump:

SymGetLineFromAddr64 returned error code 487.
at RtlRestoreContext, address 0xBC0D5D70.
SymGetLineFromAddr64 returned error code 487.
at RtlRaiseException, address 0xBC0D38E0.
SymGetLineFromAddr64 returned error code 487.
at KiUserExceptionDispatcher, address 0xBC112550.
at ITQDumpStackScenario::TestCallStack in d:\dvpt\itquants\itqdumpstack\itqdumpstack.cpp: line: 26: address: 0xA56619E0
at ITQDumpStackScenario::Run in d:\dvpt\itquants\itqdumpstack\itqdumpstack.cpp: line: 32: address: 0xA5661A10
at sophis::scenario::CSRScenario::Simulation in d:\git_7.1.3.9\risque\vobrisk\risk\sources\cdc\csrtraitementnuit.cpp: line: 504: address: 0x8059FFF0
at InterfaceScenario::Do in d:\git_7.1.3.9\risque\vobrisk\risk\sources\foliohier\interfacescenario.cpp: line: 389: address: 0x7B27DA30
at sophis::CSRPortfolioAnalysisCommand::Launch in d:\git_7.1.3.9\risque\vobrisk\risk\sources\foliohier\csrportfolioanalysiscommand.cpp: line: 133: address: 0x7B228440
at CSWindFolio::Commande in d:\git_7.1.3.9\risque\vobrisk\risk\sources\foliohier\foliohier.cpp: line: 6069: address: 0x7B270690

Enjoy!

Tags:
Categories: Sophis, C++

Search blog