Register forum user name Search FAQ

Gammon Forum

Notice: Any messages purporting to come from this site telling you that your password has expired, or that you need to verify your details, confirm your email, resolve issues, making threats, or asking for money, are spam. We do not email users with any such messages. If you have lost your password you can obtain a new one by using the password reset link.
 Entire forum ➜ MUSHclient ➜ Lua ➜ Nick Gammon's DebugFunction for Lua

Nick Gammon's DebugFunction for Lua

It is now over 60 days since the last post. This thread is closed.     Refresh page


Pages: 1 2  

Posted by Dave K   (7 posts)  Bio
Date Sat 17 Nov 2018 10:14 PM (UTC)

Amended on Sat 17 Nov 2018 10:17 PM (UTC) by Dave K

Message
This code proposed by Nick Gammon results in error. The assert is triggered because _G[f] is nil. Can anyone tell me why and how to fix this?

------------------------------

Posted by Nick Gammon Australia (21,841 posts) [Biography] bio Forum Administrator
Date Mon 07 May 2007 03:13 AM (UTC)
Amended on Mon 07 May 2007 05:00 AM (UTC) by Nick Gammon

Message
The code below illustrates how to make a special debugging version of any Lua function (including the MUSHclient world functions).

What it does is save the original function into a local variable, and then replace the function of that name in the global table (_G) with a debug version.

The debug version accepts any number of arguments (by using "..."), and then displays:


The name of the function that was called
Where it was called from (function name and line number)
A list of all the arguments which were sent to the function.


Then it calls the original function, passing any arguments that were sent to it.


It uses TraceOut to display this information, so to see it you need to turn Trace on (Game Menu -> Trace).



function DebugFunction (f)
assert (type (f) == "string" and type (_G [f]) == "function",
"DebugFunction must be called with a function name")
local old_f = _G [f] -- save original one for later
_G [f] = function (...) -- make new function
local t = debug.getinfo (2, "ln") -- get line and caller name
t.name = t.name or "<unknown place>"
TraceOut ("Function ", f, " called.") -- show name
TraceOut ("Called from ", t.name, " at line ", t.currentline) -- show caller
local n = select ("#", ...) -- number of arguments
if n == 0 then
TraceOut ("No arguments.")
else
for i = 1, n do -- show each argument
TraceOut ("Argument ", i, " = ", tostring ((select (i, ...))))
end -- for each argument
end -- have some arguments
old_f (...) -- call original function now
end -- debug version of the function
end -- DebugFunction


Now to use it, you call DebugFunction for each function you want to debug, passing the function name as an argument (you must get the spelling and capitalization exactly right):


DebugFunction ("ColourNote")
DebugFunction ("BroadcastPlugin")


This effectively replaces (in this example) ColourNote and BroadcastPlugin with debugging versions.

Now to test it:


function mytest ()
ColourNote ("red", "green", "hello, world") -- line 25
BroadcastPlugin (100, "some message") -- line 26
end -- mytest

mytest ()

Output

TRACE: Function ColourNote called.
TRACE: Called from mytest at line 25
TRACE: Argument 1 = red
TRACE: Argument 2 = green
TRACE: Argument 3 = hello, world
hello, world
TRACE: Function BroadcastPlugin called.
TRACE: Called from mytest at line 26
TRACE: Argument 1 = 100
TRACE: Argument 2 = some message


You can see from the trace output, that our debug version was called from "mytest" function, we are told the exact line number, and we are told the exact arguments.
- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,120 posts)  Bio   Forum Administrator
Date Reply #1 on Sun 18 Nov 2018 06:03 AM (UTC)

Amended on Sun 18 Nov 2018 06:07 AM (UTC) by Nick Gammon

Message
It would have been a lot clearer to link to the post in question rather than copying and pasting it, unformatted.


https://www.gammon.com.au/forum/?id=7862

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Nick Gammon   Australia  (23,120 posts)  Bio   Forum Administrator
Date Reply #2 on Sun 18 Nov 2018 06:11 AM (UTC)
Message
Dave K said:

This code proposed by Nick Gammon results in error. The assert is triggered because _G[f] is nil. Can anyone tell me why and how to fix this?


I've just tested the code in the linked post and it works exactly how it did in 2007.

So instead of saying it "results in error" you need to post exactly what you did, and the exact error message you got.

Perhaps you misspelt the name of the function you were trying to test? Or it wasn't a Lua function? Without any details we are just guessing here.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Dave K   (7 posts)  Bio
Date Reply #3 on Sun 18 Nov 2018 07:43 AM (UTC)

Amended on Sun 18 Nov 2018 07:58 AM (UTC) by Nick Gammon

Message
Nick, My apologies. You are absolutely correct. I have copy & paste the code. The only thing i have added are the two print(type) lines. Which results in string for f and nil for _G[f]. So then the assert is triggered as you can see in the output. I am using Lua 5.1
-------------------------------------------------------------------------------


function DebugFunction (f)

  print(type(f))
  print(type(_G[f]))
  
  assert (type (f) == "string" and type (_G [f]) == "function", 
          "DebugFunction must be called with a function name")
  local old_f = _G [f]  -- save original one for later
  _G [f] = function (...)  -- make new function
    local t = debug.getinfo (2, "ln")  -- get line and caller name
    t.name = t.name or "<unknown place>"
    TraceOut ("Function ", f, " called.")  -- show name
    TraceOut ("Called from ", t.name, " at line ", t.currentline)  -- show caller 
    local n = select ("#", ...)  -- number of arguments
    if n == 0 then
      TraceOut ("No arguments.")
    else
      for i = 1, n do  -- show each argument
        TraceOut ("Argument ", i, " = ", tostring ((select (i, ...))))
      end -- for each argument
    end -- have some arguments
    old_f (...)  -- call original function now
  end -- debug version of the function
end -- DebugFunction 

DebugFunction ("ColourNote")
DebugFunction ("BroadcastPlugin")

function mytest ()
  ColourNote ("red", "green", "hello, world")  -- line 25
  BroadcastPlugin (100, "some message")   -- line 26
end -- mytest

mytest ()



-------------------------------------------------------------
output:


Program completed in 1.09 seconds (pid: 12656).
"string"
"nil"
Nick.lua:5: DebugFunction must be called with a function name
stack traceback:
	[C]: in function 'assert'
	Nick.lua:5: in function 'DebugFunction'
	Nick.lua:25: in main chunk
Debugging session completed (traced 0 instructions).
Top

Posted by Nick Gammon   Australia  (23,120 posts)  Bio   Forum Administrator
Date Reply #4 on Sun 18 Nov 2018 08:01 AM (UTC)
Message
Quote:


Nick.lua:5: DebugFunction must be called with a function name



Judging by the fact that you are running Nick.lua, you are not running this code inside MUSHclient. Therefore the functions ColourNote and BroadcastPlugin do not exist.

You need to test with some function that you are interested in debugging. This code is not intended to run "stand alone".

You will also find that TraceOut does not exist if you run from the command line.

- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Dave K   (7 posts)  Bio
Date Reply #5 on Sun 18 Nov 2018 07:06 PM (UTC)

Amended on Mon 19 Nov 2018 09:34 PM (UTC) by Fiendish

Message
Thanks again. I created the functions (dummy versions) and added them. Also replace traceout with print. It works well now. I have two minor issies.
1- the tostring ((select (i, ...))) doesn't work as the third paramter of the print statement. it does if I move it like so print (tostring ((select (i, ...))),"Argument ", i, " = ") I was wondering if you know why that is.
2- If I change the function test() with a table wrapping our two functions, it does not catch the table's name. Is there any way to catch the table's name if our functions are inside a table?


function DebugFunction (f)
   
  assert (type (f) == "string" and type (_G [f]) == "function", 
          "DebugFunction must be called with a function name")
  local old_f = _G [f]  -- save original one for later
  _G [f] = function (...)  -- make new function
    local t = debug.getinfo (2, "ln")  -- get line and caller name
    t.name = t.name or "<unknown place>"
    print ("Function ", f, " called.")  -- show name
    print ("Called from ", t.name, " at line ", t.currentline)  -- show caller 
    local n = select ("#", ...)  -- number of arguments
    print("number of args:", n)
    if n == 0 then
      print ("No arguments.")
    else
      for i = 1, n do  -- show each argument
        print ("Argument ", i, " = ",tostring ((select (i, ...))))
      end -- for each argument
    end -- have some arguments
    old_f (...)  -- call original function now
  end -- debug version of the function
end -- DebugFunction 

ColourNote = function(p1,p2,p3)
   --print("What you sent is :",p1,p2,p3)
end

BroadcastPlugin = function(pr1,pr2)
   --print("What you sent is :",pr1,pr2)
end


DebugFunction ("ColourNote")
DebugFunction ("BroadcastPlugin")

myTable = {1,2,3,

--function mytest ()
ColourNote ("red", "green", "hello, world") , 
BroadcastPlugin (100, "some message")   
--end -- mytest

--mytest ()
}
Top

Posted by Fiendish   USA  (2,533 posts)  Bio   Global Moderator
Date Reply #6 on Mon 19 Nov 2018 04:02 PM (UTC)

Amended on Mon 19 Nov 2018 09:35 PM (UTC) by Fiendish

Message
Quote:
he tostring ((select (i, ...))) doesn't work

"Doesn't work" is almost never sufficient information for debugging. Doesn't work in what way?

Also
Template:codetag To make your code more readable please use [code] tags as described here.


[EDIT] I've done it for you

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Dave K   (7 posts)  Bio
Date Reply #7 on Mon 19 Nov 2018 05:29 PM (UTC)
Message
Thank you I have since resolved the tostring issue.

The only remaining issue is how to get it to recognize the function was called, is in a table. And what that tables name is.

Thank you.
Top

Posted by Fiendish   USA  (2,533 posts)  Bio   Global Moderator
Date Reply #8 on Mon 19 Nov 2018 09:37 PM (UTC)
Message
Dave K said:

The only remaining issue is how to get it to recognize the function was called, is in a table. And what that tables name is.

This request is underspecified. You could be pointing to the functions from a million different tables. What then?

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Nick Gammon   Australia  (23,120 posts)  Bio   Forum Administrator
Date Reply #9 on Tue 20 Nov 2018 02:31 AM (UTC)
Message

Can you give an example of what you are talking about? If you pass a table then only a reference to the table (the address) is passed. You could conceivably pass the name of a table, and then use _G[name] to find the table address.

That was why DebugFunction was called with a string (the function name) rather than the actual function. That way I can save the function name and then look up its address.


- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

Posted by Dave K   (7 posts)  Bio
Date Reply #10 on Tue 20 Nov 2018 03:40 PM (UTC)

Amended on Tue 20 Nov 2018 11:36 PM (UTC) by Dave K

Message
yes of course. Take your code for instance


function mytest ()
ColourNote ("red", "green", "hello, world") , 
BroadcastPlugin (100, "some message")   
end -- mytest

mytest ()


and change it to this. Lets call this instance 1


myTable =                                              -- assume this is on line 32
{1,2,3,                
ColourNote ("red", "green", "hello, world") , 
BroadcastPlugin (100, "some message")   
}


or even this ( instance 2 )


function mytest ()                                    -- assume this is on line 39

myTable = {1,2,3,                                     -- assume this s on line 40
ColourNote ("red", "green", "hello, world") , 
BroadcastPlugin (100, "some message")   
}

end -- mytest

mytest ()


I would like it to recognize that
in instance 1, the functions were called from inside a table called "myTable: on line 32

in instance 2, the functions were called from inside a function called mytest on line 40 which is in table myTable at line 39


essentially adding the ability to recognize where the functions reside (were called from) whether another function or a table. Because honestly majority of our functions are inside tables. so it would be a great help to know which table the called function belong to.
Top

Posted by Fiendish   USA  (2,533 posts)  Bio   Global Moderator
Date Reply #11 on Tue 20 Nov 2018 09:45 PM (UTC)

Amended on Tue 20 Nov 2018 10:40 PM (UTC) by Fiendish

Message
Dave K said:

in instance 1, the functions were called from inside a table called "myTable: on line 32

This is false.
What you've done is call the function and then assign its returned value to a location in the table. But the function is not in the table or being called from inside the table.

Also, tables don't have names. Tables are unnamed structures in memory. You can assign variables to reference those structures, but the variable is not the table.

For instance:

A = {1, 2, 3}
B = A

B[2] = "b"
print(A[2]) -- this will print "b"


Is the table's name A or is it B?

Answer: neither.

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Fiendish   USA  (2,533 posts)  Bio   Global Moderator
Date Reply #12 on Tue 20 Nov 2018 10:32 PM (UTC)

Amended on Tue 20 Nov 2018 10:40 PM (UTC) by Fiendish

Message
Further, tables don't store complex things. They reference them.



function my_function()
   print("hi")
end

table_A = {
    my_function
}

another_reference_to_my_function = table_A[1]

table_B = {
   another_reference_to_my_function
}

print(table_B[1]) -- prints a memory address
print(table_A[1]) -- prints the same memory address


So which table is my function inside?
Answer: neither, because that's not how memory references work.

https://github.com/fiendish/aardwolfclientpackage
Top

Posted by Dave K   (7 posts)  Bio
Date Reply #13 on Tue 20 Nov 2018 11:47 PM (UTC)

Amended on Wed 21 Nov 2018 04:16 PM (UTC) by Dave K

Message
Thank you for your reply. You have to forgive my ignorance. coming form C++ I am not sure what Lua can or cannot do.

So is there no way to track the execution of a lua script line by line? in order to produce a report such as below?


in Table abc

Called: function xyz  
It has n parameters
Param 1: apples
Param 2: oranges
.
.

It has m returns
return 1: red
return 2: yellow
.
.

Called: function xyz2 
It has n parameters
Param 1: pears
Param 2: bananas
.
.
It has m reruns
return 1: heavy
return 2: light
.
.
Exit Table abc

and so on....to the next table etc etc.
Top

Posted by Nick Gammon   Australia  (23,120 posts)  Bio   Forum Administrator
Date Reply #14 on Wed 21 Nov 2018 04:52 AM (UTC)
Message

I think we are heading into X-Y problem land.

So is there no way to track the execution of a lua script line by line?

Why do you want to do this?

You are not really “in” a table. A table is a data structure. If you have some code that reads that table and calls functions in it you could report what you are doing as you traverse the table.

There is some debugging stuff built into Lua.

As Fiendish points out, this code isn’t really going to do anything useful:

This makes a table “myTable” which will probably have something like this actually in it:

That is because you are storing the results of two function calls in that table. I don’t see how that is useful.

The code I presented earlier makes an intermediate function which is supposed to help you track wanted functions. In other words, if you are tracking “ColourNote” it replaces ColourNote with another function which displays that you have called ColourNote and its arguments, and then calls the real (saved) ColourNote.

This concept on its own should solve your problem, except I don’t think Fiendish nor I understand exactly what your problem is. This talk of tables is confusing in this context.


- Nick Gammon

www.gammon.com.au, www.mushclient.com
Top

The dates and times for posts above are shown in Universal Co-ordinated Time (UTC).

To show them in your local time you can join the forum, and then set the 'time correction' field in your profile to the number of hours difference between your location and UTC time.


42,780 views.

This is page 1, subject is 2 pages long: 1 2  [Next page]

It is now over 60 days since the last post. This thread is closed.     Refresh page

Go to topic:           Search the forum


[Go to top] top

Information and images on this site are licensed under the Creative Commons Attribution 3.0 Australia License unless stated otherwise.