Friday, December 23, 2011

How to Get Garbage Collectors info in Java

There are many GC policy or algorithm you can use in your application, here is a good mapping between JVM configuration and GC collectors:
Note: Thanks for the Reference: http://www.kjkoster.org/zapcat/Zabbix_Java_Template.html

Here is a demo code on how to grab detail GC info of your application
BTW, I have added those capabilities to my customized Btrace source code already :)
 package cputime;  
 import java.lang.management.*;  
 import java.util.HashSet;  
 import java.util.List; 
 
 public class GCInfo {  
   final static HashSet<String> YoungGenGCType = new HashSet<String>();  
   static{  
     YoungGenGCType.add("Copy");  
     YoungGenGCType.add("ParNew");  
     YoungGenGCType.add("PS Scavenge");  
     YoungGenGCType.add("G1 Young Generation");  
   }  
   final static HashSet<String> OldGenGCType = new HashSet<String>();  
   static{  
     OldGenGCType.add("MarkSweepCompact");  
     OldGenGCType.add("PS MarkSweep");  
     OldGenGCType.add("ConcurrentMarkSweep");  
     OldGenGCType.add("G1 Old Generation");  
   }  
   public static void main(String[] args) throws InterruptedException{  
     List<GarbageCollectorMXBean> gcTypes = ManagementFactory.getGarbageCollectorMXBeans();  
       for (int i=0; i<gcTypes.size();i++){  
       System.gc();  
       if(YoungGenGCType.contains(gcTypes.get(i).getName()))  
       {  
         System.out.println("Minor GC: " + gcTypes.get(i).getName());  
         System.out.println("Minor GC Total Count:" + gcTypes.get(i).getCollectionCount());  
         System.out.println("Minor GC Total Time:" + gcTypes.get(i).getCollectionTime());  
       }  
       else{  
         System.out.println("Full GC: " + gcTypes.get(i).getName());  
         System.out.println("Full GC Total Count:" + gcTypes.get(i).getCollectionCount());  
         System.out.println("Full GC Total Time:" + gcTypes.get(i).getCollectionTime());  
       }  
     }  
   }  
 }  

Monday, November 28, 2011

Dealing with "Not enough storage is available to process this command" exception by PsExec

When I am using Btrace to profiling JVM started with JavaServiceWrapper, I got such an annoyed exception:
CMD> C:\btrace\bin>btrace 3272 Test.java
Not enough storage is available to process this command
1> Login as a admin user anyway (remotely or locally)
2> Download PsTools which includes PsExec.exe , and "PsExec is a light-weight telnet-replacement that lets you execute processes on other systems, complete with full interactivity for console applications, without having to manually install client software", or you can download directly from : http://technet.microsoft.com/en-us/sysinternals/bb897553
3> Get your target PID(java.exe) with processExplorer.exe, download from: http://technet.microsoft.com/en-us/sysinternals/bb896653
4> Then Run your command line again using PsExec help:

CMD> D:\ProcessExplorer\PsTools>PsExec.exe -s "C:\btrace\bin\btrace.bat" 3272 C:\Test.java
 Not only for Btrace, the other external tools can also be run like this way to avoid such a wired Error...

Monday, November 21, 2011

如何计算消息队列中的消息处理时间


求某一时刻进入队列的消息,计算其出队列时间 EL? 

定义: 
S 入队列速率 (根据load的情况,S随不同时间段而变化)
P 出队列吞吐量, 通常在给定的系统配置条件下可以认为是常量
  TKK时刻
 TN是指对应K时刻进入消息队列的消息出队列所到达的时刻
  QD 为初始时刻Queue中剩余的Message的数量

情况1> 假设在一定时间段内,S为定值
S 总是<=P时, 在一段时间后任意时刻均无剩余message堆积, 这里暂时不考虑这类问题
S > P 时, Queue depth 会不断增长
所以,TN = (S * TK)/P + QD/P 
EL
TNTK
EL
  (S * TK)/P + QD/P TK (S/P - 1)*Tk + QD/P
其中,SP均为固定值,并且可以被measure
(一般可以通过SQL语句来统计S和P的值, 例如-- select count(*) from dbo.myqueue where ms.timestamp == ctime

情况2> 假设S为不断变化的正玄函数(斜率即为某时刻的进队速率S‘),则公式可变换为:
 EL = (∫ sin(cx)dx) /P + QD/P -TK , 其中
积分的范围是0-TK

 
因此,S需要不断的采样,利用曲线拟合方法(Curve fitting),确定它符合什么样的函数,但不管什么情况,都可以套用类似的模型解决此类问题

Remove the value of the param in URL

It should be formatted your URL when doing aggregation for further analysis, here is Ruby code to be copy with the URL which includes params within it:
 string1 = "http://www.google.com.hk/search?sclient=psy-ab&hl=en&newwindow=1&safe=strict&biw=1440&bih=693&noj=1&source=hp&q=wicket+1.5+URL&oq=wicket+1.5+URL&aq=f&aqi=g2&aql=&gs_sm=e&gs_upl=3231l4087l0l4226l4l4l0l0l0l2l302l832l2-2.1l3l0";  
 if (string1.include? "?")  
  string1.insert(-1, "&")  
  string1.gsub!(/=(.*?)&/, "&")  
  puts string1.chomp!("&")  
 end  
The output result:
 http://www.google.com.hk/search?sclient&hl&newwindow&safe&biw&bih&noj&source&q&oq&aq&aqi&aql&gs_sm&gs_upl  

Tuesday, November 15, 2011

Essential Counters for Performance Monitoring

The goals of performance monitoring i always think about are:

- Health indicator: it will show you if the current status of your application farm is in good shape or not; or let you predict the trend;
- Find out the bottleneck of your system from a high level, it is really helpful for Engineers to get start to get issue fixed;
- Help to do trouble shooting: the counters will drive us to the right direction from different angles to find the root cause instead of "smart guess";
- Operation management: Capacity Planning and Risk management, try to resolve any performance or stability issues before it comes!





Counters For OSExplaination
Server Uptimeelapse time since server recent start up
Processor--Total CPU%the percentage of elapsed time that the processor spends to execute a non-Idle thread.
Processor--% User Timethe percentage of elapsed time the processor spends in the user mode.
System--Processor Queue Lengththe number of threads in the processor queue. There is a single queue for processor time even on computers with multiple processors. Therefore, if a computer has multiple processors, you need to divide this value by the number of processors servicing the workload. A sustained processor queue of less than 10 threads per processor is normally acceptable, dependent of the workload.
Memory--% MEM in usethe ratio of Memory\\Committed Bytes to the Memory\\Commit Limit.
Memory--pages/secthe rate at which pages are read from or written to disk to resolve hard page faults.
Disk Space--DISK C:the percentage of Used space on Disk C
Disk Space--DISK D:the percentage of Used space on Disk D
Disk Space--DISK E:the percentage of Used space on Disk E
DISK IO --Avg. Disk Queue Lengththe average number of both read and write requests that were queued for the selected disk during the sample interval.
DISK IO --% Disk Timethe percentage of elapsed time that the selected disk drive was busy servicing read or write requests.
DISK IO --Avg. Disk sec/Readthe average time, in seconds, of a read of data from the disk.
DISK IO --Avg. Disk sec/Writethe average time, in seconds, of a write of data to the disk.
DISK IO --Disk Reads/secthe rate of read operations on the disk.
DISK IO --Disk Writes/secthe rate of write operations on the disk.
NetWork IO--Packets Sent/secthe rate at which packets are sent on the network interface.
NetWork IO--Packets received/secthe rate at which packets are received on the network interface.
TCP--TCP Connections ESTthe number of TCP connections for which the current state is either ESTABLISHED or CLOSE-WAIT
Total Process cntTotal number of processes on the server


Counters For Specific Process instanceExplaination
Apache Http Server -- Apache Process
Process Uptimeelapse time since apache process recent start up
Process CPU%the percentage of elapsed time that the processor spends on Apache Process.
Process Memory in useMemory usage by apache process
Busy workers cntthe number of threads which are in use for requests
Idle workers cntthe number of threads which are not receiving any request
requests/secthroughput of apache Http server
KB/secthroughput of apache Http server
Application Server -- Java Process
Process Uptimeelapse time since java process recent start up
Process CPU%the percentage of elapsed time that the processor spends on java Process.
Private Bytesthe current size, in bytes, of memory that this process has allocated that cannot be shared with other processes.
Working SetWorking Set is the current size, in bytes, of the Working Set of this process. The Working Set is the set of memory pages touched recently by the threads in the process
Used Heap SizeJVM Heap size is in use currently
Live Threads cntThe number of threads currently active in this process
Accumulate GCTimethe total time taken by GC activities
Async Server--JMS MQ Process
Process Uptimeelapse time since JMS process recent start up
Process CPU%the percentage of elapsed time that the processor spends on JMS Process.
Private Bytesthe current size, in bytes, of memory that this process has allocated that cannot be shared with other processes.
Working SetWorking Set is the current size, in bytes, of the Working Set of this process. The Working Set is the set of memory pages touched recently by the threads in the process
Message Queue Depththe current number of messages that are waiting on the queue
DLQ cntthe current number of messages which in Dead Letter Queue
Live Threads cntThe number of threads currently active in this process
DB Connections cntthe current number of open database connections used by this process
DB Server -- SQL Server Process
Process Uptimeelapse time since SQL Server process recent start up
Process CPU%the percentage of elapsed time that the processor spends on SQL Server Process.
Process Memory in useMemory usage by SQL Server process
Free Space in Temp DBTracks free space in tempdb in kilobytes
User Connectionsthe current number of connections (and users) are using the server
Buffer Cache Hit Ratioindicates how often SQL Server goes to the buffer, not the hard disk, to get data. Had better larger than 90%
Full Scans/Secthe number of unrestricted full scans during unit time. These can either be base table or full index scans.
Transactions/SecThe number of transactions started for the database during unit time
Average Wait Timethe average amount of wait time (milliseconds) for each lock request that resulted in a wait

More counters you add and higher granularity you made will bring more overhead to your system, you should make trade offs on selecting counters and sample interval.There is no silver bullet after all...

Wednesday, October 26, 2011

Add time stamp to GC log to ease your pain

When analyzing Gc log like these:
170609.479: [GC 170609.479: [ParNew: 1699544K->17689K(1887488K), 0.0537160 secs] 1728778K->47004K(5033216K) icms_dc=0 , 0.0538078 secs] [Times: user=0.08 sys=0.00, real=0.06 secs] 
172980.190: [GC 172980.190: [ParNew: 1695513K->4310K(1887488K), 0.0147373 secs] 1724828K->33657K(5033216K) icms_dc=0 , 0.0148245 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
174228.363: [Full GC 174228.363: [CMS: 29346K->31421K(3145728K), 0.5658221 secs] 955586K->31421K(5033216K), [CMS Perm : 58212K->57316K(58536K)] icms_dc=0 , 0.5660007 secs] [Times: user=0.51 sys=0.00, real=0.56 secs] 
174228.930: [GC [1 CMS-initial-mark: 31421K(3145728K)] 31421K(5033216K), 0.0013385 secs] [Times: user=0.00 sys=0.00, real=0.00 secs] 174228.931: [CMS-concurrent-mark-start] 
175694.396: [GC 175694.396: [ParNew: 1677824K->25622K(1887488K), 0.0726440 secs] 1709245K->57044K(5033216K) icms_dc=0 , 0.0727338 secs] [Times: user=0.06 sys=0.00, real=0.06 secs] 
177309.350: [GC 177309.350: [ParNew: 1703446K->23200K(1887488K), 0.0730725 secs] 1734868K->54622K(5033216K) icms_dc=0 , 0.0731623 secs] [Times: user=0.06 sys=0.00, real=0.06 secs] 179729.274: [GC 179729.274: [ParNew: 1701024K->4428K(1887488K), 0.0138419 secs] 1732446K->35850K(5033216K) icms_dc=0 , 0.0139156 secs] [Times: user=0.02 sys=0.00, real=0.02 secs] 
I am always confused and painful to get what the exactly time stamp of each GC activity is(BTW, currently i am using GCViewer to do the analysis), so that i can not easily to correlate the GC log with Access log when doing further analyzing and trouble shooting the memory problems... So i am writing a ruby program to calculate the time stamp for your further investigation, called gcanalyzer.rb:

Pre-Condition: you should have your Ruby1.9.2 installed on your computer, Ruby 1.8.* may not work properly.

 require 'csv'  
 def gcanalyzer(filename)  
 i = File.new(filename);  
 arr1 = [0];  
 arr2 = [];  
 @@maxpasuetime = 0.0000;  
 @@fullgc_count = 0;  
 m = 0;  
 endstamp = i.mtime  
 if (!endstamp.isdst) #Summer Time  
  zone = 15  
 else  
  zone = 16  
 end  
 a = i.readlines()  
 endsec = a[a.count-1].to_i  
 outputfilename = 'GC_log' + endstamp.strftime("_%m-%d-%H%M%S") + '.csv';  
 j = File.new(filename);  
 open(outputfilename, "a"){|f|  
  j.each_line { |line|  
   if (s = line.split('->')[2])!= nil  
    offset = endsec - line.split(':')[0].to_i  
    ctime = endstamp - offset -zone*3600  
    timestamp = ctime.strftime("%m/%d-%H:%M:%S")  
    usedheap = s.split('K(')[0]  
    f << timestamp + "," + usedheap + "\n"  
    #prepare pause time  
    # This is for +UseParallelGC  
    if line =~/PSYoungGen/  
     if line =~ /Full/  
       @@fullgc_count = @@fullgc_count + 1  
       arr2.push(timestamp);  
     end  
     t1 = line.split(',')[1]  
    elsif # This is for CMS case  
     if line =~ /Full/  
       @@fullgc_count = @@fullgc_count + 1  
       arr2.push(timestamp);  
       t1 = line.split(',')[3]  
     elsif  
       t1 = line.split(',')[2]  
     end  
    end  
    pausetime = t1.split('secs')[0].strip.to_f  
    arr1[0] = arr1[0] + pausetime  
    #prepare maxpausetime  
    if pausetime > @@maxpasuetime  
      @@maxpasuetime = pausetime;  
    end  
   end  
  }  
 }  
 elapsedday = endsec/(24*3600);  
 ed = (endsec - elapsedday*24*3600);  
 elapsedhour = ed/3600;  
 elapsedmin = (ed - elapsedhour*3600)/60;  
 printf("Total Elapse Time: %d (%dday-%dhour-%dmin)\n", endsec, elapsedday, elapsedhour,elapsedmin) ;  
 printf("Total GC Puase Time: %.3f \n" , arr1[0]) ;  
 printf("GC Throughput: %.3f \n" ,(1-arr1[0]/endsec)*100) ;  
 printf("Max Pause Time: %.3f \n",@@maxpasuetime) ;  
 printf("Avg Puase Time: %.3f \n", (arr1[0]/(a.count-1)));  
 printf("Total GC Occurency: %d \n", (a.count-1));  
 printf("GC Frenquency: %.3f \n", endsec/(a.count-1));  
 printf("Full GC Occurency: %d \n", @@fullgc_count);  
 printf("Full GC Time stamp: #{arr2}");  
 # consider to make a chart based on new file in the future  
 end  

How to use it:
>ruby gcanalyzer.rb "D:\\app01_garbage_collection.log"
There will be a new file created with accurate time stamp of each GC, and also Output Summary info to the console, something like this:
Total Elapse Time: 2341621 (27day-2hour-27min)
Total GC Puase Time: 636.061
GC Throughput(%): 99.973
Max Pause Time: 0.566
Avg Puase Time: 0.162
Total GC Occurency: 3936
GC Frenquency(sec): 594.000
Full GC Occurency: 1
Full GC Time stamp: ["09/25-05:51:15"]

Thursday, September 01, 2011

Find Optimal Concurrency for your Application

Although I found this article is more "academic", it is really helpful to understand how computer works when there are high concurrency coming in. So not always good idea for setting a very high number of workers/threads to handle large volume of concurrent requests , by given the certain hardware environment. Want to know why? please take a look at the post which is from book :

Finding the Optimal Concurrency [From High Performance MySQL]

Every web server has an optimal concurrency—that is, an optimal number of concurrent
connections that will result in requests being processed as quickly as possible,
without overloading your systems. A little trial and error can be required to find this
“magic number,” but it’s worth the effort.

It’s common for a high-traffic web site to handle thousands of connections to the
web server at the same time. However, only a few of these connections need to be
actively processing requests. The others may be reading requests, handling file
uploads, spoon-feeding content, or simply awaiting further requests from the client.

As concurrency increases, there’s a point at which the server reaches its peak
throughput. After that, the throughput levels off and often starts to decrease. More
importantly, the response time (latency) starts to increase.

To see why, consider what happens when you have a single CPU and the server
receives 100 requests simultaneously. One second of CPU time is required to process
each request. Assuming a perfect operating system scheduler with no overhead,
and no context switching overhead, the requests will need a total of 100 CPU seconds
to complete.

What’s the best way to serve the requests? You can queue them one after another, or
you can run them in parallel and switch between them, giving each request equal time
before switching to the next. In both cases, the throughput is one request per second.
However, the average latency is 50 seconds if they’re queued (concurrency = 1), and
100 seconds if they’re run in parallel (concurrency = 100). In practice, the average
latency would be even higher for parallel execution, because of the switching cost.
[Ideally]For a CPU-bound workload, the optimal concurrency is equal to the number of
CPUs (or CPU cores).
[Normally]However, processes are not always runnable, because they
make blocking calls such as I/O, database queries, and network requests. Therefore,
the optimal concurrency is usually higher than the number of CPUs. [That's why we are able to add more threads to handle concurrent requests]

You can estimate the optimal concurrency, but it requires accurate profiling.
[Conclusion:]It’s usually easier to experiment with different concurrency values and see what gives the
peak throughput without degrading response time.

Monday, August 15, 2011

Performance books I have or I wish to have

Overall Performance Engineering Methodology:
* Performance Analysis for Java Web Sites By Stacy Joines
* Software Performance and Scalability: A Quantitative Approach By Henry Liu
* Improve .Net Application Performance and Scalability By Microsoft
* Building Scalable Web Sites By Cal Henderson

Performance Testing Process & Practice:
* Performance Testing Guidance for Web Application By Microsoft
* The Art of Application Performance Testing By Ian Molyneaux

DB Performance tuning:
* Inside SQL Server 2005 Query Tuning and Optimization By Kalen Delaney
* High Performance MySQL By Baron Shwartz

Programming Language Performance Tuning:
* Effective Java By Joshua Bloch
* Java Concurrency in Practice By Brian Goetz
* The Art of Concurrency by Clay Breshears
* Java Performance Tuning by Jack Shirazi

Front-End Performance Optimization Practice:
* High Perforamnce WebSites By Steve Souders
* Even Faster WebSites By Steve Souders
* High Performance Javascript By Zakas

Web Operations and Capacity Planning:
* Web Operations By John Allspaw
* The Art of Capacity Planning By John Allspaw

Tuesday, August 09, 2011

C3p0 infinity wait for "availble" connections by default

If you have connection leaks on C3p0 which means the Connections are being checked-out that don't make it back into the pool. You will be in trouble...

By default, C3p0 will wait for the connection forever if there is no one available in the pool currently.

When testing against the application, i found some threads are wait for the connections for a long time, and after monitoring the JMX console in VisualVM, all the connections are taken, here is the thread dump i took:
 "1658227@qtp-25018827-56" - Thread t@321  
   java.lang.Thread.State: WAITING  
      at java.lang.Object.wait(Native Method)  
      - waiting on <103344b> (a com.mchange.v2.resourcepool.BasicResourcePool)  
      at com.mchange.v2.resourcepool.BasicResourcePool.awaitAvailable(BasicResourcePool.java:1315)  
      at com.mchange.v2.resourcepool.BasicResourcePool.prelimCheckoutResource(BasicResourcePool.java:557)  
      at com.mchange.v2.resourcepool.BasicResourcePool.checkoutResource(BasicResourcePool.java:477)  
      at com.mchange.v2.c3p0.impl.C3P0PooledConnectionPool.checkoutPooledConnection(C3P0PooledConnectionPool.java:525)  
      at com.mchange.v2.c3p0.impl.AbstractPoolBackedDataSource.getConnection(AbstractPoolBackedDataSource.java:128)  
      at org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSessionFromDataSource(DefaultSqlSessionFactory.java:72)  
      at org.apache.ibatis.session.defaults.DefaultSqlSessionFactory.openSession(DefaultSqlSessionFactory.java:32)  

I am not discussing why the code makes connection leaks here in this post.(Actually, we did not invoke session.close from our code finally ), i just want to make sure if you meet this kind of similar pattern of problem, then you can temperately change some C3p0 configuration before you fix your bad code...

we are using C3p0 0.9.1.2 version now, here is the source code which have the infinity loop to wait for the connection, FYI
 1297        while ((avail = unused.size()) == 0)   
  1298        {  
  1299          // the if case below can only occur when 1) a user attempts a  
  1300          // checkout which would provoke an acquire; 2) this  
  1301          // increments the pending acquires, so we go to the  
  1302          // wait below without provoking postAcquireMore(); 3)  
  1303          // the resources are acquired; 4) external management  
  1304          // of the pool (via for instance unpoolResource()   
  1305          // depletes the newly acquired resources before we  
  1306          // regain this' monitor; 5) we fall into wait() with  
  1307          // no acquires being scheduled, and perhaps a managed.size()  
  1308          // of zero, leading to deadlock. This could only occur in  
  1309          // fairly pathological situations where the pool is being  
  1310          // externally forced to a very low (even zero) size, but   
  1311          // since I've seen it, I've fixed it.  
  1312          if (pending_acquires == 0 && managed.size() < max)  
  1313            _recheckResizePool();  
  1314    
  1315          this.wait(timeout);  
  1316          if (timeout > 0 && System.currentTimeMillis() - start > timeout)  
  1317            throw new TimeoutException("A client timed out while waiting to acquire a resource from " + this + " -- timeout at awaitAvailable()");  
  1318          if (force_kill_acquires)  
  1319            throw new CannotAcquireResourceException("A ResourcePool could not acquire a resource from its primary factory or source.");  
  1320          ensureNotBroken();  
  1321        }  

timeoutvalue is set to 0 by default in C3p0, which means wait indefinitely
checkoutTimeout
Default: 0
The number of milliseconds a client calling getConnection() will wait for a Connection to be checked-in or acquired when the pool is exhausted. Zero means wait indefinitely. Setting any positive value will cause the getConnection() call to time-out and break with an SQLException after the specified number of milliseconds.

And also if you have connection leaks with your code, and you hardly find it in short time, then you should pay attention to this parameter:
unreturnedConnectionTimeout
Default: 0
Seconds. If set, if an application checks out but then fails to check-in [i.e. close()] a Connection within the specified period of time, the pool will unceremoniously destroy() the Connection. This permits applications with occasional Connection leaks to survive, rather than eventually exhausting the Connection pool. And that's a shame. Zero means no timeout, applications are expected to close() their own Connections. Obviously, if a non-zero value is set, it should be to a value longer than any Connection should reasonably be checked-out. Otherwise, the pool will occasionally kill Connections in active use, which is bad. This is basically a bad idea, but it's a commonly requested feature. Fix your $%!@% applications so they don't leak Connections! Use this temporarily in combination with debugUnreturnedConnectionStackTraces to figure out where Connections are being checked-out that don't make it back into the pool!

So you can try to set these two parameters to the same time as your goal keeper, say 30 seconds. So that you can prevent some extremely leak cases, but dev need to find the root cause for sure :)
checkoutTimeout= 30000
unreturnedConnectionTimeout=30

also adjust your maxPoolSize to 30 or more instead of 15 by default under a heavy load situation.

Friday, July 08, 2011

Weekly Health check for SQL Server 2005 using DMV

Every Week, you can set up a benchmark by running following DMV, it can help to provide high level view to show whether your DB is healthy or not currently without any monitoring tools.

While, Some limitations you may have to pay attention to when you are using following DMV Queries:
(Thanks for a good reference provided by Vance: http://www.mssqltips.com/tip.asp?tip=1843)

1. Limitation with DMV queries: Keep this in mind when you are using the DMVs for query usage and performance stats. If you are using inline T-SQL and sp_executesql you may not be capturing all of the data that you need.
—Suggestion : think about using stored procedures for all data related operations instead of using inline T-SQL or sp_executesql in your application code.
2. Limitation with dbid column: there is a problem that it is limiting the result data to queries with a database id. The reason for this is that the dbid column is NULL for ad hoc and prepared SQL statements, So you can comment out the where condition which having dbid in (...);
—Suggestion : you may just comment out the dbid constrain or using “dbid = null” instead of assign a dbid in “where clause”


-- DMV FOR CHECKING CPU USAGE:
 
 SELECT TOP 50   
      DB_Name(dbid) AS [DB_Name],  
      total_worker_time/execution_count AS [Avg_CPU_Time],  
      total_elapsed_time/execution_count AS [Avg_Duration],  
      total_elapsed_time AS [Total_Duration],  
      total_worker_time AS [Total_CPU_Time],  
      execution_count,  
   SUBSTRING(st.text, (qs.statement_start_offset/2)+1,   
     ((CASE qs.statement_end_offset  
      WHEN -1 THEN DATALENGTH(st.text)  
      ELSE qs.statement_end_offset  
      END - qs.statement_start_offset)/2) + 1) AS statement_text  
 FROM sys.dm_exec_query_stats AS qs  
 CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st  
 WHERE dbid in (  
           SELECT DB_ID('yourtablename') AS [Database ID]  
      )  
 ORDER BY Avg_CPU_Time DESC;  

-- DMV FOR CHECKING I/O USAGE
 SELECT TOP 50  
      DB_Name(dbid) AS [DB_Name],  
      Execution_Count,  
      (total_logical_reads/Cast(execution_count as Decimal(38,16))) as avg_logical_reads,  
      (total_logical_writes/Cast(execution_count as Decimal(38,16))) as avg_logical_writes,  
      (total_physical_reads/Cast(execution_count as Decimal(38,16))) as avg_physical_reads,  
      max_logical_reads,  
      max_logical_writes,  
      max_physical_reads,  
   SUBSTRING(st.text, (qs.statement_start_offset/2)+1,   
     ((CASE qs.statement_end_offset  
      WHEN -1 THEN DATALENGTH(st.text)  
      ELSE qs.statement_end_offset  
      END - qs.statement_start_offset)/2) + 1) AS statement_text  
 FROM sys.dm_exec_query_stats AS qs  
 CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st  
 WHERE dbid in (  
           SELECT DB_ID('yourtablename') AS [Database ID]  
      )  
 ORDER BY avg_logical_reads DESC;  

-- DMV FOR CHECKING INDEX USAGE
 SELECT     top 50   
           idx.name as Index_name  
           ,obj.name   
           ,dmv.object_id  
           ,sampledatetime=Getdate()  
           ,dmv.index_id  
           ,user_seeks  
           ,user_scans  
           ,user_lookups   
 FROM sys.dm_db_index_usage_stats dmv  
 INNER JOIN sys.indexes idx on dmv.object_id = idx.object_id and dmv.index_id = idx.index_id  
 Cross Apply sys.objects obj  
 WHERE dmv.object_id = obj.object_id and database_id in (  
 SELECT DB_ID('yourtablename') AS [Database ID]  
 )  
 ORDER BY user_scans desc  

-- DMV FOR CHECKING OBJECT BLOCKING/WAITING
 SELECT TOP 50  
      DB_NAME(qt.dbid),  
      [Average Time Blocked] = (total_elapsed_time - total_worker_time) / qs.execution_count,  
      [Total Time Blocked] = total_elapsed_time - total_worker_time,  
      [Execution count] = qs.execution_count,  
      SUBSTRING(qt.text, (qs.statement_start_offset/2)+1,   
     ((CASE qs.statement_end_offset  
      WHEN -1 THEN DATALENGTH(qt.text)  
      ELSE qs.statement_end_offset  
      END - qs.statement_start_offset)/2) + 1) AS statement_text,
      [Parent Query] = qt.text
 FROM sys.dm_exec_query_stats qs  
 CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) as qt  
 WHERE DB_NAME(qt.dbid) = 'yourtablename'  
 ORDER BY [Average Time Blocked] DESC;  

-- DMV FOR CHECKING TEMPDB USAGE
 SELECT getdate(),   
      SUM(user_object_reserved_page_count) * 8 as user_objects_kb,  
      SUM(internal_object_reserved_page_count) * 8 as internal_objects_kb,  
      SUM(version_store_reserved_page_count) * 8 as version_store_kb,  
      SUM(unallocated_extent_page_count) * 8 as freespace_kb  
 FROM sys.dm_db_file_Space_Usage  
 where database_id = 2  

Thursday, June 30, 2011

Performance Toolkit in My Pocket

Please Note, this is a Draft Version, the list will be updated on the fly.

Kindly Warning:
Don’t be a slave of tools, but you can not leave without tools!
Be a master of your job with tools :)

Performance Toolkit in My Pocket:

1> Perf Testing Tools:
- Jmeter (Load generate tool for different Protocol)
- Loadrunner (Load generate tool for different Protocol)
- SoapUI (WebService load Testing preferred, or help to create mock services)
- Traffic Shaper XP (Network Bandwitch limiter)
- Badboy (HTTPS recording supported for .jmx scripts)
- JMeter plugin : http://code.google.com/p/jmeter-plugins/
- WebDriver Automation Framework for End-End Performance measurement
- ^Unit Performane testing tool need to be filled in...$ (Method level performance testing)

2> Perf Monitoring Tools:
- JConsole
- JVisualVM
- Task manager/PerfMon
- Process Explorer
- Hyperic HQ
- NetXMS
- Netstat
- typeperf Command line (with Ruby Programming)

3> Perf Profiling Tools:
- Jprofiler
- Btrace
- Jmap
- SQL Profiler
- Perf4j
- Guice AOP Profiling methods for Automation test
- HttpWatch
- Firebug
- Chrome Developer Tools
- Fiddler
- Charles
- Wireshark
- DBCC Command

4> Perf Analysis and Tuning Tools:
- Dynatrace Ajax
- MemoryAnalyzer
- TDA
- DB tuning adviser
- Yslow
- Page Speed
- Image Opertimazer : http://www.imageoptimizer.net/Pages/Home.aspx
- jpegmini - Sprite Me :http://spriteme.org/
- Minify JS :http://www.minifyjs.com/
- WebPageTest : http://www.webpagetest.org/

5> MISC:
- Text Editor/IDE you perferred: Netbeans with Ruby for me
- Windows Grep
- Regular Expression
- T-SQL
- Ruby/Python/Perl/Shell/Awk : http://www.ibm.com/developerworks/cn/education/aix/au-gawk/index.html
- STAF/STAX :
- Excel
- LogBack/Log4j
- JSLint (looks for problems in JavaScript programs)
- User Agent analysis: http://www.useragentstring.com/index.php

Tuesday, May 31, 2011

Dealing With High CPU% of SQL Server 2005

Sometimes, we experience performance problem with high CPU% on DB Server. How can we detect which process take most "contribution" to this pheromone step by step?

Step 1. Check if SQL Server Process has problem or not?
 DECLARE @ts_now bigint;  
   SELECT @ts_now = cpu_ticks / CONVERT(float, cpu_ticks_in_ms) FROM sys.dm_os_sys_info   
   SELECT TOP(10) SQLProcessUtilization AS [SQL Server Process CPU Utilization],  
           SystemIdle AS [System Idle Process],  
           100 - SystemIdle - SQLProcessUtilization AS [Other Process CPU Utilization],  
           DATEADD(ms, -1 * (@ts_now - [timestamp]), GETDATE()) AS [Event Time]  
   FROM (  
      SELECT record.value('(./Record/@id)[1]', 'int') AS record_id,  
         record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int')  
         AS [SystemIdle],  
         record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int')  
         AS [SQLProcessUtilization], [timestamp]  
      FROM (  
         SELECT [timestamp], CONVERT(xml, record) AS [record]  
         FROM sys.dm_os_ring_buffers  
         WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR'  
         AND record LIKE '%<SystemHealth>%') AS x  
      ) AS y  
 ORDER BY record_id DESC;  

Then you will get result like this:

The picture above shows 70% of CPU% is taking by SQL Server 2005 , not by other processes on DB server.

Step2. Drill down to particular Loginuser and SPID which is taking most of CPU%:
 select  loginame,  *    
 from  master.dbo.sysprocesses  
 where  spid> 50  
 order by cpu desc   

PS: Why "spid > 50" here? Notice that SPIDs 1 to 50 are reserved for internal SQL Server processes, while SPIDs 51 and above are external connections. So most of time we assume that the suspects for High CPU% are from external connections.

Step3: Query the most expensive CPU process by spid (taking spid = 102 for example):

 dbcc inputbuffer(102)   

Then you can start to take further actions for tuning expensive Query or SPs... or just kill the abnormal ones for free :)

Monday, May 30, 2011

To enable Performance Profiling log with Guice AOP and Logback

I always want my methods to be profiled, so that I can learn about how fast/slow it is from a high level perspective at first. Then break it down to make it even faster.

I am using WebDriver to do automation testing, so i want to trace certain end user experience from performance perspective as well.

For example, if I want to measure Login action's response time, my intention is to enable my profiling log by just add @Profiled annotation in front of corresponding "Login" method:
 
@Profiled
public void clickLoginBtn() throws InterruptedException{  
           driver.findElement(By.id("login")).click();  
           if(!myWaiter.waitForMe(By.cssSelector("div.starcite-hyperlink"), 25, timeout)) return ;  
      }  

To make @Profiled work, i chose Guice as it is so called light weight and easy of use:

Step1: Create an annotation called Profiled:
 import java.lang.annotation.ElementType;  
 import java.lang.annotation.Retention;  
 import java.lang.annotation.RetentionPolicy;  
 import java.lang.annotation.Target;  
 @Retention(RetentionPolicy.RUNTIME)  
 @Target(ElementType.METHOD)  
 public @interface Profiled {  
 }  

Step2:Create matchers for the classes and methods to be intercepted:

 import com.google.inject.AbstractModule;  
 import com.google.inject.matcher.Matchers;  
 public class ProfiledModule extends AbstractModule {  
      @Override  
      public void configure() {  
           // TODO Auto-generated method stub  
           PerfTracing perftracing = new PerfTracing();  
           requestInjection(perftracing);  
           bindInterceptor(  
                     Matchers.any(),  
            Matchers.annotatedWith(Profiled.class),  
            perftracing);  
        }  
      }  

Step3: Write your own MethodInterceptor to do profiling using Logback
 import java.util.Arrays;  
 import org.aopalliance.intercept.MethodInterceptor;  
 import org.aopalliance.intercept.MethodInvocation;  
 import org.slf4j.Logger;  
 import org.slf4j.LoggerFactory;  
 public class PerfTracing implements MethodInterceptor{  
      private Logger logger = LoggerFactory.getLogger("PerfLog");  
      @Override  
      public Object invoke( MethodInvocation invocation) throws Throwable {  
           // TODO Auto-generated method stub  
           long start = System.nanoTime();  
     try {  
       return invocation.proceed();  
     }   
     finally {  
          if(logger.isDebugEnabled()){  
               Object[] paramArray = {     invocation.getMethod().getName(),   
                                             Arrays.toString(invocation.getArguments()),   
                                             (System.nanoTime() - start) / 1000000};  
            logger.debug("Invocation of method: {} with parameters: {} took: {} ms." , paramArray);  
          }  
     }  
      }  
 }  

Why I choose Logback for logging? please refer to this post :)

For Logback configuration, here is my sample:
 <?xml version="1.0" encoding="UTF-8"?>  
 <!-- Reference Manual http://logback.qos.ch/manual/index.html -->  
 <configuration>  
   <appender name="stdout" class="ch.qos.logback.core.ConsoleAppender">   
     <encoder charset="UTF-8">  
       <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>  
     </encoder>  
   </appender>   
   <appender name="PerfLog" class="ch.qos.logback.core.rolling.RollingFileAppender">  
     <rollingPolicy class="ch.qos.logback.core.rolling.TimeBasedRollingPolicy">  
       <fileNamePattern>PerfLog-%d{yyyy-MM-dd}.log</fileNamePattern>  
       <maxHistory>30</maxHistory>  
     </rollingPolicy>   
     <encoder>  
       <pattern>%d{HH:mm:ss.SSS} [%thread] %-5level %logger - %msg%n</pattern>  
     </encoder>  
   </appender>  
   <logger name="PerfLog" additivity="false">  
        <level value="DEBUG"/>  
        <appender-ref ref="PerfLog" />  
   </logger>  
   <root level="ERROR">  
     <appender-ref ref="stdout" />  
   </root>  
 </configuration>  

The output in your log file should be like this:
 15:02:49.351 [main] DEBUG PerfLog - Invocation of method: clickLoginBtn with parameters: [] took: 3027 ms.  

Please notice Guice AOP's Limitations:
* Classes must be public or package-private.
* Classes must be non-final
* Methods must be public, package-private or protected
* Methods must be non-final
* Instances must be created by Guice by an @Inject-annotated or no-argument constructor

So You may have to change your source code to make Guice AOP work properly.

Saturday, May 21, 2011

My Current “Waiter” Class used by WebDriver Tests

My Current “Waiter” Class used by WebDriver automation tests, so keep away from your hard coded Thread.sleep() :

 import java.util.List;  
 import org.openqa.selenium.By;  
 import org.openqa.selenium.WebDriver;  
 import org.openqa.selenium.WebElement;  
 import org.openqa.selenium.support.ui.WebDriverWait;  
 import com.google.common.base.Function;  
 /*  
  * Two usage examples in your test code:  
  * MyWaiter myWaiter = new MyWaiter(driver);  
   WebElement search = myWaiter.waitForMe(By.name("btnG"), 10);  
   or  
   if(!myWaiter.waitForMe(By.name("btnG"), 1, 10)) return;  
   or if (!myWaiter.waitForMeDisappear(By.name("btnG"), 10)) return;  
  */  
 public class MyWaiter {  
      private WebDriver driver;  
      public MyWaiter(WebDriver driver){  
           this.driver = driver;  
      }  
      public WebElement waitForMe(By locatorname, int timeout){  
           WebDriverWait wait = new WebDriverWait(driver, timeout);  
           return wait.until(MyWaiter.presenceOfElementLocated(locatorname));  
      }  
      //Given certain number of web element to see if it is found within timeout  
      public Boolean waitForMe(By locatorname, int count, int timeout) throws InterruptedException{  
           long ctime = System.currentTimeMillis();  
           while ((timeout*1000 > System.currentTimeMillis()- ctime)){  
                List<WebElement> elementList = driver.findElements(locatorname);  
                if ((elementList.size()< count)){  
                     Thread.sleep(300);  
                }  
                //element is found within timeout   
                else  
                     return true;  
           }  
           // otherwise element is not found within timeout  
           return false;  
      }  
      //Given certain number of web element to see if it is disappear within timeout  
      public Boolean waitForMeDisappear(By locatorname, int timeout) throws InterruptedException{  
           long ctime = System.currentTimeMillis();  
           while ((timeout*1000 > System.currentTimeMillis()- ctime)){  
                List<WebElement> elementList = driver.findElements(locatorname);  
                if ((elementList.size()!= 0)){  
                     Thread.sleep(300);  
                }  
                //element is Disappear within timeout   
                else  
                     return true;  
           }  
           // otherwise element is still show up within timeout  
           return false;  
      }  
      public static Function<WebDriver, WebElement> presenceOfElementLocated(final By locator) {  
           // TODO Auto-generated method stub  
           return new Function<WebDriver, WebElement>() {  
                @Override  
                public WebElement apply(WebDriver driver) {  
                     if (driver.findElement(locator)!= null){  
                          return driver.findElement(locator);  
                     }  
                     else return null;  
                }  
           };  
      }  
 }  

Another useful method on implicitwait(), I also wrote a post related to it, FYI : http://joychester.blogspot.com/2010/09/webdriver-wait-is-easier-after-using.html

Wednesday, May 18, 2011

6 years since we met -- 2011-01-26

Cheng Shawn

PS: for how to draw a heart, please take a look at this link :)




Tuesday, May 17, 2011

Compressing Image with Image Opertimazer

Compressing plain text is easy by Zipping the files, however, images are said to be compressed by default, so often we are ignoring optimizing it.

I just found one awesome tool to optimize the website images without sacrificing much quality:Image Opertimazer

Here is the default Image, original size is 52KB:

After optimization, the size is 33KB, about 20% saving:

And you may heard of WebP created by Google, it is said that "WebP images were 39.8% smaller than jpeg images of similar quality", give it a try! Update: another awesome service to do Image compression, called JPEGmini : http://www.jpegmini.com/main/home

Tuesday, April 19, 2011

Leverage browser caching by dummy configuration on Apache HTTP Server

Leverage browser caching is quite critical for web performance, especially you have , but sometime you may have your static files get changed. So you may control the risks as much as possible.

There is a cool strategy to Use fingerprinting to dynamically enable caching if you can, but if you just want to do some simple configurations with limited risks, then here is my sample httpd.conf:

 
 LoadModule headers_module modules/mod_headers.so
 ... 
 #Disable Last-Modified Response Header  
 <FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf|html|rpc)$">  
 Header unset Last-Modified  
 </FilesMatch>

 #Default Cache-control header for most static files  
 <FilesMatch "\.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$">  
 Header set Cache-Control "max-age=7200, public"  
 </FilesMatch>  
 # Revalidate For No-cache files each time  
 <FilesMatch "\.nocache\.(ico|pdf|flv|jpg|jpeg|png|gif|js|css|swf)$">  
 Header set Cache-Control "max-age=0, no-cache, must-revalidate"  
 </FilesMatch>  
 # Revalidate html/rpc files each time  
 <FilesMatch "\.(html|rpc)$">  
 Header set Cache-Control "max-age=0, no-cache, must-revalidate"  
 </FilesMatch>  
 # Cache "forever" due to files are given a unique name every time they are built  
 <FilesMatch "\.cache\.(html|js|png|gif)$">  
 Header set Cache-Control "max-age=2592000, public"  
 </FilesMatch>  

Let’s assume one yourapp.js has been requested for the first time and load into Disk cache:

1. when the browser requests yourapp.js within 2 hours(7200 seconds) since loaded into Disk cache, it will be load from Disk cache directly without sending any http request;

2. when the browser requests yourapp.js after 2 hours(7200 seconds) since loaded into Disk cache, it will be fetched either from static file server (200 code returned) if Etag has been changed(which means static file changes) or from Disk Cache (304 code returned) if Etag is still the same as request head brings ("if-none-match" request header) (which means static file has no changes)

3. Some no Cached files will always re-validate to the server, to check if the the file is the latest or not based on Etag

4. Some Cached files will have far further cache-control header (set to 1 month from my side), Due to every new build, the static file name will be refreshed, and we do monthly release usually.

Friday, April 15, 2011

Aggeragte Performance Profiling Data using Ruby

 require 'csv'  
 def perflogana(csvfile)  
  #initial an Two-dimensional array
   a = Array.new(1){ Array.new(4) }  
   a[0][0] = '';  # Transaction name 
   a[0][1] = 0;  # Total Response time
   a[0][2] = 0;  # Invocations
   a[0][3] = 0;  # Average Response Time
   i = 0;  
   t = 0;  
   n = 0;  
   flag = 'unfound'  
  # Read each line from CSV file  
  CSV.foreach(csvfile) do |row|  
   if (!row[1].nil?) 
      #Trim Oid for the URL using regex
      if row[1].gsub!(/\/([\d\D][^\/]+?-)+\S*/, "")
         row[1] << ")"
      end 
    b = 0;  
    # modify total response time and invocations if current line being found  
    while b <= i  
     if !a[b][0].eql?(row[1])  
       b= b+1;  
       next;  
     else  
       # modify total response time and invocations  
       a[b][1] = a[b][1] + row[2].to_s.to_i;  
       a[b][2] = a[b][2] + 1;  
       flag = 'find'  
       break;  
     end  
    end  
    #append a new line if new transaction being found  
    if flag.eql?('unfound')  
      a << [row[1],row[2].to_s.to_i,1]  
      i = i+1;  
    end  
    flag = 'unfound'  
   end  
  end  
  a.shift  
  j = a.size  
  #calculate average response time for each transaction  
  while t < j  
   a[t][3] = a[t][1]/a[t][2]  
   t = t+1;  
  end  
  # sort by avg time and print to console  
  while n < j  
   print a[n][0],",",a[n][1],",",a[n][2],",",a[n][3]  
   puts  
   n = n + 1  
  end  
 end  
 perflogana("D:\\GWT_automation\\perf\.csv");  

Thursday, April 14, 2011

Enable SSL With Load Balancer on Apache HTTP server

 ------------------------------------<httpd.conf>------------------------------------  
 Listen 80
 Listen 443
 ...
 LoadModule ssl_module modules/mod_ssl.so
 ...
 SSLSessionCache    "shmcb:D:/Service/sslcert/logs/ssl_scache(512000)"  
 SSLSessionCacheTimeout 300  
 NameVirtualHost *:80  
 <VirtualHost *:80>  
      RewriteEngine On  
      RewriteCond %{HTTPS} off  
      RewriteRule (.*) https://%{HTTP_HOST}%{REQUEST_URI}  
 </VirtualHost>  
 ProxyPass / balancer://platform/ lbmethod=byrequests  
 ProxyPassReverse / http://arch04.eng.app.com:9000   
 ProxyPassReverse / http://arch06.eng.app.com:9000  
 <Proxy balancer://platform>  
      BalancerMember http://arch04.eng.app.com:9000  
      BalancerMember http://arch06.eng.app.com:9000  
 </Proxy>  
 ProxyPreserveHost On  
 #SSL VirtualHost Conf  
 NameVirtualHost *:443  
 <VirtualHost *:443>  
      SSLEngine on  
      SSLCertificateFile "D:/Service/sslcert/Service-2013.cert"  
      SSLCertificateKeyFile "D:/Service/sslcert/Service-2013.key"  
 </VirtualHost>  
 ------------------------------------<httpd.conf>------------------------------------  

Sunday, March 13, 2011

Reverse*a*Sentence*With*Ruby code

For example , if you want "One, Two, Three, Four." to be converted into “.Four ,Three ,Two ,One”, take a look at my ruby version for your reference, hope it helps in some degree :) I know you may never need this function, but some people may let you write such kind of things...

 # Ruby Version 1.9.2-p180  
 def reverseTest(inputtext)  
  @a=""  
  regextext = /[!,.;"()<>\[\]{}]/  
  arr = inputtext.split(/\s/)  
  arr.reverse_each { |item|  
   len = @a.length  
   # Dealing with a word surrounding by one or multiple punctuation  
   if item.partition(regextext).at(1)!=''  
    # Partition the word by particular punctuation, until partition by the last punctuation  
    begin  
     @a .insert(len,item.partition(regextext).at(0))  
     @a .insert(len,item.partition(regextext).at(1))  
     item = item.partition(regextext).at(2)  
    end while item.partition(regextext).at(1)!='' && item.partition(regextext).at(2)!=''  
    # When punctuation is at the end coming after a word  
    @a .insert(len,item.partition(regextext).at(0))  
    @a .insert(len,item.partition(regextext).at(1))  
    @a << ' '  
   # Dealing with a word without surrounding any punctuation  
   else  
    @a << item  
    @a << ' '  
   end  
  }  
  #Remove space at the end of the string and swap symmetry punctuation  
  @a.strip!  
  @a.gsub!(/[()<>\[\]{}]/, '('=>')', ')'=>'(', '<'=>'>', '>'=>'<', '['=>']',']'=>'[', '{'=>'}', '}'=>'{')  
  print @a  
 end  

Thursday, March 03, 2011

Using JNative to load AutoITX.dll

I am working on JNative to load AutoITX.dll, and get my complex UI methods work for WebDriver.

First download JNative.jar from website then add JNative.jar to your build path.

Download your AutoITX from http://www.autoitscript.com/site/autoit/downloads/
2 useful docs which you can refer to:
Function reference
http://www.autoitscript.com/autoit3/docs/functions.htm
and AutoITX Help doc -> DLL interface introduction(Find it after installing AutoITV3)

Then start to create your own method based on AutoITX great Functions which in AutoITX.dll

Here is a sample code to create mouse move methods in Java

 package org;  
 import org.xvolks.jnative.JNative;  
 import org.xvolks.jnative.exceptions.NativeException;  
 public class LoadDllSample {  
      public static void main(String[] args) throws IllegalAccessException {  
           // TODO Auto-generated method stub            
           try {  
                GetMyMouseMove(100, 100 ,40);  
           } catch (NativeException e) {  
                // TODO Auto-generated catch block  
                e.printStackTrace();  
           }  
      }  
      /*  
       *      AutoItX Interface For AU3_MouseMove() Function  
            AU3_API long WINAPI AU3_MouseMove(long nX, long nY, long nSpeed);  
       */  
      public static void GetMyMouseMove(int x, int y, int s) throws NativeException, IllegalAccessException{  
           JNative nGetMyMouseMove = new JNative("C:\\Program Files\\AutoIt3\\AutoItX\\AutoItX3.dll", "AU3_MouseMove");  //load .dll file and specific func
           nGetMyMouseMove.setParameter(0,x);//The screen x coordinate to move the mouse to.  
           nGetMyMouseMove.setParameter(1,y);//The screen y coordinate to move the mouse to.  
           nGetMyMouseMove.setParameter(2,s);//The speed s to move the mouse (optional)  
           nGetMyMouseMove.invoke();  
           return;  
      }  
 }  

I will work on more functions and hope they may help my WebDriver automation tests soon :) Enjoy!

Update: The source code has been uploaded to my github, check it out if you like : https://github.com/joychester/AutoIT-in-Java

Tuesday, March 01, 2011

Profiling OSGi project with Btrace

I met "java.lang.NoClassDefFoundError" when i did profiling with my OSGi project:
Error com.myapp.template.TemplateServlet - TemplateServlet.doGet() exception
java.lang.NoClassDefFoundError: CodeLineTiming at com.myapp.service.proxy.abc.impl.ServiceProxyImpl.$btrace$CodeLineTiming$onCall(ServiceProxyImpl.java) at com.myapp.service.proxy.abc.impl.ServiceProxyImpl.getRfpWithChangeLog(ServiceProxyImpl.java:467) at
...

So two things need to be modified from my side:
1. add package to btrace code , for example "package com.sun.btrace.samples;"
 
 package com.sun.btrace.samples; 

 import com.sun.btrace.annotations.*;  
 import static com.sun.btrace.BTraceUtils.*;  
 import com.sun.btrace.aggregation.*;  
 @BTrace  
 public class MethodResponseTime {  
...
}

2. add OSGI config.properties in felix/conf/ to include org.osgi.framework.bootdelegation=com.sun.btrace,com.sun.btrace.*

Then start your application, enjoy profiling with Btrace:)

Thanks for @yardus great help and also good post from this: http://blogs.sun.com/binublog/entry/glassfish_v3_profiling

Tuesday, February 22, 2011

Making VisualVM as a Windows service on remote server

I am often Adding a remote JMX connection to the VisualVM to do monitoring work to capture JVM performance status. however, some of application are running as a windows service remotely, so i can not use Btrace Workbench then. if you do not want to change the Windows service back to console mode, then here is what I solved mine:

PS:Making sure Latest JDK1.6 installed for getting jVisualVM and Btrace, more info please refer to https://visualvm.dev.java.net/pluginscenters.html, your JAVA_HOME can be a separate one for your java application.

Step1: Remote to the application server using a console mode: mstsc /console /v:(For Winows server 2003 or winXP sp2) or admin mode:mstsc /admin /v:(Windows XP Service Pack 3, Windows Vista Service Pack 1 and Windows Server 2008 or later), why? please see this link

Step2: Use AppToService.exe to create VisualVM as a Windows service
Type such command line in cmd:
AppToService.exe /Install "C:\Program Files\Java\jdk1.6.0_24\bin\jvisualvm.exe" /AbsName:"VisualVM" /Interact:1

Step3: Start the "VisualVM" in Services

So you can see the VisualVM will launch on your remote desktop, then the Java process on that server running as a service can be monitoring as a local process.



Some Useful References:
http://blogs.sun.com/nbprofiler/entry/monitoring_java_processes_running_as
http://vicevoice.blogspot.com/2009/09/vaas-visualvm-as-service.html
http://www.microsoftnow.com/2008/01/no-more-mstscexe-console.html
More update from Sun blog: https://blogs.oracle.com/nbprofiler/entry/monitoring_java_processes_running_as My Friend Roy and Rudy's help as well:)

Thursday, February 17, 2011

消失的纯白2011--A flash Game of my good Friend

转载我一个好朋友自创的Flash Game,在新的一年里,祝福他和他的家人--坚强,幸福!

Monday, January 17, 2011

Little's Law on Performance Test

Little's Law, as a part of Queuing theory, which was introduced to me by Wilson Mar about 2 years ago. We can make use of it to help performance test planning and modeling.

I also would like to usually calculate the arrival rate of system :
L = λW
λ = L/W

Where,
W = Average response time + Think time
L = Virtual Users we simulate in load generation tool


After calculating the arrival rate λ , I will compare arrival rate λ with Throughput X to see if the system can catch up the incoming load, if not, then start tuning it!

One example:

W =(0.352 + 0); -- which means no think time for test
L = 8;

then,
λ = L/W = 8/(0.352 + 0) = 22.73

Meanwhile, get Throughput measured by load generation tool
Throughput = 22
Not that bad currently :)

Some Update after several years I met with such an elegant equation, following results are what i have worked with my perf team to prove the Little's law:

Little's Law for Finding Response Time: 

MeanResponseTime(AVG) = MeanNumberInSystem(VUs) / MeanThroughput(TPS)

Assumptions:
  • Stable system, in the long terms
  • No Obvious System Bottlenecks
  • Similar Actions in the system

Test And Measurement -- Get XXX Pages: 
VUs
AVG(ms)
90%(ms)
TPS
Calculated VUs using Little's Law
1587216.980.99
1274108161.6311.96
2583121297.9924.73
50138209360.6749.77
75197349379.5974.78
100261492381.2199.50
125326636381.80124.71



Predicting and Modeling: 
  • Find relations between VU and Avg Response Time By Curve fitting:
y = 0.0067x2 + 1.3698x + 53.613

  • Response Time Validation

VusPredict ResultTest Result
35109.7101
65170.9172
90231.1238
110285.3290

  • Find relations between VU and Throughput By Curve fitting:
y = 82.39ln(x) + 7.8124

  • Throughput Validation: 
VusPredict ResultTest Result
35300.7342.61
65351.7376.35
90378.5374.49
110395377.74