Difference between revisions of "Debugging Tips"
Myopic Mole (talk | contribs) (added instructions on how to use the discreet debug script that got chopped off the end) |
m (<lsl> tag to <source>) |
||
(7 intermediate revisions by 5 users not shown) | |||
Line 5: | Line 5: | ||
The solution is to define a simple debugging utility function as follows. | The solution is to define a simple debugging utility function as follows. | ||
< | <source lang="lsl2"> | ||
notify(string | notify(string message) | ||
{ | { | ||
llOwnerSay(message); | |||
} | } | ||
</ | </source> | ||
then whenever we need to check the value of a variable simple call | then whenever we need to check the value of a variable simple call | ||
< | <source lang="lsl2"> | ||
notify("your debug message here"); | // default | ||
</ | // { | ||
// touch_start(integer num_detected) | |||
// { | |||
notify("your debug message here"); | |||
// } | |||
// } | |||
</source> | |||
don't forget to typecast as necessary, for example: | don't forget to typecast as necessary, for example: | ||
< | <source lang="lsl2"> | ||
integer a = 3; | // default | ||
integer b = 4; | // { | ||
integer result = a * b; | // touch_start(integer num_detected) | ||
notify((string)result); | // { | ||
</ | integer a = 3; | ||
integer b = 4; | |||
integer result = a * b; | |||
notify((string)result); | |||
// } | |||
// } | |||
</source> | |||
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". | 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". | ||
< | <source lang="lsl2"> | ||
notify(string | notify(string message) | ||
{ | { | ||
//llOwnerSay(message); | |||
} | } | ||
</ | </source> | ||
if you're working with list use something along the lines of | if you're working with list, you could use something along the lines of | ||
notify(llList2String( | <source lang="lsl2"> | ||
// notification contains one list element | |||
notify(llDumpList2String( | notify(llList2String(listOfNames, integerIndex)); | ||
</source> | |||
or | |||
<source lang="lsl2"> | |||
// notification contains entire list as a string | |||
notify(llDumpList2String(listOfNames, ",")); | |||
// or alternatively a CSV (comma-seperated-value string): | |||
// notify(llList2CSV(listOfNames)); | |||
</source> | |||
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: | |||
<source lang="lsl2"> | |||
notifyList(list listOfMessages) | |||
{ | |||
llOwnerSay(llDumpList2String(listOfMessages, ",")); | |||
// or: | |||
// llOwnerSay(llList2CSV(listOfMessages)); | |||
} | |||
</source> | |||
==Error Reporting and Debugging== | ==Error Reporting and Debugging== | ||
We can use the same concept for tracking issues after the code is complete as follows | We can use the same concept for tracking issues after the code is complete as follows | ||
< | <source lang="lsl2"> | ||
notify(string genus, string | notify(string genus, string message) | ||
{ | { | ||
key creator = "a822ff2b-ff02-461d-b45d-dcd10a2de0c2"; | |||
// uncoment next line to turn notifications off | |||
// genus = "off" | |||
if (genus == "dbg") | |||
{ | |||
// coment out next line to turn off debugging | |||
llInstantMessage(creator, "DEBUG: " + message); | |||
} | |||
else if (genus == "err") | |||
{ | |||
llOwnerSay("ERROR: " + message); | |||
llInstantMessage(creator, "ERROR: " + message + " in object " + llGetObjectName() + " that belongs to " | |||
+ llKey2Name(llGetOwner()) + " in region " + llGetRegionName()); | |||
} | |||
else if (genus == "info") | |||
{ | |||
// no prepend because it's a normal message | |||
llOwnerSay(message); | |||
} | |||
else | |||
{ | |||
;//do nothing, notifications were turned off | |||
} | |||
} | } | ||
</ | </source> | ||
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 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 | We're using [[llOwnerSay]] 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. | Other mechanisms that could be used include [[llEmail]] or [[llHTTPRequest]] to post a message to an external logging system perhaps. | ||
You define your own | You define your own custom message types and communicate using the most appropriate method. | ||
==More Reuse== | ==More Reuse== | ||
We can extend the reuse even further by making this a discreet script on it's own within the object as follows: | We can extend the reuse even further by making this a discreet script on it's own within the object as follows: | ||
< | <source lang="lsl2"> | ||
notify(string genus, string | notify(string genus, string message) | ||
{ | { | ||
key creator = "a822ff2b-ff02-461d-b45d-dcd10a2de0c2"; | |||
// uncoment this line to turn notifications off | |||
// genus = "off" | |||
if (genus == "dbg") | |||
{ | |||
// coment out this line to turn off debugging | |||
llInstantMessage(creator, "DEBUG: " + message); | |||
} | |||
else if (genus == "err") | |||
{ | |||
llOwnerSay("ERROR: " + message); | |||
llInstantMessage(creator, "ERROR: " + message + " in object " + llGetObjectName() + " that belongs to " | |||
+ llKey2Name(llGetOwner()) + " in region " + llGetRegionName()); | |||
} | |||
else if (genus == "info") | |||
{ | |||
// no prepend because it's a normal message | |||
llOwnerSay(message); | |||
} | |||
else | |||
{ | |||
;//do nothing, notifications were turned off | |||
} | |||
} | } | ||
default | default | ||
{ | { | ||
state_entry() | |||
{ | { | ||
notify(str, key) | notify("info", "notification system ready"); | ||
} | |||
link_message(integer sender_num, integer num, string str, key id) | |||
{ | |||
// we're using the key as a second string parameter | |||
notify(str, id); | |||
} | } | ||
} | } | ||
</ | </source> | ||
To send a notification you simply use in your implementation: | To send a notification you simply use in your implementation: | ||
< | <source lang="lsl2"> | ||
default | default | ||
{ | { | ||
touch_start(integer | touch_start(integer num_detected) | ||
{ | { | ||
key id = llDetectedKey(0); | |||
string message = llKey2Name(id) + " attempted to steal Mjölnir!"; | |||
// 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 | |||
llMessageLinked(LINK_SET, 0, "info", message); | |||
} | } | ||
} | } | ||
</ | </source> | ||
---- | |||
Return to [[Good_Building_Practices]] |
Latest revision as of 14:36, 25 January 2015
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.
notify(string message)
{
llOwnerSay(message);
}
then whenever we need to check the value of a variable simple call
// default
// {
// touch_start(integer num_detected)
// {
notify("your debug message here");
// }
// }
don't forget to typecast as necessary, for example:
// default
// {
// touch_start(integer num_detected)
// {
integer a = 3;
integer b = 4;
integer result = a * b;
notify((string)result);
// }
// }
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".
notify(string message)
{
//llOwnerSay(message);
}
if you're working with list, you could use something along the lines of
// notification contains one list element
notify(llList2String(listOfNames, integerIndex));
or
// notification contains entire list as a string
notify(llDumpList2String(listOfNames, ","));
// or alternatively a CSV (comma-seperated-value string):
// notify(llList2CSV(listOfNames));
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:
notifyList(list listOfMessages)
{
llOwnerSay(llDumpList2String(listOfMessages, ","));
// or:
// llOwnerSay(llList2CSV(listOfMessages));
}
Error Reporting and Debugging
We can use the same concept for tracking issues after the code is complete as follows
notify(string genus, string message)
{
key creator = "a822ff2b-ff02-461d-b45d-dcd10a2de0c2";
// uncoment next line to turn notifications off
// genus = "off"
if (genus == "dbg")
{
// coment out next line to turn off debugging
llInstantMessage(creator, "DEBUG: " + message);
}
else if (genus == "err")
{
llOwnerSay("ERROR: " + message);
llInstantMessage(creator, "ERROR: " + message + " in object " + llGetObjectName() + " that belongs to "
+ llKey2Name(llGetOwner()) + " in region " + llGetRegionName());
}
else if (genus == "info")
{
// no prepend because it's a normal message
llOwnerSay(message);
}
else
{
;//do nothing, notifications were turned off
}
}
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 llOwnerSay 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 custom 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:
notify(string genus, string message)
{
key creator = "a822ff2b-ff02-461d-b45d-dcd10a2de0c2";
// uncoment this line to turn notifications off
// genus = "off"
if (genus == "dbg")
{
// coment out this line to turn off debugging
llInstantMessage(creator, "DEBUG: " + message);
}
else if (genus == "err")
{
llOwnerSay("ERROR: " + message);
llInstantMessage(creator, "ERROR: " + message + " in object " + llGetObjectName() + " that belongs to "
+ llKey2Name(llGetOwner()) + " in region " + llGetRegionName());
}
else if (genus == "info")
{
// no prepend because it's a normal message
llOwnerSay(message);
}
else
{
;//do nothing, notifications were turned off
}
}
default
{
state_entry()
{
notify("info", "notification system ready");
}
link_message(integer sender_num, integer num, string str, key id)
{
// we're using the key as a second string parameter
notify(str, id);
}
}
To send a notification you simply use in your implementation:
default
{
touch_start(integer num_detected)
{
key id = llDetectedKey(0);
string message = llKey2Name(id) + " attempted to steal Mjölnir!";
// 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
llMessageLinked(LINK_SET, 0, "info", message);
}
}
Return to Good_Building_Practices