98_apptime.pm 7.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239
  1. ################################################################
  2. # 98_apptime:application timing
  3. # $Id: 98_apptime.pm 12820 2016-12-18 15:22:37Z martinp876 $
  4. ################################################################
  5. #####################################################
  6. #
  7. package main;
  8. use strict;
  9. use warnings;
  10. use vars qw(%defs); # FHEM device/button definitions
  11. use vars qw(%intAt);
  12. use vars qw($nextat);
  13. sub apptime_Initialize($);
  14. sub apptime_Initialize($){
  15. $cmds{"apptime"}{Fn} = "apptime_CommandDispTiming";
  16. $cmds{"apptime"}{Hlp} = "[clear|<field>] [top|all] [<filter>],application function calls and duration";
  17. }
  18. sub HandleTimeout() {
  19. return undef if(!$nextat);
  20. my $now = gettimeofday();
  21. return ($nextat-$now) if($now < $nextat);
  22. $now += 0.01;# need to cover min delay at least
  23. $nextat = 0;
  24. #############
  25. # Check the internal list.
  26. foreach my $i (sort { $intAt{$a}{TRIGGERTIME} <=>
  27. $intAt{$b}{TRIGGERTIME} } keys %intAt) {
  28. my $tim = $intAt{$i}{TRIGGERTIME};
  29. my $fn = $intAt{$i}{FN};
  30. if(!defined($tim) || !defined($fn)) {
  31. delete($intAt{$i});
  32. next;
  33. } elsif($tim <= $now) {
  34. my $arg = $intAt{$i}{ARG};
  35. $arg = "" if (!$arg);
  36. apptime_getTiming("global","tmr-".$fn.";".$arg,$fn,$tim,$arg);
  37. delete($intAt{$i});
  38. } else {
  39. $nextat = $tim if(!$nextat || $nextat > $tim);
  40. }
  41. }
  42. return undef if(!$nextat);
  43. $now = gettimeofday(); # possibly some tasks did timeout in the meantime
  44. # we will cover them
  45. return ($now+ 0.01 < $nextat) ? ($nextat-$now) : 0.01;
  46. }
  47. sub CallFn(@) {
  48. my $d = shift;
  49. my $n = shift;
  50. if(!$d || !$defs{$d}) {
  51. $d = "<undefined>" if(!defined($d));
  52. Log 0, "Strange call for nonexistent $d: $n";
  53. return undef;
  54. }
  55. if(!$defs{$d}{TYPE}) {
  56. Log 0, "Strange call for typeless $d: $n";
  57. return undef;
  58. }
  59. my $fn = $modules{$defs{$d}{TYPE}}{$n};
  60. return "" if(!$fn);
  61. my @ret = apptime_getTiming($d,$fn,$fn,0,@_);
  62. if(wantarray){return @ret;}
  63. else {return $ret[0];}
  64. }
  65. sub apptime_getTiming($$$@) {
  66. my ($e,$fnName,$fn,$tim,@arg) = @_;
  67. my $h;
  68. if (!$defs{$e}{helper} ||
  69. !$defs{$e}{helper}{bm} ||
  70. !$defs{$e}{helper}{bm}{$fnName} ){
  71. %{$defs{$e}{helper}{bm}{$fnName}} =(max =>0, mAr =>"",
  72. cnt =>1, tot =>0,
  73. dmx =>0);
  74. $h = $defs{$e}{helper}{bm}{$fnName};
  75. }
  76. else{
  77. $h = $defs{$e}{helper}{bm}{$fnName};
  78. $h->{cnt}++;
  79. }
  80. my $ts1 = gettimeofday();
  81. if ($tim){
  82. my $td = int(($ts1-$tim)*1000);
  83. $h->{dmx} = $td if ($h->{dmx} < $td);
  84. }
  85. no strict "refs";
  86. my @ret = &{$fn}(@arg);
  87. use strict "refs";
  88. $ts1 = int((gettimeofday()-$ts1)*1000);
  89. if ($ts1 && $h->{max}<$ts1){
  90. $h->{max}=$ts1;
  91. $h->{mAr}= \@arg;
  92. }
  93. $h->{tot}+=$ts1;
  94. return @ret;
  95. }
  96. #####################################
  97. sub apptime_CommandDispTiming($$@) {
  98. my ($cl,$param) = @_;
  99. my ($sFld,$top,$filter) = split" ",$param;
  100. $sFld = "max" if (!$sFld);
  101. $top = "top" if (!$top);
  102. my %fld = (name=>0,funktion=>1,max=>2,count=>3,total=>4,average=>5,maxDly=>6,clear=>99);
  103. return "$sFld undefined field, use one of ".join(",",keys %fld)
  104. if(!defined $fld{$sFld});
  105. my @bmArr;
  106. my @a = map{"$defs{$_}:$_"} keys (%defs); # prepare mapping hash 2 name
  107. $_ =~ s/[HASH\(\)]//g foreach(@a);
  108. foreach my $d (sort keys %defs) {
  109. next if(!$defs{$d}{helper}||!$defs{$d}{helper}{bm});
  110. if ($sFld eq "clear"){
  111. delete $defs{$d}{helper}{bm};
  112. next;
  113. }
  114. foreach my $f (sort keys %{$defs{$d}{helper}{bm}}) {
  115. next if(!defined $defs{$d}{helper}{bm}{$f}{cnt});
  116. next if($filter && $d !~ m/$filter/ && $f !~ m/$filter/);
  117. my ($n,$t) = ($d,$f);
  118. ($n,$t) = split(";",$f,2) if ($d eq "global");
  119. $t = "" if (!defined $t);
  120. my $h = $defs{$d}{helper}{bm}{$f};
  121. my $arg = "";
  122. if ($h->{mAr} && scalar(@{$h->{mAr}})){
  123. foreach my $i (0..scalar(@{$h->{mAr}})){
  124. if(ref(${$h->{mAr}}[$i]) eq 'HASH' and exists(${$h->{mAr}}[$i]->{NAME})){
  125. ${$h->{mAr}}[$i] = "HASH(".${$h->{mAr}}[$i]->{NAME}.")";
  126. }
  127. }
  128. $arg = join ("; ",@{$h->{mAr}});
  129. }
  130. push @bmArr,[($n,$t
  131. ,$h->{max}
  132. ,$h->{cnt}
  133. ,$h->{tot}
  134. ,$h->{tot} /$h->{cnt}
  135. ,$h->{dmx}
  136. ,$arg
  137. )];
  138. }
  139. }
  140. my $field = $fld{$sFld};
  141. if ($field>1){@bmArr = sort { $b->[$field] <=> $a->[$field] } @bmArr;}
  142. else {@bmArr = sort { $b->[$field] cmp $a->[$field] } @bmArr;}
  143. my $ret = sprintf("\n %35s %20s %6s %6s %8s %8s %s",
  144. "name","function","max","count","total","average","maxDly","param Max call");
  145. my $end = ($top && $top eq "top")?20:@bmArr-1;
  146. $end = @bmArr-1 if ($end>@bmArr-1);
  147. $ret .= sprintf("\n %35s %20s %6d %6d %8d %8.2f %6d %s",@{$bmArr[$_]})for (0..$end);
  148. return $ret;
  149. }
  150. 1;
  151. =pod
  152. =item command
  153. =item summary support to analyse function performance
  154. =item summary_DE unterstuetzung der analyse der Performance von Funktionen
  155. =begin html
  156. <a name="apptime"></a>
  157. <h3>apptime</h3>
  158. <ul>
  159. <code>apptime</code>
  160. <br>
  161. <br>
  162. apptime provides information about application procedure execution time.
  163. It is designed to identify long runner jobs causing latency as well as
  164. overall high cpu usage jobs<br>
  165. No information about FHEM kernel times and delays will be provided. <br>
  166. Once started apptime monitors tasks. User may reset counter during operation.
  167. apptime adds about 1% CPU load in average to FHEM.
  168. in order to remove apptime shutdown restart is necessary.
  169. <br>
  170. <br>
  171. <b>Features:</b><br>
  172. <ul>
  173. <li><code>apptime</code><br>
  174. apptime is started with the its first call nad continously monitor operation.<br>
  175. To unload apptime shutdown restart is necessary<br> </li>
  176. <li><code>apptime clear</code><br>
  177. reset all counter and start fom Zero<br> </li>
  178. <li><code>apptime [count|funktion|average|clear|max|name|total] [all]</code><br>
  179. display a table sorted by the field selected<br>
  180. <b>all</b> will display the complete table while by default only the top lines are printed. <br></li>
  181. </ul>
  182. <br>
  183. <b>Columns:</b><br>
  184. <ul>
  185. <li><b>name</b><br>
  186. name of the entity executing the procedure<br>
  187. if it is a function called by InternalTimer the name starts with <b>tmr-</b>.
  188. by then it gives the name of the funktion to be called<br>
  189. </li>
  190. <li><b>function</b><br>
  191. procedure name which was executed<br>
  192. if it is an InternalTimer call it gives its calling parameter <br>
  193. </li>
  194. <li><b>max</b><br>
  195. longest duration measured for this procedure in ms <br> </li>
  196. <li><b>count</b><br>
  197. number of calls for this procedure<br> </li>
  198. <li><b>total</b><br>
  199. accumulated duration of this procedure over all calls monitored<br> </li>
  200. <li><b>average</b><br>
  201. average time a call of this procedure takes<br> </li>
  202. <li><b>maxDly</b><br>
  203. maximum delay of a timer call to its schedules time. This column is not relevant
  204. for non-timer calls.<br> </li>
  205. <li><b>param Max call</b><br>
  206. gives the parameter of the call with the max duration<br> </li>
  207. </ul>
  208. <br>
  209. </ul>
  210. =end html
  211. =cut