With the introduction of TraceObject it becomes possible to create and store trace logs for further analysis. In March, I started to create a little tracetool.rex/tracutil.cls to help the Rexx programmers analyze their code. Testing and analyzing what can be inferred, it turned out that in the case of a started message and a reply statement, no information about the caller/invoker is currently available, making it very difficult, if not impossible, at times to find out which instruction caused the creation of a new activity.

In the discussions on the developer list, it became clear that storing a StackFrame object itself may cause crashes if the activity for which it was created has gone away if I understood the problem correctly. Therefore, TraceObject would not store the StackFrame object but rather create a StringTable receiving the relevant StackFrame information such that accessing the StringTable later would not cause any problems.

This information gets only added to TraceObject if a TRACE_PREFIX_INVOCATION is active (tracing the invocation of a routine or  method). Looking around in the source code for ways to safely make the caller's stackframe information available in the case that a new activity gets spawned as a result of a start message or a reply keyword statement, the following idea would be proposed, hoping that it is safe and does not impact the interpreter unduly:

 *

   define a method Activity::generateCallerStackFrame(bool skipFirst)

     o

       this returns the caller's StackFrame

 *

   define for Activity a field "StringTable *spawnerStackFrameInfo"

     o

       this is used to store a StringTable with the caller's StackFrame 
information

 *

   define a method Activity::setCallerStackFrameAsStringTable(Activity 
*oldActivity, Activity
   *newActivity, bool skipFirst)

     o

       o    this method gets only invoked in "Method::start()" and in the reply 
section of
       "RexxActivation::run(...)", which are the two cases where a new activity 
gets spawned to
       execute the message in the case of "Method:start()" and the remaining 
instructions in the
       case of a reply keyword instruction

     o

       this method queries the caller's stackframe using
       Activity::generateCallerStackFrame(skipFirst) and using
       "RexxActivation::getStackFrameAsStringTable(StackFrameClass * 
stackFrame)" returning a
       StringTable with the stackframe information and storing it with 
"spawnerStackFrameInfo" field

     o

       in addition, it will get an entry THREAD with the thread ID added, such 
that a) the correct
       invocation can be located (the same location could be used over and over 
from different
       threads; it is important to become able to identify which 
activity/thread was used) and b)
       one can distinguish with the callerStackFrame in TraceObject whether it 
got created

An experimental implementation seems to work, and so far, no adverse behavior 
has been observed.

Here the diff:

   *Index: interpreter/classes/MessageClass.cpp*
   ===================================================================
   --- interpreter/classes/MessageClass.cpp     (revision 12859)
   +++ interpreter/classes/MessageClass.cpp     (working copy)
   @@ -537,6 +537,10 @@
         // spawn a new activity off of the old activity
         Activity *oldActivity = ActivityManager::currentActivity;
         Activity *newActivity = oldActivity->spawnReply();
   + + // save current frame info in a StringTable as it has caused the 
creation of a new activity
   + Activity::setCallerStackFrameAsStringTable(oldActivity, newActivity, 
true); // create
   stackframe from parent/caller +
         // mark which activity we're running on, then dispatch the message
         // on the new activity (which is sitting waiting for work to perform)
         setField(startActivity, newActivity);
   *Index: interpreter/concurrency/Activity.cpp*
   ===================================================================
   --- interpreter/concurrency/Activity.cpp     (revision 12859)
   +++ interpreter/concurrency/Activity.cpp     (working copy)
   @@ -1159,31 +1159,54 @@
     }
+
     /**
   - * Generates a StackFrame from the parent and returns it.
   + * Generate the caller's stack frame.
      *
   - * @return parent's StackFrame or NULLOBJECT, if no parent exists
   + * @param skipFirst Determines if we should skip the first frame: + * false 
for reply in
   RexxActivation::run(...), + * true for Message::start() and in the general 
case. + * + * @return
   The stackframe of the caller which caused a spawned activity.
      */
   -StackFrameClass* Activity::generateParentStackFrame()
   +StackFrameClass* Activity::generateCallerStackFrame(bool skipFirst)
     {
   -    // create lists for both the stack frames and the traceback lines
   -    StackFrameClass *parentStackFrame = NULLOBJECT;
   -
         ActivationFrame *frame = activationFrames;
   + StackFrameClass *callerStackFrame = NULL;
- if (frame != NULL)
   + if (frame && skipFirst)
         {
   -        frame = frame->next;    // get parent
   -        if (frame != NULL)
   -        {
   -            parentStackFrame = frame->createStackFrame();
   -        }
   + frame = frame->next;
         }
   -    return parentStackFrame;
   + if (frame) + { + callerStackFrame = frame->createStackFrame(); + } + 
return callerStackFrame;
     }
/**
   + * Generates and saves a StringTable to store stackFrame infos of 
oldActivity in newActivity +
   * (for TraceObject), used by MessageClass::start(). + * + */ +void
   Activity::setCallerStackFrameAsStringTable(Activity *oldActivity, Activity 
*newActivity, bool
   skipFirst) +{ + // create lists for both the stack frames and the traceback 
lines + StringTable
   *spawnerStackFrameInfo = NULLOBJECT; + + StackFrameClass *oldActivityStackFrame 
= oldActivity ->
   generateCallerStackFrame(skipFirst); + // returns a StringTable that may be 
empty if
   oldActivityStackFrame==NULLOBJECT + newActivity ->
   
spawnerStackFrameInfo=RexxActivation::getStackFrameAsStringTable(oldActivityStackFrame);
 + //
   save thread ID to indicate from which thread ID the spawnReply() came from 
(helpful, if
   StringTable is empty) + newActivity -> spawnerStackFrameInfo -> 
put(new_integer(oldActivity ->
   getIdntfr()), GlobalNames::THREAD ); + return; +}
   +
   +
   +
   +/**
      * Build a message and perform the indicated substitutions.
      *
      * @param messageCode
   *Index: interpreter/concurrency/Activity.hpp*
   ===================================================================
   --- interpreter/concurrency/Activity.hpp     (revision 12859)
   +++ interpreter/concurrency/Activity.hpp     (working copy)
   @@ -159,8 +159,12 @@
         void        unwindToFrame(RexxActivation *frame);
         void        cleanupStackFrame(ActivationBase *poppedStackFrame);
         ArrayClass  *generateStackFrames(bool skipFirst);
   -    StackFrameClass  *generateParentStackFrame();
+ // allow TraceObject's callerStackFrame entry to indicate the caller that caused the spawned
   activity + StackFrameClass* Activity::generateCallerStackFrame(bool ); + 
static void
   Activity::setCallerStackFrameAsStringTable(Activity *oldActivity, Activity 
*newActivity, bool);
   + StringTable *spawnerStackFrameInfo;
   +
         Activity *spawnReply();
void exitKernel();
   *Index: interpreter/execution/RexxActivation.cpp*
   ===================================================================
   --- interpreter/execution/RexxActivation.cpp (revision 12859)
   +++ interpreter/execution/RexxActivation.cpp (working copy)
   @@ -702,6 +702,9 @@
activity = oldActivity->spawnReply(); + // save current frame info in a StringTable as it has caused the creation of a new activity +
   Activity::setCallerStackFrameAsStringTable(oldActivity, activity, false);
   +
                     // save the pointer to the start of our stack frame.  We're
                     // going to need to release this after we migrate 
everything
                     // over.
   @@ -5157,8 +5160,23 @@
         }
         else if (tracePrefix == TRACE_PREFIX_INVOCATION)    // tracing an 
invocation entry
         {
   -        StackFrameClass *stackFrame = activity -> 
generateParentStackFrame();
   -        traceObject -> put(stackFrame ? 
getStackFrameAsStringTable(stackFrame) : TheNilObject, 
GlobalNames::CALLERSTACKFRAME);
   + StackFrameClass *stackFrame = activity -> generateCallerStackFrame(true); 
+ if (stackFrame) +
   { + traceObject -> put(getStackFrameAsStringTable(stackFrame), 
GlobalNames::CALLERSTACKFRAME); +
   } + else + { + if (activity -> spawnerStackFrameInfo) // spawner's frame 
infos saved as a
   StringTable? + { + // allows for analyzing trace logs to identify the caller 
of a spawned
   activity + traceObject -> put(activity -> spawnerStackFrameInfo, 
GlobalNames::CALLERSTACKFRAME);
   + } + else + { + traceObject -> put(TheNilObject, 
GlobalNames::CALLERSTACKFRAME); + } + }
         }
// METHODCALL, fill in method related information
   @@ -5232,26 +5250,28 @@
     *   the identityHash of the StackFrame entries EXECUTABLE and TARGET if 
they are not
     *   .nil.
     *
   -*   @param stackFrame the StackFrame object to use
   -*   @return a StringTable with all the entries of stackFrame
   +* @param stackFrame the StackFrame object to use, may be NULLOBJECT +* 
@return a StringTable
   with all the entries of stackFrame, if available
     */
     StringTable * RexxActivation::getStackFrameAsStringTable(StackFrameClass * 
stackFrame)
     {
   -    ProtectedObject result;
         StringTable *tmpStringTable = new_string_table();
   -    // array
   -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::ARGUMENTS , 
result), GlobalNames::ARGUMENTS );
   + if (stackFrame != NULLOBJECT) + { + ProtectedObject result; + // array + 
tmpStringTable ->
put(stackFrame->sendMessage(GlobalNames::ARGUMENTS , result), GlobalNames::ARGUMENTS ); - // strings
   -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::LINE      , 
result), GlobalNames::LINE      );
   -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::NAME      , 
result), GlobalNames::NAME      );
   -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::TRACELINE , 
result), GlobalNames::TRACELINE );
   -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::TYPE      , 
result), GlobalNames::TYPE      );
   + // strings + tmpStringTable -> 
put(stackFrame->sendMessage(GlobalNames::LINE , result),
   GlobalNames::LINE ); + tmpStringTable -> 
put(stackFrame->sendMessage(GlobalNames::NAME ,
   result), GlobalNames::NAME ); + tmpStringTable ->
   put(stackFrame->sendMessage(GlobalNames::TRACELINE , result), 
GlobalNames::TRACELINE ); +
   tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::TYPE , result), 
GlobalNames::TYPE );
- // objects
   -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::EXECUTABLE, 
result), GlobalNames::EXECUTABLE);
   -    tmpStringTable -> put(stackFrame->sendMessage(GlobalNames::TARGET    , 
result), GlobalNames::TARGET    );
   -
   + // objects + tmpStringTable -> 
put(stackFrame->sendMessage(GlobalNames::EXECUTABLE, result),
   GlobalNames::EXECUTABLE); + tmpStringTable -> 
put(stackFrame->sendMessage(GlobalNames::TARGET ,
   result), GlobalNames::TARGET );
   +    }
         return tmpStringTable;
     }
To get at the patch directly, I created a RFE with the above patch at <https://sourceforge.net/p/oorexx/feature-requests/842/>.

Any comments, suggestions?

---rony

_______________________________________________
Oorexx-devel mailing list
Oorexx-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/oorexx-devel

Reply via email to