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

Hi, Could someone please help me - I am getting an out of memory error with the following code and I am not sure why. Thanks.

#!/usr/local/bin/perl ###################################################################### +######## # ScriptName: PrfMon.pl + # # Author: Archna Saini + # # Date Created: 10/03/2005 + # # Purpose: 1. To schedule the start times of all the System + # # Performance subroutines. + # # + # # Usage: perl - w <ScriptName.pl> + # # + # # Version: 1.0 Initial Version + # ###################################################################### +######## # Load Modules. use strict; use Time::Local; use Carp ( ); eval { Carp::confess("init") }; use Win32::ODBC; # Enables ODBC database connectivity. use vars qw(%data $batch_db $data_db $datax_db $work_db $stage_db $cpc +t_db $vstage_db $vm_db $sproc_db $dcm_db $logon $unix_logon +$unix_password $util_logon); # Declare variables my $drive = "X:"; my $home = "$drive/development/common/"; my $dir = "$drive/development/perlib/code/Capacity_Management/"; my $script1 = 'PrfMon.pl'; my %hash; my $hash; my $rc; my $sql; my @pmsr; my $pmsr; my $sql1; my @date; my %date; # Load configuration data.'; my $config = "APOConfig.cfg"; Load_config("$home$config"); # Open ODBC session to database. my $odbc_session = new Win32::ODBC("$logon"); # SQL statement to select the values from the DAPO_CPCT_MGMT.DWH_Prfmn +_Rstrt table. $sql = " select DWH_Prfmn_Msr ,DWH_Strt_Tm ,DWH_Frqnc_Msr FROM $cpct_db.DWH_Prfmn_Rstrt ORDER BY 1 "; # Submit SQL through the ODBC session. $rc = $odbc_session->Sql("$sql"); # Check for SQL errors. if(defined $rc) { my $sql_error = $odbc_session->Error; my @sql_error = split /([-\[\]"])/,$sql_error; print "$sql\n"; print "SQL error: @sql_error[4,-2]\n"; exit; } # Retrieve answerset. while($odbc_session->FetchRow()) { # Stores the hash variable. my %pmsr = $odbc_session->DataHash(); push @pmsr, \%pmsr; # Pushes each value in the DWH_PrfMsr column i +nto the @pmsr. } # SQL statement to select the current date. $sql1 = " select '\$date{'''||trim(row_number() over (order by calendar_date) +) ||'''} = \"'||cast((calendar_date (format 'dd-mm-yyyy')) as cha +r(10))||'\";' from sys_calendar.calendar where calendar_date between current_date and current_date + 730 "; # Submit SQL through the ODBC session. $rc = $odbc_session->Sql("$sql1"); # Check for SQL errors. if(defined $rc) { my $sql_error = $odbc_session->Error; my @sql_error = split /([-\[\]"])/,$sql_error; print "$sql1\n"; print "SQL error: @sql_error[4,-2]\n"; exit; } # Retrieve answerset. while($odbc_session->FetchRow()) { # Stores the hash variable. eval $odbc_session->Data(); } for(;;) { foreach $pmsr(@pmsr) { # $pmsr->{'DWH_Prfmn_Msr'}; # print "$pmsr->{'DWH_Prfmn_Msr'}\n"; if (defined $pmsr->{'DWH_Strt_Tm'} && ${Datetime()}{'curr_hour'} = ${Datetime()("T","$pmsr->{'DWH_Str +t_Tm'}")}{'supp_hour'} && ${Datetime()}{'curr_min'} = 0 && ${Datetime()}{'curr_sec'} < 31) { system("$dir/$script1 $pmsr->{'DWH_Prfmn_Msr'}"); } if($pmsr->{'DWH_Frqnc_Msr'} != 0 && ${Datetime()}{'curr_epoch'} >= $pmsr->{'DWH_Nxt_Run'}) { $pmsr->{'DWH_Nxt_Run'} = ${Datetime()}{'curr_epoch'} + $pmsr->{' +DWH_Frqnc_Msr'}; system("$dir/$script1 $pmsr->{'DWH_Prfmn_Msr'}"); } } sleep 30; } exit 0; sub Datetime { # Declare variables. my %datehash; my $thedate; my $date; my $thetime; my $time; my $datetime; my $count = 0; # Accept Function Input. if(defined $_[0] and $_[0] eq 'T') { shift; $thetime = shift; } elsif(defined $_[0] and $_[0] eq 'D') { shift; $thedate = shift; } elsif(defined $_[0] and $_[0] eq 'B') { shift; $thedate = shift; $thetime = shift; } # Work out current date/time detail. my @datetime = ((localtime)[0,1,2,3,4,5]); foreach(@datetime) { if($count == 4) { $_ += 1 } elsif($count == 5) { $_ += 1900 } if(length $_ == 1) { $_ = "0$_"; } $count++; } my($sec,$min,$hour,$day,$month,$year) = @datetime; $datehash{'date'} = "$day-$month-$year"; $datehash{'time'} = "$hour:$min:$sec"; $datehash{'datetime'} = "$day-$month-$year $hour:$min:$sec"; $datehash{'curr_day'} = "$day"; $datehash{'curr_month'} = "$month"; $datehash{'curr_year'} = "$year"; $datehash{'curr_hour'} = "$hour"; $datehash{'curr_min'} = "$min"; $datehash{'curr_sec'} = "$sec"; $datehash{'curr_epoch'} = timelocal($sec,$min,$hour,$day,$month,$year) +; $hour = $hour * (60 * 60); $min = $min * 60; $datehash{'curr_time_sec'} = $hour + $min + $sec; # Work out supplied date detail. if(defined $thedate) { ($day,$month,$year) = (split /-/,$thedate); $datehash{'supp_day'} = "$day"; $datehash{'supp_month'} = "$month"; $datehash{'supp_year'} = "$year"; } # Work out supplied time detail. if(defined $thetime) { ($hour,$min,$sec) = (split /:/,$thetime); $datehash{'supp_time_epoch'} = timelocal( $sec, $min, $ho +ur, $datehash{'curr_day'}, $datehash{'curr_month'}, $datehash{'curr_year'}); if(defined $thedate) { $datehash{'supp_epoch'} = timelocal($sec, $min, $hour, $day, $ +month, $year); } $hour = $hour * (60 * 60); $min = (60 * 60); $datehash{'supp_hour'} = "$hour"; $datehash{'supp_min'} = "$min"; $datehash{'supp_sec'} = "$sec"; $datehash{'supp_time_sec'} = $hour + $min + $sec; } my $datehash = \%datehash; return $datehash; } # end Datetime. # Load configuration data. sub Load_config { # Accept function input. my $config_file = shift; # Load config file. do "$config_file"; # Convert config hash into scalar variables. while(my($key,$val) = each %data) { if($key eq 'databases') { while(my($key1,$val1) = each %$val) { unless(defined $val1) {next;} my $assign_val = "\L\$$key1"." = '\L$val1'"; eval $assign_val; } } else { unless(defined $val) {next;} my $assign_val = "\L\$$key"." = '$val'"; eval $assign_val; } } } # end Load_config. + + # Close ODBC session. $odbc_session->Close(); exit;

2005-03-11 Janitored by Arunbear - added readmore tags, as per Monastery guidelines

Replies are listed 'Best First'.
Re: out of memory!
by tall_man (Parson) on Mar 11, 2005 at 00:39 UTC
    This is very long. Would you please narrow it down a little? You could put in a few print statements and see how far it's getting.
      sorry about that - i am new to this. for testing purposes i have deleted the second sql statement (sql1 - which selects the current date). it will print out the first sql statement, but will give me the error (out of memory)when i run it with the foreach loop. hope that makes sense!
Re: out of memory!
by Mugatu (Monk) on Mar 11, 2005 at 00:58 UTC
    I agree with tall_man. You have posted a very long piece of code and asked for us to debug it for you. It would be better if you could show some effort and begin debugging it yourself. A simple way you could do this is to start taking out unnecessary parts of the code, until you have shrunken it down to just a few lines of code that produce the same problem you are describing. Then you could paste those few lines of code.
Re: out of memory!
by jasonk (Parson) on Mar 11, 2005 at 14:01 UTC

    Looks to me like you are creating an infinite loop (using for(;;) {) that contains a system() command which runs itself. An infinite loop spawning an infinite number of copies of itself is very likely to use up all the memory, and to do it rather quickly.


    We're not surrounded, we're in a target-rich environment!
Re: out of memory!
by graff (Chancellor) on Mar 12, 2005 at 19:21 UTC
    In addition to jasonk's very apt observation, I'd also wonder about this line:
    # Retrieve answerset. while($odbc_session->FetchRow()) { # Stores the hash variable. eval $odbc_session->Data(); }
    (Maybe it's just because I've never used odbc stuff -- but it looks like you're telling perl to execute something as code when it comes from a database. I'd be worried about that.)

    Also, maybe this is just a nit-pick, but I'd change this:

    while($odbc_session->FetchRow()) { # Stores the hash variable. my %pmsr = $odbc_session->DataHash(); ## push @pmsr, \%pmsr; # Pushes each value in the DWH_PrfMsr column i +nto the @pmsr. push @pmsr, {%pmsr}; # Pushes an anon.hash-ref copy of %pmsr onto + @pmsr. }
    (Of course, it's not clear to me why you're doing this anyway -- maybe you really should be doing something completely different in the first place.)

    As for nit-picks that have nothing to do with your question, there's probably a lot in the "Datetime" sub that could be done better; e.g. this:

    my @datetime = ((localtime)[0,1,2,3,4,5]); foreach(@datetime) { if($count == 4) { $_ += 1 } elsif($count == 5) { $_ += 1900 } if(length $_ == 1) { $_ = "0$_"; } $count++; } my($sec,$min,$hour,$day,$month,$year) = @datetime;
    could just be this:
    my($sec,$min,$hour,$day,$month,$year) = map {sprintf("%02d",$_)} (loca +ltime)[0..5]; $month++; $year += 1900;
    But there are probably a lot of other simplifications possible here -- including how this function is being used in the main part of the code (what you have is rather ugly and confusing).

    BTW, it would be nice if your indentation style were consistent (and more conventional -- cf. perldoc perlstyle).

    Oh, and did you notice that you have two different exit statements? And that the line to "Close ODBC session" comes after the first "exit" (hence is never reached)?