Device | Count | Time |
---|---|---|
$p | " . $stats{"$p"}{cnt} . " | " . $stats{"$p"}{time} . " |
"; return $ret; } } # return usage hint else { return $usage; } return undef; } ################################### sub freezemon_Attr($) { my ( $cmd, $name, $aName, $aVal ) = @_; my $hash = $defs{$name}; # $cmd can be "del" or "set" # $name is device name # aName and aVal are Attribute name and value #Log3 $name, 3, "$cmd $aName $aVal"; if ( $cmd eq "set" ) { if ( $aName eq "fm_forceApptime" ) { if ( $aVal > 1 or $aVal < 0 ) { Log3 $name, 3, "$name: $aName is either 0 or 1: $aVal"; return "Attribute " . $aName . " is either 0 or 1"; } } elsif ( $aName eq "fm_freezeThreshold" ) { if ( !looks_like_number($aVal) ) { return "Attribute " . $aName . " has to be a number (seconds) "; } } elsif ( $aName eq "fm_logKeep" ) { if ( !looks_like_number($aVal) or $aVal <= 0 ) { return "Attribute " . $aName . " has to be a number > 0"; } } elsif ( $aName eq "fm_logFile" ) { if ( $aVal ne "" ) { $aVal =~ m,^(.*)/([^/]*)$,; my $path = $1; $path =~ s/%L/$attr{global}{logdir}/g if ( $path =~ m/%/ && $attr{global}{logdir} ); if ( opendir( DH, $path ) ) { freezemon_install_log_wrapper($hash); closedir(DH); } else { return "Attribute " . $aName . ": $path is not a valid directory"; } } else { return "Attribute " . $aName . ": Enter a valid path or delete the attribute to disable."; } } elsif ( $aName eq "fm_CatchFnCalls" ) { if ( $aVal ne 0 ) { freezemon_install_callFn_wrapper($hash); $fmFnLog = $aVal; $fmName = $name; } elsif ( defined( $hash->{helper}{mycallFn} ) ) { Log3( "", 0, "[Freezemon] $name: Unwrapping CallFn" ); { no warnings; *main::CallFn = $hash->{helper}{mycallFn}; $hash->{helper}{mycallFn} = undef; } } else { Log3( "", 0, "[Freezemon] $name: Unwrapping CallFn - nothing to do" ); } } elsif ( $aName eq "fm_CatchHttp" ) { if ( $aVal ne 0 ) { freezemon_install_http_wrapper($hash); $fmHttpLog = $aVal; $fmName = $name; } elsif ( defined( $hash->{helper}{HttpUtils_NonblockingGet} ) ) { Log3( "", 0, "[Freezemon] $name: Unwrapping HttpUtils_NonblockingGet" ); { no warnings; *main::HttpUtils_NonblockingGet = $hash->{helper}{HttpUtils_NonblockingGet}; $hash->{helper}{HttpUtils_NonblockingGet} = undef; } } else { Log3( "", 0, "[Freezemon] $name: Unwrapping CallFn - nothing to do" ); } } elsif ( $aName eq "fm_CatchCmds" ) { if ( $aVal ne 0 ) { freezemon_install_AnalyzeCommand_wrapper($hash); $fmCmdLog = $aVal; $fmName = $name; } elsif ( defined( $hash->{helper}{AnalyzeCommand} ) ) { Log3( "", 0, "[Freezemon] $name: Unwrapping AnalyzeCommand" ); { no warnings; *main::AnalyzeCommand = $hash->{helper}{AnalyzeCommand}; $hash->{helper}{AnalyzeCommand} = undef; } } else { Log3( "", 0, "[Freezemon] $name: Unwrapping AnalyzeCommand - nothing to do" ); } } elsif ( $aName eq "disable" ) { if ( $aVal == 1 ) { RemoveInternalTimer($hash); readingsSingleUpdate( $hash, "state", "inactive", 1 ); $hash->{helper}{DISABLED} = 1; freezemon_unwrap_all($hash); } elsif ( $aVal == 0 ) { freezemon_start($hash); } elsif ( $aName eq "fm_statistics" ) { if ( $aVal == 1 ) { $hash->{helper}{statistics} = 1; } elsif ( $aVal == 0 ) { $hash->{helper}{statistics} = 0; } } } elsif ( $aName eq "fm_statistics_low" ) { if ( !( $aVal =~ /\d+\:\d+/ ) ) { return "Attribute " . $aName . " has to be in the format 1:2 (meaning count 1:seconds 2)"; } } } elsif ( $cmd eq "del" ) { if ( $aName eq "disable" ) { freezemon_start($hash); } elsif ( $aName eq "fm_logFile" ) { my $status = Log3( "", 100, "" ); Log3( "", 0, "[Freezemon] $name: Unwrapping Log3" ); *main::Log3 = $hash->{helper}{Log3}; $hash->{helper}{Log3} = undef; } elsif ( $aName eq "fm_CatchFnCalls" ) { Log3( "", 0, "[Freezemon] $name: Unwrapping CallFn" ); { no warnings; *main::CallFn = $hash->{helper}{mycallFn}; $hash->{helper}{mycallFn} = undef; } } elsif ( $aName eq "fm_CatchCmds" ) { Log3( "", 0, "[Freezemon] $name: Unwrapping AnalyzeCommand" ); { no warnings; *main::AnalyzeCommand = $hash->{helper}{AnalyzeCommand}; $hash->{helper}{AnalyzeCommand} = undef; } } elsif ( $aName eq "fm_CatchHttp" ) { Log3( "", 0, "[Freezemon] $name: Unwrapping HttpUtils_NonblockingGet" ); { no warnings; *main::HttpUtils_NonblockingGet = $hash->{helper}{HttpUtils_NonblockingGet}; $hash->{helper}{HttpUtils_NonblockingGet} = undef; } } elsif ( $aName eq "fm_statistics" ) { $hash->{helper}{statistics} = 0; } } return undef; } ################################### # Helper Functions # ################################### ################################### sub freezemon_start($) { my ($hash) = @_; my $name = $hash->{NAME}; if ( exists( $hash->{helper}{DISABLED} ) and $hash->{helper}{DISABLED} == 1 ) { readingsSingleUpdate( $hash, "state", "initialized", 0 ); freezemon_install_log_wrapper($hash) if AttrVal( $name, "fm_logFile", "" ) ne ""; freezemon_install_callFn_wrapper($hash) if AttrVal( $name, "fm_CatchFnCalls", 0 ) > 0; freezemon_install_AnalyzeCommand_wrapper($hash) if AttrVal( $name, "fm_CatchCmds", 0 ) > 0; } $fmName = $name; $fmCmdLog = AttrVal( $name, "fm_CatchCmds", 0 ); $fmFnLog = AttrVal( $name, "fm_CatchFnCalls", 0 ); $hash->{helper}{DISABLED} = 0; my $next = int( gettimeofday() ) + 1; $hash->{helper}{TIMER} = $next; InternalTimer( $next, 'freezemon_ProcessTimer', $hash, 0 ); Log3 $name, 2, "[Freezemon] $name: ready to watch out for delays greater than " . AttrVal( $name, "fm_freezeThreshold", 1 ) . " second(s)"; if ( AttrVal( $name, "fm_logExtraSeconds", undef ) ) { Log3 $name, 1, "[Freezemon] $name: Attribute fm_logExtraSeconds is deprecated and not considered anymore by Freezemon. Please delete the attribute."; } } ################################### sub freezemon_apptime($) { my ($hash) = @_; my $name = $hash->{NAME}; my $ret = ""; my ( $fn, $tim, $cv, $fnname, $arg, $shortarg ); if (%prioQueues) { foreach my $prio ( keys %prioQueues ) { foreach my $entry ( @{ $prioQueues{$prio} } ) { #Log3 $name, 5, "Freezemon: entry is ".Dumper($entry); $cv = svref_2object( $entry->{fn} ); $fnname = $cv->GV->NAME; $ret .= $fnname; #$shortarg = ( defined( $entry->{arg} ) ? $entry->{arg} : "" ); if ( defined( $entry->{arg} ) ) { $shortarg = $entry->{arg}; #Log3 $name, 5, "Freezemon: found a prioQueue arg ".ref($shortarg); if ( ref($shortarg) eq "HASH" ) { if ( !defined( $shortarg->{NAME} ) ) { $shortarg = "N/A"; } else { $shortarg = $shortarg->{NAME}; } } elsif ( ref($shortarg) eq "ARRAY" ) { $shortarg = $entry->{arg}; } ( $shortarg, undef ) = split( /:|;/, $shortarg, 2 ); } $shortarg = "N/A" unless defined($shortarg); $ret .= ":" . $shortarg . " "; #Log3 $name, 5, "Freezemon: found a prioQueue, returning $ret"; } } } return $ret; } ################################### sub freezemon_getDevice($$) { my ( $hash, $d ) = @_; my $name = $hash->{NAME}; my $fn = $d->{FN}; if ( ref($fn) ne "" ) { my $cv = svref_2object($fn); my $fnname = $cv->GV->NAME; return $fnname; } my $arg = $d->{ARG}; my $shortarg = ( defined($arg) ? $arg : "" ); if ( ref($shortarg) eq "HASH" ) { if ( !defined( $shortarg->{NAME} ) ) { if ( AttrVal( $name, "fm_extDetail", 0 ) == 1 ) { if ( $fn eq "BlockingKill" or $fn eq "BlockingStart" ) { $shortarg = $shortarg->{abortArg}{NAME} if defined( $shortarg->{abortArg}{NAME} ); } elsif ( $fn eq "HttpUtils_Err" ) { #Log3 $name, 5, "[Freezemon] HttpUtils_Err found" . Dumper($shortarg); if ( ref( $shortarg->{hash}{hash} ) eq "HASH" and defined( $shortarg->{hash}{hash}{NAME} ) ) { $shortarg = $shortarg->{hash}{hash}{NAME}; } } elsif ( $fn = "FileLog_dailySwitch" ) { $shortarg = $shortarg->{NotifyFn}; } else { #Log3 $name, 5, "[Freezemon] $name found something without a name $fn" . Dumper($shortarg); $shortarg = "N/A"; } } else { $shortarg = "N/A"; } } else { $shortarg = $shortarg->{NAME}; } } elsif ( ref($shortarg) eq "REF" ) { if ( $fn eq "DOIF_TimerTrigger" ) { my $deref = ${$arg}; #seems like $arg is a reference to a scalar which in turm is a reference to a hash $shortarg = $deref->{'hash'}{NAME}; #at least in DOIF_TimerTrigger } else { $shortarg = "N/A"; #added 06.01.2020 (0.0.30) Forum topic,83909.msg1009807.html#msg1009807 #Log3 $name, 5, "[Freezemon] $name found a REF $fn " . Dumper( ${$arg} ); } } elsif ( ref($shortarg) eq "" ) { #Log3 $name, 5, # "[Freezemon] $name found something that's not a REF $fn " . ref($shortarg) . " " . Dumper($shortarg); ( undef, $shortarg ) = split( /:|;/, $shortarg, 2 ); } else { Log3 $name, 5, "[Freezemon] $name found something that's a REF but not a HASH $fn " . ref($shortarg); #. Dumper($shortarg); $shortarg = "N/A"; } if ( !defined($shortarg) ) { Log3 $name, 5, "Freezemon: something went wrong $fn "; # . Dumper($arg); $shortarg = "N/A"; } else { ( $shortarg, undef ) = split( /:|;/, $shortarg, 2 ); } return $shortarg; } ################################### sub freezemon_unwrap_all($) { my ($hash) = @_; my $name = $hash->{NAME}; Log3( "", 0, "[Freezemon] $name: Unwrapping CallFn" ); { no warnings; *main::CallFn = $hash->{helper}{mycallFn} if defined( $hash->{helper}{mycallFn} ); $hash->{helper}{mycallFn} = undef; } Log3( "", 0, "[Freezemon] $name: Unwrapping AnalyzeCommand" ); { no warnings; *main::AnalyzeCommand = $hash->{helper}{AnalyzeCommand} if defined( $hash->{helper}{AnalyzeCommand} ); $hash->{helper}{AnalyzeCommand} = undef; } Log3( "", 0, "[Freezemon] $name: Unwrapping HttpUtils_NonblockingGet" ); { no warnings; *main::HttpUtils_NonblockingGet = $hash->{helper}{HttpUtils_NonblockingGet} if defined( $hash->{helper}{HttpUtils_NonblockingGet} ); $hash->{helper}{HttpUtils_NonblockingGet} = undef; } my $status = Log3( "", 100, "" ); Log3( "", 0, "[Freezemon] $name: Unwrapping Log3" ); { no warnings; *main::Log3 = $hash->{helper}{Log3} if defined( $hash->{helper}{Log3} ); $hash->{helper}{Log3} = undef; } } ################################### sub freezemon_callFn($@) { my ( $lfn, @args ) = @_; # take current time, then immediately call the original function my $t0 = [gettimeofday]; my ( $result, $p ) = $lfn->(@args); my $ms = tv_interval($t0); if ( $ms >= 0.5 ) { my $d = $args[0]; my $n = $args[1]; push @fmFn, [ $n, $d ]; #$fm_fn .= "$n:$d "; Log3 $fmName, $fmFnLog, "[Freezemon] $fmName: Long function call detected $n:$d - $ms seconds"; } return ( $result, $p ) if ($p); return $result; } ################################### sub freezemon_AnalyzeCommand($$$;$) { my ( $lfn, $cl, $cmd, $cfc ) = @_; # take current time, then immediately call the original function my $t0 = [gettimeofday]; my $result = $lfn->( $cl, $cmd, $cfc ); my $ms = tv_interval($t0); if ( $ms >= 0.5 ) { my $d = ""; my $n = $cmd; if ( exists( $cl->{SNAME} ) ) { $d = $cl->{SNAME}; } else { $d = "N/A"; } push @fmCmd, [ $n, $d ]; #$fm_fn .= "$n:$d "; Log3 $fmName, $fmCmdLog, "[Freezemon] $fmName: Long running Command detected $n:$d - $ms seconds"; } #return ($result,$p) if ($p) ; return $result; } ################################### sub freezemon_checkCallFnWrap() { return "freezemon called"; } ################################### sub freezemon_Log3($$$$) { my ( $lfn, $dev, $loglevel, $text ) = @_; # take current time, then immediately call the original log function my ( $seconds, $microseconds ) = gettimeofday(); my $result = $lfn->( $dev, $loglevel, $text ); my @entry = ( $seconds + $microseconds * 1e-6, $dev, $loglevel, $text ); push( @logqueue, \@entry ) unless ( $loglevel > 5 ); # print LOG "logqueue has now ".(scalar @logqueue)." entries"; return $result; } ################################### sub freezemon_http($$) { my ( $lfn, $param ) = @_; if ( ref($param) eq "HASH" ) { if ( !exists( $param->{fm_originalCallback} ) ) { $param->{fm_originalCallback} = $param->{callback}; $param->{callback} = \&freezemon_http_Callback; Log3 $fmName, 5, "[Freezemon] $fmName: switching callback from $param->{fm_originalCallback} to $param->{callback}"; } $lfn->($param); } else { Log3 $fmName, 3, "[Freezemon] $fmName: Noticed something wrong: " . Dumper($lfn) . "---" . Dumper($param); } return undef; } ################################### sub freezemon_http_Callback($) { my ( $param, $err, $data ) = @_; # take current time, then immediately call the original function my $t0 = [gettimeofday]; my $callback = $param->{fm_originalCallback}; $param->{callback} = $param->{fm_originalCallback}; Log3 $fmName, 5, "[Freezemon] $fmName: Calling original sub $callback"; $param->{callback}( $param, $err, $data ); my $ms = tv_interval($t0); if ( $ms >= 0.5 ) { my $cv = svref_2object( $param->{callback} ); my $name = "N/A"; if ( ref($param) eq "HASH" ) { $name = $param->{hash}{NAME} if ( ref( $param->{hash} ) eq "HASH" and defined( $param->{hash}{NAME} ) ); } my $gv = $cv->GV; my $cb = $gv->NAME; push @fmHttp, [ $cb, $name ]; } return undef; } ################################### sub freezemon_wrap_callFn($) { my ($fn) = @_; return sub(@) { my @a = @_; return "already wrapped" if $a[1] eq "freezemon_checkCallFnWrap"; return freezemon_callFn( $fn, @a ); } } ################################### sub freezemon_wrap_http($) { my ($fn) = @_; return sub($) { my ($param) = @_; return "already wrapped" if ( defined( $param->{url} ) && $param->{url} eq "file://freezemon_wrap.txt" ); return freezemon_http( $fn, $param ); } } ################################### sub freezemon_wrap_AnalyzeCommand($) { my ($fn) = @_; return sub($$;$) { my ( $cl, $cmd, $cfc ) = @_; return "already wrapped" if ( defined($cl) && $cl eq "freezemon" ); return freezemon_AnalyzeCommand( $fn, $cl, $cmd, $cfc ); } } ################################### sub freezemon_wrap_Log3($) { my ($fn) = @_; return sub($$$) { my ( $a, $b, $c ) = @_; return "already wrapped" if ( $b == 99 ); return $fn if ( $b == 100 ); return freezemon_Log3( $fn, $a, $b, $c ); } } ################################### #AnalyzeCommand($$;$) sub freezemon_install_AnalyzeCommand_wrapper($;$) { my ( $hash, $nolog ) = @_; my $name = $hash->{NAME}; $name = "FreezeMon" unless defined($name); my $status = AnalyzeCommand( "freezemon", "" ); if ( !defined($status) || $status ne "already wrapped" ) { $hash->{helper}{AnalyzeCommand} = \&AnalyzeCommand; Log3( "", 3, "[Freezemon] $name: Wrapping AnalyzeCommand" ); { no warnings; *main::AnalyzeCommand = freezemon_wrap_AnalyzeCommand( \&AnalyzeCommand ); } } elsif ( !defined($nolog) ) { Log3 $name, 3, "[Freezemon] $name: AnalyzeCommand already wrapped"; } } ################################### sub freezemon_http_wrapper_Callback($) { my ( $param, $err, $data ) = @_; my $hash = $param->{hash}; my $name = $hash->{NAME}; if ( $err =~ /freezemon_wrap.txt/ ) { $hash->{helper}{HttpUtils_NonblockingGet} = \&HttpUtils_NonblockingGet; Log3( "", 3, "[Freezemon] $name: Wrapping HttpUtils_NonblockingGet" ); { no warnings; *main::HttpUtils_NonblockingGet = freezemon_wrap_http( \&HttpUtils_NonblockingGet ); } } else { Log3 $name, 3, "[Freezemon] $name: HttpUtils_NonblockingGet already wrapped"; } } ################################### sub freezemon_install_http_wrapper($;$) { my ( $hash, $nolog ) = @_; my $name = $hash->{NAME}; $name = "FreezeMon" unless defined($name); my $param = { url => "file://freezemon_wrap.txt", hash => $hash, callback => \&freezemon_http_wrapper_Callback }; HttpUtils_NonblockingGet($param); } ################################### sub freezemon_install_callFn_wrapper($;$) { my ( $hash, $nolog ) = @_; my $name = $hash->{NAME}; $name = "FreezeMon" unless defined($name); my $status = CallFn( $name, "freezemon_checkCallFnWrap" ); if ( !defined($status) || $status ne "already wrapped" ) { $hash->{helper}{mycallFn} = \&CallFn; Log3( "", 3, "[Freezemon] $name: Wrapping CallFn" ); { no warnings; *main::CallFn = freezemon_wrap_callFn( \&CallFn ); } } elsif ( !defined($nolog) ) { Log3 $name, 3, "[Freezemon] $name: CallFn already wrapped"; } } ################################### sub freezemon_install_log_wrapper($) { my ($hash) = @_; my $name = $hash->{NAME}; $name = "FreezeMon" unless defined($name); my $status = Log3( "", 99, "" ); if ( !defined($status) || $status ne "already wrapped" ) { Log3( "", 3, "[Freezemon] $name: Wrapping Log3" ); $hash->{helper}{Log3} = \&Log3; { no warnings; *main::Log3 = freezemon_wrap_Log3( \&Log3 ); } } else { Log3 $name, 5, "[Freezemon] $name: Log3 is already wrapped"; } } ################################### sub freezemon_purge_log_before($$) { my ( $hash, $before ) = @_; my $name = $hash->{NAME}; my @t = localtime($before); my $tim = sprintf( "%04d.%02d.%02d %02d:%02d:%02d.%03d", $t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0], 0 ); Log3 $hash, 5, "[Freezemon] $name: purging log entries before $tim."; my $cnt = 0; while ( scalar @logqueue > 0 && $logqueue[0]->[0] < $before ) { shift @logqueue; $cnt += 1; } #Log3 $hash, 5, "[Freezemon] $name: $cnt entries purged from logqueue, size is now ".(scalar @logqueue); } ################################### sub freezemon_dump_log2($$$$) { my ( $name, $msg, $logfile, $queue ) = @_; #my $name = $hash->{NAME}; #my @queue = @{ $hash->{helper}{logqueue} }; return unless scalar @$queue; my ( $seconds, $microseconds ) = gettimeofday(); my $currlogfile = $logfile; return unless defined($currlogfile) && $currlogfile ne ""; Log3 $name, 4, "[Freezemon] $name: dumping " . ( scalar @$queue ) . " log entries to $currlogfile"; open( fm_LOG, ">>$currlogfile" ) || return ("Can't open $currlogfile: $!"); print fm_LOG "=========================================================\n"; print fm_LOG $msg . "\n"; my $last_ts; foreach my $entry (@$queue) { my ( $ts, $dev, $loglevel, $text ) = @$entry; my $seconds = int($ts); my $microseconds = int( 1e6 * ( $ts - $seconds ) ); $dev = $dev->{NAME} if ( defined($dev) && ref($dev) eq "HASH" ); #next if ( defined($dev) && ( $dev eq $name ) ); my @t = localtime($seconds); my $tim = sprintf( "%04d.%02d.%02d %02d:%02d:%02d.%03d", $t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0], $microseconds / 1000 ); printf fm_LOG "--- log skips %9.3f secs.\n", $ts - $last_ts if ( defined($last_ts) && $ts - $last_ts > 1 ); print fm_LOG "$tim $loglevel: $text\n"; $last_ts = $ts; } print fm_LOG $msg . "\n"; close(fm_LOG); return $currlogfile; } ################################### sub freezemon_dump_log($$$) { my ( $hash, $start, $msg ) = @_; my $name = $hash->{NAME}; my @queue = @{ $hash->{helper}{logqueue} }; return unless scalar @queue; my ( $seconds, $microseconds ) = gettimeofday(); my $currlogfile = $hash->{helper}{logfile}; return unless defined($currlogfile) && $currlogfile ne ""; Log3 $name, 4, "[Freezemon] $name: dumping " . ( scalar @queue ) . " log entries to $currlogfile"; open( fm_LOG, ">>$currlogfile" ) || return ("Can't open $currlogfile: $!"); print fm_LOG "=========================================================\n"; print fm_LOG $msg . "\n"; my $last_ts; foreach my $entry (@queue) { my ( $ts, $dev, $loglevel, $text ) = @$entry; my $seconds = int($ts); my $microseconds = int( 1e6 * ( $ts - $seconds ) ); $dev = $dev->{NAME} if ( defined($dev) && ref($dev) eq "HASH" ); #next if ( defined($dev) && ( $dev eq $name ) ); my @t = localtime($seconds); my $tim = sprintf( "%04d.%02d.%02d %02d:%02d:%02d.%03d", $t[5] + 1900, $t[4] + 1, $t[3], $t[2], $t[1], $t[0], $microseconds / 1000 ); printf fm_LOG "--- log skips %9.3f secs.\n", $ts - $last_ts if ( defined($last_ts) && $ts - $last_ts > 1 ); print fm_LOG "$tim $loglevel: $text\n"; $last_ts = $ts; } print fm_LOG $msg . "\n"; close(fm_LOG); return $currlogfile; } ################################### sub freezemon_logLink($$) { my ( $name, $link ) = @_; return "" if !$link; my $me; if ( defined($FW_ME) ) { $me = $FW_ME; } else { $me = "fhem"; } my $ret = " [Log]"; return $ret; } ################################### sub freezemon_getLogFiles($;$) { my ( $name, $reverse ) = @_; my @fl; my $path = freezemon_getLogPath($name); return @fl if ( !$path ); my $lf = AttrVal( $name, "fm_logFile", "" ); $lf =~ m,^(.*)/([^/%]*).*$,; my $pattern = $2; if ( opendir( DH, $path ) ) { while ( my $f = readdir(DH) ) { push( @fl, $f ) if ( $f =~ /$pattern.*/ ); } closedir(DH); if ( !$reverse ) { @fl = sort { ( CORE::stat("$path/$b") )[9] <=> ( CORE::stat("$path/$a") )[9] } @fl; } else { @fl = sort { ( CORE::stat("$path/$a") )[9] <=> ( CORE::stat("$path/$b") )[9] } @fl; } } return @fl; } ################################### sub freezemon_getLogPath($) { my ($name) = @_; my $lf = AttrVal( $name, "fm_logFile", "" ); return undef if $lf eq ""; $lf =~ m,^(.*)/([^/%]*).*$,; my $path = $1; $path =~ s/%L/$attr{global}{logdir}/g if ( $path =~ m/%/ && $attr{global}{logdir} ); return $path; } 1; =pod =item helper =item summary An adjusted version of PERFMON that helps detecting freezes =item summary_DE Eine angepasste Version von PERFMON, die beim Erkennen von freezes hilft =begin html
jump to the end
"; while ( my $row = <$fh> ) { $ret .= $row . "
"; } $ret .= "
jump to the top
define <devicename> freezemon
define <devicename> freezemon