Record Counts for a basic block
Record Time for a basic block (Quantify only)
The Rational Quantify/Rational PureCoverage Interpreter Interface (QII) was designed to drive the Quantify/PureCoverage runtime in order to simulate execution of instrumented code primarily in interpreted environments (like Visual Basic or Java). It can be also used by language vendors to insert profiling functionality into their tools. This document describes typical use cases for this API.
Note! This API was optimized for performance, and trivial consistency checks were omitted to speed up the execution. It assumes that input data is valid and verified. Please use it carefully! Invalid data, bad pointers or inconsistent line number information can potentially break Quantify or PureCoverage during execution.
A typical use case involves an interpreter executing code you want to profile. This API assumes that your language system is procedure-based. For the detailed source level information you should be able to provide line tables (see Line number table below).
Use dynamic linking for this API to obtain maximum flexibility. You should use the LoadLibrary and GetProcAddress calls to link your code with QII at runtime. You can use static linking if you intend to use your program with Quantify only or PureCoverage only. Dynamic linking provides the maximum flexibility with this API.
Keep Qprof.dll and Cprof.dll in a directory listed in the PATH variable, such as the PurifyPlus product directory.
Function prototypes for QII are listed in QProf.h\CProf.h. This file also contains some programming tips.
System threads are threads that are known to the operating system. For native programs, threads started by calling C Runtime functions such as CreateThread() are system threads.
Virtual threads are threads known to the application. Note that when an application is called by a Java JVM it will be passed a thread identifier (the JNIEnv* parameter from the JVMPI) which may or may not be a unique system thread. The JVM may use a system thread for multiple virtual threads, which the application sees as multiple threads.
QProf tracks system threads by default. Using extensions documented in this paper an application can choose to tell QProf to track virtual threads instead. This can be done by calling qprof_initialize_ex(qprof_InitInfo) with a value of true for the usingVirtualThreads parameter. Subsequent calls to QProf should use the function names with the "_vt" suffix, where applicable.
This function should be called before any other call to QII. This call initializes Quantify (for QProf.dll) or PureCoverage (for Cprof.dll) and sets the initialization options for the product. Either of the following functions may be used:
int _EXT_REF qprof_initialize(const char *initial_options,
void(*callback_get_method_info)(qprof_MethodID));
int _EXT_REF qprof_initialize_ex(qprof_InitInfo *qprof_init_info);
initial_options any Quantify or PureCoverage run time options. See your product documentation for options and syntax.
callback_get_method_info - Callback function for fixing unknown method IDs. This function is called by the QII when runtime detects a method entry for an unregistered method ID (see qprof_register_method_id). If you register IDs for all possible methods to be called during program execution, this shouldnt happen. If you dont register IDs for all methods, this gives you a chance to call qprof_register_method_id when the method is called.
qprof_init_info - A struct that contains initial_options, callback_get_method_info, and additional information. The "ex" in the function name refers to "extensible" as this functions parameter, qprof_init_info, may be extended in the future. At this time, only version 0 is supported. This struct contains a field named usingVirtualThreads, described below.
usingVirtualThreads - A 0 value indicates virtual threads will not be monitored, only system threads. This is the same behaviour implied by using the qprof_initialize() function. A non-zero value indicates that virtual threads should be monitored.
This should be the last call during execution. Important: do not call any other QII functions after this call, as it may break the program. This call forces Quantify or PureCoverage to summarize runtime information and display the result. Call one of the following:
void _EXT_REF qprof_shutdown(void);
void _EXT_REF qprof_shutdown_vt(VthreadID virtual_thread_id);
virtual_thread_id - ID of the virtual thread that is requesting the shutdown.
qprof_shutdown_vt() should be used only if a non-zero value for usingVirtualThreads was specified on initialization.
Get runtime option for Quantify/PureCoverage.
You can access the Quantify/PureCoverage option system using qprof_get_option. This function returns a "void *" which you should cast to a "const char *" for string options, or a bool, int, or unsigned for numeric options. You must know the type of the option you're asking for and cast appropriately. If you ask for a nonexistent option, the function returns NULL, which you can't tell from a zero value for a valid option (you also can't tell whether the option was allowed to default or was set manually).
void * _EXT_REF qprof_get_option(const char *optionName);
optionName null-terminated string containing the name of the Quantify/PureCoverage run time option.
void * pointer to the buffer where the option value is stored. The datatype of the value must conform to the expected datatype of the option.
void _EXT_REF qprof_thread_create(qprof_ThreadID id);
void _EXT_REF qprof_thread_create_vt(qprof_ThreadID id, VthreadID virtual_thread_id);
id Unique thread id (unsigned long)
virtual_thread_id - ID of the new virtual thread.
qprof_thread_create_vt() should be used only if a non-zero value for usingVirtualThreads was specified on initialization.
Associates an ASCII string with the current thread.
void _EXT_REF qprof_set_thread_name(const char *new_name);
new_name String to be used as the thread name.
Signals thread destruction.
Call one of the following:
void _EXT_REF qprof_thread_destroy(qprof_ThreadID id);
void _EXT_REF qprof_thread_destroy_vt(VthreadID virtual_thread_id);
id Same id as was used in qprof_thread_create.
virtual_thread_id - ID of the current virtual thread.
qprof_thread_destroy_vt() should be used only if a non-zero value for usingVirtualThreads was specified on initialization.
Get the current method ID. Call one of the following:
qprof_MethodID _EXT_REF qprof_current_method_id(void);
qprof_MethodID _EXT_REF qprof_current_method_id_vt(VthreadID virtual_thread_id);
Returns:
qprof_MethodID Current method ID (the last method that was entered on the current thread).
virtual_thread_id - ID of the current virtual thread.
qprof_current_method_id_vt() should be used only if a non-zero value for usingVirtualThreads was specified on initialization.
void _EXT_REF qprof_register_method_id(qprof_MethodID id,
const char *demangled_name,
const char *class_name,
int flags,
const char *source_file,
const qprof_LineInfo *line_number_table,
int line_number_size);
void _EXT_REF qprof_register_method_id_vt(qprof_MethodID id,
const char *demangled_name,
const char *class_name,
int flags,
const char *source_file,
const qprof_LineInfo *line_number_table,
int line_number_size,
VthreadID virtual_thread_id);
id - Unique method ID.
demangled_name - Method (function) name. This name will be used in all profiler data for this function.
class_name Class name if this method belongs to the class, otherwise NULL
flags Method characterization flags.
QPROF_METHOD_FLAGS_NONE
not defined
QPROF_METHOD_FLAGS_BLOCKS
method blocks the execution
QPROF_METHOD_FLAGS_WAITS synchronous method waiting for system return
source_file Source file name
line_number_table Line number table (see below)
line_number_size Number of entries in the line number table
virtual_thread_id - ID of the current virtual thread.
The line_number_table argument must point to an array of
qprof_LineInfo structs, or something with identical layout. Each entry gives
the starting code offset value of the code produced by a given line number. Code
at offsets from a starting offset until the next one is
assumed to have come from the indicated line.
For example:
0 15
7 16
12 19
This table means that code offsets from 0-6 associate with line 15, and 7-11 associate with line 16, and anything 12 and above associates with line 19. The table should be sorted in ascending order of code offsets. The line numbers don't have to be unique or in increasing order, your optimizer can do anything it wants with them. If the table doesn't start with code-offset zero, then offsets from zero to the first value are associated with the first line number.
qprof_register_method_id_vt() should be used only if a non-zero value for usingVirtualThreads was specified on initialization.
Signals a function entry.
Call one of the following:
void _EXT_REF qprof_record_fn_entry(qprof_MethodID method_id, qprof_StackID stack_id);
void _EXT_REF qprof_record_fn_entry_vt(qprof_MethodID method_id, qprof_StackID stack_id, VthreadID virtual_thread_id);
method_id Unique method id (same as in the qprof_register_method_id call).
Stack_id Stack id (see below).
virtual_thread_id - ID of the current virtual thread.
This is an ID value that you must determine and use
consistently. When you report function entry, you must pass the qprof_MethodID of
the function being entered, and a qprof_StackID value to represent the
location of the stack pointer at that time. You must remember the
qprof_StackID so you can report function exits properly. Unfortunately,
the StackID usage is a little complicated. It's not as simple as passing a
number at function entry time, and passing that same number at function exit
time. What really happens is that you pass a number (call it X) to identify the
frame you're in at function entry time, and later when child functions exit you
pass that same number, saying, "We're back in frame X."
For example:
main() {
fun_one();
fun_two();
}
fun_one() {
fun_three();
}
fun_two() {
//
empty
}
On entry to main, let's say you choose qprof_StackID 65 to
represent that stack frame. The
sequence of reports and stack IDs for this program would be:
fn_entry(main,
65)
fn_entry(fun_one,
66)
fn_entry(fun_three,
67)
fn_exit(66)
fn_exit(65)
fn_entry(fun_two,
66)
fn_exit(65)
fn_exit(xx)
The last fn_exit is "xx" because the example
doesn't show what the right value would be.
As you can see, the stack frame for function
"main" is identified with the qprof_StackID value 65. Subsequently,
the qprof_StackID value 65 is consistently used to mean, "We're back in
main". The same goes for the qprof_StackID value 66 identifying the frame
for fun_one.
The qprof_StackID values must be maintained on a
per-thread basis, because the stack itself is a per-thread object. StackID
values don't have to be unique across threads.
Note that some implementations can get away with using
actual machine SP values as StackID values. This is not the case if the
execution stack of native C code is different from the "virtual"
stack maintained in the environment you're trying to profile. Also note that
the stack ID values can count either up or down.
In practice, you can read the fn_exit notification as
"Pop frames off the shadow stack until you get to one whose ID matches
this value". Exceptions, which can cause execution to continue in a frame
that is many levels up the stack from the last fn_entry, can be
handled in one of two ways. One way is to have the environment you're profiling
give you a "fn_exit" notification for every frame as it's unwound,
and you pass the notifications along to QProf. The other is for the
environment to give you a single "exception notification" including a
stack ID value for the frame where execution will continue. You can call
fn_exit once using that value; this will unroll the shadow stack all the way up
to the corresponding level.
Note! Do not use a StackID of zero to identify a real stack frame: it has special internal meaning to QProf. Also, if you mistakenly pass a StackID to fn_exit which does not correspond to any previous fn_entry, the shadow stack will be completely unrolled. This is a side-effect of the algorithm, not a feature. If you do it, the library might crash.
qprof_record_fn_entry() should be used only if a non-zero value for usingVirtualThreads was specified on initialization.
Signals exit for a function stored on the top of the shadow stack. Call one of the following:
void _EXT_REF qprof_record_fn_exit(qprof_StackID id);
void _EXT_REF qprof_record_fn_exit_vt(qprof_StackID id, VthreadID virtual_thread_id);
Stack_id Stack id (see Stack Id above).
virtual_thread_id - ID of the current virtual thread.
qprof_record_fn_exit_vt() should be used only if a non-zero value for usingVirtualThreads was specified on initialization.
Record time counter value for the current call. Call one of the following:
void _EXT_REF qprof_record_counts(qprof_Counts arg);
void _EXT_REF qprof_record_counts_vt(qprof_Counts arg, VthreadID virtual_thread_id);
arg Current counter value
virtual_thread_id - ID of the current virtual thread.
qprof_record_counts_vt() should be used only if a non-zero value for usingVirtualThreads was specified on initialization.
Record counter value for a basic block.
void _EXT_REF qprof_record_bb_counts(int bb_num,
qprof_Counts arg);
bb_num Basic block number (offset in the line number table)
arg Counter value
Record time counter value for the basic block bb_num. Time is recorded using the system clock.
Call one of the following:
void _EXT_REF qprof_record_bb_time(int bb_num);
void _EXT_REF qprof_record_bb_time_vt(int bb_num, VthreadID virtual_thread_id);
bb_num Basic block number (offset in the line number table)
virtual_thread_id - ID of the current virtual thread.
qprof_record_bb_time_vt() should be used only if a non-zero value for usingVirtualThreads was specified on initialization.
Copyright © IBM Corporation. 2001,2007. All rights reserved.
Rational, the Rational logo, Quantify, and PureCoverage are trademarks or registered trademarks of Rational Software Corporation in the United States and in other countries.