# $Header: emdb/sysman/admin/scripts/db/hanganalyze.pl /st_emdbsa_11.2/1 2011/04/28 01:41:28 pbhogara Exp $
#
# hanganalyze.pl
# 
# Copyright (c) 2004, 2011, Oracle and/or its affiliates. All rights reserved. 
#
#    NAME
#      hanganalyze.pl - Script for hang analysis
#
#    DESCRIPTION
#      This script invokes ORADEBUG HANGANALYZE and returns
#      the <blocking-session, blocked-session> pairs
#
#    NOTES
#      -
#
#    MODIFIED   (MM/DD/YY)
#    pbhogara    04/26/11 - Setting LD_LIBRARY_PATH_64 for solaris 64 bit
#                           platforms
#    rtakeish    03/28/07 - bug5940949, hanganalysis
#                           doesn't work for rac
#    jsoule      05/31/05 - get trace file properly 
#    zsyed       04/19/05 - Fixing GC new issue for GRID AGENT 
#    zsyed       02/02/05 - Fixing new break 
#    zsyed       11/22/04 - Removing references to emdw 
#    zsyed       10/27/04 - Retrieving OS pid 
#    zsyed       06/22/04 - Creation
#

use strict;

require "emd_common.pl";

my $oracle_home = trim($ARGV[0]);
my $oracle_sid  = trim($ARGV[1]);
my $sqldir      = trim($ARGV[2]);

# To enable cluster wide information
my $analysisAreaOption = trim($ARGV[3]);
my $analysisAreaLocal  = 'local';

if (!$analysisAreaOption)
{
  $analysisAreaOption = $analysisAreaLocal;
}

################################
# Produce an ORADEBUG HANGANALYZE trace file and get its name.
# Also record ORA- errors.
################################

my $trace_file = '';
my @errors;

{
  # Locally instantiate the relevant pieces of the ENV array so that SQL*Plus
  #  is spawned in the correct context.
  # Override the ORACLE_HOME, ORACLE_SID, LD_LIBRARY_PATH env variables.
  local $ENV{'ORACLE_HOME'}     = $oracle_home;
  local $ENV{'ORACLE_SID'}      = $oracle_sid;
  local $ENV{'LD_LIBRARY_PATH'} = $oracle_home."/lib:".$ENV{'LD_LIBRARY_PATH'};

  # On some 64 bit platforms this is needed
  if(defined($ENV{'LD_LIBRARY_PATH_64'}))
  {
    $ENV{'LD_LIBRARY_PATH_64'} = $oracle_home."/lib:".$ENV{'LD_LIBRARY_PATH_64'};
  }

  # SQL Files for hanganalyze
  my $sqlcommfile     = $sqldir."hanganalyze.sql";
  my $sqlcommracfile  = $sqldir."hanganalyzerac.sql";

  # Open a pipe from SQL*Plus and accept its output.
  if ( $analysisAreaOption eq $analysisAreaLocal )
  {
    open(SQLPLUS_OUTPUT, "$oracle_home/bin/sqlplus -s \"/ as sysdba\" < $sqlcommfile |");
  }
  else
  {
    open(SQLPLUS_OUTPUT, "$oracle_home/bin/sqlplus -s \"/ as sysdba\" < $sqlcommracfile |");
  }

}
while (<SQLPLUS_OUTPUT>)
{
  # Look for the line matching "Hang Analysis in <my trace file>"
  if (/Hang Analysis in (.*)/)
  {
    $trace_file = $1;
    last;
  }
  elsif (/(ORA-\d+:.*)/)
  {
    # Gather up errors in case no trace file can be found.
    $errors[++$#errors] = $1;
  }
}
close(SQLPLUS_OUTPUT);

if (!$trace_file)
{
  my $err_msg = "ORADEBUG HANGANALYZE could not be run";
  if ($#errors)
  {
    $err_msg .= " [" . join(', ',@errors) . "]";
  }
  die $err_msg;
}
elsif (EMAGENT_isPerlDebugEnabled())
{
  EMAGENT_PERL_DEBUG("ORADEBUG HANGANALYZE trace file: $trace_file");
}

################################
# Open file and read content lines into an array
################################

open(INFO, $trace_file) or die "Cannot open file $trace_file";
my @waitfor_lines = ();
my @tmp_waitfor_lines = ();
my $check_end = 0;
while (<INFO>)
{
  while (<INFO>)
  {
    if (/State of nodes/)
    {
      $check_end = 0;

      # keep old waitfor_line
      @tmp_waitfor_lines = @waitfor_lines;

      # clear old waitfor_line to get new one
      @waitfor_lines = ();

      # skip all lines upto and including this one
      last;
    }
  }
  while (<INFO>)
  {
    if (/END OF HANG ANALYSIS/)
    {
      $check_end = 1;

      # back out the one before this match...
      if ( $analysisAreaOption eq $analysisAreaLocal )
      {
        $#waitfor_lines--;
      }
      else
      {
        $#waitfor_lines = $#waitfor_lines - 3;
      }

      # ...and break
      last;
    }
    else
    {
      # this is a content line; take off the trailing '\n'...
      chop;

      # ...and add it to the list
      $waitfor_lines[++$#waitfor_lines] = $_;
    }
  }
  if ( $analysisAreaOption eq $analysisAreaLocal )
  {
    last;
  }
}
close(INFO);

# If "END OF HANG ANALYSIS" is not found before end of file,
# back to the last waitfor_lines
if ( $check_end == 0 )
{
  @waitfor_lines = @tmp_waitfor_lines;
}

################################
# Find positions of columns of interest
################################

my $i = 0;
my $nodenumcol = 0;
my $cnodecol = -1;
my $sidcol = 0;
my $sesssrncol = 0;
my $sesscol = 0;
my $adjlistcol = 0;
my $predlistcol = 0;
my $pidcol = 0;

my $colname;
my @colnames = split("/", $waitfor_lines[0]);

foreach $colname (@colnames)
{
    if($colname =~ /nodenum/)
    {
	$nodenumcol = $i;
    }
    elsif($colname =~ /cnode/)
    {
        $cnodecol = $i;
    }
    elsif($colname =~ /ospid/)
    {
        $pidcol = $i;
    }
    elsif($colname =~ /sid/)
    {
	$sidcol = $i;
    }
    elsif($colname =~ /adjlist/)
    {
	$adjlistcol = $i;
    }
    elsif($colname =~ /sess_srno/)
    {
	$sesssrncol = $i;
    }
    elsif($colname =~ /session/)
    {
	$sesscol = $i;
    }
    elsif($colname =~ /predecessor/)
    {
	$predlistcol = $i;
    }
    
    $i++;
}

################################
# Return data
################################

# First write number of total sessions found
print "em_result=$#waitfor_lines|";

my $count;
my $adj_entry;
my $ospid;
my $instNum;
my @colvalues;

# Then only return blocked/blocking sessions with columns of interest being
# communicated.
for($count = 1; $count < @waitfor_lines; $count++)
{
  @colvalues = split("/", $waitfor_lines[$count]);
    
  # Return only blocked or stuck sessions.
  # Blocked sessions have a non-"none" predecessor list.
  # Root blocking sessions have no adjacency list.
  if ($colvalues[$adjlistcol] || ($colvalues[$predlistcol] ne "none"))
  {
    # If session is a root blocker, return "none" as adjacency list element
    if (!$colvalues[$adjlistcol])
    {
      $adj_entry = "none";
    }
    else
    {
      $adj_entry = $colvalues[$adjlistcol];
    }

    # If ospid is on other node, return "Unavailable" instead of ""
    if (!$colvalues[$pidcol])
    {
      $ospid = "Unavailable";
    }
    else
    {
      $ospid = $colvalues[$pidcol];
    }

    # If cnode is null, return -1
    # If not, return istance_number = cnode + 1
    if ( $cnodecol == -1 )
    {
      $instNum = -1;
    }
    else
    {
      $instNum = $colvalues[$cnodecol] + 1;
    }
    
    # Return nodenum, sid, serial#, address, blockers
    print "$colvalues[$nodenumcol],";
    print "$colvalues[$sidcol],";
    print "$colvalues[$sesssrncol],";
    print "$colvalues[$sesscol],";
    print "$adj_entry,";
    print "$ospid,";
    print "$instNum|";
  }
}
print "\n";

################################
# If $analysisAreaOption eq $analysisAreaLocal, remove ORADEBUG HANGANALYZE trace file
#
# NOTE: <instance>_diag_<process id>.trc for cluster wide information
#       is overwrited until ora_diag_<instance> is down.
#       And, other user may use the file.
#       So, remove the file only if $analysisAreaOption eq $analysisAreaLocal
################################

if ( $analysisAreaOption eq $analysisAreaLocal )
{
  unlink($trace_file);
}

sub trim {
 my $string = shift(@_);
 $string =~ s/\s//g;
 return $string;
}

exit 0;

