This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
CoolisTheName007's profile picture

debug.traceback in CC! Traceback with file extracts!

Started by CoolisTheName007, 22 March 2013 - 09:17 AM
CoolisTheName007 #1
Posted 22 March 2013 - 10:17 AM
Ever needed a full traceback in CC, telling you not only that test:35 throwed an error, but which function had called that one, from which file, and even see what code errored without having to open the file? Fear no more!

Example:
This code

os.loadAPI'debug' --or dofile'debug'
debug.override()

local function c()
  error('a')
end
local function b()
  c()
end
local function a()
  b()
end
gives, give or take the paths:



(the * counts the nested calls to the error-ing function in a line)

That's it! The functions used are available in the debug table. See 'Mor'e Examples' for it.
You can, for instance, get only the traceback string without throwing an error using debug.traceback(),
and print text one page at a time using debug.step_print(text).


Code
pastebin get YWwLUUpk debug
or here: http://pastebin.com/YWwLUUpk

Mor'e advanced examples:
Spoiler

debug=dofile('debug')
function B()
  print(debug.traceback())
  os.pullEvent('key')

  s=''
  for i=1,100 do s=s..i..'\n' end
  very_long_text=s
  --will wait for user to press keys if the text is too big for the screen
  debug.printError(very_long_text)
  os.pullEvent('key')--wait
  --all in one go:

  function d()
	error(debug.traceback('ups!'))
  end
  local ok,err=pcall(d)
  if not ok then print(err) end
  os.pullEvent('key')--wait

  --with xpcall:

	--this catches latent/runtime erros not caused by error() calls:
	  function e()
		a=''..{}
	  end
	  local ok, err=xpcall(c,debug.traceback)
	  if not ok then debug.printError(err) end
	  os.pullEvent('key')--wait
	
	--dev section


	--however, this fails, but the shell still runs, right till a new function is created I suppose
	--(don't rerun this test)
	  function c()
		 c()
	  end
	  local ok2, ok, err=pcall(xpcall(c,function(...) end))
	  if not ok2 then debug.printError(ok)
	  elseif not ok then debug.printError(err) end
	  --[[java vm overflows aren't caught by xpcall
		xpcall does not destroys the stack before calling the handler function.
		If the stack is already at max capacity, it won't even accept the handler?
		Who knows...
	  ]]
end
function A()
	B()
endA()

External functions used: Kingdaroo's wrap text function, found somewhere. Thanks.

Changelog:
?-separated traceback from error function.
?-file extracts
24/03/13 - toerror function for pcall produced errors (only one level of trace depth).
09/04/13 - now an api, overrides are optional, xpcall is great for catching errors not throwed by error(), but java vm overflows are not catched by it. pcall seems to do fine, but won't get the traceback. My opinion? xpcall, for tracebacks in almost all ocasions.

Future:
Special shell that opens/closes editor based on the error line automatically?

Dev:
xpcall(function() local f,e=loadstring('1\n1') end,print) prints an error message, but returns true. I.e., the error handler is called, but the execution of the function continues.
FuuuAInfiniteLoop(F.A.I.L) #2
Posted 22 March 2013 - 12:25 PM
Say you have a function f, which is erroring, and that successive calls to it won't change it's behavior.
All you have to do to get a traceback is to run this nifty debug function like this:

debug(f,...)
where f is the erroring function and … it's arguments, to get something like:

bios:365
test:35
test:23
test:47: message
debug function and test, run for example:

function debug(f,...)
  local terr={}
  local err,ok=''
  local j=0
  repeat
	local old=_G._error or error
	rawset(_G,'_error',old)
	j=j+1
	rawset(_G,'error',function(s,l)
		return old(s,(l or 0)+j)
	end)
	terr[err]=true
	ok,err=pcall(f,...)
	if ok then print('No errors.') return
	else table.insert(terr,1,j==1 and err or err:match('^[^:]+:%d+')) end
  until (terr[err])
  rawset(_G,'error',_G._error)
  rawset(_G,'_error',nil)
  print(table.concat(terr,'\n'))
end


function test()
b=function()
  c()
end
c=function()
  error('message')
end
a=function()
  b()
end
a()
end
debug(test)

This can be improved; in my utilities pack I override loadfile so that Lua chunks get as name the full file path, instead of just the filename (for me it's an improvement because I tend to have init.lua files in different directories, and I want to distinguish them).
Another possibility (which needs the previous tweak) is to use the file location to actually display the code corresponding to the line numbers in the traceback. Then again, maybe someone already did this?
I have a program that do this, i dont remember in where section of the fomus only remember the title, debugger :)/>
CoolisTheName007 #3
Posted 22 March 2013 - 12:49 PM
I have a program that do this, i dont remember in where section of the fomus only remember the title, debugger :)/>
Hum, yours does not makes a traceback over the called functions, that is, if someone gets an error in function a called from function b, your log won't show that b called a. That's what my script does.
SuicidalSTDz #4
Posted 22 March 2013 - 12:54 PM
I have a program that do this, i dont remember in where section of the fomus only remember the title, debugger :)/>
Your 'debugger' was more of a 'bugger' seeing as it corrupted some files of mine and others as I recall.

On topic: This is pretty neat. It is definately nice to see new things on the forums. Keep it up! ^_^/>
CoolisTheName007 #5
Posted 23 March 2013 - 09:25 AM
Bumping for something incredible.
CoolisTheName007 #6
Posted 23 March 2013 - 03:02 PM
Bumping for extraction of code surrounding error and calling functions from files and a …screenshot!
theoriginalbit #7
Posted 23 March 2013 - 03:06 PM
Bumping for something incredible.
Fell like making the traceback more of a, well, real traceback?

<function_name> <file_path:line_number>
where line_number is the line the error or function call is on
so it would be like

c()  test/file:2
b()  test/file:5
a()  test/file:8
      test/file:11
theoriginalbit #8
Posted 23 March 2013 - 03:09 PM
If you remove [[ if err:match('^[^:]+')=='bios' then break end ]] from the new_error function, you will get the traceback going till the bios.
Naw only the bios. I wanted to go to the Java :P/> btw shell should come up before bios in your example of showing the bios.
CoolisTheName007 #9
Posted 23 March 2013 - 03:15 PM
Fell like making the traceback more of a, well, real traceback?
Done, not exactly as you wanted (that would imply parsing Lua code, e.g. one line , function calls in chains, x,y,z=a(), b(),c(), doesn't seems trivial, but, then again, it's pretty late for me. EDIT see? missed a parenthesis)
theoriginalbit #10
Posted 23 March 2013 - 03:18 PM
cool cool. thanks. NeverCast and I implemented a way of doing this for cctube. it was not very complicated but required a lot of work from us. but I like our little logger :)/>

Example of our error logs
Spoiler
####################
So this program walks in to an error…
CCTube error log: 772.85, time: 5.045, day: 120
2 loaded routines.
####################
API: Loader, Loaded: true
API: CCTUtils, Loaded: true
API: CCTFiles, Loaded: true
API: CCTLogger, Loaded: true
API: CCTState, Loaded: true
API: CCTDLogo, Loaded: true
API: CCTStream, Loaded: true
API: StreamWatcher, Loaded: true
API: PacketStream, Loaded: true
API: CCTVideoReader, Loaded: true
API: StringX, Loaded: true
API: Controls, Loaded: true
API: FrameImage, Loaded: true
API: DeltaBuffer, Loaded: true
API: FrameRenderer, Loaded: true
API: CCTNetworking, Loaded: true
API: CCTUpdate, Loaded: true
API: CCTGui, Loaded: true
API: CConfig, Loaded: true
API: Crc, Loaded: true
API: Json, Loaded: true
API: LoadingBar, Loaded: true
####################
thread: 1d1167e4 : main thread

CCTGui: runAccountLoginGui
Controls: runWindow
Controls: enterCallback
CCTState: push
CCTGui:511: Unable to push invalid state, nil
####################
thread: 29a59ac1 : networking

CCTNetworking: run
####################
Network activity
[768] CCTNetworking Init!
[770] get: http://cctube.info/online
[771] Wait completed: http_success
CoolisTheName007 #11
Posted 23 March 2013 - 03:39 PM
cool cool. thanks. NeverCast and I implemented a way of doing this for cctube. it was not very complicated but required a lot of work from us. but I like our little logger :)/>
So, you basically had to do for every function/process you wanted logged, do something like ok, err=pcall(f,…) and save the errr somewhere, then error again? Or did you had the same idea, using pcall(error,'',level)?
theoriginalbit #12
Posted 23 March 2013 - 04:53 PM
So, you basically had to do for every function/process you wanted logged, do something like ok, err=pcall(f,…) and save the errr somewhere, then error again? Or did you had the same idea, using pcall(error,'',level)?
Nah we had a system where in every function that we felt it was required (we missed some unimportant ones) we added this line to the start of the function

CCTLogger.enter(FILENAME, 'func_name')
and at the end of the function (or where ever we had a return)

CCTLogger.leave()
and when leave was called we used the fenv to make sure we were leaving the same one we entered ( to avoid errors :P/> )
NeverCast #13
Posted 04 April 2013 - 05:53 PM
It worked really well too, I loved it :D/>
superaxander #14
Posted 04 April 2013 - 09:47 PM
Super usefull
Kilobyte #15
Posted 05 April 2013 - 11:36 PM
Definately sounds neat :D/>

Might use it in my OS (modified though, with credts ofc) if you are ok with it. was looking for something like this :D/>

You are the man
CoolisTheName007 #16
Posted 06 April 2013 - 07:05 AM
Might use it in my OS (modified though, with credts ofc) if you are ok with it. was looking for something like this :D/>

I'm ok with it!
CoolisTheName007 #17
Posted 10 April 2013 - 07:46 AM
Bumping for bugfixes, a step_print function (press key for next page), with the help of one of Kingdaroo's functions, and advanced examples, where errors not caused by error calls (e.g. a=''..{}) can still trigger a traceback, by using xpcall, except for overflow errors, in which case hell breaks loo… I mean, the computer crashes.
FuuuAInfiniteLoop(F.A.I.L) #18
Posted 10 April 2013 - 11:18 AM
I have a program that do this, i dont remember in where section of the fomus only remember the title, debugger :)/>
Your 'debugger' was more of a 'bugger' seeing as it corrupted some files of mine and others as I recall.

On topic: This is pretty neat. It is definately nice to see new things on the forums. Keep it up! ^_^/>

If you have to read the description that says thatthe file is remplaced with some code that is processed by the program and the program can clean that to get the program, and mine shows what the program do and in the order is called
SuicidalSTDz #19
Posted 10 April 2013 - 11:38 AM
If you have to read the description that says thatthe file is remplaced with some code that is processed by the program and the program can clean that to get the program, and mine shows what the program do and in the order is called
Grammar is in need of some tweaking.. Better than usual though ^_^/>

Your debugger inserted lines of text into my document, causing it to fail. Which could technically be considered malicious, but who cares :P/>
robhol #20
Posted 04 June 2013 - 09:53 AM
Tried using this today… failed. First, getting it to run was a problem in itself, I ran os.loadAPI and override() somehow wasn't in the table. It worked when I did debug=dofile("debug"), though.
Then it seems to mess with the error() function.. causing it to not exist, for some reason.
KaoS #21
Posted 04 June 2013 - 11:17 AM
I remember doing this too :)/> although my reasons were less ethical. If only you could track what parameters were passed to the function
CoolisTheName007 #22
Posted 04 June 2013 - 11:34 AM
Tried using this today… failed.
Fixed a table access, was debug.tracebackError and not debug.traceback. Should work now, but can't really test. I'm kinda of done with CC.
robhol #23
Posted 26 June 2013 - 03:31 AM
That's a shame, as it's still not doing anything. I'm getting

myFile:23:bad argument blablabla and no further info at all. My startup file loads the debug api and calls debug.override().
jaredallard #24
Posted 18 September 2014 - 07:52 PM
Year old thread, I know, but what's this licensed under?