|
Description  |
|
|
FIELD OF THE INVENTION
The present invention relates generally to computer systems, and more particularly to a method and apparatus for monitoring the performance of computer systems.
BACKGROUND OF THE INVENTION
Systems for monitoring computer system performance are extremely important to hardware and software engineers. Hardware engineers need systems to determine how new computer hardware architectures perform with existing operating systems and
application programs. Specific designs of hardware structures, such as memory and cache, can have drastically different, and sometimes unpredictable utilizations for the same set of programs. It is important that any flaws in the hardware architecture
be identified before the hardware design is finalized.
Software engineers need to identify critical portions of programs. For example, compiler writers would like to find out how the compiler schedules instructions for execution, or how well execution conditional branches are predicted to provide
input for code optimization.
It is a problem to accurately monitor hardware and software systems performance. Known systems typically are hand crafted. Costly hardware and software modifications may need to be implemented to ensure that system operations are not affected
by the monitoring systems.
Many monitoring systems are known for different hardware and software environments. One class of systems simply counts the number of times each basic block of machine executable instructions is executed. A basic block is a group of instructions
where all the instructions of the group are executed if the first instruction of the group is executed. The counts can be studied to identify critical portions of the program.
Monitoring references to instructions and data addresses are usually performed by tracing systems. Data address traces can be used to improve the design of caches, and increase the efficiency of in-memory data structures. Instruction address
traces can identify unanticipated execution paths.
In another class of systems, the simulated operation of the computer system is monitored. Simulators attempt to mimic the behavior of computer systems without actually executing software in real time.
There are problems with traditional monitoring systems. Most systems monitor a limited number of specific system characteristics, for example, executed instructions or referenced data. It is difficult for users to modify such systems for other
purposes. Building specialized systems is not a viable solution since the number of system characteristics to be monitored is large and variable. If the performance data supplied by the monitoring system is less than what is desired the system is of
limited use. If the system supplies too much performance data, the system is inefficient.
Most monitoring systems which count basic blocks accumulate counts for all the blocks of the program. Other than by tedious modifications, it usually is not possible to monitor selected blocks of interest.
Most known tracing systems inefficiently gather detailed address data. A typical trace for a small program can include gigabytes of trace data. A user interested in monitoring branch behavior has to sift through entire traces just to find, for
example, conditional branch instructions.
Simulating the execution of programs at the instruction level can consume enormous quantifies of system resources. In addition, it is extremely difficult to accurately simulate the hardware and software behavior of a complex computer system.
Simulated performance data does not always reliably reflect real run-time performance.
There also are problems with the means used to communicate performance data. Most systems use expensive inter-processor data communications channels to communicate performance data. Inter-processor communication channels are generally
inefficient and may disturb the processing environment being monitored. Some systems make difficult modifications to the operating system to improve the efficiency of monitoring computer systems. Furthermore, unfiltered performance data can consume
large quantifies of disk storage space.
There is a need for a flexible and efficient monitoring system which can easily be adapted to a diverse set of monitoring tasks, ranging from basic block counting to measuring cache utilization. Communications between the monitoring system and
the user should be simple. The information data should be precise, and reflect the actual operation of the computer system.
SUMMARY OF THE INVENTION
The invention avoids the above and other problems of known performance monitoring systems and methods, and satisfies the foregoing described needs. In accordance with the invention, a system for monitoring the performance of a computer system,
while executing a program/modifies the program prior to execution. The modifications to the program include calls to user analysis routines. The user analysis routines collect performance data while the program is executing.
The program is created as a collection of source code modules in the form of a high level language. Each of the source code modules is compiled into a corresponding object code module. The object code modules are translated into a single linked
code module in the form of a machine independent register transfer language. All addresses referenced in the linked code module are maintained in a logical symbol table.
The linked code module is partitioned into basic program components. The basic program components include procedures, basic blocks within procedures, and instructions within basic blocks. Procedures include instructions which are related for
execution, instructions within each procedure are grouped into basic blocks. A basic block is a group of instructions where all of the instructions of the group are executed if the first instruction of the group is executed.
Fundamental instrumentation routines are provided to identify and locate specific program components, the identified specific program components are modified to call user analysis routines. The user analysis routines are combined with the
modified linked code module. The linked code module is converted to machine executable code to be executed in the computer system so that performance data can be collected by the user analysis routines when the modified program is executed.
BRIEF DESCRIPTION OF THE DRAWINGS
FIG. 1 is a block diagram of a computer system which can be monitored according to the principles of the invention;
FIG. 2 is a top level flow diagram of a system for monitoring the computer system of FIG. 1;
FIG. 3 is a detailed flow diagram of the monitoring system of FIG. 2;
FIG. 4 is a flow diagram of routines for instrumenting a program;
FIG. 5 is a flow diagram of a program showing calling edges;
FIG. 6 is a flow diagram of an instrumentation procedure for branch analysis; and
FIG. 7 is block diagram of allocated memory for the monitoring system at run-time.
FIG. 8 show the allocation of memory storage space for the program after modifications for monitoring.
DETAILED DESCRIPTION OF A PREFERRED EMBODIMENT
FIG. 1 shows a computer system 1 to be monitored according to the principles of the invention. The computer system 1 includes a CPU 2, a memory 3, and an I/O 4 coupled to each other by a communications bus 5. The computer system 1 can be a
personal computer, a work-station, a main-frame, or part of a network of computers executing process steps independently, or in parallel.
With respect to the component structures of the computer system 1, the CPU 2 may be of a variety of architectures, such as complex or reduced instructions computing (CICS, RISC), and the like. The CPU 2 can include a set of general purpose and
dedicated registers 8. The registers 8 are for temporally storing data acquired from the memory 3.
The memory 3 can include a cache 7 to accelerate the data flow between the CPU 2 and the memory 3. The cache 7 can include specialized data and instruction caches. The structure of the bus 5 is general, and can include dedicated high-frequency
data paths for communicating data, for example between the cache 7 and the CPU 2. The I/O 4 can include input and output interfaces for acquiring and distributing data.
During operation of the computer system 1, data and processing steps are acquired by the CPU 2, usually from the memory 3 or cache 7 via the bus 5. The processing steps can be in the form of a sequence of machine executable code, e.g.,
instructions. The data and the instructions are usually permanently retained by the I/O 4, and loaded into the memory 3 as needed. The instructions process the data, and store the processed data back in the memory 3.
It is desirable to monitor the performance of the computer system 1 while it is operating. Performance data can be used to optimize the design of the hardware and software components of the system. Accurate performance data depends on
monitoring the computer system 1 without substantially disturbing the processing environment.
For the purpose of monitoring, a program for executing in the computer system 1 can be viewed as a linear collection of procedures, the procedures including basic blocks, and the basic blocks including instructions. For the purpose of this
description, the program can exist in a number of different forms, including source, object, linked, instrumented and executable form.
It is desirable to monitor the execution of the program at the procedure, basic block, and instruction level. For example, monitoring the execution of the first instruction of basic blocks yields block counts. Monitoring instructions which load
and store data in the memory allows the user to study cache utilization. The efficiency of branch prediction can be estimated by monitoring conditional branch instructions at the end of basic blocks.
FIG. 2 is an overview of a system which can be used for monitoring the performance of the computer system 1 of FIG. 1. Programmers, typically, via the I/O 4, define a process or program 20 in a high-level machine-independent language using an
editor 10. If the program 20 is very large, the program 20 is created as a library or collection of smaller program segments, usually called source code modules 21-23.
A compiler translates the high-level language of the program 20 to object code stored in a collection of object modules 41-43. Usually, there is a one-to-one correspondence between the source modules 21-23 and the object modules 41-43. The
object modules 41-43 are associated with corresponding relocation tables and symbol tables (RST) 44-46. The RSTs 44-46 are used to resolve logical addresses when the source program is converted to executable form. Frequently used portions of the
program can be retrieved from a library of object modules.
A monitor 50 can combine the object modules 41-43. The monitor 50 can also identify specific portions of the program to be monitored. Directions on which portions of the program to monitor can be supplied by the user of the computer system 1.
The monitor 50 can also modify the program to enable the monitoring of the performance of the system 1. The modified program is converted to machine dependent executable code 60 which can be loaded in the memory 3 for subsequent execution by the
CPU 2 of the computer system 1 of FIG. 1. During execution of the program, the CPU 2 receives and provides user data 9A via the memory 3. The CPU 2 also generates performance data 9B stored in the memory 3, the performance data 9B can be retrieved via
the I/O 4.
It is a goal of the invention to facilitate the identification of portions of the program to be monitored, and to modify the program in such a way that the performance characteristics of the computer system 1 are not disturbed.
FIG. 3 is a flow diagram of the monitoring system according to the preferred embodiment of the invention. The monitor 50 includes a translator 51, an organizer 54, an instrumentor 55, and a code generator 57, which are introduced here, and
described in greater detail below.
The translator 51 transforms the program in object module form, e.g., the collection of object modules 40 are converted to a single linked code module 52. The collection of object modules 40 can include procedures selected from a library of
pre-compiled object modules. The linked code module 52 is in the form of an intermediate machine-independent register transfer language (RTL) which is independent of any one particular computer systems. The specific architectures of target computer
systems can be maintained in a CPU architectures description 19.
The translator 5 1 also transforms all object code operand addresses into symbolic addresses. The relocation tables and the symbol tables 44-46 are likewise transformed into a logical symbol table (LST) 53. The linked code module 52, the LST
53, and the CPU architecture description 19 can be stored in the memory 3. Translating the object modules 40 into an intermediate form expedites the identification of portions of the program to be monitored, and also allows aggressive modification of
the program 20 for monitoring purposes.
The organizer 54 partitions the program into basic structural components including program segments, procedures, basic blocks, and instructions, collectively 100. The program segments may correspond to segmentation specified in the source code.
The procedures 101-103 include instructions which are generally related for execution. A basic block 105 is a sequence of instructions which are all executed if the first instruction of the sequence is executed. The instructions are simply the binary
operation codes and operands that characterize machine executable code.
The organizer 54 also builds a procedure flow graph (PFG) 200 and a program call graph (PCG) 300 in the memory 3. The PFG 200 maps the flow of control through the basic blocks 105 of the procedures 101-103. The PCG 300 indicates how the
procedures 101-103 are called by each other. The monitor 50 can use the graphs 200 and 300 to trace the execution flow through the program while the organized program 100 is not executing. When the program is not executing it is said to be "static."
The instrumentor 55, under the direction of the user, identifies and modifies specific portions of the program to be monitored. The process of identifying and modifying portions of the program for the purpose of performance monitoring is
sometimes known as "instrumentation" or "instrumenting the code." The instrumentor 55 is described in further detail below, with reference to FIG. 4.
The code generator 57 generates machine-dependent code for the target hardware architecture. The CPU description 19 can be used to translate the instrumented program 100 from RTL form to machine executable code. The CPU description 19 may
include instruction operand and operator field specifications for the different classes of instructions, e.g., data reference, branch, etc., and timings information such as fetch latencies. The CPU description 19 can also describe cache, memory, and
register characteristics, including addressing schemes.
Now for a more detailed description of the monitoring system according to the preferred embodiment of the invention.
The translator 51 converts the program into a linked module 52 in an intermediate form. The intermediate representation of the program is in the form of the register transfer language and the logical symbol table 53. The RTL may be
machine-independent, in the preferred embodiment of the invention, the RTL has been oriented for reduced instruction set computing (RISC) architectures.
The instructions of the RTL include generic operands such as load, store, jump, branch, and "operate." Only the load and store operands reference data in the memory 3. Procedure calls in the RTL are simple transfer of control instructions, for
example "jump." Any parameters passed upon transfer of control are explicitly exposed. Conditional and unconditional transfer of control is accomplished with branch type of instructions.
All arithmetic and logical instructions "operate" on data stored in the registers 8. Unlike traditional machine code, the RTL assumes an expandable set of registers, with some of the registers possibly having dedicated functions, for example,
floating point arithmetic, a stack-pointer (SP) and a program counter (PC).
All address references in the linked code module 52 are symbolic. For example, addresses expressed relative to the PC are converted to targets which are labels in the logical symbol table 53. Similarly, all references, direct or displaced, to
data stored in the memory 3 are converted to symbolic memory references. Converting all addresses to symbolic form has the advantage of enabling unconstrained modification of the instructions of the linked code module 52 to enable performance
monitoring.
The organizer 54 partitions the program into portions which can be monitored. First, the program is partitioned into the collection of procedures 100. Instructions in each of the procedures 101-103 are further grouped into basic blocks 105.
Basic blocks 105 can be classified as either "normal" blocks or "control" blocks. A normal block merely manipulates dam, control blocks do not manipulate dam, they alter the flow of execution. The basic blocks 105 facilitate the tracing of the
execution flow through the static program.
The procedure flow graph 200 is built for each procedure, and the complete program call graph 300 is built for the entire program. These execution control structures are used during the subsequent instrumentation of the program.
Organizing the linked code 52 enables the annotation of the linked module 52 in a program description 56. The program description 56 can be stored in the memory 3. The program description 56 facilitates the manipulation of the program and also
eases the identification of the fundamental organizational portions of the program by subsequent processing steps. The program description 56 can be incorporated into the linked module as, for example, comment fields.
The original structure of the source-level program is recovered so that the monitor 50 can have as much knowledge of the program organization and control flow of the program as the compiler did. For example, a source-level case-statement is
compiled into object code as an indirect jump to an address from some location in a jump table index by the case index value. The jump table for case-statements is usually stored in a read-only data area with the addresses of different jump target
location stored in successive locations. This makes it possible to recognize case-statements in the object code. The address of the jump table can be obtained by examining the case-statement object code.
By identifying all of the case-statements in a program, the jump table can be partitioned into a set of branch tables of a known size. This in turn reveals all possible execution destinations. The execution destinations can be used to create
the control graphs 200 and 300.
The structure and operation of the instrumentor 55 is now described in greater detail with reference to FIG. 4. Instrumentation of the program is a static process, e.g., the program is not executing. Under the user's direction, portions of the
program to be monitored are identified by "navigating" through the program using the program and CPU description 56 and 19, and the graphs 200, 300. In the monitoring system according to the preferred embodiment of the invention, a number of fundamental
procedures or routines are provided to navigate through the program. The monitor also includes standard routines for modifying the program, for monitoring purposes.
The instrumentor 55 has as input the program to be instrumented in the form of the organized linked module 100. The instrumentor 55 also uses the LST 53, the program description 56, the PFG 200, the PCG 300, and the CPU description 19. The
instrumentor 55 also uses fundamental instrumentation routines (FIR) 47, user instrumentation routines (UIR) 48, and user analyzer routines (UAR) 49.
The FIR 47 are a set of basic procedures for identifying and modifying programs to be monitored. The user supplied UIR 48, in cooperation with the D3R 47 locate and modify specific portions of the program. The program is modified, in part, by
inserting calls to the user analysis routines 49. The user supplied UAR 49 are procedures for collecting and analyzing performance data.
The fundamental instrumentation routines (FIR) 47 can be incorporated as a standard component of the instrumentor 55 when it is created. For example, the FIR 47 can be written in a high-level source language such as C. The source modules for the
FIR 47 can be compiled and linked with the source modules of the monitor 50 using standard programming techniques. The user analysis routines (UAR) 49 can also be written in a high-level source language. The UAR 49 can be part of the library of object
modules submitted to the monitor 50 along with the object modules 40 of the programs to be monitored.
The fundamental instrumentation routines 47 include navigational, operational, parsing, and modification routines (47a-47d). The navigational routines 47a are used to traverse the static program to deduce structural information and execution
flow information. The operational routines 47b retrieve specific information about the organizational structured traversed by the navigational routines 47a. The parsing routines 47c are used to identify and parse identified instructions. The
modification routines 47d change the program so that it may be monitored.
Table 1 lists the navigational routines 47a of the FIR 47.
TABLE 1 ______________________________________ Navigational Routines Procedure Block Instruction Edge ______________________________________ GetFirstProc GetFirstBlock GetFirstInst GetFirstSuccEdge GetLastProc GetLastBlock GetLastInst
GetNextSuccEdge GetNextProc GetNextBlock GetNextInst GetFirstPredEdge GetPrevProc GetPrevBlock GetPrevInst GetNextPredEdge GetBlockProc GetInstBlock GetEdgeTo GetEdgeFrom ______________________________________
GetFirstProc returns as an output a pointer to the first procedure of the program that will be executed at run-time. The user can call this fundamental instrumentation routine to initiate static navigation through the program with a call from
one of the UIR 48. Similarly, a call to GetLastProc returns as an output the pointer to the last procedure of the program.
Given a procedure pointer, GetNextProc and GetPrevProc return pointers to the next and previous procedures, respectively, unless there are no more procedures to navigate, in which case a "null" is returned. The GetBlockProc receives as an input
a pointer to a block, and returns as an output a pointer to a parent procedure. The parent procedure is the one including the block.
The navigational routines for block, and instruction perform similar functions at the block and instruction level. For example, GetInstBlock receives as an input a pointer to an instruction and returns as output a pointer to a parent block of
the instruction. The procedures of the program, and the blocks of the procedure can be located by following the procedure flow graphs 200 and the program call graph 300.
The "edge" routines are described with reference to FIG. 5. FIG. 5 shows the procedure flow graph 200 and the program call graph 300 superimposed on the organized program 100. While executing, the interfaces or "edges" between the procedures
101-103 of the program are conjugate pairs of basic blocks, for example, call blocks 151 and return blocks 152. The call block 151 is the last block executed in a procedure before execution control is transferred to another procedure. Upon return, the
return block 152 is executed first.
Each call block 151 has a single successor entry block 151 in the called procedure. The entry block is executed first when a procedure is called by another procedure of the program. Each return block 152 has a single predecessor exit block 154. The exit block 154 is the last block executed before execution control is transferred to the calling procedure. An entry block may have many predecessor blocks, likewise for successors of the exit blocks 154. The normal blocks 155 are not involved in
the interprocedural transfer of execution control, the normal blocks 155 define, use and consume variables and registers, for example variables x, y, and z. Following the "edges" of the program enables the tracing of the execution flow while the program
is static.
GetFirstSuccEdge and GetNextSuccEdge can be used to follow all possible execution paths from a specified block. Tracing the predecessor edges using GetFirstPredEdge and GetNextPredEdge locates all the possible execution paths that lead to the
first instruction of the specified basic block. GetEdgeTo and GetEdgeFrom can be used to trace the execution of the program from edge to edge.
Now continuing with description of the instrument or 55 of FIG. 4, the user instrumentation routines (UIR) 48 are constructed by including calls to the fundamental instrumentation routines 47 to find specific organizational structure of the
program and to trace the execution flow. For example, the user can locate the last instruction of every procedure, the first instruction of every block, and only instructions which call other procedures, to give but a few examples.
The operational routines 47b are listed in Table 2. The operational routines provide information about portions of the program traversed by the navigational routines 47a.
TABLE 2 ______________________________________ Operational Routines Program Procedure Block ______________________________________ GetProgramInfo GetProcName GetProgramInstArray GetFileName GetProgramInstCount GetProcPC GetBlockPC
GetProgramName GetNamedProcedure ______________________________________
GetProgramInfo returns static information about the program such as the starting memory address of the program, and the size of the memory space used by the program while executing. Storage space is required for the instructions or "text" of the
program and for data manipulated by the program. Memory storage allocation will be described in greater detail with reference to FIGS. 7 and 8.
GetProgramInstArray returns a pointer to an identified set of instructions of the program. The identified instructions can be passed to the user analyzer routine 49 for collateral manipulation during execution. For example, the user analyzer
routines 49 may want to collect dynamic address information from the instructions while the program is executing.
GetProgramInstCount returns the number of the instructions in the set identified by the GetProgramInstArray routine.
GetProgramName returns the name of the program. GetNameProc receives as input a procedure name, and returns the pointer to the named procedure.
GetProcName takes as an input a pointer to a procedure and returns as an output the name of the procedure. GetFileName, given the pointer to the procedure, returns the name of the file that is used to store the procedure. GetProcPC returns the
run-time memory address of the procedure. GetNamedProc, given a procedure, name returns the pointer to the procedure. A null is returned if the named procedure does not occur in the program.
GetBlockPC given a pointer to a block returns the memory address (PC) of the first instructions of the block.
Table 3 lists the parsing routines 47c which can be used to identify and parse specific instructions.
TABLE 3 ______________________________________ Parsing Routines Routine Value Returned ______________________________________ IsInstType instruction type GetInstInfo instruction information GetInstRegEnum register type GetInstRegUsage
register usage GetInstPC instruction memory address GetInstBinary instruction binary code GetInstClass instruction classification GetInstProcCalled procedure called ______________________________________
GetInstType will return a logical true condition if the specified instruction is of a particular type. Different types of instructions include load, store, jump, branch, multiply, divide, floating point, etc. This routine can be used to
determine if an instruction references memory or a register, and if execution flow is conditionally or unconditionally changed by, for example, branch instructions.
GetInstInfo parses the instruction into fields such as: operation code (opcode) field, memory displacement field, branch displacement field, addressing mode field, register fields, and the like.
To determine which of the registers are referenced by an instruction, one can use the GetInstReg routine. GetInsReg takes as input an instruction and an instruction type, and returns the type of registers used in the instruction, for example,
integer, double precision, floating point, specialized, e.g., PC, SP etc.
To determine the actual usage of registers the GetInstRegUsage routine is used. For a given instruction, GetInstRegUsage indicates which registers are simply read, and which registers are written.
GetInstPC returns the memory address or PC of the instruction.
GetInstProcCalled returns the name of the procedure directly called by the instruction, presuming the instruction is either a branch or jump type of instruction.
GetInstBinary receives as in input a PC and returns the binary coded instruction stored at that memory address. This routine can be used to retrieve, for example, all the instructions of the program.
GetInstClass returns the instruction class. The instruction classes define instructions for specific hardware architectures. The classes of instructions can be stored, for example, in the CPU description 19. Hardware specific classes can
include integer and floating point load and stores, integer and floating point arithmetic, logical functions, e.g., AND, OR, XOR, shift functions, e.g., rotate, scale, etc.
Table 4 lists the fundamental instrumentation routines that are available for modifying the program so that performance data can be collected.
TABLE 4 ______________________________________ Modification routines ______________________________________ AddCallProto AddCallProgram AddCallProc AddCallBlock AddCallInst AddCallEdge ______________________________________
The program can be modified before or after the execution of an identified portion. In the preferred embodiment of the invention, all communication between the modified program and the user analysis procedures 49 is by procedures calls.
Procedure calls reduce processing time associated with the monitoring process. The identified portion can be the program, procedures, block, edge and insauction.
Any data stored in registers used by the user analysis routines 49 must remain unchanged to preserve the execution state of the modified program. The data can be saved in the stack before execution control is passed to the analysis procedures
49, and restored upon return.
The routine AddCallProto is used to define what performance data are to be passed to the analysis routines during execution. Typical data to be passed may include static data, such as opcode, or dynamic data, for example, register or memory
contents. It is also possible to pass computed values such as effective memory address, and a conditional value. The effective memory address should only be used with instructions which reference memory, i.e., load and store. The computed value is the
base memory address plus the sign extended relative displacement. The condition value should only be used when instrumenting branch instructions. If the branch is not taken, the value passed is zero.
It is also possible to pass arrays to the analysis routines. The following sample user instrumentation routine, in the C language, creates a data structure that contains the program counters for each procedure, the data structure is passed as an
argument to an "Open File" user analysis routine.
______________________________________ Sample User Instrumentation Routine ______________________________________ int number char prototype [100] number = 0 for (proc = GetFirst Proc(),proc != NULL, proc=GetNextProc(proc)) { number ++; }
pcArray = (long*) malloc(size of(long)* number); number = 0; for (proc = GetFirst Proc(),proc != NULL, proc=GetNextProc(proc)) { pcArray [number++] = ProcPC(proc); sprintf(prototype, "OpenFile(long[%d])",number); AddCallProto(prototype);
AddCallProgram(ProgramBefore, "OpenFIle",pcArray); ______________________________________
AddCallProgram takes as an argument the location or place where the program is to be modified, for example, the beginning or the end. AddCallProgram inserts a procedure call to an analysis routine at the specified location. During execution,
the call causes transfer of control to the analysis routine so that performance data can be collected.
AddCallProc is similar at the procedure level. The semantics of modifying the program before and after procedures and basic blocks are maintained even if there are multiple entry points and multiple exit points. For example, if a procedure has
multiple entry points, adding a call before the procedure will add the call for each entry point of the procedure, and will only call the analysis routine once, regardless of which entry point is selected during execution.
The UIR 48 in cooperation with the fundamental instrumentation routines are used to traverse the entire program. The UIR 48 can locate and identify specific portions of the program, e.g., procedures, blocks, and instructions, to be modified for
monitoring.
The UIR 48 can be created by the user as a high-level source code module. The UIR 48 when compiled and linked with the FIR 47 adapt the instrumentor 55 to perform specific monitoring task.
By way of example, FIG. 6 shows a user instrumentation routine BRANCH 400 for locating all conditional branch instruction in the program. The branch instructions are found by tracing the execution flow through the static program and identifying
only instructions of the branch type. Thus located branch instructions can be modified so that the branch predication rate can be determined.
In steps 401 and 410 and, the procedures 101-103 of the program are located. Step 420 locates the blocks 105 of each procedure, and in step 430 the last instruction of each block is located. In step 440, a determination is made to | | |