├── DrupalStress.jmx
├── README
└── jmetergraph.pl
/DrupalStress.jmx:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 | false
6 |
7 |
8 | false
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
17 | outputDir
18 | /tmp
19 | =
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 | urls.login
28 | /user/login
29 | =
30 |
31 |
32 | urls.logout
33 | /logout
34 | =
35 |
36 |
37 | host
38 | dev6.thinky
39 | =
40 |
41 |
42 | meta.name
43 | Drupal6
44 | =
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 | urls.login
53 | /user/login
54 | =
55 |
56 |
57 | urls.logout
58 | /user/logout
59 | =
60 |
61 |
62 | host
63 | dev7.thinky
64 | =
65 |
66 |
67 | meta.name
68 | Drupal7
69 | =
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 | ${host}
79 | 80
80 |
81 |
82 | http
83 |
84 |
85 |
86 |
87 |
88 |
89 | MySQL server has gone away
90 | Unable to connect to database server
91 |
92 | false
93 | 6
94 | Assertion.response_data
95 |
96 |
97 |
98 | false
99 | 20
100 |
101 |
102 | 1184192411000
103 | stopthread
104 | 1
105 |
106 | false
107 | 10
108 |
109 | 1184192411000
110 |
111 |
112 |
113 |
114 |
115 |
116 |
117 |
118 |
119 |
120 |
121 |
122 | /node
123 | GET
124 | true
125 | false
126 | true
127 | false
128 |
129 |
130 |
131 | false
132 |
133 |
134 |
135 |
136 |
137 |
138 |
139 |
140 |
141 |
142 |
143 |
144 |
145 | /taxonomy/term/1
146 | GET
147 | true
148 | false
149 | true
150 | false
151 |
152 |
153 |
154 | false
155 |
156 |
157 |
158 |
159 |
160 |
161 | Accept-Language
162 | en-us,en;q=0.5
163 |
164 |
165 | Accept
166 | text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
167 |
168 |
169 | Keep-Alive
170 | 300
171 |
172 |
173 | User-Agent
174 | Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3
175 |
176 |
177 | Accept-Encoding
178 | gzip,deflate
179 |
180 |
181 | Accept-Charset
182 | ISO-8859-1,utf-8;q=0.7,*;q=0.7
183 |
184 |
185 |
186 |
187 |
188 |
189 |
190 |
191 |
192 |
193 |
194 |
195 |
196 |
197 |
198 | /node/1
199 | GET
200 | true
201 | false
202 | true
203 | false
204 |
205 |
206 |
207 | false
208 |
209 |
210 |
211 |
212 |
213 |
214 | Accept-Language
215 | en-us,en;q=0.5
216 |
217 |
218 | Accept
219 | text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
220 |
221 |
222 | Keep-Alive
223 | 300
224 |
225 |
226 | User-Agent
227 | Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3
228 |
229 |
230 | Accept-Encoding
231 | gzip,deflate
232 |
233 |
234 | Accept-Charset
235 | ISO-8859-1,utf-8;q=0.7,*;q=0.7
236 |
237 |
238 |
239 |
240 |
241 |
242 | false
243 |
244 | saveConfig
245 |
246 |
247 | true
248 | true
249 | true
250 |
251 | true
252 | true
253 | true
254 | true
255 | false
256 | true
257 | true
258 | false
259 | false
260 | true
261 | false
262 | false
263 | false
264 | false
265 | false
266 | 0
267 | true
268 |
269 |
270 |
271 |
272 |
273 |
274 | false
275 |
276 | saveConfig
277 |
278 |
279 | true
280 | true
281 | true
282 |
283 | true
284 | true
285 | true
286 | true
287 | false
288 | true
289 | true
290 | false
291 | false
292 | false
293 | false
294 | false
295 | false
296 | false
297 | false
298 | 0
299 |
300 |
301 | ${outputDir}/${meta.name}/anon-summary.xml
302 | true
303 |
304 |
305 |
306 |
307 | false
308 | 10
309 |
310 |
311 | 1184192411000
312 | stopthread
313 | 1
314 |
315 | false
316 | 10
317 |
318 | 1184192411000
319 |
320 |
321 |
322 |
323 |
324 |
325 |
326 |
327 | true
328 | false
329 | name
330 | editor
331 | =
332 |
333 |
334 | true
335 | false
336 | pass
337 | editor
338 | =
339 |
340 |
341 | true
342 | false
343 | form_id
344 | user_login
345 | =
346 |
347 |
348 | true
349 | false
350 | op
351 | Log+in
352 | =
353 |
354 |
355 |
356 |
357 |
358 |
359 |
360 |
361 |
362 | ${urls.login}
363 | POST
364 | true
365 | false
366 | true
367 | false
368 |
369 |
370 |
371 | false
372 |
373 |
374 |
375 |
376 |
377 |
378 |
379 |
380 |
381 |
382 |
383 |
384 |
385 |
386 | /node
387 | GET
388 | true
389 | false
390 | true
391 | false
392 |
393 |
394 |
395 | false
396 |
397 |
398 |
399 |
400 |
401 |
402 |
403 |
404 |
405 |
406 |
407 |
408 |
409 | /taxonomy/term/1
410 | GET
411 | true
412 | false
413 | true
414 | false
415 |
416 |
417 |
418 | false
419 |
420 |
421 |
422 |
423 |
424 |
425 | Accept-Language
426 | en-us,en;q=0.5
427 |
428 |
429 | Accept
430 | text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
431 |
432 |
433 | Keep-Alive
434 | 300
435 |
436 |
437 | User-Agent
438 | Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3
439 |
440 |
441 | Accept-Encoding
442 | gzip,deflate
443 |
444 |
445 | Accept-Charset
446 | ISO-8859-1,utf-8;q=0.7,*;q=0.7
447 |
448 |
449 |
450 |
451 |
452 |
453 |
454 |
455 |
456 |
457 |
458 |
459 |
460 |
461 |
462 | /node/1
463 | GET
464 | true
465 | false
466 | true
467 | false
468 |
469 |
470 |
471 | false
472 |
473 |
474 |
475 |
476 |
477 |
478 | Accept-Language
479 | en-us,en;q=0.5
480 |
481 |
482 | Accept
483 | text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
484 |
485 |
486 | Keep-Alive
487 | 300
488 |
489 |
490 | User-Agent
491 | Mozilla/5.0 (Macintosh; U; Intel Mac OS X 10.6; en-US; rv:1.9.1.3) Gecko/20090824 Firefox/3.5.3
492 |
493 |
494 | Accept-Encoding
495 | gzip,deflate
496 |
497 |
498 | Accept-Charset
499 | ISO-8859-1,utf-8;q=0.7,*;q=0.7
500 |
501 |
502 |
503 |
504 |
505 |
506 |
507 | false
508 |
509 |
510 |
511 |
512 |
513 |
514 |
515 |
516 |
517 |
518 |
519 |
520 |
521 |
522 | ${urls.logout}
523 | GET
524 | true
525 | false
526 | true
527 | false
528 |
529 |
530 |
531 | false
532 |
533 |
534 |
535 |
536 |
537 | false
538 |
539 | saveConfig
540 |
541 |
542 | true
543 | true
544 | true
545 |
546 | true
547 | true
548 | true
549 | true
550 | false
551 | true
552 | true
553 | false
554 | false
555 | true
556 | false
557 | false
558 | false
559 | false
560 | false
561 | 0
562 | true
563 |
564 |
565 |
566 |
567 |
568 |
569 | false
570 |
571 | saveConfig
572 |
573 |
574 | true
575 | true
576 | true
577 |
578 | true
579 | true
580 | true
581 | true
582 | false
583 | true
584 | true
585 | false
586 | false
587 | false
588 | false
589 | false
590 | false
591 | false
592 | false
593 | 0
594 |
595 |
596 | ${outputDir}/${meta.name}/auth-summary.xml
597 | true
598 |
599 |
600 |
601 |
602 | false
603 | 10
604 |
605 |
606 | 1181576981000
607 | stopthread
608 | 4
609 |
610 | false
611 | 10
612 |
613 | 1181576981000
614 |
615 |
616 |
617 |
618 |
619 |
620 | true
621 | false
622 | name
623 | editor
624 | =
625 |
626 |
627 | true
628 | false
629 | pass
630 | editor
631 | =
632 |
633 |
634 | true
635 | false
636 | form_id
637 | user_login
638 | =
639 |
640 |
641 | true
642 | false
643 | op
644 | Log+in
645 | =
646 |
647 |
648 |
649 |
650 |
651 |
652 |
653 |
654 |
655 | ${urls.login}
656 | POST
657 | true
658 | false
659 | true
660 | false
661 |
662 |
663 |
664 | false
665 |
666 |
667 |
668 |
669 |
670 |
671 |
672 |
673 |
674 |
675 |
676 |
677 |
678 | /user/
679 | GET
680 | true
681 | false
682 | true
683 | false
684 |
685 |
686 |
687 | false
688 |
689 |
690 |
691 |
692 |
693 |
694 |
695 |
696 |
697 |
698 |
699 |
700 |
701 | ${urls.logout}
702 | GET
703 | true
704 | false
705 | true
706 | false
707 |
708 |
709 |
710 | false
711 |
712 |
713 |
714 |
715 |
716 | false
717 |
718 |
719 |
720 | false
721 |
722 | saveConfig
723 |
724 |
725 | true
726 | true
727 | true
728 |
729 | true
730 | true
731 | true
732 | true
733 | false
734 | true
735 | true
736 | false
737 | false
738 | false
739 | false
740 | false
741 | false
742 | false
743 | false
744 | 0
745 |
746 |
747 | ${outputDir}/${meta.name}/user-summary.xml
748 | true
749 |
750 |
751 |
752 |
753 | false
754 |
755 | saveConfig
756 |
757 |
758 | true
759 | true
760 | true
761 |
762 | true
763 | true
764 | true
765 | true
766 | false
767 | true
768 | true
769 | false
770 | false
771 | false
772 | false
773 | false
774 | false
775 | false
776 | false
777 | 0
778 |
779 |
780 |
781 |
782 |
783 |
784 | false
785 |
786 | saveConfig
787 |
788 |
789 | true
790 | true
791 | true
792 |
793 | true
794 | true
795 | true
796 | true
797 | false
798 | true
799 | true
800 | false
801 | false
802 | false
803 | false
804 | false
805 | false
806 | false
807 | false
808 | 0
809 |
810 |
811 | ${outputDir}/${meta.name}/overall-summary.xml
812 | true
813 |
814 |
815 |
816 | false
817 |
818 | saveConfig
819 |
820 |
821 | true
822 | true
823 | true
824 |
825 | true
826 | true
827 | true
828 | true
829 | false
830 | true
831 | true
832 | false
833 | false
834 | false
835 | false
836 | false
837 | false
838 | false
839 | false
840 | 0
841 |
842 |
843 | ${outputDir}/${meta.name}/overall-graph.xml
844 |
845 |
846 |
847 |
848 |
849 |
--------------------------------------------------------------------------------
/README:
--------------------------------------------------------------------------------
1 | Drupal Performance Testing Suite.
2 | To run:
3 | 1. Open up the jmx file
4 | 2. Enable either Drupal-6 or Drupal-7 config sections (not both).
5 | 3. Edit the config section and change the host to reflect your target machine
6 | 4. (optional) change the output location in the general test configuration
7 | 5. (optional) run jmetergraph.pl on the *summary.xml files to generate charts.
8 |
--------------------------------------------------------------------------------
/jmetergraph.pl:
--------------------------------------------------------------------------------
1 | #!/usr/bin/perl
2 | #---
3 | # Original script by Christoph Meissner (email unknown)
4 | # Taken from http://wiki.apache.org/jakarta-jmeter-data/attachments/LogAnalysis/attachments/jmetergraph.pl
5 | # Fixes by George Barnett (george@alink.co.za)
6 | #
7 | # Fixes:
8 | # - Modified to 'use strict'
9 | # - Fixed scoping of various variables
10 | # - Added DEBUG print option
11 | #
12 | # Script Options:
13 | # perl jmetergraph.pl [-alllb] [-stddev] [-range] [ ... ]
14 | # -alllb [Script does not draw stacked chart for requests that are < 1% of total. This disables this behaviour]
15 | # -stddev [Will draw the std dev]
16 | # -range [Wil draw request range]
17 | #
18 | #---
19 | #
20 | # This script parses xml jtl files created by jmeter.
21 | # It extracts timestamps, active threads and labels from it.
22 | #
23 | # Further, based on this data
24 | # it builds below chart files (see sample files):
25 | #
26 | # 1. one chart containing the overall response times related to active threads.
27 | # The resulting png file is named 'entire_user.png'.
28 | #
29 | # 2. one chart containing the overall response times related to throughput
30 | # The resulting png file is named 'entire_throughput.png'.
31 | #
32 | # 3. one chart that will contain bars for each label
33 | # which were stacked over response intervals (expressed in msec)
34 | # The resulting png file is named 'ChartStacked.png'
35 | #
36 | # 4. one chart that will contain stacked bars for each label
37 | # which were stacked over response intervals (expressed in msec)
38 | # The resulting png file is named 'ChartStackedPct.png'
39 | #
40 | # 5. one chart per label containing response times related to active threads and throughput
41 | # This chart png is named like the label itself with the non-word characters substituted by '_'
42 | # If it is related to active threads then '_user' is appended to the graph's file name -
43 | # otherwise a '_throughput'.
44 | #
45 | # IMPORTANT:
46 | # ==========
47 | # The script works best with jmeter log format V2.1.
48 | # I never tested it on log's of either older or newer versions.
49 | # Also, I was too sluggish to include an XML parser.
50 | # Thus the script parses the jtl files by using regex.
51 | #
52 | # The graphs are built depending on the names of the labels of your requests.
53 | # Thus group your label names with this in mind
54 | # when you are about to create your jmeter testplan,
55 | #
56 | # Also, only the labels on the 1st level are considered.
57 | # They accumulate the response time, active users and throughput
58 | # for all sub levels.
59 | #
60 | #
61 | # FAQ:
62 | # ====
63 | # How to make this script to create proper charts?
64 | #
65 | # Open your testplan with jmeter and
66 | # 1. insert listener 'Aggregate Report' (unless not present already).
67 | # 2. invoke 'Configure' there
68 | # 3. make sure that below checks are activated at least:
69 | # 'Save as XML'
70 | # 'Save Label'
71 | # 'Save Time Stamp'
72 | # 'Save Thread Name'
73 | # 'Save Active Thread Counts'
74 | # 'Save Elapsed Time'
75 | # 4. tell 'Aggregate Report' to write all data into a file
76 | # 5. when building your testplan
77 | # you should name your critical request samplers in a way that data can be grouped into it
78 | # (eg. 'Login', 'Host request', 'continue page', 'Req Database xyz', ...)
79 | #
80 | # Perl requisites:
81 | # 6. install the perl 'GD' package
82 | # 7. install the perl 'Chart' package
83 | # 8. test perl.
84 | # This means that
85 | # perl -MGD -MChart::Lines -MChart::Composite -MChart::StackedBars
86 | # shouldn't fail
87 | #
88 | # To run the script:
89 | # perl jmetergraph.pl ...
90 | #
91 | # The png graphs will be created in current working directory
92 | #
93 | # I created this checklist to my best knowledge.
94 | # However, if it fails in your computer environment, ...
95 | #
96 | #---
97 |
98 | use strict;
99 | use Chart::StackedBars;
100 | use Chart::Composite;
101 | use Chart::Lines;
102 |
103 | #---
104 | # arguments passed?
105 | #---
106 | my @files = grep {!/^-/ && -s "$_"} @ARGV;
107 | our @args = grep {/^-/ && !-f "$_"} @ARGV;
108 |
109 | my $DEBUG = 1;
110 |
111 | my %entire = ();
112 | my %glabels = ();
113 | my %gthreads = ();
114 | my $atflag = 0; # if active threads found then according graphs will be created
115 | #---
116 | # data received within this intervall will be averaged into one result
117 | #---
118 |
119 | my $collectinterval = 180; # 60 seconds
120 | #---
121 | # cusps aggregate response times
122 | #---
123 | our @cusps = (200, 500, 1000, 2000, 5000, 10000, 60000);
124 |
125 | #---
126 | # labels determine the name of output charts
127 | #---
128 | our @labels = ();
129 | our @threads = ();
130 |
131 | #---
132 | # intermediate values
133 | #---
134 | our %timestamps = ();
135 | our $respcount = 0;
136 | our $measures = 0;
137 |
138 | # Some variables. Cunt who wrote this script didn't use strict so it's fucked ITO scoping
139 | my ($entireta,$entirecnt,$entireby);
140 | my ($respcount,$sumresptimes,$sumSQresptimes);
141 |
142 | #---
143 | # define colors for stacked bar charts
144 | #---
145 | our %colors = (
146 | dataset0 => "green",
147 | dataset1 => [0, 139, 139], # dark cyan
148 | dataset2 => [255, 215,0], # gold
149 | dataset3 => "DarkOrange",
150 | dataset4 => "red",
151 | dataset5 => [255, 0, 0], # red
152 | dataset6 => [139, 0, 139], # dark magenta
153 | dataset7 => [0, 0, 0], # black
154 | );
155 |
156 | #---
157 | # here we go thru all files and collect data
158 | #---
159 |
160 | while(my $file = shift(@files)) {
161 | print "Opening file $file\n" if $DEBUG;
162 | open(IN, "<$file") || do {
163 | print $file, " ", $!, "\n";
164 | next;
165 | };
166 |
167 | print "Parsing data from $file\n" if $DEBUG;
168 | while() {
169 | my ($time,$timestamp,$success,$label,$thread,$latency,$bytes,$DataEncoding,$DataType,$ErrorCount,$Hostname,$NumberOfActiveThreadsAll,$NumberOfActiveThreadsGroup,$ResponseCode,$ResponseMessage,$SampleCount);
170 | if(/^<(sample|httpSample)\s/) {
171 |
172 | ($time) = (/\st="(\d+)"/o);
173 | ($timestamp) = (/\sts="(\d+)"/o);
174 | ($success) = (/\ss="(.+?)"/o);
175 | ($label) = (/\slb="(.+?)"/o);
176 | ($thread) = (/\stn="(.+?)"/o);
177 | ($latency) = (/\slt="(\d+)"/o);
178 | ($bytes) = (/\sby="(\d+)"/o);
179 | ($DataEncoding) = (/\sde="(\d+)"/o);
180 | ($DataType) = (/\sdt="(.+?)"/o);
181 | ($ErrorCount) = (/\sec="(\d+)"/o);
182 | ($Hostname) = (/\shn="(.+?)"/o);
183 | ($NumberOfActiveThreadsAll) = (/\sna="(\d+)"/o);
184 | ($NumberOfActiveThreadsGroup) = (/\sng="(\d+)"/o);
185 | ($ResponseCode) = (/\src="(.+?)"/o);
186 | ($ResponseMessage) = (/\srm="(.+?)"/o);
187 | ($SampleCount) = (/\ssc="(\d+)"/o);
188 |
189 | } elsif(/^ $cusps[$i]))) {
227 | $glabels{$label}{$cusps[$i]} += 1;
228 | $entire{$cusps[$i]} += 1;
229 | last;
230 | }
231 | }
232 | #---
233 | # stddev
234 | #---
235 | $respcount += 1;
236 | $sumresptimes += $time;
237 | $sumSQresptimes += ($time ** 2);
238 | if($respcount > 1) {
239 | my $stddev = sqrt(($respcount * $sumSQresptimes - $sumresptimes ** 2) /
240 | ($respcount * ($respcount - 1)));
241 |
242 | $entire{$tstmp, 'stddev'} = $glabels{$label}{$tstmp, 'stddev'} = $stddev;
243 |
244 | }
245 |
246 | #---
247 | # avg
248 | #---
249 | $entire{$tstmp, 'avg'} = $sumresptimes / $respcount;
250 |
251 | $glabels{$label}{$tstmp, 'responsetime'} += $time;
252 | $glabels{$label}{$tstmp, 'respcount'} += 1;
253 | $glabels{$label}{$tstmp, 'avg'} = int($glabels{$label}{$tstmp, 'responsetime'} / $glabels{$label}{$tstmp, 'respcount'});
254 |
255 | #---
256 | # active threads
257 | #---
258 |
259 | if(!$entire{$tstmp, 'activethreads'}) {
260 | $entireta = 0;
261 | $entirecnt = 0;
262 | $entireby = 0;
263 | }
264 |
265 | if($NumberOfActiveThreadsAll > 0) {
266 | $atflag = 1;
267 | }
268 |
269 | $entirecnt += 1;
270 |
271 | if($atflag == 1) {
272 | $entireta += $NumberOfActiveThreadsAll;
273 | $entire{$tstmp, 'activethreads'} = int($entireta / $entirecnt);
274 |
275 | if(!$glabels{$label}{$tstmp, 'activethreads'}) {
276 | $glabels{$label}{$tstmp, 'lbta'} = 0;
277 | $glabels{$label}{$tstmp, 'lbby'} = 0;
278 | }
279 | $glabels{$label}{$tstmp, 'lbta'} += $NumberOfActiveThreadsAll;
280 | $glabels{$label}{$tstmp, 'activethreads'} = sprintf("%.0f", $glabels{$label}{$tstmp, 'lbta'} / $glabels{$label}{$tstmp, 'respcount'});
281 |
282 | } else {
283 | #---
284 | # if NumberOfActiveThreads is not available
285 | # use threadname to extrapolate active threads later
286 | #---
287 | if($NumberOfActiveThreadsAll eq '') {
288 | if(!$gthreads{$thread}{'first'}) {
289 | $gthreads{$thread}{'first'} = $tstmp;
290 | push(@threads, $thread);
291 | }
292 |
293 | $gthreads{$thread}{'last'} = $tstmp;
294 | }
295 | }
296 |
297 | #---
298 | # throughput
299 | #---
300 | if($bytes > 0) {
301 | $entireby += $bytes;
302 | $entire{$tstmp, 'throughput'} = int($entireby / $entirecnt);
303 |
304 | $glabels{$label}{$tstmp, 'lbby'} += $bytes;
305 | $glabels{$label}{$tstmp, 'throughput'} = $glabels{$label}{$tstmp, 'lbby'}; # counts per $collectinterval
306 | }
307 |
308 | }
309 | print "Closing $file\n" if $DEBUG;
310 | close(IN);
311 | }
312 |
313 | print "Found $#labels labels\n" if $DEBUG;
314 |
315 | # Sort the labels.
316 | print "Sorting labels\n" if $DEBUG;
317 | my @tmplabels = sort @labels;
318 | @labels = @tmplabels;
319 |
320 | #---
321 | # if required (no NumbersOfActiveThreads)
322 | # then extrapolate users
323 | #---
324 | if($atflag == 0) {
325 | print "using timestamps to calculate active threads\n";
326 | my @tstmps = sort { $a <=> $b } keys(%timestamps);
327 | foreach my $label ('entire', @labels) {
328 | print "tracking $label\n";
329 | foreach my $thread (@threads) {
330 | foreach my $tstmp (@tstmps) {
331 | if($gthreads{$thread}{'first'} <= $tstmp && $gthreads{$thread}{'last'} >= $tstmp) {
332 | $glabels{$label}{$tstmp, 'activethreads'} += 1;
333 | }
334 | }
335 | }
336 | }
337 | }
338 |
339 | #---
340 | # charts will be created
341 | # if something could be parsed
342 | #---
343 | if($respcount > 0) {
344 | #---
345 | # number of time stamps
346 | #---
347 | $measures = scalar(keys(%timestamps));
348 |
349 | print "Generating stacked bars absolute\n" if $DEBUG;
350 | &ChartStackedBars();
351 |
352 | print "Generating stacked bars relative\n" if $DEBUG;
353 | &ChartStackedPct();
354 |
355 | foreach my $label ('entire', @labels) {
356 | if($entireby > 0) {
357 | &ChartLines($label, 'throughput');
358 | }
359 | &ChartLines($label, 'users');
360 | }
361 | }
362 | #-------------------------------------------------------------------------------
363 | sub ChartStackedPct {
364 |
365 | if(scalar(@labels) == 0) {
366 | return undef;
367 | }
368 |
369 | my $ChartStacked = Chart::StackedBars->new(1024, 768);
370 |
371 | #---
372 | # cusps
373 | #---
374 | my @xaxis = ();
375 | my @xlabels = ();
376 | foreach my $label (@labels) {
377 | print "Attempting to add $label to StackedPCT graph\n" if $DEBUG;
378 | if(($glabels{$label}{'respcount'} > ($respcount / 100)) || grep(/-alllb/i, @args)) {
379 | push(@xaxis, $label);
380 | if (length $label > 25) {
381 | $label = substr($label,0,25) . " " . substr($label,25);
382 | }
383 | push(@xlabels, $label);
384 | print " Added $label\n" if $DEBUG;
385 | }
386 | }
387 | $ChartStacked->add_dataset(@xlabels);
388 |
389 | my ($value,$i,$label);
390 | my @data = ();
391 | my @legend_labels = ();
392 |
393 | for($i = 0; $i <= $#cusps; $i++) {
394 | @data = ();
395 | foreach my $label (@xaxis) {
396 | $value = $glabels{$label}{$cusps[$i]};
397 | if(!defined $value) {
398 | $value = 0;
399 | }
400 | $value = (100 * $value) / $glabels{$label}{'respcount'};
401 | push(@data, $value);
402 | }
403 | $ChartStacked->add_dataset(@data);
404 |
405 | push(@legend_labels, "< " . $cusps[$i] . " msec");
406 | }
407 |
408 | my %settings = (
409 | transparent => 'true',
410 | title => 'Response Time %',
411 | y_grid_lines => 'true',
412 | legend => 'right',
413 | legend_labels => \@legend_labels,
414 | precision => 0,
415 | y_label => 'Requests %',
416 | max_val => 100,
417 | include_zero => 'true',
418 | point => 0,
419 | colors => \%colors,
420 | x_ticks => 'vertical',
421 | precision => 0,
422 | );
423 |
424 | $ChartStacked->set(%settings);
425 |
426 | print "Generated ChartStackedPct.png\n" if $DEBUG;
427 | $ChartStacked->png("ChartStackedPct.png");
428 | }
429 | #-------------------------------------------------------------------------------
430 | sub ChartStackedBars {
431 |
432 | if(scalar(@labels) == 0) {
433 | return undef;
434 | }
435 |
436 | my $ChartStacked = Chart::StackedBars->new(1024, 768);
437 |
438 | #---
439 | # cusps
440 | #---
441 | my @xaxis = ();
442 | my @xlabels = ();
443 | foreach my $label (@labels) {
444 | print "Added $label to StackedPCT graph\n" if $DEBUG;
445 | if(($glabels{$label}{'respcount'} > ($respcount / 100)) || grep(/-alllb/i, @args)) {
446 | push(@xaxis, $label);
447 | push(@xlabels, $label);
448 | }
449 | }
450 | $ChartStacked->add_dataset(@xlabels);
451 |
452 | my ($value,$i,$label);
453 | my @data = ();
454 | my @legend_labels = ();
455 | for($i = 0; $i <= $#cusps; $i++) {
456 | @data = ();
457 | foreach my $label (@xaxis) {
458 | $value = $glabels{$label}{$cusps[$i]};
459 | if($value == undef) {
460 | $value = 0;
461 | }
462 | push(@data, $value);
463 | }
464 | $ChartStacked->add_dataset(@data);
465 |
466 | push(@legend_labels, "< " . $cusps[$i] . " msec");
467 | }
468 |
469 | my %settings = (
470 | transparent => 'true',
471 | title => 'Response Time',
472 | y_grid_lines => 'true',
473 | legend => 'right',
474 | legend_labels => \@legend_labels,
475 | precision => 0,
476 | y_label => 'Requests',
477 | include_zero => 'true',
478 | point => 0,
479 | colors => \%colors,
480 | x_ticks => 'vertical',
481 | precision => 0,
482 | );
483 |
484 | $ChartStacked->set(%settings);
485 |
486 | print "Generating ChartStacked.png\n" if $DEBUG;
487 | $ChartStacked->png("ChartStacked.png");
488 | }
489 | #-------------------------------------------------------------------------------
490 | sub ChartLines {
491 | my ($label, $mode) = @_;
492 |
493 | my %labelmap = (
494 | 'entire' => 'total',
495 | );
496 |
497 | my $title = $label;
498 | $title = $labelmap{$label} if($labelmap{$label});
499 |
500 | my $ChartComposite = Chart::Composite->new(1024, 768);
501 |
502 | my @tstmps = sort { $a <=> $b } keys(%timestamps);
503 | my @responsetimes = ();
504 | my @plusstddev = ();
505 | my @minusstddev = ();
506 | my @users = ();
507 | my @throughput = ();
508 | my @xaxis = ();
509 | my $y2label;
510 |
511 |
512 | #---
513 | # response times
514 | #---
515 | my $tstmp;
516 | my ($pstd, $mstd) = (0, 0);
517 | foreach my $tstmp (@tstmps) {
518 | if($glabels{$label}{$tstmp, 'avg'}) {
519 | push(@xaxis, $tstmp);
520 | push(@responsetimes, $glabels{$label}{$tstmp, 'avg'});
521 |
522 | $mstd = $glabels{$label}{$tstmp, 'avg'} - $glabels{$label}{$tstmp, 'stddev'};
523 | $pstd = $glabels{$label}{$tstmp, 'avg'} + $glabels{$label}{$tstmp, 'stddev'};
524 | $mstd = 1 if($mstd < 0); # supress lines below 0
525 | push(@plusstddev, $pstd);
526 | push(@minusstddev, $mstd);
527 | }
528 | }
529 | $ChartComposite->add_dataset(@xaxis);
530 | $ChartComposite->add_dataset(@responsetimes);
531 |
532 | my %colors = (
533 | dataset0 => "green",
534 | dataset1 => "red",
535 | );
536 | my @ds1 = (1);
537 | my @ds2 = (2);
538 | if(grep(/-stddev/ || /-range/, @args)) {
539 | $ChartComposite->add_dataset(@plusstddev);
540 | $ChartComposite->add_dataset(@minusstddev);
541 | @ds1 = (1, 2, 3);
542 | @ds2 = (4);
543 |
544 | %colors = (
545 | dataset0 => "green",
546 | dataset1 => [189, 183, 107], # dark khaki
547 | dataset2 => [189, 183, 107], # dark khaki
548 | dataset3 => "red",
549 | );
550 | }
551 |
552 | if($mode eq 'users') {
553 | #---
554 | # users
555 | #---
556 | foreach my $tstmp (@xaxis) {
557 | push(@users, $glabels{$label}{$tstmp, 'activethreads'});
558 | }
559 |
560 | $ChartComposite->add_dataset(@users);
561 | $y2label = "active threads";
562 | } else {
563 | #---
564 | # throughput
565 | #---
566 | foreach my $tstmp (@xaxis) {
567 | push(@throughput, $glabels{$label}{$tstmp, 'throughput'});
568 | }
569 | $ChartComposite->add_dataset(@throughput);
570 | $y2label = "throughput bytes/min";
571 | }
572 |
573 | my $skip = 0;
574 | if(scalar(@xaxis) > 40) {
575 | $skip = int(scalar(@xaxis) / 40) + 1;
576 | }
577 |
578 | my @labels = ($label, $mode);
579 | if(grep(/-stddev/, @args)) {
580 | @labels = ($label, "+stddev", "-stddev", $mode);
581 | }
582 |
583 | my $type = 'Lines';
584 | if(grep(/-range/i, @args)) {
585 | @labels = ($label, "n.a", "n.a", $mode);
586 | $type = 'ErrorBars';
587 | }
588 |
589 | my %settings = (
590 | composite_info => [ [$type, \@ds1], ['Lines', \@ds2 ]],
591 | transparent => 'true',
592 | title => 'Response Time ' . $title,
593 | y_grid_lines => 'true',
594 | legend => 'bottom',
595 | y_label => 'Response Time msec',
596 | y_label2 => $y2label,
597 | legend_labels => \@labels,
598 | legend_example_height => 1,
599 | legend_example_height0 => 10,
600 | legend_example_height1 => 2,
601 | legend_example_height2 => 2,
602 | legend_example_height3 => 10,
603 | legend_example_height4 => 10,
604 | include_zero => 'true',
605 | x_ticks => 'vertical',
606 | skip_x_ticks => $skip,
607 | brush_size1 => 3,
608 | brush_size2 => 3,
609 | pt_size => 6,
610 | point => 0,
611 | line => 1,
612 | f_x_tick => \&formatTime,
613 | colors => \%colors,
614 | precision => 0,
615 | );
616 |
617 | $ChartComposite->set(%settings);
618 |
619 | my $filename = $label;
620 | $filename=~ s/\W/_/g;
621 | $filename .= '_' . $mode . '.png';
622 | print $filename, "\n";
623 |
624 | $ChartComposite->png($filename);
625 | }
626 | #-------------------------------------------------------------------------------
627 | sub formatTime {
628 | my ($tstmp) = @_;
629 |
630 | my $string = scalar(localtime($tstmp));
631 |
632 | my ($rc) = ($string =~ /\s(\d\d:\d\d:\d\d)\s/);
633 |
634 | return $rc;
635 | }
636 | #-------------------------------------------------------------------------------
637 | #-------------------------------------------------------------------------------
638 |
639 |
--------------------------------------------------------------------------------