View RSS Feed

Development Team Blog

How to get detail information from the callstack

Rate this Entry
Recently I received a support call from a developer who was very pleased with the fact that the product now has a command to return the current callstack. He said however it is one string and I would like to retrieve detailed information from the callstack. I told the developer to look at string manipulation functions and that made him not so happy. The amount of work was high he thought.

Because I said it is not so difficult he challenged me to make a function that makes it easier to digest information from the callstack dump.

I started to examine the result of the command CallStackDump. The following is an example;

Code:
GET_GETCALLSTACK (8853) - OSYSTEM_LAST_COMPANY_ID (349) - at address 52034
MSG_SHOWSTACK (8854) - OSYSTEM_LAST_COMPANY_ID (349) - at address 52092
MSG_PROCESS_ACCELERATOR (455) - OSYSTEM_LAST_COMPANY_ID (349) - in native code
[start] - at address 52131
It contains a CR/LF pair for each line in the stack. This means we can iterate the callstack by searching for the CR/LF combination. Each stack line contains the message name, the line number, the object name, the object handle and the address where the instruction started. The address only when the message is not inside the virtual machine. The initiating callstack line is always "[start] - at address NNN".

Because of the structured information I created a struct with the following definition.

Code:
Struct tCallStack
    String sMessage
    UInteger uiLine
    String sObjectName
    Handle hObject
    Handle hAddress
    String sStackLine
End_Struct
In the following function an array variable of this struct is created to store the determined information. Via string manipulation functions as Pos, Left, Right information is retrieved and stored in the struct members of an array element.

Code:
Function GetCallStack Returns tCallStack[]
    String sStack sTmpStackLine sChar sAddress
    tCallStack[] CallStackInfo
    Integer iPos iElement iLength
 
    CallStackDump sStack
    While (sStack <> "")
        Move (Pos (Character (13) + Character (10), sStack)) to iPos
        If (iPos > 0) Begin
            Move (SizeOfArray (CallStackInfo)) to iElement
            Move (Left (sStack, iPos - 1)) to sTmpStackLine
            Move (Right (sStack, Length (sStack) - iPos - 1)) to sStack
            Move sTmpStackLine to CallStackInfo[iElement].sStackLine
            Move (Pos ('(', sTmpStackLine)) to iPos
            If (iPos > 0) Begin
                Move (Left (sTmpStackLine, iPos - 1)) to CallStackInfo[iElement].sMessage
                Move (Right (sTmpStackLine, Length (sTmpStackLine) - iPos)) to sTmpStackLine
                Move (Pos (')', sTmpStackLine)) to iPos
                If (iPos > 0) Begin
                    Move (Left (sTmpStackLine, iPos - 1)) to CallStackInfo[iElement].uiLine
                    Move (Right (sTmpStackLine, Length (sTmpStackLine) - iPos)) to sTmpStackLine
                    Move (Replace (' - ', sTmpStackLine, '')) to sTmpStackLine
                    Move (Pos ('(', sTmpStackLine)) to iPos
                    If (iPos > 0) Begin
                        Move (Left (sTmpStackLine, iPos - 1)) to CallStackInfo[iElement].sObjectName
                        Move (Right (sTmpStackLine, Length (sTmpStackLine) - iPos)) to sTmpStackLine
                        Move (Pos (')', sTmpStackLine)) to iPos
                        If (iPos > 0) Begin
                            Move (Left (sTmpStackLine, iPos - 1)) to CallStackInfo[iElement].hObject
                            Move (Right (sTmpStackLine, Length (sTmpStackLine) - iPos)) to sTmpStackLine
                            Move (Replace (' - ', sTmpStackLine, '')) to sTmpStackLine
                            Move (Length (sTmpStackLine)) to iLength
                            Move '' to sAddress
                            For iPos from 1 to iLength
                                Move (Mid (sTmpStackLine, 1, iPos)) to sChar
                                If ('0123456789' contains sChar) Begin
                                    Move (sAddress - sChar) to sAddress
                                End
                            Loop
                            Move sAddress to CallStackInfo[iElement].hAddress
                        End
                    End
                End
            End
        End
        Else Begin
            Move sStack to CallStackInfo[SizeOfArray (CallStackInfo)].sStackLine
            Move "" to sStack
        End
    Loop
 
    Function_Return CallStackInfo
End_Function
Now we have two ways to use the call stack in a Visual DataFlex program, a string based dump and an array.

Comments

  1. seanyboy's Avatar
    Nice work, but is anyone else frustrated at how many lines of code this took, and how complex it is.
    I'd love to see some work put into the VDF string routines. Especially given that the Web Stuff is becoming more important.

    e.g.
    Code:
    move 0 to iTmp
    delimiters = {") - "," ("}
    Lines = (split(sStack,sCRLF))
    for each sTmp in Lines
        LineParts = (split(sTmp,delimiters))
    
        move LineParts[0] to CallStackInfo[iTmp].msgName
        move LineParts[1] to CallStackInfo[iTmp].LineNo
        move LineParts[2] to CallStackInfo[iTmp].objName
        move LineParts[3] to CallStackInfo[iTmp].objNumber
    
        move (iTmp+1) to iTmp
    loop
  2. Focus's Avatar
    Agreed