How to implement breakpoints in Lua using the standard hooks? - debugging

This question is motivated by Exercise 25.7 on p. 264 of Programming in Lua (4th ed.), and more specifically, the optimization proposed in the hint (I've emphasized it in the quote below):
Exercise 25.7: Write a library for breakpoints. It should offer at least two functions
setbreakpoint(function, line) --> returns handle
removebreakpoint(handle)
We specify a breakpoint by a function and a line inside that function. When the program hits a breakpoint, the library should call debug.debug. (Hint: for a basic implementation, use a line hook that checks whether it is in a breakpoint; to improve performance, use a call hook to trace program execution and only turn on the line hook when the program is running the target function.)
I can't figure out how to implement the optimization described in the hint.
Consider the following code (this is, of course, an artificial example concocted only for the sake of this question):
function tweedledum ()
while true do
local ticket = math.random(1000)
if ticket % 5 == 0 then tweedledee() end
if ticket % 17 == 0 then break end
end
end
function tweedledee ()
while true do
local ticket = math.random(1000)
if ticket % 5 == 0 then tweedledum() end
if ticket % 17 == 0 then break end
end
end
function main ()
tweedledum()
end
Function main is supposed to represent the program's entrypoint. Functions tweedledum and tweedledee are almost identical to each other, and do little more than invoke each other repeatedly.
Suppose I set a breakpoint on tweedledum's assignment line. I can implement a call hook can check whether tweedledum has been invoked, and then sets a line hook that will check when the desired line is being invoked1.
More likely than not, tweedledum will invoke tweedledee before it breaks out of its loop. Suppose that this happens. The currently enabled line hook can detect that it is no longer in tweedledum, and re-install the call hook.
At this point the execution can switch from tweedledee to tweedledum in one of two ways:
tweedledee can invoke tweedledum (yet again);
tweedledee can return to its invoker, which happens to be tweedledum.
And here's the problem: the call hook can detect the event in (1) but it cannot detect the event in (2).
Granted, this example is very artificial, but it's the simplest way I could come up with to illustrate the problem.
The best approach I can think of (and it's very weak!) is to keep track of the stack depth N at the first invokation of tweedledum, have the line hook reinstall the call hook only when the stack depth sinks below N. Thus, the line hook will be in force as long as tweedledee is in the stack, whether it is being executed or not.
Is it possible to implement the optimization described in the hint using only the standard hooks available in Lua?2
1 My understanding is that, by installing the line hook, the call hook essentially uninstalls itself. AFAICT, only one hook can be active per coroutine. Please do correct me if I am wrong.
2 Namely: call, line, return, and count hooks.

And here's the problem: the call hook can detect the event in (1) but it cannot detect the event in (2).
And that's where you're wrong: There's three possible hook events: l for line, c for call and r for return.
Inside your hook function you can treat return and call events as almost the same, except when the return event is fired, you're still inside the called function, so the target function is one place higher in the stack.
debug.sethook(function(event, line)
if event == "call" or event == "return" then
if debug.getinfo(event=='call' and 2 or 3).func == target then
debug.sethook(debug.gethook(), 'crl')
else
debug.sethook(debug.gethook(), 'cr')
end
elseif event == 'line' then
-- Check if the line is right and possibly call debug.debug() here
end
end, 'cr')
It's all in the manual ;)
Note that, when setting the hook, you may need to check if you're currently inside the target function; otherwise you may skip a break point unless you call (and return from) another function before reaching it.

Related

Lua and conditional "compilation" : need clarification

I understood that there is no preprocessor in LUA, so nothing like #define and so on.
But I'd like to have "debug" options. For example, I'd like an optional console debug like :
if do_debug then
function msg(s)
print(s)
end
else
function msg(s)
end
end
msg(string.format(".............",v1,v2,......))
It works, but I wonder what is the CPU cost in "no debug" mode.
The fact is that I call a lot of these msg() function with large strings, sometimes built and formated with a lot of variables. So I would like to avoid extra work. But I suppose that LUA is not clever enough to see that my function is empty, and that there's no need to build its parameter...
So is there a turnaround to avoid these extra costs in LUA ?
NB : you may say that the CPU cost is negligible, but I'm using this for a realtime audio process and CPU does matter in this case.
You can't avoid the creation and unwinding of the stack frame for the msg function.
But what you can improve, at least in the snippet shown, is moving the string.format call into msg:
if do_debug then
function msg(...)
print(string.format(...))
end
else
function msg() end
end
msg(".............",v1,v2,......)
Another approach, trading readability for performance, would be to always do the if do_debug right where you want to print the debug message. A conditional check is much faster than a function call.
But the only way to truly avoid the function call that I know of would be to compile your own version of the Lua interpreter, with a (at least rudimentary) preprocessor added to the parser.

Is the term trap (of Bash) more accurate than the generic "callback" term in programming?

Please consider:
scripttmp=$(mktemp -d)
cleanup() {
rm -rf "${scripttmp}"
}
trap cleanup EXIT
I understand cleanup is a call(ed)back function, as it is being called just before exiting from the main function, from which it is part (I grasp the main function as a function in the general sense even though there is no function syntax around its code).
If I never actually called cleanup before --- I don't really "call it back"; I just call it before exiting, but not "back".
Is the term trap more accurate than the generic "callback" term in programming?
"Callback" comes from the fact that you give a routine a piece of code for later execution (for whenever some condition is fulfilled), and the routine "calls back" by executing that code.
Compare this with giving someone your phone number for when they need it. When they need it, they call you back. At that point, they may never have called you before. The process of "calling" is the callback.
In a shell script, trap is used to install a callback that will be evaluated when a signal is "caught" or "trapped". A standard shell also allows trapping a special event, namely when the shell exits (EXIT), and the bash shell additionally supports trapping errors (ERR), exit from a function (RETURN), and every simple command (DEBUG).
The trap utility does not allow for calling a callback function for generic asynchronous events.
In your example, cleanup could be called a callback function. It is installed with trap and will execute just before the current shell exits. In other words, the trap utility installs an EXIT trap that will call the callback function cleanup when the EXIT event is caught.
The code installed by trap action event will be executed in a manner equivalent to eval action when the given event occurs. The action could therefore be any shell code, not necessarily just a function call.
Another word for your cleanup function would be a "handler", a routine that handles something (in this case, handling the termination of the script), and possibly more specifically "an EXIT handler". If it was used to handle a caught signal, it would be "the signal handler for that particular signal". It is also common to call this function a "trap handler" (a handler installed by trap) or just "trap", although this is not "more accurate".
The term "call back" does not imply anyhow that it has to be called again (it's NOT called callagain or such).
It just means that the caller tells the callee how to notify him at some defined condition (so how to call him back then) - or which action to take at that condition (in behalf, the execution of the action goes back to the caller as the caller initiated that action through handing it over to the calle as the callback).
So the wording callback is very clear and consistent.
So for #Kusalananda example there are different possibilities.
Normally a single (or a single batch of) callback(s) is handed over to the callee (the routine, someone). It is well defined which type(s) of information(s) the callee will supply to the callback (so give it as arguments to the routine or tell it on phone to the recipient of the call). Possibly it's no information at all, just a call to the routine or a telephone call (so when the callback is defined to have no arguments or optional arguments). But anyhow normally the caller has some intention to hand the callback over to the callee, so the defined callback-routine or the one whos phone is ringing (it can also be a computer that gets the incoming call) is doing something the caller initiated when he gave that callback to the callee (so the action goes back to him, whether the target can identify the caller or not). Of course a callaback can be defined in a way that the callee that calls the callback always hands over some information to the callback-routine or to the someone answering the phone, it can be some detailed information about the condition, some information gathered by the calle in between or even a reference to the original caller.
On the other hand trap is a very specific word and not at all similiar or interchangeable with callback. It means about what the official translation/definition of the spoken word trap is. A trap is not called by the callee and is not handed over to a callee. A callee can be trapped. In Your example above the calee is just the remaining procedure following after installing the trap (up to the point where the trap is uninstalled or deactivated). A trap somehow is the opposite of a callback as the callee (or the trap insatlled by callee) traps some condition but from outside of the possible calees context.

Create a reduced size communicator for REDUCE operations

Sometimes not all ranks are needed for a compute task. I am trying to tailor the communicator to the need but MPI appear to stop after the reduced communicator has been constructed (rank=8). MPI debug says 'fatal error in PMPI_Comm_rank: invalid communicator'. My essential code is:
PROGRAM mpi_comm_create
USE MPI
IMPLICIT NONE
INTEGER comm,i,ierr,group,rank,rank1,root,size,size1,redcomm,redgroup
INTEGER,ALLOCATABLE::ranks(:)
comm=MPI_COMM_WORLD
!---------------------------------------------------------------------------
CALL MPI_init(ierr)
CALL MPI_comm_size(comm,size,ierr)
CALL MPI_comm_rank(comm,rank,ierr)
CALL MPI_comm_group(comm,group,ierr)
!--------------------------------------------------------------------------
size1=size-2
ALLOCATE(ranks(size1))
ranks(1:size1)=[0:size1-1]
!---------------------------------------------------------------------------
!Define new group redgroup and communicator redcomm with size1 processes
CALL MPI_group_incl(group,size1,ranks,redgroup,ierr)
CALL MPI_comm_create(comm,redgroup,redcomm,ierr)
CALL MPI_comm_rank(redcomm,rank1,ierr)
!---------------------------------------------------------------------------
!Use redcomm in a REDUCE operation
!---------------------------------------------------------------------------
CALL MPI_group_free(redgroup,ierr)
CALL MPI_comm_free(redcomm,ierr)
!---------------------------------------------------------------------------
CALL MPI_FINALIZE(ierr)
DEALLOCATE(ranks)
STOP; END
Firstly use MPI_COMM_SPLIT - it is easier as indicated in replies to your essentially identical earlier question.
Secondly the error is because all processes are making the second call to mpi_comm_rank, but not all processes are in the communicator specified in that call.
The root cause of the crash is ranks [size1:size-1] invoke MPI_Comm_rank() on MPI_COMM_NULL, which is not allowed since MPI_COMM_NULL is not a valid communicator. In order to get rid of the crash, you can replace
CALL MPI_comm_rank(redcomm,rank1,ierr)
with
IF (MPI_COMM_NULL.ne.redcomm) CALL MPI_comm_rank(redcomm,rank1,ierr)
From a performance point of view (which is what your question is really about), i do not expect any significant difference between MPI_Comm_create() and MPI_Comm_split().
From a semantic point of view, both MPI_Comm_split() and MPI_Comm_create() are collective operations, and they must be invoked by all MPI tasks from MPI_COMM_WORLD. If MPI tasks [size1:size-1] cannot/should not be involved in the creation of redcomm, then you can use MPI_Comm_create_group(), that should only be called by MPI tasks [0:size1-1].
If all tasks can be involved in the creation of redcomm, then i suggest you stick to MPI_Comm_split() in order to keep your code simpler. If you do not need MPI tasks [size1:size-1] being part of a valid communicator redcomm, then i also suggest you use color=MPI_UNDEFINED on these tasks, so redcomm will be MPI_COMM_NULL instead of a valid communicator. But keep in mind MPI_COMM_NULL is not a valid communicator, so it is up to your code not to invoke MPI_Comm_rank(), MPI_Comm_size(), MPI_Comm_free() and other subroutines on it.

Debugger implementation - Step over issue

I am currently writing a debugger for a script virtual machine.
The compiler for the scripts generates debug information, such as function entry points, variable scopes, names, instruction to line mappings, etc.
However, and have run into an issue with step-over.
Right now, I have the following:
1. Look up the current IP
2. Get the source line from that
3. Get the next (valid) source line
4. Get the IP where the next valid source line starts
5. Set a temporary breakpoint at that instruction
or: if the next source line no longer belongs to the same function, set the temp breakpoint at the next valid source line after return address.
So far this works well. However, I seem to be having problems with jumps.
For example, take the following code:
n = 5; // Line A
if(n == 5) // Line B
{
foo(); // Line C
}
else
{
bar(); // Line D
--n;
}
Given this code, if I'm on line B and choose to step-over, the IP determined for the breakpoint will be on line C. If, however, the conditional jump evaluates to false, it should be placed on line D. Because of this, the step-over wouldn't halt at the expected location (or rather, it wouldn't halt at all).
There seems to be little information on debugger implementation of this specific issue out there. However, I found this. While this is for a native debugger on Windows, the theory still holds true.
It seems though that the author has not considered this issue, either, in section "Implementing Step-Over" as he says:
1. The UI-threads calls CDebuggerCore::ResumeDebugging with EResumeFlag set to StepOver.
This tells the debugger thread (having the debugger-loop) to put IBP on next line.
2. The debugger-thread locates next executable line and address (0x41141e), it places an IBP on that location.
3. It calls then ContinueDebugEvent, which tells the OS to continue running debuggee.
4. The BP is now hit, it passes through EXCEPTION_BREAKPOINT and reaches at EXCEPTION_SINGLE_STEP. Both these steps are same, including instruction reversal, EIP reduction etc.
5. It again calls HaltDebugging, which in turn, awaits user input.
Again:
The debugger-thread locates next executable line and address (0x41141e), it places an IBP on that location.
This statement does not seem to hold true in cases where jumps are involved, though.
Has anyone encountered this problem before? If so, do you have any tips on how to tackle this?
Since this thread comes in Google first when searching for "debugger implement step over". I'll share my experiences regarding the x86 architecture.
You start first by implementing step into: This is basically single stepping on the instructions and checking whether the line corresponding to the current EIP changes. (You use either the DIA SDK or the read the dwarf debug data to find out the current line for an EIP).
In the case of step over: before single stepping to the next instruction, you'll need to check if the current instruction is a CALL instuction. If it's a CALL instruction then put a temporary breakpoint on the instruction following it and continue execution till the execution stops (then remove it). In this case you effectively stepped over function calls literally in the assembly level and so in the source too.
No need to manage stack frames (unless you'll need to deal with single line recursive functions). This analogy can be applied to other architectures as well.
Ok, so since this seems to be a bit of black magic, in this particular case the most intelligent thing was to enumerate the instruction where the next line starts (or the instruction stream ends + 1), and then run that many instructions before halting again.
The only gotcha was that I have to keep track of the stack frame in case CALL is executed; those instructions should run without counting in case of step-over.

implementing step over, dwarf

Im working on a source level debugger. The debug info available in elf
format. How could be 'step over' implemented?
The problem is at 'Point1', anyway I can wait for the
next source line (reading it from the .debug_line table).
Thanks
if (a == 1)
x = 1; //Point1
else if (a == 2)
x = 1;
z = 1;
I'm not sure I understand the question entirely, but I can tell you how GDB implements its step command.
Once control has entered a particular compilation unit, GDB reads that CU's debugging information; in particular, it reads the CU's portion of the .debug_line section and builds a table that maps instruction addresses to source code positions.
When the step begins, GDB looks up the source location for the current PC. Then it steps by machine instruction, looking up the source location of the new PC each time, until the source location changes. When the source location changes, the step is complete.
It also computes the frame ID—the base address of the stack frame, and the start address of the function—after each step, and checks if that has changed. If it has, that means that we've stepped into or returned from a recursive call, and the step is complete.
To see why it's necessary to check the frame ID as well as the source location, consider stepping through a call to the following function:
int fact(n) { if (n > 0) { return n * fact(n-1); } else return 1; }
Since this function is defined entirely on the same source line, stepping by instruction until the source line changes would step you through all the recursive calls without stopping. However, when we enter a new call to fact, the stack frame base address will have changed, indicating that we should stop. This gives us the following behavior:
fact (n=10) at recurse.c:4
(gdb) step
fact (n=9) at recurse.c:4
(gdb) step
fact (n=8) at recurse.c:4
GDB's next command combines this general behavior with appropriate logic for recognizing function calls and letting them return to completion. As before, one must use frame IDs in deciding when calls have truly returned to the original frame; and there are other complications.
It's worth thinking a bit about how to treat inlined instances of functions (which DWARF does describe). But that's a bit much for this question.
Not to discourage experimentation, but if I were beginning a debugger project, I would want to look at Apple's work-in-progress debugger, lldb, which is open source.

Resources