|
A Team Script Framework For LoadRunnerHere is a full-featured framework written in LoadRunner VuGen script that adds agile feature and overcomes several inconveniences and annoyances associated with large-scale performance testing of Web applications | Topics this page: |
|
Annoyances and Solutions
This framework makes irrelevant several limitations to using the advanced Eclipse IDE to edit and compile LoadRunner scripts, because the framework handles run-time and parameter settings from a spreadsheet read by the framework. The Eclipse platform also cannot step through LoadRunner scripts as they execute. But the framework also makes that mostly irrelevant because the framework enables stop points and changes to verbosity to be specified between any individual action. |
What's The Catch - The Down Side?This framework takes time to initiatlize its variable every time it starts. This takes about 10 seconds if the library is in the Init section and 3 seconds if the library is in a header library. I agree with Eric Gamma that the bigger the framework becomes, the greater the chances that it will want to do too much, the bigger the learning curves become, and the more difficult it becomes to maintain it. There is a learning curve to edit the script to what the framework needs and extra complexity in managing different driver files. However, my belief is that the extra up-front efforts are repaid in time savings from avoiding errors -- from not writing code, and future flexibility. The up-front effort is lessened by my code generator that creates framework call code from the DriverParms spreadsheet used to run load tests. |
Getting StartedYou save time by using this shared framework because you only need to specify a few lines of code to benefit from a whole set of features. You don't have to understand how the functions, variables, and attributes interact with each other in the library. This "LoadRunner on Rails" framework is easy to begin using, and thus also easy to stop using because it sits "on top" of any existing scripts you may have. In the script's standard vuser_init are two functions: #include "wi_lib.h" vuser_init() { wi_vuser_init_vars(); // to initialize utility variables and attributes. }This script's vuser_end that LoadRunner runs once at the end is also simple: vuser_end() { rc=wi_vuser_end(); // function within the wi_lib.c }Within LoadRunner's Action section that LoadRunner iterates, insert this framework function: Action() // invoked by LoadRunner for each iteration. { int rc=LR_PASS; rc=wi_action_Keyword_loop() return rc; } The workings of these functions are described a scetion below. |
Editing Recorded Scripts
These are explained in the sections below.
|
Easy Transaction Name ManipulationUnlike LoadRunner transaction commands, this script does not require you to come up with a transaction name used to attach statistics. Between LoadRunner request functions, instead of inserting lr_start_transaction("w"); and lr_end_transaction("w",LR_AUTO); insert two framework function calls: wi_startTrans(); rc=web_submit_data(CurrentTrans, ... ); wi_endTrans();
Function wi_startTrans() obtains its transaction name from the the "Trans" column within the current Driver Parms row.
later section.
If no transaction name is specified in the driver file, function wi_startTrans() makes up its own transaction name such as "T1" with the TransNamePrefix C character defined in wi_app.c plus a counter incremented every time that function is invoked.
The script framework saves the transaction name specified into the CurrentTrans
C character variable (array) so that coders do not need to retype names in transaction end functions.
wi_startTrans() also defines LoadRunner web_reg_save_param() functions to capture response text. The wi_endTrans() accumulate statistics such as the amount of run time and number of bytes downloaded.
Alternatively, if you want to explicitly specify a transaction name in your application-specific script,
use this function instead of
wi_startTransName("X1"); rc=web_submit_data(CurrentTrans, ... ); wi_endTrans(); Function wi_startTransName("X1") ignores what is specified in the "Trans" column within Driver Parms. The script framework also provides a set of functions to obtain totals across several transactions: wi_startTotal(); wi_startTrans(); rc=web_submit_data(CurrentTrans, ... ); wi_endTrans(); wi_startTrans(); rc=web_submit_data(CurrentTrans, ... ); wi_endTrans(); wi_endTotal();Currently, only one total is maintained at a time. Internally, LoadRunner makes available as the {pDriverTrans} LoadRunner parmeter that the script moves into the charDriverTrans C variable. This is illustrated in a |
Response Evaluation{pPlaybackMode}The wi_startTrans() function also appends"_txt" to transaction names associated with "MODE=HTTP" and "_all" to transaction names associated with "MODE=HTML". This text can be changed to whatever you want in function wi_app_init() within wi_app.c Action script. sprintf( strGetURLOnlySuffix, "_txt"); sprintf( strGetAllSuffix, "_all"); Capturing Text Returned From ServerThis script framework invokes loadrunner web_reg_save_param commands to capture from each web page returned into LoadRunner parameters:
These are in addition to the response time and bytes downloaded from each request. If no web_... function is defined between startTrans() and endTrans(), LoadRunner issues an error message about "web_reg_param" not used. So in such a case, instead of startTrans() and endTrans(), code this function: wi_stubTrans();
Clear CookiesBefore the request is made, if the "ClearCookies" attribute is set to "Y", cookies are cleared out.
Output Response Header and Body Returned From ServerIf a value of "Y" is specified for run-time advanced attribute named "OutputBody", the transaction functions will capture and display the HTML response body returned from the server (without adding line numbers, line breaks, and other extraneous text).Similarly, "OutputHeaders" controls the output of HTTP response headers. Additionally, the wi_endTrans() functions will make a request to the Diagnostic web service if the "ProbeShow" attribute is set to "Y". |
Message FormattingMessages within this script are constructed using the "my_msg" global variable and the printMessage() function: sprintf( my_msg, "... " ); printMessage("debug", my_msg); The "debug" in this coding example refer to Java log4j type levels controlling message output. trace messages are issued within loops. This includes all the message levels below. debug messages should not be printed when the application is in production. This includes all the message levels below. info messages are issued to display the condition of major events. This includes all the message levels below. warn messages are issued when the application is able to carry on without a problem, such as when a default value is overridden. This includes all the message levels below. error messages are issued when an incorrect administrator-supplied configuration parameter is detected. This includes all the message levels below. fatal messages are issued before the script run must be aborted, such as when a required value is not found. User selections in Run-Time Settings Log settings are mostly ignored by this script. I say "mostly" because a "Advanced" settings will result in a dump of internal settings before the script's own message handler take over.
|
SiteScope and Web Services Diagnostics Probe Metrics
Metric collection is off by default.
Diagnostics Probe statistics for the instrumented application server under test are retrieved from the Diagnostic server if the "ProbeShow" attribute is defined with a value of "Y". Defaults for run-time attributes ProbeHost and ProbeName need to be specified to avoid an error. Run-time attributes ProbeUserName, ProbePassword do not need to be changed if the diagnostics probe was installed with default values. Like Oracle STATSPACK reports, SiteScope statistics are obtained at the start and end of the run. But this framework also takes readings after each iteration (if enough time has gone by, as describe below), which provides even more information than running STATSPACK before and after test runs. SiteScope statistics in the middle of runs are calculated based on incremental change in values elapsed since the last reading. This is necessary because numbers from Oracle V$SYSSTAT are cumulative from when the database was restarted. Each probe gathering can take a substantial amount of time (up to several seconds) to retrieve and process its raw data. To avoid overwhelming the server being probed, the "ProbeSecs" attribute is queried to limit the amount of time between probe gathering requests. The wi_SvcDiag_request() function called to make the probe request does not actaully make a request and returns with an LR_FAIL condition if the specified time has not elapsed. Its default value is 60 seconds (one minute) if this attribute is not defined. The script will bypass making an observation if the number of seconds elapsed since the previous observation is not at least the "BetweenMonSecs" attribute specified. The default is 60 seconds if this attribute is not defined. Not all statistics reported on the web page retrieved are of interest to this framework, so each metric is retrieved with a separate web_reg_save_param command scanning the web page retrieved. This framework script adjusts the numeric scale as necessary, such as turning raw bytes to KiloBytes (KB) by dividing with 1024. "k_rows_per_sort" is thousands of rows per sort operation. The framework issues ratios as a user defined data point in the LoadRunner Controller and in the Wiki summary file. Other important stats, such as the library cache hit ratio, must be manually setup in SiteScope. According to KB 45014 & 36377, the default SiteScope/templates.applications/commands.oraclejdbc file specifies SQL retrieving Oracle tables V$SYSSTAT V$INSTANCE V$STATNAME V$SESSION dba_data_files This occurs through jdbc:oracle:thin:@IP Address:1521:Database and Oracle 10g client drivers in classes12.zip expanded into the SiteScope/java/lib/ext direcory.
In the SiteScope/groups/master.config file
it helps to cache database connections (which limits the amount of connections and CPU usage) by adding
Queries are limited to 4096 bytes. Results can be clipped if these settings are lower than: _databaseMaxColumns=10 _databaseMaxRows=1 _databaseMaxSummary=20
| Key ratios DBAs use to manage Oracle are calculated by this framework:
buffer_cache_hit_ratio
These ratios depend on having SiteScope edited to report:
consistent gets
SiteScope should also be edited to report Oracle activity volume statistics. |
The apdex Threshold Ratio
Several stardard statistics are calculated by the LoadRunner Analysis program:
(The median is not shown by the Controller Run tab because that can only be calculated after all values are available at the end of the run.) The trouble with these common statistical measures is that they do not reflect the standard specified in SLA (Service Level Agreements), which have definitions of unacceptable response time such as "4 seconds 90% of the time (including WAN traffic time)". The "90%" reported by LoadRunner is calculated from response times collected throughout the entiraty of a particular run. LoadRunner does not report whether transactions met its performance criteria at various times as loads vary throughout a run. These shortcommings are resolved by a customer-oriented statistic recently developed by the apdex alliance created and led by Peter Sevcik of NetForecat. LoadRunner does not calculate this because it's so new. But this framework does calculate it and report it as a user-defined metric. The apdex (application performance index) reflects the ratio of measurements over a period of time that meet (or nearly meet) performance thresholds. I say "nearly meet" because Apdex gives "half credit" to individual response time measurements that is between two performance standards:
Beyond this point, measurements are not counted as part of the index. On graphs, excellent performance numbers are shown higher than unacceptable performance numbers.
A 0 (0%) value is obtained when all measurements are frustrating to users. A 0.6 (60%) value is obtained when 40% of measurements are tolerating or frustrating to the user. Because the apdex is geared toward production performance monitoring, which yields stats summarized by day, week, month, the Apdex specification v1.1 considers sample sizes of 100 per index value normal. It asks that smaller sample sizes be marked with an asterisk.
|
|
Transaction LoggingThis script framework provides coding to create two logs to the folder specified in the OutputLogFolder attribute, which contain values such as "C:\\Temp". The double slashes are necessary. "Y" in the OutputWikiLog attribute will allow the output of rows in a text file formatted with vertical bars to separate columns within a wiki page table after each successful transaction. An example:
For convenience, the elapsed time and amount of data captured from returning transactions is expressed in seconds and KiloBytes rather than the milliseconds and individual bytes. "KB" is 1024 bytes. The "KB/Sec" column provides a rough calculation for a general estimation. This is of course way low becuase it includes server processing time plus transmission time. The "X" column is the number of times the actual bytes returned versus the expected number of bytes specified in the Driver Bytes column. For example:
"-.5X" is obtained when 500 bytes were returned vs. 1000 bytes expected. The "XKB" column reports the bytes expected after convertion to KiloBytes. "Y" in the OutputCsvLog attribute will allow the output of comma separate values in a ".csv" (comma separated) file for import into Excel This is done after every driver control record is processed, whether it was success or not. |
Time Boxing
This script framework enables you to achieve a consistent throughput rate as measured by the number of transactions completed per minute. Time boxing is useful to determine the amount of resources (CPU % utilization, etc.) consumed at a particular level of throughput, usually to mimic a specific mix of production transactions determined by analyzing production web logs. Simply adding a fixed amount of time after a transaction cannot achieve a consistent throughout rate because actual transaction times vary. Time boxing ensure that each request takes a consistent amount of time by adjusting the amount of wait time after a transaction to fit a fixed time frame. This period of time specified two ways: Time within each transaction time box includes:
If a transaction takes longer than the time box time, an error message is issued and time is taken away from the next transaction's wait time. This is an attempt to keep the intended schedule by "making up" for the extra time. An excessive number of these errors of course means that the time boxes should be longer. Time boxing behaviour is on by default. To disenable it, add a "TimeBox" run-time attribute with a value of "N". Client TimeoutsWhen the "ClientTimeout" attribute is set to "max", CONNECT, RECEIVE, and STEP timeout settings on the client are set to their maximums (1000, 1000, and 32000 seconds, respectively).This is time the script waits if the server does not repond.
A value of "defaults" sets the timeouts to 120 seconds.
This script overrides these settings to the value of TimeBox Wait seconds to avoid creating time outs. |
Execution Control with Keyword DriversThe call to script framework function wi_action_Keyword_loop() is the "key active ingredient" that makes this script framework save you time by being so powerful and flexible. Instead of you coding repetitive script code, what happens during a test run is controlled by command requests defined in a "driver" parameter file. This framework loops through a DriverParms.csv parameter file to control what is executed. Within each row of the driver parameter file is are keywords that causes the framework to act a certain way. Rather than changing a series of script code and recompiling, a spreadsheet program is used to define keywords and control data that the script carries out as calls to functions within the script. For example, instead of hunting down a particular section of script coding to comment out logic (and recompiling), you can simply change the NOT PROCESS flag in the control data and the script will not execute that request. Using data to control what the script does eliminates the need to repetitively code (and debug) many other features, described below. |
Sample DriverParm.csvThis sample script is driven by a parameter (.csv or .dat) file. Its columns are aligned vertically when openned from within Excel
U,V,TT,Rand,Trans,Think,Act,Timebox,Node,Info1,Info2,Info3,Bytes,Tries,Seq Y,,,100,,,ParmSet,Version_PubID,1826102,,,,1 Y,,,100,,,VarSet,OutputResponseFile,Y,,,1 Y,,,100,Login,0,Login,http://127.0.1.1:5001/webapp1/,myself@somewhere.com,Pa$$word12,,,2 Y,,,70,,3,Logout,,,,3 |
U (Include in Run)Whenever the Action script encounters any value other than NULL or "Y" in the first column, that row is skipped and processing continues to the next row. This value is limited to 1 character.This is a great time-saving and error-proofing feature because by changing just one character, you can avoid time-consuming typing mistakes. V (Vuser ID)This column specifies Vuser ID used by the LoadRunner Controller to coordinate release of transactions based on lr_rendezvous points during contention testing runs.TT (Task Type)This is used to filter execution to requests application only to specific type of tests.Any character in this column causes the request to be carried out only if it matches what is in the "TaskType" run-time attribute. In other words, a value such as "L" would not be executed unless the TaskType attribute is also set to "L". A blank in this column specifies that the request should be carried out if TaskType is set to blank in the Info1. "init" runs only once. An AttrSet request for TaskType value "EnvID" in Info1 sets the TaskType value at the EnvID attribute value. This value is limited to 16 characters. Rand (Random Execution)The control file contains a "Rand" column to enable the specification of the percentage chance that a particular request will execute.
The "Rand" column specifies the percentage of time that the row is invoked.
It can be any number from 0 to 100 (with blanks assumed to be 100).
The larger this number is, the more likely its row will be executed.
This number is compared against a random number generated at the start of each iteration.
"else" in the Rand column means that the current request is carried out if the previous transaction was not carried out due to random chance. This essentially provides a type of "if/then" logic. TransThe "Trans" column contains the prefix of transaction names which will appear in LoadRunner Analysis. These names do not contain suffixes such as "_txt" which are added automatically depending on what else is specified for the run.This value is limited to 64 characters. ThinkThink times simulate the length of time a user spends viewing a page or entering data. The amount of time end-users spending "thinking" about what to do next have significant impact on their productivity and their usage of the system. If not enough think time is simulated during load tests, simulated users would navigate the site unrealistically fast, which would yield artificially poor performance results.To emulate the time users need to look for the data to enter, find the key/mouse location, contemplate, etc., "think time" can be added before a specific request specified in the DriverParms file simply by adding a value in the "think" column of that request. Unlike the LoadRunner default, this can be a floating point value such as 3.6. Rather than numbers, the framework has been programmed to recognize several keywords, in both English and Italian tempo names used by musicians. This table is referenced to assign the think time appropriate to each transaction and user group.
To add the same amount of think time before every transaction, in Run-Time Settings Additional Attribute add a framework attribute "ThinkSecs" or add a line specifying "AttrSet" in the Action column, "ThinkSecs" in the Node column. The time specified is added to time in Think columns in DriverParms. To ignore all these settings during a run, specify a value of "N" (any value other than "Y") for attribute "ThinkBefore". For additional accuracy, overhead "wasted" time that the framework needs to perform internal processing before each transaction (such as reading driver parameters and writing out log header records) are automatically calculated and included as part of think time (if any). ActThe "Act" column contains the keyword which the script matches with the appropriate function. It is limited to 64 characters. If this field is blank, the value from the previous row is assumed. If no previous value is available, that row is ignored with an error message.The framework recognizes several act keywords:
Parmset stores a LoadRunner parameter named in Node with a value in Info1.
ParmShow displays the LoadRunner parameter named in Node. The confirmation message: INFO: ParmShow of Version_PubID=1826102 AttrSet stores a new value for a pre-defined run-time attribute. VarSet stores a C variable named in Node with a value in Info1. The app_init function recognizes valid variable names and issues an error if one is not recognized. Unlike parameters, globle variables need to be coded into vuser_init.c. Braces in the Info1 value "{lrname}" stores that LoadRunner parameter name.
Total with Node value of "start" begins a running total of seconds and bytes returned.
URL causes a "text/html; file to be retrieved from the address specified in the Node column.
These are all processed as sub transactions. Application-specific acts are added as additional LoadRunner Action script files referenced from framework function wi_app_act_modules(). Examples of these include:
Node"Node" specifies what object or component is affected. If this field is blank, the value from the previous row is assumed. If no previous value is available, that row is ignored with an error message. This value is limited to 128 characters.TimeBox/Wait"TimeBox" or "Wait" column specifies the amount of time boxing that the transaction should take.Info1This information is specific to each type of request. With Login transactions, this may be a hard-code User ID value. To determine whether this field is blank in the Driver request parameter record, use this coding:if( strlen( lr_eval_string("{pDriverInfo1}") ) == ZERO ){ ... it has information ... } This value is limited to 256 characters. Info2"Info2" values depend on the request. Usually it contains the title which the framework automatically matches against what is returned between <TITLE> and </TITLE> HTML tags. This value is limited to 256 characters.Info3"Info3" values depend on the request. For login requests this column contains the suffix added to subsequent transaction names for that user.Bytes"Bytes" specifies the number of bytes expected from a URL request. This field is blank for requests no associated with an interation with a server. This value is typically obtained from a prior run, or from a file directory."Y" in the CompareBytes run-time attribute will add comparisons of the bytes returned as a multiple of the expected bytes defined in this field. The MaxBytesDiff run-time attribute is used as the threshold of bytes difference before issuing an error message. Tries"Tries" is the number of times that the transaction is attempted before being declared an error. A blank in this column assumes a single try.This column is also used to specify repeatition. "x 2" (an X with a space and a number) specifies the number of times that the request is repeated. SequenceThe last column, "Seq", is a sequence number to uniquely identify each row. This column does not trigger any activity in the script. It's in the last row to make incorrect comma separtors easier to identify. An error message is issued if a "1" is found anywhere else but in the first record. This is done to identify mistakes with specifying the DriverRecs number."end" in the Seq column of the last request designates the ending record number, which does not include the header row. This number of records read can be prematurely limited by defining a DriverRecs attribute. Separating program control data from script code provides you an option to reuse 4) keyword files controlling functional tests |
Circular Data Feedback LoopHaving control information in a data file provides tremendous flexibility. 80% of most changes to the script can be accomplished with a change to the driver file.
Changing data in a spreadsheet is
The OpenOffice spreadsheet program is not usable for this purpose because it adds double quotes to header records that LoadRunner does not recognize as valid. |
File Architecture For Flexibility in Managing Growth in ComplexityFramework script code load Run-time attributes into C global variables defined in the vuser_init.c file or in a .h C header file, which can also contain function definitions. To determine what to run, framework script functions read Driver fields in the DriverParams.csv file. This file can be viewed and updated using a spreadsheet program such as Microsoft Excel to copy and paste data controlling script execution. Different behaviours in the script can be specified by different editions of the internal content of files (the DriverParms.csv driver file, .c script files, or the .prm file defining parameters). For example, a different set of files are copied into the script folder for a load test vs. a benchmark test run. There are several techniques to replace the internal content of files with files of the same name in a different folder. When VuGen saves a script as a new name, only files which are directly referenced within VuGen script are copied to the new script folder. So it is necessary to have variations of data files stored away from the script folder.
The LoadRunner Controller normally copies to load generators all files in the script folder.
But it can be told to send different files to load generators using the "More", "Files" GUI.
Such requests can be automated using
Shell scripts at the operating system level can
|
Custom Module Act DistributorAfter the framework (within vuser.init.c or wi_lib.c) processes "Act" values it recognizes, function wi_app_act_modules() (at the bottom of file wi_app.c) is called. wi_app_act_modules() // Called from within wi_lib.c to process custom Act values { int rc=LR_PASS; if( strcmp( charDriverAct,"Login") == FOUND ){ if( strcmp(strProcessWithinInit,"Y") == FOUND ){ rc=LR_PASS; }else{ rc=wi_app_login(); } }else if( strcmp( charDriverAct,"DoSomethingElse") == FOUND ){ rc=application_specific_module_xyz(); }else{ sprintf( my_msg, "Seq=%s Act/Module=%s in not recognized {wi_app_act_modules}" ,charDriverSeq ,charDriverAct ); wi_printMessage("error", my_msg ); return UNKNOWN; } return rc; // back to wi_process_driver_tries() in wi_lib.c } The framework expects that the "application_specific_module_xyz()" function return a LR_PASS static value if it successfully processed the request or a LR_FAIL if it has not. This hierarchial architecture of the script framework enables the use of a single script file from unit test through benchmark to load test to scalability tests, with all features built-in. Several script developers can work at the same time if they each check out from Subversion/CVS the latest version of the script framework which contains coding to recognize Act value "" as a call to application_specific_module_xyz, which can refer to a new LoadRunner application_specific_module_xyz.c file or a function within a script file (or header file) with another name. The individual developer would then focus only on changing the application_specific_module_xyz.c file, since only this file is checked back into the Subversion/CVS repository for the entire team to use.
|
Coding Individual Screens
This script sends different requests when the Node is blank (NULL)
and when it contains "Spec".
At the bottom of the script, if the script cannot recognize the Node provided to it, it returns the "UNKNOWN" static variable back to the framework to handle. if( strcmp( strGetExtras,"Y" ) == FOUND ){
Avoid coding more than one request per function. |
Modular Core Utility Function Libraries
Within the wi_lib.h are definitions of global utility variables used
Note that the names of functions in the script all begin with the name of the action file where they reside. Functions return values using static variables LR_PASS, LR_FAIL, UNKNOWN. |
Catalog of Run-time AttributesMarkers such as {4} have been placed throughout the code to provide a linkage between where global variables are defined in wi_lib.h or vuser_init.c with where they are retrieved, edited, and used in other parts of the script.
|
Parameters
|
This script displays with each message and output log entry a unique identifier to each request: 060211015432S0GNoneU-1I1.1-1R3 This identifier includes a timestamp. The first part of this is used to name log files:
The second part of this is used to identify processing sequence:
|
Trouble ShootingAfter you install this framework, run it before making any changes. If you get weird compile messages, you may not have the right version of LoadRunner installed. This script does not work with LoadRunner 7.8 and earlier versions.
This framework was written by creating a new multi-protocol script of protocol "Web (Click and Script)"
using LoadRunner 8.1 FP4.
Script created through VuGen recording should have a comment of "Recorder Version : 1289".
|
Compatibility with Functional Keyword Driver DataAt of this moment, control files in this script is not recognize the control file format of any public keyword engine such as STAF, as described in my page keyword driven (single-user) functional test specification files STAF has several levels of control files. However, LoadRunner currently only handles a single level of control files. This can be remedied with a library of functions which retrieves more files and iterates through them. To fully accept even the first level of driver files, this script will need to be updated to recognize the dozens of commands that STAF recognizes. Not all commands for functional testing are relevant to load testing. |
Related Topics:
Performance Testing
NT Perfmon / UNIX rstatd Counters
Mercury LoadRunner
Mercury Virtual Table Server (VTS)
Mercury WinRunner
Rational Robot
Free Training!
Tech Support
| Your first name: Your family name: Your location (city, country): Your Email address: |
Top of Page
Thank you! |