Keyboard Maestro 6 Macro Debugger
Another great new feature introduced in Keyboard Maestro 6 is the Macro Debugger. In this tutorial we'll see how to use it and how it can help you debug and fix your macros.
Is this tutorial for you?
Developers are familiar with debuggers, but if many developers use Keyboard Maestro, many Keyboard Maestro users are not developers. This tutorial assumes that you are in the latter group and have never used a debugger.
If debuggers already have a good place in your toolbox, you'll probably prefer to read the official documentation instead.
What is a debugger?
A debugger is a tool that helps tracking down and fixing bugs—in software. If your macros always do what they're expected to do without a single hiccup, then obviously you don't need one. Congratulations!
But as your macros grow bigger and more complex, they may start to fail in puzzling ways. And when that happens, it can be tricky to figure out not only why they fail, but where in the macro it actually happens.
Imagine that at some point in the flow of a long macro, you copy some text. If that copy fails for some reason (maybe the window where you copy from didn't have time to come to the front and the action that should have selected the text fired up too early), the macro could continue running many other actions until the failed copy caused it to misbehave. And when the macro runs at full speed, there's no easy way to check that every action succeeds.
This is where KM's debugger comes into play: by inserting pauses (A.K.A. breakpoints) between actions, it allows you execute the macro one step at a time, checking that the conditions are indeed those you expect at each step.
The Macro Debugger Window
Let's see it in action. In KM's status Menu, select Start Debugging. The Macro Debugger Window opens:
Check the Pause New Macros checkbox and trigger any macro (preferably one that has several actions). Here's an example:
The bold text is the name of the macro, and the text below describes the next action that will be executed (starting with the first one). Some actions can also have more levels of text (for example if an action called another macro, you'd also see the name of that macro too, and so on).
The buttons on the left allow you to control the execution.
: Resume execution (runs the macro until completion or until a breakpoint is met).
: Pause execution (pauses the macro as soon as the action that is currently executing finishes).
: Step Over the next action.
: Step Into the next action if possible or over it otherwise.
: Step Out back to the caller action.
And the button on the right:
: Kill the macro instantly.
At this point you're probably wondering what all those steps are. After all, you didn't come here for dancing lessons, right?
From the debugger's point of view, there are two types of actions:
- those that simply execute at the same level (for the sake of briefness, let's call them flat),
- and those that bring the execution flow to another level, either by calling another macro, by calling different actions depending on some conditions, or by entering a Repeat loop (let's call them multidimensional).
When you step through a macro that only contains flat actions, you have no decision to make on where to follow the flow. You can use the Step Over control or the Step Into control indifferently, and in both cases the debugger will run the next action then pause. But when you reach a multidimensional action, you have two possibilities:
- you can either dive into the other level (using the Step Into control), and then Step Over through that macro, conditional or loop until it ends and you're taken back in the first macro (the caller),
- or you can tell the debugger to treat it like a flat action (by using the Step Over control), so it will just execute it in one go and pause after it (assuming this extra level doesn't contain any breakpoint of its own, because if it did, the debugger would pause when it meets them).
The last control, Step Out, is useful when you stepped into a macro and want to jump back directly to the action that called it, without having to step through all its remaining actions. If you use it from a first level macro, it will be equivalent to using Resume (that is the macro will run until completion or until another breakpoint is met).
Try it for yourself by stepping through the execution of a macro.
KM also provides a number of actions in relation with the Macro Debugger. Some of them are meant to control the debugger itself, and some are meant to be inserted into the macro to debug. You can find them in the Debugger category of the Action chooser.
1. The actions intended to control the debugger are:
Debugger Start, Debugger Finish, Debugger Toggle, Debugger New Macros Paused, Debugger New Macros Run, Debugger Step Over Other Macros, Debugger Step Into Other Macros, Debugger Step Out Other Macros, and Debugger Continue All Macros.
Most of these actions are equivalent to clicking the corresponding button (or checkbox) in the Macro Debugger Window. You'll typically use them only once, if ever at all, to define hotkeys that let you control the debugger.
2. The actions that are meant to be inserted into the macro to debug are:
Debugger Breakpoint This Macro Insert that action into a macro to pause its execution and enter the debugger at the point this action is reached.
Debugger Breakpoint All Macros Insert that action into a macro to pause the execution of all macros and enter the debugger at the point this action is reached. This could be useful if you have macros that are triggered automatically (for example from a timer) and you want to make sure they won't interfere with the macro you're debugging.
Debugger Continue This Macro Insert that action into a macro to resume its execution from the point this action is reached until its completion or until another breakpoint is reached.
Debugger Continue All Macros Insert that action into a macro to resume the execution of all paused macros at the point this action is reached. This could be useful if you used the Debugger Breakpoint All Macros action to start debugging a macro and want to allow all other macros to resume running.
These actions are handy when you need to debug a specific part of a macro and don't want to have to step through all of its actions until you reach this part. Using them, you can have the macro run normally, pause (and maybe also pause all other macros) when it reaches a certain action, so you can check variables, other applications conditions, etc, and then resume the execution.
Using the Debugger
How do you use the debugger, then? Well, the easiest way to see that is to take a real life case, so that's what we're going to do.
Let's say I'm making a macro that will speak some text with different accents. I called it "Speak (Selection or Prompt) With Accent…". I want it to prompt me with a dialog that will contain by default either the text currently selected in the frontmost application, or if no text is selected, the text I had spoken the last time I used the macro. Here are its actions:
- The macro starts by setting the clipboard to _EMPTY_.
- Then it tries to copy the selection.
- If the clipboard changed, its contents is copied into a variable (otherwise that variable keeps its current value).
- Then the macro restores the clipboard to what it was before it started.
- It displays the prompt, using that variable to define the text to speak and the Voice variable to preselect the last used voice.
- And finally it speaks the text with the selected voice.
So let's try it! I trigger the macro, and get this prompt:
Good, just what I expected. I type "Hello there!" into the Text field, click OK, and that text is spoken by UK Daniel. Great!
Let's try with a selection, then. I type "Hello World!" into a text document, select it, trigger the macro… and I'm greeted with that:
Err… what? I expected Hello World!
After I spent five minutes looking at the macro without spotting what's wrong (let's say I have dyslexia and a poor eyesight), I decide to debug it.
Step 1: I insert a Debugger Breakpoint This Macro action at the top of the macro. And since I know the problem cannot come from the part that resets the clipboard, I don't want to have to step through it. So I insert a Debugger Continue This Macro action before that part, and a Debugger Breakpoint This Macro after:
Step 2: I trigger the macro. The debugger opens on this:
Step 3: I press Step Over and get this:
Step 4: I Step Over again and get:
Step 5: Now the next action being multidimensional, I have a choice on what to do: just skip over it, or dive into it. Since I want to find out which path the flow takes, I choose the latter and press Step Into:
OK, I know I can only get here when the clipboard has changed, so I know the copy worked.
Step 6: I press Resume and (thanks to "Debugger Continue This Macro" and "Debugger Breakpoint This Macro") I'm taken directly before the prompt:
I know the text should get into that prompt through the variable "Text". So let's check this variable:
- Bring KM to the front
- Press Cmd-, to open the Preferences window
- Click the Variables toolbar item
- Find the variable:
It's set to "Hello there!". Where is my "Hello World!", then? Since I know it was copied, I must have mistyped the variable name. I look at the action, and sure enough, I used Test instead of Text (silly me!):
I kill the macro, and rename the variable in that action. And since I want to test the macro without debugging it, I also disable both "Debugger Breakpoint This Macro" actions by selecting them and clicking on √.
Step 8: Then I trigger the macro, and get:
Now what?! OK, that's a token but it appears as a regular string of characters. I don't need the debugger for that, because I know the only reason why KM could fail to evaluate a token, here, is that I mistyped it. So I double-check that action I just fixed and yes, I also typed %CurrentClipboarb" instead of "CurrentClipboard% (I told you I was pretending to be dyslexic—no offense to people with real dyslexia, BTW, that's not an easy nor a fun condition!).
Step 9: After I corrected this typo I try the macro again, and finally:
Step 10: Now that the macro is fixed, I can delete the debugger actions I added to it, and I'm done.
OK, I have to admit that was a very simple case and the macro could have easily been fixed without the debugger. But my goal here was to show you how and when to use it, and I hope you now have a better understanding of that.
As we saw, the three most important things the debugger allows are:
- checking that the execution flow follows the expected path
- checking that the conditions at the time of execution are the expected ones
- checking that the data we use (variables, clipboards, etc) have the values we expect
How can we improve these points even further, then?
1. Following the flow
In that example, following the flow was rather easy, thanks to the debugger always showing the next action to execute. But sometimes it can get tricky. For example at Step 5 I knew the text was copied successfully because otherwise I wouldn't have seen the Set Variable To Text action in the debugger. But in some cases, conditional actions will be nested, and then the flow can be much more difficult to follow. A tool that helps a lot, in these cases, is the Comment action.
If we come back to the first conditional action of that macro we debugged, I could have inserted a Comments that reads "Got the selected text" at the top of the first branch, and one that reads "There was no selection to copy" into the (currently empty) second branch. Since comments do nothing during execution, that wouldn't affect the macro in any way. But even if they do nothing, Comment actions will appear in the debugger (as long as they're not disabled), and therefore you can use them to know easily where the flow took you.
2. Checking the conditions
We didn't have to check the conditions in this example, because it didn't matter what application was in front the first time we ran the macro, and the second time the text document we wanted to copy from was already at the front. But some macros involve switching back and forth between several applications, or waiting for conditions to be met in an application (like a given dialog being displayed) and the only efficient tool we have to make sure the conditions are those we expect are our own eyes.
However, you may run into situations where a macro that fails when running at full speed succeeds when running in the debugger. This is a clear indication that the failure is caused by the macro running faster than the system or the applications it drives and relies on. When that happens, the best way to solve the problem is to insert Pause actions to force KM to wait for the necessary conditions to be met.
There are two different Pause actions, and which one to choose depends on what you want KM to wait for. Of course, you could always use the Pause action with a value of 1 second or less (like .3) but then if the condition you're waiting for is met earlier, KM will still keep waiting for the defined time. So when possible it's much better to use the Pause Until action, that will check the conditions for you and pause for the absolute minimum of time in every circumstance.
This action is quite powerful and allows you to define very precise conditions, but it's also pretty easy to use. For example if your macro launches an application or takes it to the front if it's already running, you can set the Pause Until action to wait for that application to be at the front (add an Application Condition and the default setting will do what you want). If you want to immediately select a menu item in that application, you would set the condition to wait for that application to be at the front AND for this menu item to be enabled (by adding a Menu condition to the Application Condition). As you see it's very flexible, and common sense should be enough to decide when and how to use it. And anyway, if the conditions were already met at the time that action runs, it would not pause at all.
3. Checking the data
Typically, the data you'll use will be either on a clipboard or in a variable. Checking the clipboard is easy thanks to KM's Clipboard History window, and checking variables is easy too, thanks to KM's Variables tab in the preferences window.
But you could also have these values displayed with an Alert action, a Notification action, a Growl action, a Display Text action, a Prompt For User Input action, or even have them spoken using the Speak action.
Or you could use the Loud Comment Plugin Action we created in another tutorial, which can be downloaded from here. It's an advanced version of KM's Comment action that can also optionally speak or display comments and variables in a dialog or as a Growl notification, so it helps with both following the flow and checking the variables.
This concludes this tutorial. Thank you for reading this far. I hope it will have given you the keys to use the debugger and make better, more reliable macros.
And as always, thanks to Peter N. Lewis for this fantastic application, and for his patience with questions and feature requests!