Friday, November 12, 2010

My load test 3

JDS Australia » Tech Tips » Monitoring Tomcat with LoadRunner
Monitoring Tomcat with LoadRunner
December 10th, 2008 Posted by Stuart Moncrieff 3 Comments »
LoadRunner does not come with a monitor for Tomcat. Fortunately, you can easily create one in about 5 minutes…

Tomcat exposes metrics related to JVM memory and Servlet container threads (and some other useful information) on a Status page at /manager (ask your Tomcat admin to enable it).

Create a standard web vuser script which loads the Tomcat Status page, and capture all the metrics you want using web_reg_save_param. Then log these values using lr_user_data_point. The metrics will be visible in the LoadRunner Controller and also in LoadRunner Analysis on the User-Defined Data Points Graph.
lr_start_transaction("monitor tomcat");

/*

JVM

Free memory: 130.99 MB Total memory: 254.18 MB Max memory: 1016.12 MB

*/

web_reg_save_param("JVMFreeMemory",
"LB=Free memory: ",
"RB= MB",
"Ord=1",
LAST);

web_reg_save_param("JVMTotalMemory",
"LB=Total memory: ",
"RB= MB",
"Ord=1",
LAST);

web_reg_save_param("JVMMaxMemory",
"LB=Max memory: ",
"RB= MB",
"Ord=1",
LAST);

web_reg_find("Text=/manager",
LAST);

web_url("status",
"URL=http://{ServerName}/manager/status",
"Resource=0",
"RecContentType=text/html",
"Referer=",
"Snapshot=t1.inf",
"Mode=HTTP",
LAST);

lr_end_transaction("monitor tomcat", LR_AUTO);

// Tomcat JVM metrics
lr_user_data_point("Tomcat JVM Free memory", atof(lr_eval_string("{JVMFreeMemory}")));
lr_user_data_point("Tomcat JVM Total memory", atof(lr_eval_string("{JVMTotalMemory}")));
lr_user_data_point("Tomcat JVM Max memory", atof(lr_eval_string("{JVMMaxMemory}")));
VuGen correlation for SAP Web Dynpro
July 27th, 2008 Posted by Stuart Moncrieff 3 Comments »
If you are trying to create a LoadRunner script for a SAP Web Dynpro application, and you are having problems correlating the SAPEVENTQUEUE in your POST request, then this Tech Tip is for you…

Here is what a typical request might look like:
web_submit_data("sap-ext-sid_2",
"Action=http://www.example.com:8000/sap/bc/webdynpro/SAP/ERC_A_WORKCENTER/;sap-ext-sid={SapExtSid2_120}",
"Method=POST",
"TargetFrame=",
"RecContentType=text/html",
"Referer=http://www.example.com:8000/sap/bc/webdynpro/SAP/ERC_A_WORKCENTER/;sap-ext-sid={SapExtSid2_98}",
"Snapshot=t18.inf",
"Mode=HTML",
ITEMDATA,
"Name=SAPEVENTQUEUE", "Value=Custom_ClientInfos~E002Id~E004WD01~E005WindowOpenerExists~E004false~E005ClientURL~E004http~003A~002F~002Fwww.example.com~003A8000~002Fsap~002Fbc~002Fwebdynpro~002FSAP~002FERC_A_WORKCENTER~002F~003Bsap-ext-sid~003DzuUt57Mx_3JozG7pOff~002AEg--U_0j6OHCaCQurUN1Pimp1Q--~E003~E002ClientAction~E004enqueue~E005ResponseData~E004delta~E003~E002~E003~E001TimeTrigger_Trigger~E002Id~E004WDE4~E003~E002ResponseData~E004delta~E005ClientAction~E004submit~E003~E002~E003", ENDITEM,
"Name=sap-charset", "Value=utf-8", ENDITEM,
"Name=_client_url_", "Value=", ENDITEM,
LAST);
Obviously the sap-ext-sid has already been correlated (this is easy to do with a Correlation Rule), but the SAPEVENTQUEUE also needs to be correlated. This is difficult, as it is constructed dynamically using JavaScript, so the value does not appear directly in any HTML response, and therefore cannot be correlated using a simple web_reg_save_param.
Examining the SAPEVENTQUEUE string, there are two repeated patterns; a series of 5 characters like “~E005?, and a series of 5 characters like “~003A” (without the “E”). Taking an educated guess, we can see that the string…
1 http~003A~002F~002Fwww.example.com~003A8000~002Fsap~002Fbc~002Fwebdynpro~002FSAP~002FERC_A_WORKCENTER~002F~003Bsap-ext-sid~003DzuUt57Mx_3J
…is an encoding of…
1 http://www.example.com:8000/sap/bc/webdynpro/SAP/ERC_A_WORKCENTER/;sap-ext-sid=zuUt57Mx_3J
…which means that…
• ~003A is :
• ~002F is /
• ~002F is /
• ~003D is =
• ~003B is ;
So it looks like SAP has invented their own way of URL Encoding values to be POSTed to a Web Dynpro server.
But what about the encoded values with an “E” at the start? Searching through the source code, we find that these are special “event separators”…
• ~E001 is EVENT
• ~E002 is SECTION_BEGIN
• ~E003 is SECTION_END
• ~E004 is KEYVALUE
• ~E005 is KEYVALUE_PAIR
• ~E006 is COLLECTION_ENTRY
As I am unlikely to want to change the separators, here is a simple function that will encode a string using SAP’s special version of URL encoding.
// This function replaces unreserved characters in a string with their encoded values.
// Encoding is in the style of SAP Web Dynpro. E.g. "abd*def" becomes "abc~002Adef".
// Reserved/unreserved characters are according to RFC3986 (http://tools.ietf.org/html/rfc3986)
// This function returns a pointer to the start of the encoded string (buf).
// Note that buf must be big enough to hold original string plus all converted entities.
char* dynpro_encode(char* plain_string, char* buf) {
int len = strlen(plain_string);
int i,j;
char hex_value[3];

if (plain_string == NULL) {
lr_error_message("Input string is empty.");
return NULL;
}

for (i=0, j=0; i= 'A' && plain_string[i] <= 'Z') || (plain_string[i] >= 'a' && plain_string[i] <= 'z') || (plain_string[i] >= '0' && plain_string[i] <= '9') || (plain_string[i] == '-') || (plain_string[i] == '_') || (plain_string[i] == '.') || (plain_string[i] == '~') ) { buf[j++] = plain_string[i]; } else if ( (plain_string[i] < 32 ) || (plain_string[i] > 126) ) {
lr_error_message("Input string contains non-printable or non-ASCII character %c at position: %d", plain_string[i], i);
return NULL;
} else {
// The unicode value for use in url encoding is the same as the hex value for the ASCII character
itoa(plain_string[i], hex_value, 16);
buf[j++] = '~';
buf[j++] = '0';
buf[j++] = '0';
buf[j++] = toupper(hex_value[0]);
buf[j++] = toupper(hex_value[1]);
}
}

buf[j] = NULL; // terminate the string
return buf;
}
And, just for completeness, here is a function that will decode a string.
char *strncpy ( char *dest, const char *source, size_t n ); // explicit declaration required

// This function replaces encoded characters from with their non-encoded value.
// Decoding is in the style of SAP Web Dynpro. E.g. "abc~002Adef" becomes "abd*def".
// Reserved characters are according to RFC3986 (http://tools.ietf.org/html/rfc3986)
// This function returns a pointer to the start of the decoded string (buf).
// Note that buf must be big enough to hold the decoded string (always equal to or shorter than the encoded string).
char* dynpro_decode(char* enc_string, char* buf) {
int len = strlen(enc_string);
int i, j;
char code[3]; // holds url encoded value e.g. "2F" (/)
int hex; // decimal value of hex code e.g. 47 (0x2F)
int rc; // return code

if (enc_string == NULL) {
lr_error_message("Input string is empty.");
return NULL;
}

for (i=0, j=0; i // Only convert entities that do not start with "~E". Do not run off the end of the string.
if ( (enc_string[i] == '~') &&
(enc_string[i+1] != 'E') &&
((i+4) < len) &&
(enc_string[i+1] == '0') &&
(enc_string[i+2] == '0') &&
(isalpha(enc_string[i+3]) || isdigit(enc_string[i+3])) &&
(isalpha(enc_string[i+4]) || isdigit(enc_string[i+4])) ) {
// Get the hex value from the input string
code[0] = enc_string[i+3];
code[1] = enc_string[i+4];
code[3] = NULL;
// Convert the hex value to the appropriate character, and add it to buf
rc = sscanf(code, "%2x", &hex);
if (rc != 1) {
lr_error_message("Invalid hex value: %s", code);
}
buf[j] = hex;
i+=4; // skip the rest of this encoded value in the input string
} else {
buf[j] = enc_string[i];
}
}

return buf;
}

No comments:

Post a Comment