• You've discovered RedGuides 📕 an EverQuest multi-boxing community 🛡️🧙🗡️. We want you to play several EQ characters at once, come join us and say hello! 👋
  • IS THIS SITE UGLY? Change the look. To dismiss this notice, click the X --->
Resource icon

Guide How to Trace Calls in an MQ2 Macro

Software Requirements
MQ2
Server Type
🏢 Live
I've encountered situations in KA12 where I couldn't make heads or tails out of where a bug (gasp... bugs? In KA?) originates. If only there was a stack trace in MQ2...

MQ2 does not provide a property that let's you know where a call originated from. It does provide the current sub name ${Macro.CurSub}. This is only a tidbit of the picture one needs to trace calls throughout MQ2 macros. I employed an array as a simple call trace stack within my doctored up KA12, and you are free to use it. KA12 has a lot going on and as a newcomer I've endeavored to understand and extend it. Legacy software is a challenge.

The method is simple. Within each sub, do this:

Call tracing within sub:
Sub Combat

        ||| Debug call stack
        CALLINGSUB
        CALLINGINDEX
        PUSHCALL

        ...the code and debug messages...     
      
        POPCALL
      
/return

The words in ALLCAPS are defines. We set them up and plunk those into our subs and even other defines.

DEFINE:
#DEFINE CURRENTSUB "${Macro.CurSub.Arg[1,(]}"
#DEFINE CALLINGSUB "/declare CallingSub string local ${CallStack[${CallStackIndex},1]}"
#DEFINE CALLINGINDEX "/declare CallingSubIndex int local ${CallStackIndex}"
#DEFINE PUSHCALL "/call PushCallStack CURRENTSUB"
#DEFINE POPCALL "/varset CallStackIndex ${CallingSubIndex}"

You can see that at the top of the sub, we retrieve the name of the calling sub (CALLINGSUB), and it's index in the array (CALLINGINDEX), which are local variables. Then we add the current sub name to the array and increase the index (PUSHCALL) which are outer variables.

POPCALL sets the stack index to the current sub index. Place POPCALL before /return or /goto. Each /call to a sub that employs the method increases the index. The limit is set to an arbitrary 100. You don't need to POPCALL each and every time you do a return, but place POPCALL at the base of likely branch conditions so the array doesn't overflow.

Our debug messages begin as defines:

Example debug define:
#DEFINE DEBUGN "/if (${Debug}) /echo \atDEBUG-${KissRevision} \agS:CURRENTSUB L:${Macro.CurLine} C:${CallingSub} CI:${CallingSubIndex} T:${Macro.RunTime} \aw"

Notice we also need an outer variable, ${Debug}, to enable the message output. ${KissRevision} is just an internal variable which isn't important here. When we use the debug define, our sub looks like this.

With debug line:
Sub Pull

        ||| Debug call stack
        CALLINGSUB
        CALLINGINDEX
        PUSHCALL

        DEBUGN Blah blah blah message with var outputs
        ...the code...
        DEBUGN And another debug message
      
        POPCALL
      
/return

The output from each debug line will include the following, which is everything you need at a barest minimum to see what's going on:

S:CURRENTSUB L:${Macro.CurLine} C:${CallingSub} CI:${CallingSubIndex}

Coupled with MQ2Log you can now see what's going on and where calls are originating from within KA.

Here is the whole shebang if you'd like to take it further / dare to peel the onion.

Example Main with subs:
||| Call trace
#DEFINE CURRENTSUB "${Macro.CurSub.Arg[1,(]}"
#DEFINE CALLINGSUB "/declare CallingSub string local ${CallStack[${CallStackIndex},1]}"
#DEFINE CALLINGINDEX "/declare CallingSubIndex int local ${CallStackIndex}"
#DEFINE PUSHCALL "/call PushCallStack CURRENTSUB"
#DEFINE POPCALL "/varset CallStackIndex ${CallingSubIndex}"

#DEFINE DEBUGN "/if (${Debug}) /echo \atDEBUG-${KissRevision} \agS:CURRENTSUB L:${Macro.CurLine} C:${CallingSub} CI:${CallingSubIndex} T:${Macro.RunTime} \aw"

#bind CallStack         /callstack

Sub Main
    /declare Debug int outer 1
    /declare KissRevision int outer 12
  
    ||| Set up the debug call stack. We start with PUSHCALL to establish the array.
    ||| Main will always be 1.
    PUSHCALL
    CALLINGSUB
    CALLINGINDEX
  
    /while (1) {
        /call Sub1
        /call Sub2
        POPCALL
    }
  
    POPCALL
:OnExit
    /call Bind_CallStack
/return

Sub Sub1

        ||| Debug call stack
        CALLINGSUB
        CALLINGINDEX
        PUSHCALL

        DEBUGN Blah blah blah message with var outputs

        ...the code...

        DEBUGN And another debug message

        POPCALL

/return

Sub Sub2

        ||| Debug call stack
        CALLINGSUB
        CALLINGINDEX
        PUSHCALL

        DEBUGN Blah blah blah message with var outputs

        /call Sub3
        ...more code...

        DEBUGN And another debug message

        POPCALL

/return

Sub Sub3

        ||| Debug call stack
        CALLINGSUB
        CALLINGINDEX
        PUSHCALL

        DEBUGN Blah blah blah message with var outputs

        ...the code...

        DEBUGN And another debug message

        POPCALL
      
        /endmacro

/return

||| ---------------------------------------------------------------------------
||| Sub PushCallStack
||| Helper for debugging. Records current sub so next sub can read it.
||| Usage: /call PushCallStack CURRENTSUB
||| ---------------------------------------------------------------------------
    Sub PushCallStack(string SubName)
        /if (!${Defined[CallStack]}) {
            ||| If 100 isn't enough something is probably wrong. Allows for some slop.
            /declare CallStack[100,1]    string    outer    null
            /declare CallStackIndex        int        outer    0
        }
        /if (${CallStackIndex}==100) {
            /echo CallStack ${SubName} exceeds max stack size 100. Dumping stack and quitting.
            /declare i int local 0
            /for i 1 to 100
                /echo ${i} ${CallStack[${i},1]}
            /next i
            /endmacro
        }
        /varcalc CallStackIndex ${CallStackIndex}+1
        /varset CallStack[${CallStackIndex},1] ${SubName}
    /return
||| ---------------------------------------------------------------------------
||| Sub PopCallStack - Not used
||| Helper for debugging. Removes the sub from the stack.
||| Usage: /call PopCallStack
||| ---------------------------------------------------------------------------
    Sub PopCallStack(int CallIndex)
        /while (${CallIndex}>${CallStackIndex})
            /varset CallStack[${CallStackIndex},1] NULL
            /varcalc CallStackIndex ${CallStackIndex}-1
        }
    /return
||| ---------------------------------------------------------------------------
||| Sub GetCall - Not used
||| Helper for debugging. Retrieves the top call from the stack (will be previous sub)
||| ---------------------------------------------------------------------------
    Sub GetCall
    /return ${CallStack[${CallStackIndex},1]}
||| ---------------------------------------------------------------------------
||| Sub Bind_CallStack
||| Helper for debugging. Records current sub so next sub can read it.
||| Usage: /callstack
||| ---------------------------------------------------------------------------
    Sub Bind_CallStack
        /if (!${Defined[CallStack]}) {
            /echo No stack to check.
            /return
        }
        /echo CallStack size ${CallStackIndex}.
        /declare i int local 0
        /for i 1 to ${CallStackIndex}
            /echo ${i} ${CallStack[${i},1]}
        /next i
    /return

Example log output:

Log output:
[2021/06/17 23:57:08] [MQ2] ATTACKING -> a tiny zombie <-

[2021/06/17 23:57:08] [MQ2] COMBAT-400 S:CombatPet L:1867 C:Combat CI:5 T:426  Enter - use /debug petcombat to go further

[2021/06/17 23:57:08] [MQ2] COMBAT-400 S:CombatPet L:1867 C:CombatPet CI:6 T:426  Enter - use /debug petcombat to go further

[2021/06/17 23:57:08] [MQ2] COMBAT-400 S:PetEngageTarget L:20942 C:CombatPet CI:7 T:426  Enter - debug further using petcombat

[2021/06/17 23:57:08] [MQ2] COMBAT-400 S:IsFriendly L:20133 C:PetAttack CI:9 T:426  Enter Target Name:a tiny zombie ID:3292 Spawn Name: a tiny zombie ID:3292

[2021/06/17 23:57:08] [MQ2] COMBAT-400 S:IsFriendly L:20139 C:PetAttack CI:9 T:426  Validate Friendlies

[2021/06/17 23:57:09] [MQ2] COMBAT-400 S:IsFriendly L:20151 C:PetAttack CI:9 T:426  MobID 3292 MobMasterID 0

[2021/06/17 23:57:09] [MQ2] COMBAT-400 S:IsFriendly L:20165 C:PetAttack CI:9 T:426  It's not me apparently

[2021/06/17 23:57:09] [MQ2] COMBAT-400 S:IsFriendly L:20169 C:PetAttack CI:9 T:426  Not in the group apparently

[2021/06/17 23:57:09] [MQ2] COMBAT-400 S:IsFriendly L:20179 C:PetAttack CI:9 T:426  Not a groupmember, pet, or whatever... supposedly may be hostile

[2021/06/17 23:57:09] [MQ2] COMBAT-400 S:IsFriendly L:20181 C:PetAttack CI:9 T:426  Leave

[2021/06/17 23:57:09] [MQ2] COMBAT-400 S:CombatPet L:1867 C:CheckBeforeCast CI:5 T:427  Enter - use /debug petcombat to go further

[2021/06/17 23:57:09] [MQ2] COMBAT-400 S:CombatPet L:1867 C:DoWeMed CI:4 T:427  Enter - use /debug petcombat to go further

[2021/06/17 23:57:09] [MQ2] COMBAT-400 S:Combat L:1180 C:DoWeMed CI:4 T:427  1: MyTargetID 3292 AggroTargetID 3292

[2021/06/17 23:57:09] [MQ2] COMBAT-400 S:Combat L:1182 C:DoWeMed CI:4 T:427  Attack 1.20 Role PetTank ChainPull 0

[2021/06/17 23:57:09] [MQ2] COMBAT-400 S:Combat L:1195 C:DoWeMed CI:4 T:427 1: MyTargetID 3292 AggroTargetID 3292
Author
AmericanNero
First release
Last update
Rating
0.00 star(s) 0 ratings

More resources from AmericanNero

Share this resource

Latest updates

  1. Another way...

    One could replace /call with a sub that then calls. E.g. /call RemoteCall "subname param0...
Back
Top