Performance Testing @ the Frontline

A hidden world where small things make a big difference

Overriding LR functions with custom code

Posted by Kim on Tuesday, June 22, 2010

Most people do not know that LR supports overriding existing functions with custom ones in a very easy way.

I had a situation where I had a pre-made script that had fixed think-time values for the lr_think_time() calls, and they of course were completely wrong. So my problem was that I had a lot of lr_think_time() places and I didn’t want to go through them all myself.

In most cases a search-and-replace would do the trick, but I stumbled upon the overriding of functions and implemented it that way instead, making my changes to the script stay in one place only instead of manipulating all the actions in the script.

Remember: DO NOT CALL THE OVERRIDDEN FUNCTION INSIDE THE NEW CODE!!! – You’ll just end up with an endless loop, and terminate in stack-errors or similar…

Here’s an empty script where I’ve overridden the lr_think_time() function with a custom one:

#define lr_think_time my_think_time  // This define overrides lr_think_time() with my_think_time()

double my_think_time(double sec)
// This simulates the 50% to 150% think-time randomness ...
{
	double x;

	x = ((50.0+rand()%100)/100)*sec; // 50% to 150% randomness

	lr_force_think_time( x ); // force the think time (this is undocumented command)

	return x; // return how many seconds we waited
}

///////////////////////////////////////////////////////////////////////////////

Action()
{
	lr_think_time(10); // This will use the my_think_time() function !!

	return 0;
}

One could get creative and start overriding all sorts of functions with this. An example would be to set specific headers for specific web_url() calls depending on the params. You could override the web_url() call, examine the parameters and add any needed HTTP headers before doing a web_custom_request() instead ..

Enjoy!

Advertisements

13 Responses to “Overriding LR functions with custom code”

  1. Simon said

    So would it be possible to do the following:
    #define lr_think_time_REAL lr_think_time // define a new name for lr_think_time
    #define lr_think_time my_think_time // override the old function

    void my_think_time(double sec)
    {
    lr_think_time_REAL(sec*2);
    }

    just wait twice as long as the user intended. Or would it end in an endless recursion?
    I would really appreciate, if you could try this for me!(I do not have a LR installation to test this)

    • Simon said

      I made a mistake, the command should be:
      #define lr_think_time lr_think_time_REAL// define a new name for lr_think_time

    • Kim said

      if you call the overridden function inside itself you end up in an endless loop. I tried this and it does stay stuck forever with 100% cpu usage.

      Especially the think-time overriding needs the lr_force_think_time() that IS defined and usable in LR, just not documented.

      • Simon said

        turns out, i only had to change the order of the function and #define
        first create the function and then write #define lr_think_time my_think_time
        This way you do not have to call lr_force_think_time but you can call lr_think_time.
        Might not be that useful in this case, but if you would like to add some log messages, this could be a nice “feature”!

      • Kim said

        Would you mind posting a short snippet of code as an example? If I understood you correctly then the overriding of functions is much more usable if the #define is inserted later in the code, as it allows for calling the original function from inside the overridden function (much like inhertied funcname; would work in Delphi).

      • Simon said

        just like this(i added this in the global.h, but should work everywhere):
        void my_think_time(double sec)
        {
        lr_think_time(sec*2);
        }

        #define lr_think_time my_think_time // override the old function

  2. H Clark said

    It’s actual referred too as function overloading and is a convention of C++. What you did was step in front of the compiler and tell it to evaluate the sting token lr_think_time as something else. Since function overloading isn’t possible in C(even though this didn’t quite achieve the real benefits of doing so) it still is a great hack and discovery. So KUDOS for achieving the seemingly impossible 🙂

    Great Blog, nice to see someone giving back on lower level tips and tricks of the trade.

    H Clark
    testrocket.org

  3. Guy said

    Hello Kim,

    I hope that I can use the overloading method to create a custom web_global_verification function.

    web_global_verification aborts the execution when it picks the text under search. This is not the behaviour I require in my script.

    I would like to write to the log in which transaction the error was picked and resume the run from the vuser.init (for example)

    Would the overloading hack can deal with what I require?

    Many Thanks,

    Guy

    • Kim said

      The overriding in your case would not work, since the web_global_verification() only sets a “watch for this string in global responses”. The actual checking is done by LR itself and is (as far as I know) not possible to intercept.

      The way I have done this “custom global verification” is to add a call to a custom function before every web_*() call, and another custom function call after each web_*() call. Doing it this way I can control what I look for, as well as handle the transaction names. I do not know of any function to “get all open transactions” in code, something an advanced error checking code would definitely need.

      Example:
      __GlobalSetChecks("A_Transaction_Name"); // Here I can set my own web_reg_find() or web_find()'s as I please
      // here we can add correlation statements that belong to the following call
      // web_*() call goes here
      __GlobalCheckResult("A_Transaction_Name"); // Here I act upon the saved parameters.

      The drawback is obviously that you need to add the calls to the __Global*() functions everywhere, and also need to set CONTINUE_ON_ERROR since otherwise you can’t check the results of a call if an error occurs (like HTTP 5xx).

      Inside __GlobalCheckResult() I do a lr_exit(LR_EXIT_ITERATION_AND_CONTINUE, LR_FAIL); if I detect an error, and also do custom lr_error_message() logging if needed (Here you can log the last transaction name).

      About resuming the execution, there is no way to resume from vuser_init(), the only thing you can do is start the next iteration, or continue as if nothing had happened. The vuser_init() action is executed only once, and cannot be executed again (unless you call it explicitly in code).

      Regards, Kim

  4. Guy said

    Hello Kim,

    I hope I can ask few more question on your proposed solution as I’m in a process of implementing it.
    1). How do you handle waste time for both functions?
    2). Do you use the these functions only for action section? I would like to have them used for the init part too. This may mean extra 2 function that will include a call vuser_init (?)
    3). Do U check for response return status 5xx also or do you use only web_reg* functions?

    My idea is to create array of char text strings (all errors) and feed them to 1 web_reg_find lr function. Check savecount and report if text was found. Would you consider this as a valid idea?

    Many Thanks,

    Guy

    • Kim said

      Wasted time is under most circumstances really not an issue, but if your code does something really time-consuming you can use the lr_wasted_time() function to remove the wasted time.

      I often have several actions that are removed from the run-tree and are called dynamically when needed, so the script is more like a real program than just a step-by-step script. I add the pre- and post- check calls to all the web_* functions, even in the vuser_init() and vuser_end() actions if needed. Most of the time however I only do initialization in vuser_init() and have parameters/flags that indicate if I need to login in etc. during the actual iterations.

      In my post process function I do check for 5xx codes, as well as body size etc. Almost all error checking that is generic goes in there.

      Using web_reg_find() is ok, as long as you don’t overdo it. Having several hundred text strings to check for is a little overkill, usually I do it so that if the container for the text is found, I parse the response and log that. Then I do not have to add all the errors and also support dynamically generated or new errors without modification to the script(s).

      • Guy said

        I’m gladly embracing this mature concept with new scripts that I’m working on right now.
        Thanks for sharing such a useful information.

        Guy

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

 
%d bloggers like this: