STEP OVER OPERATION FOR MACHINE CODE FUNCTION CALLS

Information

  • Patent Application
  • 20140344789
  • Publication Number
    20140344789
  • Date Filed
    May 17, 2013
    11 years ago
  • Date Published
    November 20, 2014
    10 years ago
Abstract
A method for implementing a step over operation by a debugger for an instruction in a routine includes receiving a step over command for an instruction and determining whether the instruction is a branch used for a function call. If the instruction is not a branch used for a function call, then the debugger treats the instruction as not a function call. If the instruction is a branch used for a function call, then the debugger determines whether the instruction is generated from source code. If the instruction is not generated from source code, then the debugger treats the instruction as not a function call. If the instruction is generated from source code, then the debugger treats the instruction as a function call.
Description

This disclosure relates to debugger operations. In particular, it relates to a debugger step function at the machine code level.


BACKGROUND

A function, also known as a procedure, routine, or subroutine, is a sequence of instructions that perform a specific operation. Functions may be used to simplify a program by breaking parts of the program into simple steps and eliminating redundancy in codes. A calling routine gives control of the program to the function, passes variables into the function, and receives a result from the function. To call a function, the calling routine may contain a collection of instructions that branch to the function and specify the return address back into the calling routine after the function has executed.


SUMMARY

A method for implementing a step over operation by a debugger for an instruction in a routine includes receiving a step over command for an instruction and determining whether the instruction is a branch used for a function call. If the instruction is not a branch used for a function call, then the debugger treats the instruction as not a function call. If the instruction is a branch used for a function call, then the debugger determines whether the instruction is generated from source code. If the instruction is not generated from source code, then the debugger treats the instruction as not a function call. If the instruction is generated from source code, then the debugger treats the instruction as a function call.





BRIEF DESCRIPTION OF THE DRAWINGS

The drawings included in the present application are incorporated into, and form part of, the specification. They illustrate embodiments of the present invention and, along with the description, serve to explain the principles of the invention. The drawings are only illustrative of typical embodiments of the invention and do not limit the invention.



FIG. 1A is an example of a function and function call written in source code.



FIG. 1B is an example of instructions and their corresponding line of source code and explanation.



FIG. 2 is a flowchart of a method to evaluate a branch instruction during a step over operation, according to embodiments of the invention.



FIG. 3 is a flowchart of a method to evaluate an instruction during a step over operation at the machine code level utilizing a call table and debug information, according to embodiments of the invention.



FIG. 4 is a flowchart for treatment of a function call in a program by a debugger for a step over operation, according to embodiments of the invention.





DETAILED DESCRIPTION

When source code created by a user calls a function, a compiler translates the function call into machine code according to a linkage (or call) convention. The linkage convention includes instructions for the function call of the calling routine, the prolog of the function, and the epilog of the function. The function call of the calling routine may include setting up the function, defining parameters and storage addresses, and branching to the function. The prolog of the function may include setting up the stack frame, storing the current register values in a save area, and preparing registers for the function's operations. The stack frame for the function contains information relating to the function, such as parameters, variables, and the program's return address. The body of the function contains the instructions of the function and corresponds to source code. The epilog of the function may include saving the result of the function, taking down the call stack frame, restoring the registers to their previous state before the function was called, and branching to the return address of the calling routine.



FIG. 1 is an example of a function call and return for IA-32 architecture. FIG. 1A is a piece of source code for a simple addition operation that includes a function for adding to numbers and a function call for adding a “1” and a “2”. FIG. 1B is a chart of the machine instructions that are generated by a compiler for the source code (instruction column), the corresponding line of source code, if any (source line column), and the corresponding purpose of the machine instruction (action column). A compiler generates machine code, represented here by assembly code, from user-generated source code. Some machine instructions, namely those that regard the function's substantive operations, are generated directly from source code, as seen from the corresponding lines of source code. Other machine instructions, namely those that regard the function's structure, are generated from the architecture's linkage conventions.


Debuggers for software programs allow a user to evaluate code in the program by setting a breakpoint in the program, executing one or more instructions up to the breakpoint, and analyzing register values and the program's instruction stacks. To simplify its operations and user interface, a debugger will commonly have step commands to navigate through lines of source and machine code in a program. These step commands allow a user to execute the program instruction by instruction (machine code) or statement by statement (source code).


A debugger may have different step commands for navigating a called function within a routine. If a user is interested in evaluating the instructions of the called function, the user can “step into” the function using a “step into” command. For a “step into” operation, the debugger may set a breakpoint on the next instruction of the calling routine as well as the first instruction of the called function. The user may then walk through each instruction of the called function, eventually returning from the function and stopping at the breakpoint. If the user is not interested in the called function's operation or is only interested in the values returned by the function, the user can “step over” the function. For a “step over” operation, the debugger may only set a breakpoint on the next instruction of the calling routine and the called function will execute in its entirety through to the breakpoint without stopping in the function. In this way a user can more efficiently debug a program using the debugger's interface.


Debuggers may perform step commands on the source code or on the machine code. For example, the dbx debugger may evaluate source code with a “step” command that steps into a called function and a “next” command that steps over the called function, as well as other step commands for stepping out of a function once in the called function. Similarly, dbx may evaluate machine code with a “stepi” command that steps into a called function and a “nexti” command that steps over a called function.


At the source code level, when a debugger performs a “step over” command on a statement, the debugger evaluates whether the statement is a call to a function. The debugger may evaluate the statement in its entirety, which may include multiple machine instructions generated by a compiler according to a specific linkage convention. A debugger that is aware of the way in which the compiler generates machine code from source code may make assumptions about the source statement's character to determine whether the statement is a function call. For example, in a call convention of an IA-32 architecture utilizing a call stack, a call to a function involves pushing a return address for the program's instruction pointer onto the call stack and jumping to the function. In this linkage convention context, a debugger is able to determine whether the source statement is a function call. Other debuggers may rely on the depth of the call stack for assurance that execution has entered a subroutine when stepping a source line. If a step over command is given, these debuggers simply execute the program until the stack depth is equal to where it started.


At the machine code level, a debugger may not have the same context for identifying whether a machine instruction is a function call as the debugger may when evaluating source code. A debugger will likely not be able to detect a change in the stack depth after executing the jump instruction associated with the call. As illustrated in FIG. 1, more than one instruction may be used to call and return from a function, and some of these instructions may be generated according to the front-end and back-end characteristics of a compiler. When the debugger encounters a single instruction, it may not have the context from linkage conventions to determine whether the instruction is a branch used to call a function. For example, for the dbx debugger's “nexti” (machine level step over) operation to determine whether a branch instruction is a function call, dbx looks up the machine instruction in a table of instructions used for function calls. If the instruction is found in the table, the debugger will assume the instruction is a function call and set a breakpoint at the next instruction in the routine; otherwise, the debugger will set a breakpoint at the next instruction and at the branch target instruction. This may create control problems, as not all branch instructions used for function calls are exclusively used for that purpose.


Referring to FIG. 1, a user may perform a step over operation on the first jump instruction 101 (jmp add2). This jump instruction may be in a call table, as it is used for functions calls, so the debugger may determine the instruction to be a function call and set a breakpoint on the next instruction 103 (add1 $8, % esp). However, this jump instruction may be used for purposes other than calling a function, such as returning to the calling routine from a function. A user may already be in the function and may perform a step over operation at the second jump instruction 102 (jmp % eip). This second jump instruction, like the first jump instruction, may be present in the call table, so the debugger may determine the instruction to be a function call and only set a breakpoint on the next instruction, instead of setting a breakpoint at the target return instruction. The program may lose control and execute through the entirety of the program or until a breakpoint is encountered.


Additional problems may occur when stepping over recursive routines, as the return point may be encountered many times before the actual return from the instance of the routine being stepped over occurs. This requires that the stack depth be checked to know when the actual stepped over instance of the routine completes. On many systems, such as IBM's zSeries, the stack depth is established in the prolog and epilog. If the user is machine stepping through the epilog or prolog and tries to do a step over operation on a branch that looks like a call, the stack depth is not realizable and can lead to unpredictable behavior. The inability to use stack depth information for machine step operations leads to awkward support for recursive routines.


Step Over Operation

According to embodiments of the invention, a debugger may selectively perform a step over operation on an instruction by evaluating the instruction in the context of its source. As discussed above, a machine instruction has less information than a source statement for the debugger regarding whether the instruction is a function call. However, a debugger is able to make more assumptions about machine code generated by a compiler from source code than machine code that is not generated by a compiler from source code. A branch instruction for a function call may be generated directly from source code while other parts of the code, such as the prolog and epilog of a function, are generated from the linkage convention for the instruction set. A compiler may generate debug information regarding the source of the machine instructions, so that a debugger may determine whether a machine instruction has been generated from source code.


When a step over command is invoked for a branch instruction, the debugger may evaluate whether the branch instruction can be used for a function call. If the instruction can be used for a function call, the debugger evaluates whether the branch instruction is generated from source code. If the instruction is not generated from source code, the debugger cannot be sure that the branch instruction will be used for a function call. If the instruction is generated from source code, then the debugger can determine, with greater certainty, that the branch instruction will be used for a function call. By restricting a debugger's treatment of perceived function calls to only those branches that are both used for function calls and have been generated from source code, a debugger may more accurately classify and handle a branch instruction in a step over operation, and may also be able to use the stack depth to ensure stopping at the return of the called instance of the routine since the stack depth is always defined within generated code.



FIG. 2 is a flowchart of a method to evaluate a branch instruction during a step over operation, according to embodiments of the invention. A user may invoke a step over command for a branch instruction using a debugger, as in 201. The debugger may determine whether the instruction may be used for a function call, as in 202. If the branch instruction is not used in a function call, the debugger treats the instruction as a branch that is not a function call, as in 203. If the branch instruction may be used in a function call, the debugger determines whether the instruction was generated from source code, as in 204. If the instruction was not generated from source code, the debugger treats the instruction as a branch that is not a function call, as in 203. If the instruction was generated from source code, the debugger treats the instruction as a function call, as in 205.


Step Over Operation Using Debug Information

According to embodiments of the invention, a debugger may determine whether an instruction has been generated from source code by utilizing debug information that includes relational information of machine instructions and source code. When a compiler creates an executable program, it may generate debug information used to later debug the program. This debug information may include source line mapping, symbol tables, and other source information that relates to machine instructions generated by the compiler. Source line mapping maps machine instructions to particular lines of source code from which they are generated. For example, in FIG. 1, the first jump instruction 101 used to call the function has been generated from line 7 of the source code. However, the second jump instruction 102 is generated as part of the architecture's calling convention, and does not map to a line of source code. Debug information may follow a debug data standard, such as DWARF, which uses debug information entries and line number tables to map machine instructions to a program's source code, prolog, and epilog.



FIG. 3 is a flowchart of a method to evaluate an instruction during a step over operation at the machine code level that utilizes a call table and debug information, according to embodiments of the invention. A user may invoke a step over command for an instruction in a current routine, as in 301. In identifying whether the instruction is a branch used as a function call, as in 202, the debugger may determine whether the instruction is a branch instruction, as in 302. For example, the compiler may generate a branch table of branch instructions as part of the debug information, and the debugger may search the branch table for the instruction to determine if the instruction is a branch. If the instruction is not a branch instruction, the debugger may set a breakpoint at the next instruction in the current routine, as in 303.


If the debugger identifies the instruction as a branch instruction, the debugger may determine whether the instruction may be used as a function call. The debugger may look up the instruction in a call table that contains branch instructions that may be used for function calls, as in 304. The call table may be a table of instructions used to implement a call. The debugger may determine whether the instruction is present in the call table, as in 305. If the instruction is not present in the call table, the debugger may treat the instruction as a branch that is not a function call by setting a breakpoint at the next instruction in the routine (for if the branch is not taken) and setting a breakpoint at the branch target instruction (for if the branch is taken), as in 306.


If the instruction is present in the call table, the debugger may determine whether the instruction has been generated from source code, as in 204. The debugger may look up the source line mapping of the instruction in the debug information generated by the compiler, as in 307, and determine whether the instruction maps to a line of source code, as in 308. The source code mapping may be present in the debug information as a debug information entry, following a debug information format such as DWARF.


If the debugger determines that the instruction does not map to a line of source code, the debugger may treat the instruction as a branch that is not a function call by setting a breakpoint at the next instruction and setting a breakpoint at the branch target instruction, as in 306. If the debugger determines that the instruction does map to a line of source code, the debugger treats the instruction as a function call by setting a breakpoint only at the next instruction. In the FIG. 1 example above, the debugger may determine that the first jump instruction 101 is a function call, and set a breakpoint on the next instruction. On the other hand, the debugger may determine that the second jump instruction 102 is not a function call, and set a breakpoint on the target instruction as the next instruction.


Recursive Routines

According to embodiments of the invention, a debugger that utilizes debug information to evaluate function calls may support recursive routines in programs using software stacks. Because debug information is known for an instruction used for a function call, debug information for that instruction may be accessed to determine the stack depth when the breakpoint for the step over operation is set. By saving a starting stack depth and evaluating an instruction with respect to the saved starting stack depth, the debugger may allow recursive routines in architectures that rely on software stacks, as the debugger may have call frame information to determine the stack depth.



FIG. 4 is a flowchart of a method to step over a recursive routine with a step over operation, according to embodiments of the invention. Once it is determined from debug information that the instruction is a branch used for a function call, a breakpoint is set at the next instruction, as in 303. To set a reference for the routine in the call stack of the program, the debugger saves the call stack depth of the program at the beginning of the step over operation, as in 401.


After setting the breakpoint and saving the starting stack depth, the program executes, as in 402. The program continues to execute, as in 403 and 404, until a breakpoint is encountered. Once a breakpoint is encountered, the debugger determines whether the current stack depth is greater than the saved stack depth, as in 405. In stacks that populate from the top to the bottom, the debugger would evaluate whether the current stack is less than the saved stack depth. If the current stack depth is greater than the saved stack depth, the program executes the next instruction, as in 404, and continues to run. If the current stack depth is not greater than the saved stack depth, then the program stops at the breakpoint, as in 406.


Although the present disclosure has been described in terms of specific embodiments, it is anticipated that alterations and modifications thereof will become apparent to those skilled in the art. Therefore, it is intended that the following claims be interpreted as covering all such alterations and modifications as fall within the true spirit and scope of the disclosure.

Claims
  • 1. A method for implementing a step over command for an instruction in a routine, comprising: receiving a step over command for an instruction;determining whether the instruction is a branch used for a function call;if the instruction is not a branch used for a function call, then treating the instruction as not a function call;if the instruction is a branch used for a function call, then determining whether the instruction is generated from source code;if the instruction is not generated from source code, then treating the instruction as not a function call; andif the instruction is generated from source code, then treating the instruction as a function call.
  • 2. The method of claim 1, further comprising: determining if the instruction is a branch;if the instruction is not a branch, then treating the instruction as not a function call; andif the instruction is a branch, then determining whether the instruction is a branch used for a function call.
  • 3. The method of claim 2, wherein determining whether the instruction is a branch used for a function call comprises: accessing a call table of instructions used for function calls;determining whether the instruction is present in the call table;if the instruction is not present in the call table, then concluding that the instruction is a branch not used for a call function; andif the instruction is present in the directory, then concluding that the instruction is a branch used for a call function.
  • 4. The method of claim 3, wherein determining whether the instruction is generated from source code comprises: accessing source line mapping in debug information created by the compiler;determining whether the instruction maps to a line of source code using the debug information;if the instruction does not map to a line of source code, then concluding that the instruction is not generated from source code; andif the instruction maps to a line of source code, then concluding that the instruction is generated from source code.
  • 5. The method of claim 4, wherein: treating the instruction as not a function call if the instruction is not a branch comprises setting a first breakpoint at a next instruction in the routine.treating the instruction as not a function call if the instruction is a branch and is not a branch used for a function call comprises setting a first breakpoint at a next instruction in the routine and setting a second breakpoint at a target instruction of the branch;treating the instruction as not a function call if the instruction is a branch used for a function call and is not generated from source code comprises setting the first breakpoint at the next instruction in the routine and setting the second breakpoint at the target instruction of the branch; andtreating the instruction as a function call if the instruction is determined to be a branch used for a function call and generated from source code comprises setting the first breakpoint at the next instruction of the routine.
  • 6. The method of claim 5, wherein treating the instruction as a function call further comprises: saving a starting stack depth;executing the instruction;determining whether a breakpoint is encountered;if a breakpoint is not encountered, then executing a next instruction and returning to determining whether a breakpoint is encountered;if a breakpoint is encountered, then determining whether a current stack depth is greater than the saved stack depth;if the current stack depth is greater than the saved stack depth, then executing the next instruction and returning to determining whether a breakpoint is encountered;if the current stack depth is not greater than the saved stack depth, then stopping at the breakpoint.
  • 7. The method of claim 7, wherein: saving the starting stack depth comprises looking up the current stack depth in the debug information; anddetermining whether the current stack depth is greater than the saved stack depth comprises looking up the current stack depth in the debug information.
  • 8. The method of claim 8, wherein the instruction is from an IBM z architecture instruction set.
  • 9. The method of claim 5, wherein the debug information is in DWARF format.
  • 10. The method of claim 5, wherein the debugger is dbx.