Difference between revisions of "Debugging Tips"

From Second Life Wiki
Jump to navigation Jump to search
m (detabbing. tab -> 4 spaces)
Line 8: Line 8:
notify(string msg)
notify(string msg)
{
{
llOwnerSay(msg);
    llOwnerSay(msg);
}
}
</lsl>
</lsl>
Line 32: Line 32:
notify(string msg)
notify(string msg)
{
{
//llOwnerSay(msg);
    //llOwnerSay(msg);
}
}
</lsl>
</lsl>
Line 39: Line 39:


notify(llList2String(listName, integerIndex)); // notification contains one list element
notify(llList2String(listName, integerIndex)); // notification contains one list element
or
    or
notify(llDumpList2String(listName, ",")); notification contains entire list as a string
notify(llDumpList2String(listName, ",")); notification contains entire list as a string


Line 47: Line 47:
notifyList(list msg)
notifyList(list msg)
{
{
llOwnerSay(llDumpList2String(msg, ","));
    llOwnerSay(llDumpList2String(msg, ","));
}
}
</lsl>
</lsl>
Line 57: Line 57:
notify(string genus, string msg)
notify(string genus, string msg)
{
{
key creator = "a822ff2b-ff02-461d-b45d-dcd10a2de0c2";
    key creator = "a822ff2b-ff02-461d-b45d-dcd10a2de0c2";
//genus = "off" //uncoment this line to turn notifications off
    //genus = "off" //uncoment this line to turn notifications off
if (genus == "dbg")
    if (genus == "dbg")
{
    {
llInstantMessage(creator, "DEBUG: " + msg); //coment out this line to turn off debugging
        llInstantMessage(creator, "DEBUG: " + msg); //coment out this line to turn off debugging
}
    }
else if (genus == "err")
    else if (genus == "err")
{
    {
llOwnerSay("ERROR: " + msg);
        llOwnerSay("ERROR: " + msg);
llInstantMessage(creator, "ERROR: " + msg + " in object " + llGetObjectName() + " that belongs to " + llKey2Name(llGetOwner()) + " in region " + llGetRegionName());
        llInstantMessage(creator, "ERROR: " + msg + " in object " + llGetObjectName() + " that belongs to " + llKey2Name(llGetOwner()) + " in region " + llGetRegionName());
}
    }
else if (genus == "info")
    else if (genus == "info")
{
    {
llOwnerSay(msg); //no prepend because it's a normal message
        llOwnerSay(msg); //no prepend because it's a normal message
}
    }
else
    else
{
    {
//do nothing, notifications were turned off
        //do nothing, notifications were turned off
}
    }
}
}
</lsl>
</lsl>
Line 93: Line 93:
notify(string genus, string msg)
notify(string genus, string msg)
{
{
key creator = "a822ff2b-ff02-461d-b45d-dcd10a2de0c2";
    key creator = "a822ff2b-ff02-461d-b45d-dcd10a2de0c2";
//genus = "off" //uncoment this line to turn notifications off
    //genus = "off" //uncoment this line to turn notifications off
if (genus == "dbg")
    if (genus == "dbg")
{
    {
llInstantMessage(creator, "DEBUG: " + msg); //coment out this line to turn off debugging
        llInstantMessage(creator, "DEBUG: " + msg); //coment out this line to turn off debugging
}
    }
else if (genus == "err")
    else if (genus == "err")
{
    {
llOwnerSay("ERROR: " + msg);
        llOwnerSay("ERROR: " + msg);
llInstantMessage(creator, "ERROR: " + msg + " in object " + llGetObjectName() + " that belongs to " + llKey2Name(llGetOwner()) + " in region " + llGetRegionName());
        llInstantMessage(creator, "ERROR: " + msg + " in object " + llGetObjectName() + " that belongs to " + llKey2Name(llGetOwner()) + " in region " + llGetRegionName());
}
    }
else if (genus == "info")
    else if (genus == "info")
{
    {
llOwnerSay(msg);  //no prepend because it's a normal message
        llOwnerSay(msg);  //no prepend because it's a normal message
}
    }
else
    else
{
    {
//do nothing, notifications were turned off
        //do nothing, notifications were turned off
}
    }
}
}


default
default
{
{
state_entry()  
    state_entry()  
{
    {
notify("info", "notification system ready");
        notify("info", "notification system ready");
}
    }
     link_message(integer source, integer num, string str, key id)
     link_message(integer source, integer num, string str, key id)
     {
     {
Line 134: Line 134:
     touch_start(integer c)
     touch_start(integer c)
     {
     {
    string msg = llKey2Name(llDetectedKey(0)) + " attempted to steal Mjölnir!";
        string msg = llKey2Name(llDetectedKey(0)) + " attempted to steal Mjölnir!";
         llMessageLinked(LINK_SET, 0, "info", msg);
         llMessageLinked(LINK_SET, 0, "info", msg);
         //this may seem odd but it's valid to use the key paramter of llMessageLinked as a second string passing facility if not actually used for a key
         //this may seem odd but it's valid to use the key paramter of llMessageLinked as a second string passing facility if not actually used for a key

Revision as of 19:54, 30 June 2012

One of the hardest things with scripting anything non trivial in LSL, especially if you come from an software development environment where you're used to combined editor and debugger, is the seeming inability to debug LSL code.

Simple Example

The solution is to define a simple debugging utility function as follows.

<lsl> notify(string msg) {

   llOwnerSay(msg);

} </lsl>

then whenever we need to check the value of a variable simple call

<lsl> notify("your debug message here"); </lsl>

don't forget to typecast as necessary, for example:

<lsl> integer a = 3; integer b = 4; integer result = a * b; notify((string)result); </lsl>

This allows us to reuse the same function for reporting anything to the owner of the object, rather than using public chat. The advantage to doing this is we can comment out a single line in the code function as follows to disable all debugging, or we can search and replace and instance of "notify" with "//notify".

<lsl> notify(string msg) {

   //llOwnerSay(msg);

} </lsl>

if you're working with list, you could use something along the lines of

notify(llList2String(listName, integerIndex)); // notification contains one list element

   or

notify(llDumpList2String(listName, ",")); notification contains entire list as a string

However, the lsl compiler is not an optimizing compiler. This means that even if you disable notify() as shown above (by commenting out its body), you will still suffer a performance hit from converting the list to a string. A better plan would be to create a separate notifyList function which takes a list parameter and dumps it:

<lsl> notifyList(list msg) {

   llOwnerSay(llDumpList2String(msg, ","));

} </lsl>

Error Reporting and Debugging

We can use the same concept for tracking issues after the code is complete as follows

<lsl> notify(string genus, string msg) {

   key creator = "a822ff2b-ff02-461d-b45d-dcd10a2de0c2";
   //genus = "off" //uncoment this line to turn notifications off
   if (genus == "dbg")
   {
       llInstantMessage(creator, "DEBUG: " + msg); //coment out this line to turn off debugging
   }
   else if (genus == "err")
   {
       llOwnerSay("ERROR: " + msg);
       llInstantMessage(creator, "ERROR: " + msg + " in object " + llGetObjectName() + " that belongs to " + llKey2Name(llGetOwner()) + " in region " + llGetRegionName());
   }
   else if (genus == "info")
   {
       llOwnerSay(msg); //no prepend because it's a normal message
   }
   else
   {
       //do nothing, notifications were turned off
   }

} </lsl>

We're doing a number of things here: handling different kids of messages, informational for normal running, error reporting to the owner and creator and debug messages. To turn off debug simply comment out the debug line as shown in the code, and uncomment the genus="off" line to disable all notifications.

We're using llOwner for the current owner and llInstantMessage for contacting the script creator that will continue to work after ownership has passed - you just have insert your own key at the top.

Other mechanisms that could be used include llEmail or llHTTPRequest to post a message to an external logging system perhaps.

You define your own custome message types and communicate using the most appropriate method.

More Reuse

We can extend the reuse even further by making this a discreet script on it's own within the object as follows:

<lsl> notify(string genus, string msg) {

   key creator = "a822ff2b-ff02-461d-b45d-dcd10a2de0c2";
   //genus = "off" //uncoment this line to turn notifications off
   if (genus == "dbg")
   {
       llInstantMessage(creator, "DEBUG: " + msg); //coment out this line to turn off debugging
   }
   else if (genus == "err")
   {
       llOwnerSay("ERROR: " + msg);
       llInstantMessage(creator, "ERROR: " + msg + " in object " + llGetObjectName() + " that belongs to " + llKey2Name(llGetOwner()) + " in region " + llGetRegionName());
   }
   else if (genus == "info")
   {
       llOwnerSay(msg);  //no prepend because it's a normal message
   }
   else
   {
       //do nothing, notifications were turned off
   }

}

default {

   state_entry() 
   {
       notify("info", "notification system ready");
   }
   link_message(integer source, integer num, string str, key id)
   {
       notify(str, key); //we're using the key as a second string parameter
   }

} </lsl>

To send a notification you simply use in your implementation:

<lsl> default {

   touch_start(integer c)
   {
       string msg = llKey2Name(llDetectedKey(0)) + " attempted to steal Mjölnir!";
       llMessageLinked(LINK_SET, 0, "info", msg);
       //this may seem odd but it's valid to use the key paramter of llMessageLinked as a second string passing facility if not actually used for a key
   }

} </lsl>


Return to Good_Building_Practices