Jump to content




Creating A Bsod, Error Handling And Custom Errors


66 replies to this topic

#1 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 14 February 2013 - 06:02 PM

Important!


Now while this method of having a BSOD is good, it is NOT a replacement for testing your code. ALWAYS test your code. Also the method of implementing a BSOD is not perfect, there are some errors which will not appear in the BSOD, for example syntax errors will not since syntax is checked before running the program.


What normally causes a Blue Screen of Death (BSoD)

In Windows a BSoD happens when a system failure or error occurs, whether its caused by hardware, software, memory problems, etc. Normally when a BSoD occurs it is very helpful and tells you why it happened and sometimes how to fix it. Find more information on a BSOD here: http://en.wikipedia....Screen_of_Death

For Unix (thats you too Mac people) this BSoD is normally refered to as a 'kernal panic'. This kernal panic is normally caused by that same things as a BSoD, although it occurs less in Unix due to the fact that, well, Unix is awesome. Find more information on a kernal panic here: http://en.wikipedia....ki/Kernel_panic



Errors and You

Getting errors are a big part of debugging programs in any programming language. They quite often provide very useful information about what went wrong and how to fix it. An example of an error in ComputerCraft is
startup:4: attempt to call nil
this error tells us that we are trying to call a function that does not exist in the program 'startup' on line 4. Now these error messages can vary, but the basic format should always tell you the program, line number and the error message. Sometimes they can be bad, like this error
String expected
which comes up from time to time, off the top of my head I think its to do with the rednet api. Knowing how to read and interpret an error can make you a very powerful debugger and make fixing your programs very easy. However I am not here to tell you more about debugging and such, I am here to help you with error handling.


Error Handling (how the BSoD works)

Now in programming languages when an error occurs it needs to be handled. Now just a bit of a deviation... When a program is executing there is a data type, known as a stack, that contains where the program is up to, each time you call a function it is pushed on the top of the stack, and when the function finishes it is pushed off the stack and the previous stack item continues from where it left off. Now back to topic... When an error is raised it will continue down the stack until something handles it. If you don not handle the error in your code it will go back to whatever has called your program, if that program does not handle the error it will continue does that programs stack until it is handled. In the case of ComputerCraft the shell handles our errors and prints them to the screen.

Now as stated we can handle the errors ourself, just like we can create errors intentionally. Now in most high level programming languages to handle an error we can use a try/catch statement. For example in Java we would use this syntax:
try
{
  // what we want to do that can throw an error
}
catch(SomePreciseException e)
{
  // print out what we want for this specific exception
}
catch(Exception e)
{
  // generically catch any other errors that we did not expect or such
}

Now sadly in Lua we do not have a try catch statement. However in Lua we have a function called pcall, which means protected call. Now pcall can only run functions, so this means that we must surround our code that could cause errors in a function.

Now what pcall does is it calls its first argument, which is the function, in protected mode so that it catches any errors while it is running. If there are no errors pcall will return a true, plus any values that are returned by the call, otherwise it will return false and the error message. Now the error message does not need to be a string, infact it could be anything, including a table, but I shall leave those bits up to you to figure out completely.

So really for all intensive purposes using the error function is a throw and the pcall is a catch.


Making Errors

In Lua, just like other languages, we can also create errors. The most common method is by using the 'error' function but we can also use the assert function. Now say we have the following example;
local number = tonumber(read())
if not number then error("invalid input, not a number") end
print(number)
now while this is a completely valid example it can be simplified with assert like so
local number = assert(tonumber(read()), "invalid input, not a number")
print(number)
So assert will attempt the first parameter and if it returns false or nil then it will raise an error with the message in the second parameter.

Now as detailed previously when an error occurs there is location information that comes with it, it includes the file and line number that the error occurs. This is the same for assert as it raises an error. Sometimes however we don not wish the error to show as ours, for example, what if we are creating an api that needs to be called with a specific parameter. This is when we use the second parameter of the error function. This second parameter is the levelthat the error should be reported, that way we can blame someone else not us. So with this example
function this(str)
  if type(str) ~= "string" then error("Expected string, got "..type(str)) end
end
and this example
function this(str)
  assert(type(str) ~= "string", "Expected string, got "..type(str))
end
these would both point the finger at us, saying that our function is the one to have caused an error. Now unfortunately the assert function does not allow the use of the level argument. So if we wish to use the level we must do it with the error function like so
function this(str)
  if type(str) ~= "string" then error("Expected string, got "..type(str), 2) end
end
This will now point the finger at the function that calls ours. Now that we all know this, onto the BSoD

EDIT: After reading some of the code made by the CC devs, I have discovered that if you do a level of 0 on the error function it will not print the error message with the file name and line number. It is essentially
printError( 'Some Error' )
error()

this is a very handy trick to quickly exit the program, print a red message, but now show it as an error in the code. as such I have updated the assert function in the next section to also do the same.


A Custom Assert

Firstly I would just like to say thank you to user: Stary2001 on the #computercraft IRC channel for helping me with this code, wasn't in the right mind of thinking and he helped me.

Now as stated above unlike error assert does not have a level. Now this is essentially how the assert works
if not [someCondition] then
  error("Some message", 1)
end
Now while this is all well and good, as you can see it provides the default level 1, pointing the blame for the error to our function. Now as stated above, we can just use error(<msg>, 2) but consider this scenario:
local function someFunc( p1, p2, p3, p4, p5, p6, p7, p8 )
  if type(p1) ~= "string" then error("Arg1 expected string",2) end
  if type(p2) ~= "string" and type(p2) ~= "nil" then error("Arg2 expected string or nil",2) end
  if type(p3) ~= "boolean" then error("Arg3 expected boolean",2) end
  if type(p4) ~= "table" then error("Arg4 expected table",2) end
  if type(p5) == "number" then error("Arg5 numbers not number",2) end
  if type(p6) ~= "boolean" then error("Arg6 expected boolean",2) end
  if type(p7) == "nil" then error("Arg7 nil not allowed",2) end
  if not p8 then error("Arg8 nil not allowed",2) end
end
now thats a lot of code there that is not overly unreadable and if you're anything like me, you may have missed one or 2 end to the if statements as well, just to check the parameters

Now this is where a custom assert can come in handy. Here is a version of assert that has, optional, levels:
local function assert( condition, message, level )
  level = tonumber( level ) or 1
  level = level == 0 and 0 or (level + 1)
  if not condition then
	error( message or "assertion failed!", level )
  end
  return condition
end
Now if you don't provide a level, it will default to pointing finger at your code causing the error, just like the default assert, however now we can provide a level to the assert. so the example we used before now becomes this
local function someFunc( p1, p2, p3, p4, p5, p6, p7, p8 )
  assert( type(p1) == "string", "Arg1 expected string", 2 )
  assert( type(p2) == "string" or type(p2) == "nil", "Arg2 expected string or nil", 2 )
  assert( type(p3) == "boolean", "Arg3 expected boolean", 2 )
  assert( type(p4) == "table", "Arg4 expected table", 2 )
  assert( type(p5) ~= "number", "Arg5 numbers not allowed", 2 )
  assert( type(p6) == "boolean", "Arg6 expected boolean", 2 )
  assert( type(p7) ~= "nil", "Arg7 nil not allowed", 2 )
  assert( p8, "Arg8 nil not allowed", 2 )
end
Now while this isn't any shorter in lines, it is easier to read, and there is no worry about forgetting an end ;)


The Code

This is the basics behind the code
local function main()
  -- main code body goes here

  return true -- this is needed so that when the program ends normally the BSOD will not appear
end

local ok, err = pcall(main)

if not ok then
  -- an error has occurred so print the BSOD here
end

Now you don not need to have all your code inside the 'main' function, you can have functions outside if it, because as stated before the error will go until something handles it, which is our pcall on 'main'. So this code would work as well
local function doSomething()
  error("Some bad error! :P/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>",2)
end

local function main()
  doSomething()

  return true -- this is needed so that when the program ends normally the BSOD will not appear
end

local ok, err = pcall(main)

if not ok then
  print(err)
end

Now there are ways for us to print our own errors and exit so the BSOD does not appear
local running = false

local function main()
  if not running then
	print("You silly person!")
	return true
  end

  return true
end

local ok, err = pcall(main)

if not ok then
  print(err)
end
By using return true we can stop the bsod from showing up, while still exiting the program and telling the person the error message that is not bad enough to require a BSOD.

We can also pass parameters to functions with pcall, there will be an example in the next code snippet.
Lets say that we have areas in code that we want to thown an error and handle it in our own code, lets say an invalid file or something, we can do this
local function openFile(path)
  local handle = assert(io.open(path, "r"), "File does not exist! :(/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>") -- now if there is a problem with this it will throw an error
  handle.close()
end

local function main()
  write("Please input a file: ")
  local input = read()
  local ok, err = pcall(openFile, input)
  if not ok then
	print(err)
  else
	print("That file exists! :)/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>/>")
  end
end

local ok, err = pcall(main)
if not ok then
  print(err)
end
Now in the above example we are handling the potential error in the code meaning that it does not go back to the BSOD, but instead to our pcall in the code.

When programming now, with the BSOD, I like to use a C-style main function like so
local function main(argc, argv)

  return true
end

local ok, err = pcall(main, #{...}, {...})
if not ok then
  print(err)
end
This code passes the runtime argument count and arguments to the main function. Very C-style programming hey ;) We can then validate the runtime arguments inside the main, obviously its just as easy to do this outside the main, but either way, does not make a huge difference.

Now this is an example that I made and send to someone else, read the comments for some information
-- functions can be out here, thats fine
local function someFunction()
  error("Input error")
end

-- this is a very C-style language feel to program entry
local function main(argc, argv)
  -- main program body goes in here
  -- you can do argument checking in here, or before the pcall if you wish
  -- either way, but there is support for it here, argc is the count, argv is the arguments

  -- functions can also be in our main function
  local function anotherFunction()
		error("Bad runtime error!")
  end

  -- you can even handle errors yourself if you do not want a certain error to go to the bsod
  local ok, err = pcall(someFunction)
  if not ok then
		print("We got an error from someFunction and 'caught' it here, it gave us "..err)
  end

  if math.random(1,4) == 4 then
		anotherFunction()
  end

  return true -- absolutely needed or else if will show BSOD when quitting without error!
end

local ok, err = pcall(main, #({...}), {...}) -- pass the argument count and the arguments to main

if not ok then
  -- errors, except syntax errors, if not caught in the main program body will come here.
  -- Errors when they occur will go down the stack returning each function until something handles it,
  -- normally this would go to the shell, but since we are using pcall we are catching this error and
  -- then printing it here

  -- draw the BSOD here however you wish
  print("BSOD: Really bad runtime error "..err)
end


A full working example, BSOD and all!

Now I'm not going to go into detail about how this works here, have a read through and see if you can understand it, and if not comment on this thread and ask
Spoiler

I hope this tutorial was helpful in learning about error handling and controlled error creation, oh and a making a BSOD. Please feel free to leave any suggestions or comments :)

Edited by theoriginalbit, 06 July 2014 - 03:23 AM.


#2 Dlcruz129

    What's a Lua?

  • Members
  • 1,423 posts

Posted 14 February 2013 - 06:21 PM

Nice! I think I'll add this to my operating system.

#3 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 14 February 2013 - 06:28 PM

View PostDlcruz129, on 14 February 2013 - 06:21 PM, said:

Nice! I think I'll add this to my operating system.
:) I'm glad you found it useful :)

#4 superaxander

  • Members
  • 609 posts
  • LocationHolland

Posted 14 February 2013 - 07:39 PM

That's gonna help me so much. Thanks now I know how assert works and pcall. In some of my old program's in lua I tried using try but it didn't work. Now here is the solution thanks. I'm so happy now!

#5 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 14 February 2013 - 08:23 PM

View Postsuperaxander, on 14 February 2013 - 07:39 PM, said:

That's gonna help me so much. Thanks now I know how assert works and pcall. In some of my old program's in lua I tried using try but it didn't work. Now here is the solution thanks. I'm so happy now!
No problems :) thank you for the feedback :) I look forward to seeing your new programs :)

#6 NDFJay

  • Members
  • 216 posts
  • LocationLeeds, England

Posted 15 February 2013 - 12:36 AM

Got to say, this is one sexy tutorial :) Made my error handling function now :) if an error is caught it does this >>

Spoiler

EDIT--

When Click on either "Email Error to NDFJay" or "Save Error Log" this happens >>
Spoiler

:D

Edited by NDFJay, 15 February 2013 - 01:14 AM.


#7 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 15 February 2013 - 12:39 AM

View PostNDFJay, on 15 February 2013 - 12:36 AM, said:

Got to say, this is one sexy tutorial :) Made my error handling function now :) if an error is caught it emails me haha
Why thank you :) Lol nice. gotta make sure that you minimise on the bugs then ;) you don't wanna be spammed with emails :P

EDIT: I LOVE the BSOD, well it looks a little like a Kernal Panic, but in any case, love it :D

Edited by TheOriginalBIT, 15 February 2013 - 12:52 AM.


#8 NDFJay

  • Members
  • 216 posts
  • LocationLeeds, England

Posted 15 February 2013 - 12:45 AM

View PostTheOriginalBIT, on 15 February 2013 - 12:39 AM, said:

View PostNDFJay, on 15 February 2013 - 12:36 AM, said:

Got to say, this is one sexy tutorial :) Made my error handling function now :) if an error is caught it emails me haha
Why thank you :) Lol nice. gotta make sure that you minimise on the bugs then ;) you don't wanna be spammed with emails :P

Thats my next job haha, so far the only error I get is that one that I forced by creating a false variable haha

#9 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 15 February 2013 - 12:50 AM

View PostNDFJay, on 15 February 2013 - 12:45 AM, said:

Thats my next job haha, so far the only error I get is that one that I forced by creating a false variable haha
I figured as much :) ;)

#10 SuicidalSTDz

    Permutator of Strings

  • Members
  • 1,308 posts
  • LocationPennsylvania

Posted 15 February 2013 - 01:07 AM

NDF-Jay is right, this is one "sexy" turtortial. I can see some places where this could benefit users with EnderOS. Nice work :)

#11 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 15 February 2013 - 01:19 AM

View PostSuicidalSTDz, on 15 February 2013 - 01:07 AM, said:

NDF-Jay is right, this is one "sexy" turtortial. I can see some places where this could benefit users with EnderOS. Nice work :)
Why thank you :) I actually have been using this in the development of cctube for quite some time and when I got some time today away from cctube development I decided to type it up and post it for others to benefit from :) glad you're all liking it :)

#12 NeverCast

  • Members
  • 400 posts
  • LocationChristchurch, New Zealand

Posted 15 February 2013 - 08:46 AM

Awesome tutorial TheOriginalBit :D
Very nice!

Also regarding Kernel Panics less often in Unix, That could be perceptive as typically smart people use Unix and are less likely to break it. Another probably reason is that the Unix Kernel is modular, unlike Windows where it's core components are loaded at boot and that's it. You can load DLL libraries, but they are in the user space, and loading drivers in to Windows often requires a restart for the reason that it's loaded during boot.

The difference here is that if for example, you have an incorrect sound or graphics driver, and your device sends an unknown command to that said driver, it could cause the driver to error. This cascades to the rest of Windows as it's one giant machine. Causing the BSOD.

In Unix, the drivers are loaded as modules ( Unless compiled in to the kernel as some default ones may be ). If a module is to crash, it will not typically take down the Kernel. Unix will just unload that driver and spill some grumbling rant in to your error logs.
This doesn't make Unix immune, and it does make drivers are little bit slower in Unix if they're not compiled in to the kernel, because they must communicate with messages and syscalls.

I hope this information is correct, this is based from my understanding of the how the two systems work and I cannot cite this information. Feel free to critique this information if you can be certain something is invalid (:

Edit: Also, it's 'Popped' off the stack :P

#13 AnthonyD98™

  • Members
  • 193 posts
  • LocationAuckland, New Zealand

Posted 15 February 2013 - 09:11 AM

This could be useful for SelectOS especially in debugging!

#14 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 15 February 2013 - 09:37 AM

View PostNeverCast, on 15 February 2013 - 08:46 AM, said:

Awesome tutorial TheOriginalBit :D
Very nice!

-big snip-
Thanx :)
If what you and I understand is correct, thats exactly my point. Unix is awesome. :P

View PostAnthonyD98™, on 15 February 2013 - 09:11 AM, said:

This could be useful for SelectOS especially in debugging!
:) glad it helped :)

#15 LBPHacker

  • Members
  • 766 posts
  • LocationBudapest, Hungary

Posted 16 February 2013 - 06:41 AM

View PostDlcruz129, on 14 February 2013 - 06:21 PM, said:

Nice! I think I'll add this to my operating system.

Heh :D Same here. (Everybody's writing an own OS huh?)

#16 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 16 February 2013 - 01:18 PM

View PostLBPHacker, on 16 February 2013 - 06:41 AM, said:

Heh :D Same here. (Everybody's writing an own OS huh?)
:D

Yeh door locks and OSes are the most common programs

#17 Dlcruz129

    What's a Lua?

  • Members
  • 1,423 posts

Posted 16 February 2013 - 04:15 PM

View PostTheOriginalBIT, on 16 February 2013 - 01:18 PM, said:

View PostLBPHacker, on 16 February 2013 - 06:41 AM, said:

Heh :D/> Same here. (Everybody's writing an own OS huh?)
:D/>

Yeh door locks and OSes are the most common programs

I'm not gonna release mine. You're welcome. :P

#18 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 16 February 2013 - 04:21 PM

View PostDlcruz129, on 16 February 2013 - 04:15 PM, said:

I'm not gonna release mine. You're welcome. :P
I'm not saying don't release it, I'm saying they are just the most common.

#19 Shnupbups

  • Members
  • 596 posts
  • LocationThat place over there. Y'know. The one where I am.

Posted 16 February 2013 - 04:32 PM

Awesome! Will put this in my custom shell (not OS) that I may or may not release.

#20 theoriginalbit

    Semi-Professional ComputerCrafter

  • Moderators
  • 7,332 posts
  • LocationAustralia

Posted 16 February 2013 - 04:35 PM

View PostShnupbups100, on 16 February 2013 - 04:32 PM, said:

Awesome! Will put this in my custom shell (not OS) that I may or may not release.
lol. thanx





1 user(s) are reading this topic

0 members, 1 guests, 0 anonymous users