On 18.08.2024 14:44, Rick McGuire wrote:
One thing you've missed is that there are situations where there is no spawning activation. For example, from native code, I can attach a thread, create a message object and send it a START message. In that situation, there is no activation to create a stack frame from. Also, the stack frame in question might be a native activation, so you need to make sure that these function as well.

thank you very much!

Ad native code remark: changed "Activity::setCallerStackFrameAsStringTable(Activity *oldActivity, Activity *newActivity, bool skipFirst)" to cater for the case that there is no oldActivity. In that case the callerStackFrame StringTable will only contain the THREAD entry.

Ad stack frame being possibly a native activation: not sure what this implies and what should be done as a result in order that these function as well? Please advise.

---rony


On Sun, Aug 18, 2024 at 8:29 AM Rony G. Flatscher <rony.flatsc...@wu.ac.at> 
wrote:

    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/>
    <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