The following article will be all around debugging your Cinema 4D plugins. Addressing mainly the beginners in plugin development, I still hope, to have one or two tricks for the veterans as well. I will start with setting up your environment for an increased fun in debugging, then go over to some general debugging techniques, before finishing with some more Cinema 4D specific techniques.
While the screenshots in this article are taken from Visual Studio 2013 and Xcode 6.0.1, everything shown and said should work in Visual Studio 2012 and Xcode 5.x or 6.1.x as well. If there’s a place this is not the case, please let me know and I will try to cover the differences.
Preparing Your Toolbox
Start Cinema 4D Automatically With Your Debugger
At first you’ll want to configure your project for debugging. Most of it is already set correctly by default (assuming you are using the project files delivered with the SDK). In order to have Cinema 4D started, once you click the “Debug” button, you need to set the path to the Cinema 4D executable.
Depending on your Cinema 4D version, operating system and the IDE in use it’s a bit different.
Visual Studio
Enter the project properties and go to Debugging settings, like so:
Menu: Project → Properties → Configuration Properties → Debugging
For Cinema 4D R15 and following it is sufficient to simply select your Cinema 4D executable for Command. The result will probably look like this ..\..\Cinema 4D 64 Bit.exe (R15) or ..\..\..\Cinema 4D.exe (R16), although the path may depend on your system and project location. Especially with R16 where you can move your projects anywhere you like.
For versions before Cinema 4D R15 I suggest to use both options marked by 1 in the screenshot: Command and Working Directory. Unfortunately Working Directory can not be a relative path. But in order to make use of the Debug mode (explained next), you will need to have Working Directory point to the directory, where the Cinema 4D executable is located (absolute path, the trailing backslash is important) and only have the name of the Cinema 4D executable as Command, like it is shown in the following screenshot.
I recommend to set the Debugging Command for Configuration: “All Configurations”. As the executable differs for 32-Bit and 64-Bit version of Cinema 4D, you’ll have to configure Platform Win32 in a second step, if you still need to build 32-Bit versions.
Xcode
Enter the scheme settings either via menu Menu: Product → Scheme → Edit Scheme... or a bit quicker via Cmd + <. At first one needs to note, that this is the place to switch between debug and release builds, which may be a bit confusing especially for developers who grew up with Visual Studio.
Then choose Executable → Other... and select your Cinema 4D executable, like in the screenshot below.
Start Cinema 4D in Debug Mode
You may want to start Cinema 4D itself in debug mode.
In debug mode Cinema 4D will provide you with all kinds additional runtime information on a system console. For example valuable information on memory management, it will even warn you about memory leaks. Please note, this will slow down Cinema 4D considerably, so you may want to enable Debug mode temporarily only. An even better way would be to have special debug build targets, which additionally enables the debug mode.
Cinema 4D < R15
Create an empty text file named c4d_debug.txt inside of the Cinema 4D program folder, next to your Cinema 4D executable. As long as you followed the Command and Working Directory setup before, it’s that simple!
Cinema 4D ≥ R15 and Visual Studio
Add g_alloc=debug to your Commandline Arguments (marked 2 in the above screenshot) for Configuration: “Debug” and Platform: “All Platforms”.
Cinema 4D ≥ R15 and Xcode
In Xcode’s scheme settings change to the arguments tab. Add g_alloc=debug as argument, like in the screenshot below.
More Commandline Arguments
- -debug
- ≥R15: Use this instead of g_alloc=debug in order to start Cinema 4D in Debug mode without the additional memory management information and memory leak detection
- g_logfile=[string]
- ≥R16: Debug Console output will be written to a file
- g_console=true
- ≥R16: Open the Debug Console (was done via -debug in versions <R16)
Even more commandline options can be found in <C4D install dir>/resource/config.txt.
Use TypeViewer for VisualStudio and Xcode
You may have seen downloads of TypeViewer for Visual Studio 2005 and 2010 in our development area. Good news for everybody, who’s working with Visual Studio 2012+ and is developing with Cinema 4D SDK R15+. The functionality is seamlessly integrated into the project files. No additional work needed. It simply works. The same is true for Xcode.
Time Is Money
You can significantly speed up your development turnaround times by creating a special start-up layout in Cinema 4D for your plugin development.
- If you have modified your Cinema 4D start-up layout before, don’t forget to save it, so you can return to it anytime you need to.
- Remove everything not needed in the context of your new plugin, especially Content Browser views can severely impede your start-up times.
- Integrate the console Menu: Script → Console... into your layout, e.g. have it docked on one side of your screen. This provides you with a quick overview on your debug output.
- Open your plugin and attach its interface or have a start-up scene using your plugin.
- Save this layout as e.g. plugin_dev.l4d as well as start-up layout.
Printf Debugging
Further Reading
Some may call it the purest form of debugging, it certainly is one of the most simple. In Cinema 4D you have several options to print information.
They differ in location of output, if the code is still active in release builds and type of string to print. Let’s go through them quickly, noting advantages and disadvantages. Afterwards you’ll find a table, showing which function outputs where and when.
void GePrint(const String& str)
Use GePrint in your plugin to show elementary messages/information to the user. For example a message on successful initialization plus version information could be printed to the script console. You should avoid to flood the script console, as the user might want to use the script console himself or other components want to have their info visible to the user as well.
Since GePrint prints regardless of build target and Debug mode, it’s less suited for debugging needs.
void GeConsoleOut(const String& str)
GeConsoleOut behaves a bit differently on Windows and Mac. On Windows it basically does the same as GePrint. Yet on Mac it prints to the debugger’s output window as well.
void GeDebugOut(const String& s)
void GeDebugOut(const Char* s, …)
As the name suggests, these functions are made for debugging. Mainly because the output can only be seen, when Cinema 4D is run in Debug mode.
GeDebugOut comes in two flavors, the first works with Cinema 4D’s String class (just like GePrint).
The second works with format strings (C-strings with special placeholders to provide formatted output), as you might know them from ANSI-C and stdio-functions, like printf. Here you can use all the format strings known from the last mentioned stdio-functions. So you can do something like this:
const Char name[] = "Cinema 4D"; UInt32 val = 5; GeDebugOut("Debugging %s plugins is %dx more fun than reading white pages (0x%p).", name, val, &val);
So you will need to take care for this.
New in Cinema 4D R15
CriticalOutput(fmt)
The name says all about its purpose. CriticalOutput is to be used in insolvable situations. Printing-wise it works much like the second GeDebugOut flavor, as it’s using C-like format strings. Yet, there are some differences:
- It triggers a breakpoint, if there is a debugger present
- Information on source-file and line number is added
- A line break is automatically appended
- No 2048 character limit
DiagnosticOutput(fmt)
This is pretty much the same as second flavor GeDebugOut. Only difference is an automatically appended line-break.
DebugOutput(flags, fmt)
Last but not least, DebugOutput will probably get your most used debug print function. It’s main advantage, it does nothing in release builds. Neither will it reveal your output to a user running Cinema 4D in Debug mode, nor will it use any CPU time in a release build. If you are developing for R15 and above, I suggest, you take a closer look at this function and try to get used to it.
Additionally you can make use of OUTPUT_FLAGS to adjust it to your needs:
- OUTPUT_DIAGNOSTIC
- Set by default
- OUTPUT_WARNING
- Prepends a line “WARNING:” to your output
- OUTPUT_CRITICAL
- Prepends a line “CRITICAL:” to your output
- OUTPUT_NOLINEBREAK
- Suppresses an automatically appended line-break, so you can use multiple calls to assemble a your line
- OUTPUT_HEADER
- Adds source file and line number information
Which Print Goes Where
Cinema 4D | Debug Mode: On | Debug Mode: Off | ||||||||
---|---|---|---|---|---|---|---|---|---|---|
Location | Script Console | Debugger | Debug Console | Script Console | Debugger | |||||
Build Target | Rel. | Dbg. | Rel. | Dbg. | Rel. | Dbg. | Rel. | Dbg. | Rel. | Dbg. |
GePrint | ||||||||||
GeConsoleOut | M | M | M | M | ||||||
GeDebugOut | ||||||||||
Additional print methods with Cinema 4D ≥ R15: | ||||||||||
CriticalOutput | ||||||||||
DiagnosticOutput | ||||||||||
DebugOutput | ||||||||||
: Output is visible : No output : Output is configurable via OUTPUT_FLAGS M: Mac only |
Conversions to String
To have all info in one place, you’ll need the following functions to print your variables:
Cinema 4D < R15 | Cinema 4D ≥ R15 |
---|---|
String::ToReal | String::ParseToFloat |
String::ToLong | String::ParseToInt32 |
LLongToString | String::IntToString |
LongToString | String::IntToString |
RealToString | String::FloatToString |
PointerToString | String::HexToString |
MemoryToString | String::MemoryToString |
Beyond Printf Debugging – Use Your Debugger
Further Reading
In the beginning we already talked about starting Cinema 4D from within your IDE and in your debugger. Some may wonder, what’s the point, as you obviously have no debug information and symbol tables for Cinema 4D. You know what? It doesn’t really matter. You can still debug your plugin, you can step through your code, set breakpoints and watch data structures. You can do basically everything you are used to with your debugger. At a point, where you are calling Cinema 4D API functions, you may feel a bit blindfolded, but you can always use “Step Out” or “Run to Cursor” functionality to get back into your own project code.
Discussing every detail of debugging with your specific IDE is way beyond the scope of this article. But I want to show the basics and point your attention to some nifty features, you may not have noticed, yet. And there’s so much more, I won’t be covering: Watch points, Symbolic breakpoints. See the Further Reading section, if you feel like diving into the details.
Visual Studio
Above screenshot shows a typical debugging situation (here the rounded tube plugin from the SDK examples).
You will want to have several windows docked to your debug layout and you should note, that there’s more than just the average breakpoint.
Debug Windows
- Output
This is where most of the debug prints write to. When Cinema 4D is starting up, you see a lot of information, not too important for the average plugin developer. But after the start phase it should be quiet here and you can use it nicely for debug outputs and output from assertions. Furthermore output from Breakpoints is shown here as well (see below). - Breakpoints
Useful to quickly toggle or reconfigure breakpoints, without the need to jump to the respective code line. In fact it can be used to quickly navigate to interesting code lines, so you may want to have this window not only in your debug layout. Sort of another category of bookmarks. - Locals
Here you will be provided with the current data visible in the current scope. No more and no less. You can add data to the watch window from here saw well. Changed data is highlighted. - Watch Window(s)
You can have several of these open in parallel. Add data to the watch windows, in order to keep an eye on, even if the variable is not in current scope. Also good to see, if data gets changed by somebody else, like a bug, when you are writing over the limits of an array. - Data Probes (not shown in screenshot)
Actually no real window, but you can attach data probes to source lines via right click “Pin to source”. You’ll get a minimal watch window attached to the source code, showing the content of a certain variable. - Call Stack
Since you are programming a computer, I assume you are familiar with the concept of a call stack. If you are not, then you should definitely learn about it. See further reading links to do so. Anyway, here you see the stack, can click to jump into prior call levels. A good place to come to know the call hierarchy (the basics), to learn how many call levels are involved with certain calls (when optimizing) and certain bugs, for example infinite recursion, are easily identified here.
Breakpoints
- The Ordinary
Added with a simple left click into the leftmost column of your editor view, these are behaving as everyone would suspect: The execution gets halted upon hit and you are ready to use your debugger. Via right click you can transform these into any of the following breakpoints. - When hit…
With this option you can change a breakpoint into a debug print. With the big advantage you can add this debug print at runtime! When selecting this option, you can enter the message to print (it’s shown in there, how to access your variables). By default the breakpoint will mutate into a debug print. You need to deselect the option to “Continue Execution”, if you want halt the execution as well. - Hit Count
A simple version of the conditional breakpoint. Simply specify how many times you want to step over this breakpoint until it finally triggers a halt of the execution. - Conditional
The swiss army knife of all breakpoints. You can specify a condition, in the same way you write the condition for an if statement. In most cases, this is all you need, to trigger on the exact occasion of a bug.
Xcode
In general Xcode offers the same options as Visual Studio. Therefore it shall be enough to describe the differences. Also I’d like to note, that my experience with Xcode is still weak. So if I overlooked something, please leave me comments.
Unfortunately I have not yet found comfortable equivalents for Visual Studio’s Watch Window and Data Probes. Yes, you can watch memory areas, but Visual Studio’s Watch Window is so much more versatile.
The screenshot below shows the call stack in Xcode. Note the small buttons in the lower left, which offer a very nice option to show only the relevant part of the stack. Nice feature.
In contrast to Visual Studio, Xcode has one breakpoint, which is vastly configurable and can do all the stuff (and more) described for Visual Studio. Lets look at Xcode’s breakpoint manager.
A side note, while we are at it, note the small “All” button in the lower left of the “Locals” view. There you have the option to blend in global variables and even registers. Nice.
Obviously a breakpoint will be configured by Ctrl-clicking or right clicking and selecting Edit Breakpoint.... The screenshot below shows a configuration doing something similar to Visual Studio’s “When hit…” breakpoint. Note that you have all the options to combine this with a condition or a “Hit Count” (Visual Studio speak, in Xcode it’s the “Ignore” option).
Built-in Bugtraps
Assertions are a good way to develop your code into a more stable product. In their simplest form, assertions can be used to trigger a breakpoint in a certain branch. Why would you want to do this? Well, first of all you may have code, that depends on certain prerequisites and/or assumptions. You can use an assertion to assure your assumptions are correct. Or you may not be sure, something is working the way you expect it to, again assertions can be used to notify you about wrong assumptions. And you can use assertions to mark code not yet completed. In this way you don’t always have to watch your comments, but will have a notification mechanism, when such incomplete branches are hit.
For example you have an enumeration and a switch/case testing these cases. Typically you will have a default case, which catches everything not covered by your cases. This can be a good place for an assertion. Whenever you extend your enumeration, the debugger will get triggered as soon as a switch/case is hit, that was not yet adapted to the extended enum. Otherwise your plugin might stumble through the default case and might show wrong behavior. Depending on the wrong behavior this can be quite difficult to notice. It may even slip through into your release…
Or consider the following code. Bad code, I admit, but for the sake of showing my point in a few lines… I hope you get the point:
#define SOMEARRAY_SIZE 16 static Int32 g_someArray[SOMEARRAY_SIZE]; void PrintSomeArray(Int32 idx) { GePrint("Value: " + String::IntToString(g_someArray[idx])); }
Now you may call PrintSomeArray, forgetting all about your array’s size. What will happen? Well, it depends… It depends on parameter idx of course. Large values will probably crash. Which in a weird way is good, as you will notice the bug. Values only slightly greater than SOMEARRAY_SIZE will most likely work, though. With these the function will print some memory content behind your array. This again may be uninitialized or initialized with some of your other data. Now there are chances you’ll receive values which look good, but it will depend on sunshine and groundfog, one time your plugin will run, next time it may crash (assuming you are actually working with the data) and on a third run it will show erratic behavior, which may even depend on what you did before with your plugin. The resulting bugs can get even more interesting, if you are writing into the array. Functions will stop working, you didn’t even intend to change… you may never have known these ever existed, if you get the wrong values right. 😉
Of course you need to check the index. If the index depends on user input, you’ll have no chance but to check idx every time. On the other hand, the index may be depending on your other code, only. Then it might be a good idea to use an assertion like this:
#define SOMEARRAY_SIZE 16 static Int32 g_someArray[SOMEARRAY_SIZE]; void PrintSomeArray(Int32 idx) { DebugAssert((idx >= 0) && (idx < SOMEARRAY_SIZE)); GePrint("Value: " + String::IntToString(g_someArray[idx])); }
Now, idx gets checked in your debug builds and wrong indices will be obvious. Release builds on the other hand will have no check and will be as fast as you intended.
In the Cinema 4D SDK you have a plethora of assertions to use. On some you can add a string to be printed, when the assertion fails, some are unconditional (e.g. for the above mentioned switch/case) and some also work in release builds. See the table below to get an overview.
In a release build b will never be assigned!
Cinema 4D < R15 | Cinema 4D ≥ R15 | String | Unconditional | Release |
---|---|---|---|---|
GeBoom() | CriticalStop(cstr) | |||
GeAssert(cond) | DebugAssert(cond) | |||
GeBreak() | CriticalStop(cstr) | |||
CriticalAssert(cond, cstr) | ||||
DebugStop(cstr) |
Be Aware of Floating Point Madness
Further Reading
I won’t talk about actual debugging here, rather bug prevention when handling Float datatypes and I won’t do much.
While you can read more on the strange stuff, you can experience with Float datatype in my article Floating-Point Weirdness, I simply want to list the things to be aware of and a few SDK functions that will make your life a bit easier.
Special care needs to be taken, when
- comparing Float values
Make use of Bool CompareFloatTolerant(a, b) - converting Float variables into integer variables
Use conversion macros SAFEINT32(), SAFEINT64(), SAFEINT() - importing floating-point values from files
Use Bool CheckFloat(float) and Float RepairFloat(float) - calculating with floating-point numbers, as floating-point exceptions are likely to be disabled in Cinema 4D
You may temporarily enable these (But pay attention! If in doubt have a look at the Floating-Point Weirdness article) using Int32 GeDebugSetFloatingPointChecks(Int32 on)
Keep an Eye on Your Memory Allocations
Allocating memory is trivial, correct freeing or releasing formerly allocated memory, that’s what many programmers seem to have problems with. And although Cinema 4D provides a lot of convenience functions, like e.g. the nice AutoAlloc mechanism, this can be as well an issue in your plugins. If I find the time, we may have an article on this particular topic.
Checking for memory leaks the above mentioned Debug mode is irreplaceable. But there’s another one you get for free. Included with the SDK examples there’s a small plugin called Memory Statistics. It’s nothing more, but a table showing the info provided by Bool GeGetMemoryStat(BaseContainer& stat). Yet, it can provide you with a nice feeling for what is going on. Simply dock it in your debug layout. Look at it from time to time, you’ll know what I mean. You’ll not only know, your plugin is running wild, but your overall feeling for memory allocations will improve.
I already said this, when talking about usage of processor time. Same is true here. Memory you don’t use, is free for others. It’s a community service to watch your allocations.
Multithreading Can Be Tricky
Cinema 4D is heavily multithreaded and this has major impact on your plugin development. While the idea of multithreading may seem obvious and simple, the implications for software developers are not. To explain the details (like correct serialization and proper locking, avoiding deadlocks and so on) would definitely be beyond the scope of this article.
In any case you should pay close attention to restrictions in regards to multithreading mentioned in the SDK documentation. We are aware our documentation on this topic is far from perfect. While we are trying to improve, we appreciate any pointers given by the community. Simply send an email to sdk_support@maxon.net.
Many functions in the Cinema 4D SDK have to be called from main thread. For example pretty much anything interacting with the GUI.
You can use Bool GeIsMainThread(void) to ensure this in your code.
Watch Your Execution Times
Downloads
To state the obvious:
Optimizing your plugin’s runtime is a community service!
Processing power not used by your plugin is available for others.
At first you will have to determine the hotspots of your code. Of course a professional profiling tool makes this job easy. Taking the cost of these tools into account, many of you won’t have access to such tools. Well, developers on Mac have a clear advantage here, as they get Instruments for free (someday I’ll probably write an article about profiling your plugins with Instruments). But instead you can simply use the debugger or debug outputs to find out, which functions are called the most or which loops have the longest duration. Take special care for functions like GetVirtualObjects() in ObjectData plugins or Message() and Draw() functions in general.
Afterwards it’s as simple as using GeGetMilliSeconds() to measure your execution time. Depending on the function, it may also be a good idea to keep track of minimum and/or maximum times.
Here’s some example code for Cinema 4D ≥ R15:
#ifndef __DET_H_ #define __DET_H_ #include "c4d.h" // use a distinct define to toggle measurement on and off, as you don't want to measure in debug builds #define DEBUG_EXECUTION_TIME // add your measurement jobs here and don't forget the names in the array below enum { DET_SPECIAL_LOOP, DET_SOME_OTHER_JOB, _DET_NUM_MEASUREMENTS, // keep last }; static const char* g_arrDETNames[_DET_NUM_MEASUREMENTS] = // class String can't be used as static global { "Special loop", "Some other job", }; #ifdef DEBUG_EXECUTION_TIME void InitDET(void); void StartDET(const Int32 idxJob); void FinishDET(const Int32 idxJob); void PrintDET(const Int32 job); #define DET_INIT() InitDET() // call this somewhere in your start-up to get proper minimum values later on #define DET_START(job) StartDET(job) #define DET_FINISH(job) FinishDET(job) #define DET_PRINT(job) PrintDET(job) #else // DEBUG_EXECUTION_TIME #define DET_INIT() #define DET_START(job) #define DET_FINISH(job) #define DET_PRINT(job) #endif // DEBUG_EXECUTION_TIME #endif // __DET_H_
#include "det.h" typedef struct _tDebugExecTime tDebugExecTime; struct _tDebugExecTime { Float64 fStart; Float64 fCurrent; Float64 fMin; Float64 fMax; }; static tDebugExecTime g_arrDET[_DET_NUM_MEASUREMENTS]; void InitDET(void) // call this somewhere in your start-up to get proper minimum values later on { for (Int32 idxJob = 0; idxJob < sizeof(g_arrDET)/sizeof(g_arrDET[0]); ++idxJob) { g_arrDET[idxJob].fMin = MAXVALUE_FLOAT64; } } void StartDET(const Int32 idxJob) { g_arrDET[idxJob].fStart = GeGetMilliSeconds(); } void FinishDET(const Int32 idxJob) { const Float64 fTime = GeGetMilliSeconds() - g_arrDET[idxJob].fStart; g_arrDET[idxJob].fCurrent = fTime; if (fTime < g_arrDET[idxJob].fMin) g_arrDET[idxJob].fMin = fTime; if (fTime > g_arrDET[idxJob].fMax) g_arrDET[idxJob].fMax = fTime; } #define DET_PRINT_MEASUREMENT(n) GePrint(String(g_arrDETNames[n]) + ": " + \ String::FloatToString(g_arrDET[n].fCurrent) + " / " + \ String::FloatToString(g_arrDET[n].fMin) + " / " + \ String::FloatToString(g_arrDET[n].fMax)) void PrintDET(const Int32 job) { if ((job > 0) && (job < _DET_NUM_MEASUREMENTS)) { DET_PRINT_MEASUREMENT(job); } else { for (Int32 idxJob = 0; idxJob < sizeof(g_arrDET)/sizeof(g_arrDET[0]); ++idxJob) { DET_PRINT_MEASUREMENT(idxJob); } } }
To use the above header, initialize somewhere in your start-up code:
DET_INIT();
Then measure like so:
DET_START(DET_SPECIAL_LOOP); while (condition) { // ... this ominous loop you always wanted to know the runtime of } DET_FINISH(DET_SPECIAL_LOOP);
And evaluate your results:
DET_INIT();
Your Own Personal Oompa Loompa
You may already know the Active Object Dialog plugin, as it’s one of the SDK examples. You will most likely have compiled it yourself, if not I recommend you do now. Most people tend to forget about it, after having it called once.
I say: You rather should not forget about it!
The Active Object Dialog plugin not only gives you a great overview about what’s going on in a scene, but it will also provide you with address information, info on object caches and dirty flags, which is great not only for ObjectData plugins. It really goes a long way in combination with your debug information or the debugger.
Have Fun
I hope I didn’t bore anyone to death and that at least one of you will have a bit more fun debugging his plugins after reading this article.
So long,
Andreas