Beefy Boxes and Bandwidth Generously Provided by pair Networks
Clear questions and runnable code
get the best and fastest answer
 
PerlMonks  

win32::daemon memory leak

by tbsky (Novice)
on Dec 22, 2015 at 09:49 UTC ( [id://1150938]=perlquestion: print w/replies, xml ) Need Help??

tbsky has asked for the wisdom of the Perl Monks concerning the following question:

hi:

I try to write windows service with win32::daemon. but I found the service will leak memory after running for long time.

it is caused by the callback event. the more callback is called, the more memory leaked.

I tried active perl and strawberry perl 5.18/5.22 64bit/32bit under winxp,win7,2008R2 but the situation is the same. 64bit is better but it still happen. I strip the code below to a "memory-leak" service. and tune the callback frequency to 30 ms to speed up the leak. so you can see leak after minutes/hours. hope someone can find out why this happened.

#!/usr/bin/perl use strict; use warnings; use Win32::Daemon; use Getopt::Long; my @cmd = @ARGV; my ($opt_g,$opt_h,$opt_i,$opt_r); if (! GetOptions('g=s' =>\$opt_g, 'h' => \$opt_h, 'i' => \$opt_i, 'r' +=>\$opt_r)){ print "command line wrong !!!\n"; exit(1); } if ($opt_h or ! @cmd){ print <<EOF; Usage: memory-leak [options] test perl service memory-leak -i install memory-leak Service -r remove memory-leak Service -h help message -g (internal use only) EOF exit(0); } # get program name our $fn = Win32::GetFullPathName($0); our ($cwd,$bn,$ext) = ($fn =~ /^(.*\\)(.*)\.(.*)$/ )[0..2]; if ($opt_i){ # install service # Determine service's path to executable based on file extension my ($path,$parameters); if ($ext eq "pl"){ # Source perl script - invoke perl interpreter $path = "\"$^X\""; # The command includes the -g go switch needed to enter servic +e mode $parameters = "\"$fn\" -g go"; }elsif ($ext eq "exe"){ # Compiled perl script - invoke the compiled script $path = "\"$fn\""; $parameters = "-g go"; }else{ # Invalid file type? print "Can not install service for $fn,file extension $ext not + supported\n"; exit(1); } # Populate the service configuration hash my %srv_config = ( name => "memory-leak", display => "memory-leak", path => $path, description => "memory-leak", parameters => $parameters, service_type => SERVICE_WIN32_OWN_PROCESS, start_type => SERVICE_AUTO_START ); # Install the service if (Win32::Daemon::CreateService(\%srv_config)){ print "memory-leak Service installed successfully\n"; }else{ print "Failed to install service: $!\n"; exit(1); } }elsif ($opt_r){ # remove service if (Win32::Daemon::DeleteService("memory-leak")){ print "memory-leak Service uninstalled successfully\n"; }else{ print "Failed to uninstall service: $!\n"; exit(1); } }elsif ($opt_g eq "go"){ Win32::Daemon::AcceptedControls(SERVICE_ACCEPT_STOP | SERVICE_ACCE +PT_SHUTDOWN); sleep(1); Win32::Daemon::RegisterCallbacks({ start => \&callback_start, timer => \&callback_running, stop => \&callback_stop, }) or die("register callback faild"); my $freq = 30; my %context = ( last_state => SERVICE_STOPPED, start_time => time(), ); Win32::Daemon::StartService(\%context,$freq); }else{ print "No valid options passed - nothing done\n"; } exit(0); sub callback_start{ my ($event,$context) = @_; $context->{last_state} = SERVICE_RUNNING; Win32::Daemon::State(SERVICE_RUNNING); return(SERVICE_RUNNING); } sub callback_running{ my ($event,$context) = @_; if ( SERVICE_CONTROL_TIMER == Win32::Daemon::State()){ } } sub callback_stop{ Win32::Daemon::State(SERVICE_STOPPED); sleep(2); Win32::Daemon::StopService(); return(SERVICE_STOPPED); }

Replies are listed 'Best First'.
Re: win32::daemon memory leak
by Mr. Muskrat (Canon) on Dec 22, 2015 at 15:51 UTC

    I haven't tried it yet but I did notice something that isn't quite right.

    if (Win32::Daemon::DeleteService("LHY-SMS-Transform")){

    I think you forgot to update this line when you converted it to a memory leak service.

      you are right. I forgot to update the line. I only test install the service and running it for leaking. I didn't test remove the service. I will update the code.
Re: Win32::Daemon memory leak
by Athanasius (Archbishop) on Dec 23, 2015 at 02:55 UTC

    Hello tbsky,

    Can you give example code for a service you run, together with the command sequence you issue to control service installation, etc.? So far, I’ve not been able to get your code to work — but that’s likely because I don’t know enough about the Win32::Daemon module. :-(

    In the meantime, perhaps you could try a module such as Test::LeakTrace. Here’s how I would proceed:

    • Wrap the top-level code into its own subroutine, say sub main
    • Change each exit statement into a return statement; for example, exit 1; becomes return 1;
    • Add the following at the top level:
      use Test::LeakTrace; { my $result; leaktrace { $result = main(); }; exit $result; }
    • Redirect program output to a log file, to facilitate analysis of the leak reports after the service has been stopped and uninstalled.

    BTW, the following isn’t very informative:

    print "Failed to install service: $!\n";

    From the documentation, the correct way to report failure is like this:

    print 'Failed to install service: ', Win32::FormatMessage(Win32::Daemon::GetLastError()), "\n";

    Also, you have use Fcntl;, but I don’t think it’s actually used anywhere, is it?

    Hope that helps,

    Athanasius <°(((><contra mundum Iustus alius egestas vitae, eros Piratica,

      hi:

      sorry the code is not clean. I have modify it. to run it please use the procedure below:

      1. use "memory-leak.pl -i" to install the "memory-leak" service to windows.

      2. use windows gui to start the service or use command "net start memory-leak" to start the service.

      3. use windows gui to watch the "perl.exe" process memory status.

      4. after testing. use windows gui to stop the service or use command "net stop memory leak" to stop the service.

      5. use "memory-leak.pl -r" to remove the service.

        I'm interested in the matter (abandoned services in Perl since the day of "service could not start in a timely fashion".. error)
        I have installed your service and now i'm monitoring it for the next 4 hours, with:
        #PID perl -E "for(1..240){system qq(tasklist /fi \"PID eq 17360\" /n +h);sleep 60}" # UPDATE: RESULTS perl.exe 17360 Services 0 + 9.136 K # 240x60 sec after.. perl.exe 17360 Services 0 + 16.768 K #it leaked 31.8K every minute

        UPDATE: The increment seems to me constant over time even if not so regular: The leak even if little is yet noticeable: the working set is passed fro 9.136K to 9.848K in 15 minutes. Private bytes also are arising. I'm using also Process Explorer. I think you need to investigate further. Have you some holidays? look at this article: identifying-memory-leak-with-process-explorer-and-windbg and share your results! in the meantime I have not enough time to investigate.
        I'll tell you more

        L*
        There are no rules, there are no thumbs..
        Reinvent the wheel, then learn The Wheel; may be one day you reinvent one of THE WHEELS.
Re: win32::daemon memory leak
by tbsky (Novice) on Dec 23, 2015 at 08:35 UTC
    hi:

    I try to rewrite the code with old style without callback. according to the win32::daemon document, this kind of style is obsolete and we should use callback.

    but the old style won't leak memory. code below. I named the service "memory-noleak".

    #!/usr/bin/perl use strict; use warnings; use Win32::Daemon; use Getopt::Long; my @cmd = @ARGV; my ($opt_g,$opt_h,$opt_i,$opt_r); if (! GetOptions('g=s' =>\$opt_g, 'h' => \$opt_h, 'i' => \$opt_i, 'r' +=>\$opt_r)){ print "command line wrong !!!\n"; exit(1); } if ($opt_h or ! @cmd){ print <<EOF; Usage: memory-noleak [options] test perl service memory-noleak -i install memory-noleak Service -r remove memory-noleak Service -h help message -g (internal use only) EOF exit(0); } # get program name our $fn = Win32::GetFullPathName($0); our ($cwd,$bn,$ext) = ($fn =~ /^(.*\\)(.*)\.(.*)$/ )[0..2]; if ($opt_i){ # install service # Determine service's path to executable based on file extension my ($path,$parameters); if ($ext eq "pl"){ # Source perl script - invoke perl interpreter $path = "\"$^X\""; # The command includes the -g go switch needed to enter servic +e mode $parameters = "\"$fn\" -g go"; }elsif ($ext eq "exe"){ # Compiled perl script - invoke the compiled script $path = "\"$fn\""; $parameters = "-g go"; }else{ # Invalid file type? print "Can not install service for $fn,file extension $ext not + supported\n"; exit(1); } # Populate the service configuration hash my %srv_config = ( name => "memory-noleak", display => "memory-noleak", path => $path, description => "memory-noleak", parameters => $parameters, service_type => SERVICE_WIN32_OWN_PROCESS, start_type => SERVICE_AUTO_START ); # Install the service if (Win32::Daemon::CreateService(\%srv_config)){ print "memory-noleak Service installed successfully\n"; }else{ print "Failed to install service: $!\n"; exit(1); } }elsif ($opt_r){ # remove service if (Win32::Daemon::DeleteService("memory-noleak")){ print "memory-noleak Service uninstalled successfully\n"; }else{ print "Failed to uninstall service: $!\n"; exit(1); } }elsif ($opt_g eq "go"){ my $SERVICE_SLEEP_TIME = 30; my $PrevState = SERVICE_START_PENDING; Win32::Daemon::StartService(); while( SERVICE_STOPPED != ( my $State = Win32::Daemon::State())){ if( SERVICE_START_PENDING == $State ){ # Initialization code Win32::Daemon::State( SERVICE_RUNNING ); $PrevState = SERVICE_RUNNING; }elsif( SERVICE_STOP_PENDING == $State ){ # "Stopping..."; Win32::Daemon::State( SERVICE_STOPPED ); $PrevState = SERVICE_STOPPED; next; }elsif( SERVICE_RUNNING == $State ){ }else{ Win32::Daemon::State( $PrevState ); } Win32::Sleep( $SERVICE_SLEEP_TIME ); } Win32::Daemon::StopService(); exit(0); }

Log In?
Username:
Password:

What's my password?
Create A New User
Domain Nodelet?
Node Status?
node history
Node Type: perlquestion [id://1150938]
Approved by Corion
Front-paged by Discipulus
help
Chatterbox?
and the web crawler heard nothing...

How do I use this?Last hourOther CB clients
Other Users?
Others perusing the Monastery: (5)
As of 2024-03-19 08:54 GMT
Sections?
Information?
Find Nodes?
Leftovers?
    Voting Booth?

    No recent polls found