Debug.Assert()

Jan 20, 2012 at 10:13 AM
Edited Jan 20, 2012 at 10:14 AM

Since I added support for Debug.WriteLine in the previous post, I figured I should just go ahead and implement Debug.Assert also since Cuda 2.0+ supports assert.

The cuda docs say that when you call assert(0); it will write to the stderr the trace information for function, line, thread, and block, but I didn't see that getting printed out to Visual Studio 2010 output window and I didn't see an option to forward stderr to output so I'm assuming VS2010 does pring stderr but its just not there. If anyone figures this out please post info.

Since Debug.Assert() supports messages and formatted messages thats not that much of a problem because you can print your own message to the console! This ends up working just like Debug.WriteLineIf() it creates an if statement, then prints the message using printf() and after that calls assert(0) BOOM!

These mods aren't as detailed as the post about Debug.WriteLine but if you review that post should be easy to figure out where to plop this code. Here are the code modifications...

CUDALanguage.cs > add the following...

private const string csASSERT = "assert";
...
OptionalHeaders.Add(new OptionalHeader(csASSERT, @"#include <assert.h>"));
...
SpecialMethods.Add(new SpecialMember("Debug", "Assert", new Func<MemberReferenceExpression, object, string>(TranslateAssert), false));
...
static string TranslateAssert(MemberReferenceExpression mre, object data)
{
    // TODO: detect architecture
    //if (CUDALanguage.Architecture != eArchitecture.sm_20)
    //    return CommentMeOut(mre, data);
    UseOptionalHeader(csASSERT);
    string assert = string.Empty;
    assert = mre.TranslateAssert(data);
    return assert;
}

SpecialMethods.Add(new SpecialMember("Debug", "Assert", new Func<MemberReferenceExpression, object, string>(TranslateAssert), false));

CUDAOutputVisitor.cs > add...

public static string TranslateAssert(this MemberReferenceExpression mre, object data)
{
    TextWriter output = new StringWriter();
    CUDAOutputVisitor visitor = new CUDAOutputVisitor(new TextWriterOutputFormatter(output), new CSharpFormattingOptions());
    var ex = data as InvocationExpression;
    if (ex == null)
        throw new ArgumentNullException("data as InvocationExpression");

    bool isWriteLine = ((MemberReferenceExpression)ex.Target).MemberName == "WriteLine";
    int i = 0;
    List<Expression> arguments = ex.Arguments.ToList();
    if (arguments.Count > 1)
    {
        // An If statement and Debug.WriteLine should go along with this assert
        output.Write("if(!(");
        arguments[0].AcceptVisitor(visitor, data);
        output.Write(")) {");

        bool shortMessageIsNull = arguments[1] is NullReferenceExpression;
        // Argument #2 is a unformatted short message, print it out without any parameters
        if (!(arguments[1] is PrimitiveExpression || shortMessageIsNull))
            throw new CudafyLanguageException("When using Debug.Assert() the second parameter must be a string literal");
        string shortMessage = arguments[1].ToString();
        if (!shortMessageIsNull && !shortMessage.StartsWith("\""))
            throw new CudafyLanguageException("When using Debug.Assert() the second parameter must be a string literal");

        // Skip printing this first message if the value is NULL. 
        // Why? Because I assume the user wants to print the message with parameters without the hassle of two messages
        if (!shortMessageIsNull)
        {
            // Insert a newline into the end of the message
            shortMessage = shortMessage.Insert(shortMessage.Length - 1, "\\n");
            // Since this message does not support parameters, escape any % to avoid ErrorUnknown
            shortMessage = shortMessage.Replace("%", "%%");

            output.Write("printf(" + shortMessage + ");");
        }

        if (arguments.Count > 2)
        {
            // Argument #3 is an un/formated detailed message with or without parameters
            if (!(arguments[2] is PrimitiveExpression))
                throw new CudafyLanguageException("When using Debug.Assert() the third parameter must be a string literal");
            string detailedMessageFormat = arguments[2].ToString();
            if (!detailedMessageFormat.StartsWith("\""))
                throw new CudafyLanguageException("When using Debug.Assert() the third parameter must be a string literal");

            // Insert a newline into the end of the message
            detailedMessageFormat = detailedMessageFormat.Insert(detailedMessageFormat.Length - 1, "\\n");
            if (arguments.Count > 3)
            {
                output.Write("printf(" + detailedMessageFormat);
                if (arguments[3] is ArrayCreateExpression)
                {
                    var arrayCreateExpression = arguments[3] as ArrayCreateExpression;
                    foreach (var arrayArg in arrayCreateExpression.Initializer.Elements)
                    {
                        output.Write(',');
                        arrayArg.AcceptVisitor(visitor, data);
                    }
                }
                else
                {
                    output.Write(',');
                    arguments[3].AcceptVisitor(visitor, data);
                }
                output.Write(");");
            }
            else if (arguments.Count == 3)
            {
                // Since this message does not support parameters, escape any % to avoid ErrorUnknown
                detailedMessageFormat = detailedMessageFormat.Replace("%", "%%");
                output.Write("printf(" + detailedMessageFormat + ");");
            }
        }
        output.Write("assert(0);");
        output.Write("}");
    }
    else
    {
        output.Write("assert(");
        arguments[0].AcceptVisitor(visitor, data);
        output.Write(")");
    }
    return output.ToString();
}

 

Tip: To avoid printing the Assert short message and the detailed message, use null for the short message. Like this...

Debug.Assert(something < somethingElse, null, "%d < %d is not true!", something, somethingElse);

 

Happy Asserting!

Coordinator
Jan 27, 2012 at 9:22 AM

Committed and working nicely!  Thank you.  

Aug 28, 2012 at 6:23 AM
Edited Aug 28, 2012 at 7:58 AM

any idea when this will hit the main releases?

as

Debug.Assert(true,"test");

ends up as 

// Debug.Assert;

in the .cu file

EDIT:

solved: you have to set the CUDA version to 2 or above:

km = CudafyTranslator.Cudafy(eArchitecture.sm_20);

edit2: when the assert fails, a TDM happens!  very interesting.  

also, thanks for adding this feature!