Handling memory within iterations in LoadRunner
Recently, I was asked to provide some assistance to a colleague for a particular data scenario that I had not come across previously. This particular scenario required that a user logged on to the Application can only perform particular searches based on criteria defined and assigned to that particular user. That is, VUSER1 can only search on terms1, terms2 and terms3 whilst VUSER2 can only search on terms4, terms5 and so on. The diagram below best describes the requirement.
My initial response to resolve this conundrum was to set up a database, import the data and query for the relevant data from within the LoadRunner script. This should work, Right?
Wrong. With timelines tight and the Projects (in)ability to install the database software on appropriate machines meant possible delay in testing that could be ill afforded. Getting the data was not a problem, however setting up a database instance required that 7 letter word starting with a P (Process). The Process required approval to install the software and also schedule times and resources to implement. These inhabitants plus the lack of experience by the tester to execute and setup database scripts within LoadRunner led me to think of a Plan B!
Therefore, Plan B was to handle this data requirement with some code within LoadRunner. How was this achieved? Firstly, two parameter files had to be created and handcrafted with the following format.
And below is the first cut of code to get a matched searchTerm for the loggedOnUser.
Action { // assumed vuser_init contains your logon details but for this example i'll assume the username is coming from a param file // LoggedOnUser therefore "loggedOnUser" is defined in vuser_init.... as a Unique & Once Parameter. // Basically loop through your param file to ensure that the loggedon user the right search terms. // Your param file will need to look something like this // searchUser, searchTerm // vuser1, john // vuser1, odecee // vuser1, perform // vuser2, test // vuser2, simple // vuser3, wow // vuser3, expert while ( (strcmp(lr_eval_string("{loggedOnUser}"),lr_eval_string("{searchUser}")) != 0)) { lr_advance_param("searchTerm"); lr_advance_param("searchUser"); }
lr_output_message("Test variables are Iteration: [%s], LoggedOnUser: [%s], SearchUser: [%s], SearchTerm : [%s]", lr_eval_string("{Iteration}"), lr_eval_string("{loggedOnUser}"), lr_eval_string("{searchUser}"), lr_eval_string("{searchTerm}")); return 0; }
Very simple piece of code to meet the requirements and no database in sight! Less than 30 minutes. Only overhead is crafting together parameter files to allow for the code to work.
However, (always one but!) a couple of issues were discovered. When the script was executed in a Scenario some of the Virtual users were failing with the error:
Error (-17991): Failed to add item to mfifo data structure
Some troubleshooting investigations found a couple of issues. First issue was that the data provided for the two files had a mismatch of data. The loggedOnUser file contained users that did not match to the searchUser therefore the script would iterate and loop through and eventually cause the virtual-user to abort due memory issues.
This leads quite nicely to the second issue with the above approach – the way lr_eval_string allocates memory internally within LoadRunner. For lr_eval_string, the memory is freed at the end of each iteration and in the code sample above, the virtual-user is evaluating a parameter as part of a loop. Generally loops of smaller size the lr_eval_string function would not be an issue. However, for this particular requirement the loop was over 200K+ records. This memory overhead impacts the scalability of the load generators as well.
As part of the more thorough testing, I monitored the mmdrv process with the above code only in a script. The mmdrv process grew from ~8000Kb to just over 40,000Kb looping through only 100K records. If the mmdrv process is large already than this code can cause a memory issue (or two!).
The solution to conserve memory is basically to not to use the lr_eval_string function at all. Instead, use lr_eval_string_ext to assign the variable and free the memory on each iteration of the loop using the lr_eval_string_ext_free.
So here is the new code with (i) an additionally condition to check for data mismatches and (ii) more importantly using the lr_eval_string_ext and lr_eval_string_ext_free functions to handle the memory within the iteration and its multiple loops.
When tested, the mmdrv process for 300K loop within an iteration stays at ~8000Kb (previously grew to 40 000Kb). Here is the code.
Action2() { unsigned long prmLen; //required to be set for use by lr_eval_string_ext char* buf; //required to be set for use by lr_eval_string_ext int loop = 0; int match = 0; int maxfilesize = 200000; // set this variable to stop the script from goin into a loop. Set to Size of your Param file +1 char loggedOnUser[16]; //might need to change this dependant on string-length of usernames // save logged on user Id here strcpy(loggedOnUser,""); strcpy(loggedOnUser, lr_eval_string("{loggedOnUser}")); while( (match == 0) && (loop < maxfilesize)) //can change the loop variable {
lr_eval_string_ext("{searchUser}",strlen("{searchUser}"), &buf, &prmLen, 0, 0, -1);
lr_output_message("ONE => buf=%s and loggedOnUser = %s", buf, loggedOnUser); if (strcmp(loggedOnUser,buf) != 0) { lr_advance_param("searchUser"); } else { match =1 ; //break out after match to searchuser and loggedOnUser is made! } // lr_output_message("Iteration %s => buf=%s, loggedOnUser = %s, searchTerm = %s , and loop = %i", lr_eval_string("{Iteration}"), buf, loggedOnUser, // lr_eval_string("{searchTerm}"), loop); lr_eval_string_ext_free(&buf); //dont forget to free! loop++; } //end of while loop if (loop >= maxfilesize) { lr_error_message("Iteration %s => buf=%s and loggedOnUser = %s and loop = %i", lr_eval_string("{Iteration}"), buf, loggedOnUser, loop); //exit user cos if this max is reached the script is looping through as NO match to users can be made in the param file. This is the first issue! } return 0; }
Overall, once the memory issues were resolved and appropriately catered for, then this code was more than adequate to meet the requirements specified above. Testing continued and was a success, all without a need for a database!



Pretty cool. Sometime simple way of doing thing is a better way
Nice Article. To add on further…
(1) There is a utility available from HP called VTS which can be used for all such sort of requirements like handling the dynamic parameters and using the parameters created during runtime through parameters and variables without pre defining them in file or so. It is quite easy to implement also and decently maintain dynamic memory allocation and de locations.
(2) I personally used a different way of handling it a long back when I was not aware about VTS. I created a flat file with fields:-
VuserName,ParamFeed
Vname1, value1,
Vname1,value2,
Vname2,value3,
Vname2,value4,
I wrote a c program to seek the required value (Vname1) inside that file by using file functions. and the moment when it was found the program searched the value between the 2 consecutive ‘,’s and saved it in a string. On completion of iteration the program was deleting the same line from the file itself and was replacing it with some garbage value so that the index of the rows should not change.
Hi Raman. Yes, well aware of the VTS within LR. Have utilized it a number of times with LR in the past, especially for unique parameter requirements (once implemented it’s a lot easier to maintain data between runs). Recently have been using Rational Performance Tester(RPT), and the way RPT and LR handle parameters is slightly different (but that’s another topic). In any case for RPT, a colleague has come up with a nice utility that cleans out datapools in RPT between test runs. I’m sure he will be blogging on this soon.
Thats great.
I am looking forward to see that code here. If I am not wrong RPT use its own proprietary scripting language.