debug.getinfo(n).name

User avatar
SegFault22
Member
 
Posts: 870
Joined: Mon May 21, 2012 03:17

debug.getinfo(n).name

by SegFault22 » Thu Aug 25, 2016 09:27

I'm working on a mod wherein it is necessary to find what function called the current function as a string value, concatenate it with the string " > ", and then concatenate that with the name of the current function. This is for an independent logging system, what would otherwise clogger-up the minetest log unnecessarily if I just implemented the minetest log system for all of the log entires (just 12 different types of materials would send over 100 log messages, most of them of significant verbosity, which is also configurable such that the most verbose stuff based on its assigned number value doesn't end up in the log, but it's still a lot even then).

When I use this to get the calling function and the current function, it looks like this:
Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
debug.getinfo(2).name.." > "..debug.getinfo(1).name

since there are so many log entires being done in several different places, it would be helpful to place this chunk of code somewhere that can be referenced later, whenever a log entry is made, instead of adding it to every single log-function call. Part of the reason for my mod is to eliminate unnecessary repetitive calls to functions or duplicate entries of the same code, but if I add this chunk of code to every log-function call, it will be a whole lot of extra crap that I don't want in my code. However, I can not find any references on the internet, especially the lua to any method of putting code inside a variable, and when I try to just slap the code onto a variable, I get errors because the interpretor tries to execute the code when the variable is set. I need the variable to simply contain the code, so that when the variable is put somewhere, the code is run there as if it were typed out.

I cannot put it into a function, because it would skew the results of the debug.getinfo() functions to be relative to the function containing it, instead of the function where the code is supposed to run.

Is it even possible to put this code in a single place, not having it executed until a reference is made to it, wherein it will run as if it were actually written-out in place of the reference?


EDIT: I figured it out! I can put it into a function. I just increment both numbers by one, to ignore the current function (the "debug info getter"), do each debug.getinfo(n).name call separately and assign each to a variable, check if the variable is nil and if it is change the value to string "MAIN", then concatenate both variables with the string " -> " between them. It looks like this:

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
function <modID>.stacktrace()
   local calling_function = debug.getinfo(3).name
   local called_function = debug.getinfo(2).name
   if calling_function == nil then calling_function = "MAIN" end
   if called_function == nil then called_function = "MAIN" end
   return calling_function.." -> "..called_function
end





--[[
This code is licensed under the GNU GPL v3.0 or later. If you run this and your computer blows up, I am not liable. It probablly won't blow up, but just in case it does, I am not liable.
--]]


This works perfectly for what I need it, as the function call "<modID>.stacktrace()" is shorter than "debug.getinfo(2).name.." -> "..debug.getinfo(1).name", keeping the code relatively clean.

If you need to use this code for something, it is licensed under the GNU GPL v3.0 (or later), and I would love to know if this helped.

EDIT2: If there was a better way to do this, please let me know. This function isn't as optimal as possible because I had to increment the stack number, but it works, and if there is a better method to do the same thing, I will gladly replace it with the better method.

EDIT3: This code fails if debug.getinfo(3) or debug.getinfo(2) returns nil, because it is not possible to access the .name field of nil, since nil is not a table. The only way for this to work is to also check whether debug.getinfo(3) and debug.getinfo(2) are nil, and avoid trying to access the .name field of either one that is nil if it is nil. I won't waste my time posting a human-readable implementation of the code that would do this properly, because it wouldn't be appreciated - it would be longer than expected, because it's so much easier to just use as little code as possible to get the expected value, only caring for having to calculate the expected input type(s), totally ignoring the possibility of an unexpected type being accidentally passed - for example, simply using the syntax of the and/or operators alone to filter the values, will produce a subtle flaw that can crash the server in certain exotic circumstances. Since part of the goal of my mod is to prevent the server crashing from stupid errors, I won't do that; I will implement some combination of the and/or operators' last-minute-fix "syntax" with logically rigid if-then-else blocks, such that it is not at all possible for an incorrect data type to be passed, ever - this will be somewhat longer "than it has to be", but I believe it's worth it, when the alternative is having your server crash for "I don't know why" reason.
 

User avatar
kaeza
Member
 
Posts: 2141
Joined: Thu Oct 18, 2012 05:00
GitHub: kaeza
IRC: kaeza diemartin blaaaaargh
In-game: kaeza

Re: debug.getinfo(n).name

by kaeza » Fri Aug 26, 2016 00:09

The `debug.traceback` function conveniently builds the entire traceback for you and returns it as a string:

That said, if you want to build the traceback yourself for whatever reason, hope this helps, just for fun:
Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
local function caller_name(level)
   local info = debug.getinfo(level+1)
   if not info then
      return
   end
   local name = info.name
   if name then
      return name
   elseif info.what == "main" then
      return "<main chunk of "..info.short_src..">"
   elseif info.what == "C" then
      return "<C code>"
   else
      return "<function @ "..info.short_src..":"..info.linedefined..">"
   end
end

local function build_traceback()
   local list, level = { }, 1
   while true do
      level = level + 1
      local name = caller_name(level)
      if not name then
         break
      end
      list[#list+1] = name
   end
   return table.concat(list, "\n")
end

local function foo()
   print(build_traceback())
end

local function bar()
   foo()
end

local function baz()
   bar()
end

local b = (function()
   baz()
end)() -- Some trickery to confuse the debug mechanism
       -- into not determining the function "name".

--[[
OUTPUT:

foo  <-- bottom (most recently called) stack frame
bar
baz
<function @ pcn.lua:44>
<main chunk of pcn.lua>
<C code>  <-- top stack frame, Lua interpreter
]]


All that said, though, if the server crashes because of errors in the code, it's way better to crash than to run in an inconsistent state.
Your signature is not the place for a blog post. Please keep it as concise as possible. Thank you!

Check out my stuff! | Donations greatly appreciated! PayPal | BTC: 1DFZAa5VtNG7Levux4oP6BuUzr1e83pJK2
 

User avatar
rubenwardy
Member
 
Posts: 4500
Joined: Tue Jun 12, 2012 18:11
GitHub: rubenwardy
IRC: rubenwardy
In-game: rubenwardy

Re: debug.getinfo(n).name

by rubenwardy » Fri Aug 26, 2016 16:19

SegFault22 wrote:for example, simply using the syntax of the and/or operators alone to filter the values, will produce a subtle flaw that can crash the server in certain exotic circumstances. Since part of the goal of my mod is to prevent the server crashing from stupid errors, I won't do that; I will implement some combination of the and/or operators' last-minute-fix "syntax" with logically rigid if-then-else blocks, such that it is not at all possible for an incorrect data type to be passed, ever - this will be somewhat longer "than it has to be", but I believe it's worth it, when the alternative is having your server crash for "I don't know why" reason.


Firstly, I was trying to help by showing a way which avoids excess if statements.
And FWIW, it's documented in the standard, and I personally find it easier to read. Logical Operators - PIL 3.3

A useful Lua idiom is x = x or v, which is equivalent to

if not x then x = v end

i.e., it sets x to a default value v when x is not set (provided that x is not set to false).

Another useful idiom is (a and b) or c (or simply a and b or c, because and has a higher precedence than or), which is equivalent to the C expression


It's also logically equivalent. This:

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
return item and item.name or "main"


does exactly the same thing as what you wrote, no matter what types you put:

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
if item then
    if item.name then
        return item.name
    end
else
    return "main"
end


Since it's part of the standard and logically equivalent, you can't argue that it's a hack or instable, it's the language. It's syntactic sugar / an idiom.

However, I admit that it strays from the typical classical binary definition of a logical operator, so you are justified in not using it due to purism reasons (but not for instability reasons), in which case, at least use:

Your phone or window isn't wide enough to display the code box. If it's a phone, try rotating it to landscape mode.
Code: Select all
if item and item.name then
    return item.name
else
    return "main"
end


tl;dr: It's fair enough not to use this due to purism reasons, but it's not instable and won't introduce anymore errors than your code above would
 

User avatar
SegFault22
Member
 
Posts: 870
Joined: Mon May 21, 2012 03:17

Re: debug.getinfo(n).name

by SegFault22 » Mon Aug 29, 2016 18:31

I actually did find in the lua.org guide part 3.3 about how the operators "and" and "or" are designed to act contrary to their logical "meaning", although in a manner that is perfectly predictable; I thought it was neat, but my project was a lot simpler back then (just an ipairs machine) so I just forgot about it. I could have used it if I really needed to, as I do now.
Really, the only better way to implement it would require a new operator in Lua itself, which is outside of my ability to create "directly" but would be very useful for cases just like this.
 


Return to Modding Discussion

Who is online

Users browsing this forum: No registered users and 2 guests

cron