Using the MAXScript Debugger

The advantage of the MAXScript Debugger over using print/format is that when you hit the break, you can look at local variables, variables up the call tree (in each stack frame), and global variables. And if you can get to a scope via one of those, you can access variables in those scopes.

So, for example, you break in a scripted plug-in event handler. Normally you wouldnot see the scripted plug-in's local variables or parameters. Using the Debugger you can not only see these, you can also change their value before continuing execution.

How many times have you gotten some sort of MAXScript error, and you couldn't tell what exactly the problem was just by looking at the values printed in the error trace-back, but if you could do some more digging to see other variables you could (thus the adding of print/format statements in the hope that they give you enough info).

3ds Max is multithreaded, and when you hit a breakpoint there can be multiple scripts running in different threads. The usual case of this is when you hit a scripted plug-in or controller while rendering. If you hit a breakpoint or exception in this case, the Debugger will default to the thread that threw it. But if you broke by hitting the break button, then it defaults to the main thread. But you may want to see what's running in a different thread, and can do so.

As you run a script and call functions, each function call creates its own stack frame. The stack frame simply contains the local variables associated with that function call. In each stack frame, the following variables are shown:

FOR EXAMPLE,

if you run the following code,

v = 0
r = 0
(
local pi = 3
for i = 1 to 1000000 do
(ss = random e pi; v += ss; v += r; if i == 999999 do throw "A")
)

and then hit Break, you would get:

#
** thread data: threadID:3364
** ------------------------------------------------------
** [stack level: 0]
** In i loop; filename: C:\Program Files\Autodesk\3ds Max 2010\ui\macroscripts\; position: 94; line: 6
** member of: anonymous codeblock
--  Parameters:
--   i: 260174
--  Locals:
--   ss: 2.79242
--   i: 260174
--  Externals:
--   owner: <CodeBlock:anonymous>
--   pi: 3
--   r: Global:r : 0
--   V: Global:V : 744008.0
--  Owner:
--   Locals:
--    pi: 3
--   Externals:
** ------------------------------------------------------
** [stack level: 1]
** called from anonymous codeblock; filename: C:\Program Files\Autodesk\3ds Max 2010\ui\macroscripts\; position: 126; line: 6
--  Locals:
--   pi: 3
--  Externals:
** ------------------------------------------------------
** [stack level: 2]
** called from top-level

The first line just tells us which thread we are in. Most of the timethis is not important. Then we start walking out through the function calls, dumping the stack frame for eachcall. In this case there are 3 levels - the body of theFORloop (which internally is handled as a function call), the code within the outer parentheses, and the Listener executing theFORloop.

For level 0, first we have the function name (i loop), and then we say what owns the function. In this case its the code block defined by the outer parenthesis. This code block is anonymous because it doesn't have a name. Other code blocks will.

FOR EXAMPLE,

if you drag the above code onto a toolbar to make a MacroScript, run the MacroScript and hit Break, the first couple of lines would look like:

** thread data: threadID:2224
** [stack level: 0]
** In i loop; filename:
C:\3dsmax8\UI\MacroScripts\DragAndDrop-Macro1.mcr; position: 186
** member of: codeblock macroScript: DragAndDrop_Macro1

Next thing shown is the Parameters, which for a for loop is just the for loop counter. The values shown here are the values passed to the function. Next are the Locals. There are 2- ss and i. Here, if a new value was assigned to variable i (the for loop counter), that new value would be shown. Next, the Externals are shown. These are variables that are used in the function, but are not local variables. They can be either defined in an outer scope, global scope, or some other outside scope. The owner variable allows you to access the owner of the function. If, for example, the function was in a scripted plug-in, the owner would be the scripted plug-in instance. Through this, you can get and set the owner's variables.

Finally, if there is an owner, the information for the owner is shown. In this case it is not that important since the next stack level shows the same thing, but in general that is not true (functions in scripted plug-ins, MacroScripts, rollouts).

The next stack level dumps the info for the caller of the function, the anonymous code block.

Let's say you wanted to change the value of variable 'pi'. You can only change a variable if you can access the variable. In this case, you can access 'pi' from within the for loop function via: 'owner.pi'

but if we had nested function calls, you wouldn't be able toget to it that way. Instead, you would have to set the current stack level to the desired level and set the variable.

FOR EXAMPLE:

>> setframe 1
** ok
>>locals
** thread data: threadID:2224
** [stack level: 1]
** In anonymous codeblock
-- Locals:
-- pi: 3.1
-- Externals:
>> setvar pi 3.5
3.5
>> getvar pi
3.5

Compare the above to the error traceback from throwing the exception:

-- Error occurred in i loop
--  Frame:
--   ss: 2.99878
--   i: 999999
--   called in anonymous codeblock
--  Frame:
--   pi: 3
-- Runtime error: A

There is also a Stop button in the MAXScript Debugger. After doing a Break, if you hit Run you will continue executing the code, if you hit Stop, the script will stop running.

See Also