#!/usr/bin/env python
#
#  Copyright 2009 University Of Southern California
#
#  Licensed under the Apache License, Version 2.0 (the "License");
#  you may not use self file except in compliance with the License.
#  You may obtain a copy of the License at
#
#  http://www.apache.org/licenses/LICENSE-2.0
#
#  Unless required by applicable law or agreed to in writing,
#  software distributed under the License is distributed on an "AS IS" BASIS,
#  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
#  See the License for the specific language governing permissions and
#  limitations under the License.

"""Parses the output of ioprof and produces summary IO statistics."""

__author__ = "Gideon Juve <juve@usc.edu>"
__all__ = []
__version__ = "1.0"

import sys, os, re
from stats import Variable
from analysis import Analysis, sorteditems

class Process:
	def __init__(self,exe,pid):
		self.exe = exe
		self.pid = pid
		self.mread = 0.0
		self.mwrite = 0.0
		
class Executable:
	def __init__(self,xform,name):
		self.xform = xform
		self.name = name
		self.mread = Variable()
		self.mwrite = Variable()

class Transformation:
	def __init__(self,name):
		self.name = name
		self.mread = Variable()
		self.mwrite = Variable()

class IOAnalysis(Analysis):
	def __init__(self):
		self.exes = {}
		self.xforms = {}
		self.file_re = re.compile("\.err(\.[0-9]{3})?$")

	def print_stats(self):
		print ",,mread,,,,,,mwrite"
		print "transformation,executable,count,min,max,avg,stddev,sum,count,min,max,avg,stddev,sum"
		for exe in sorteditems(self.exes):
			print "%s,%s,%s,%s" % (exe.xform,exe.name,exe.mread,exe.mwrite)

		print "\n"

		print ",mread,,,,,,mwrite"
		print "transformation,count,min,max,avg,stddev,sum,count,min,max,avg,stddev,sum"
		for xform in sorteditems(self.xforms):
			print "%s,%s,%s" % (xform.name,xform.mread,xform.mwrite)
		
	def update_xform(self,xform_name,mread,mwrite):
		# Update transformation stats
		if xform_name in self.xforms:
			xform = self.xforms[xform_name]
		else:
			xform = Transformation(xform_name)
			self.xforms[xform_name] = xform
		xform.mread.update(mread)
		xform.mwrite.update(mwrite)
	
	def update_exe(self,xform_name,exe_name,mread,mwrite):
		id = xform_name+"$"+exe_name
		if id in self.exes:
			exe = self.exes[id]
		else:
			exe = Executable(xform_name,exe_name)
			self.exes[id] = exe
		exe.mread.update(mread)
		exe.mwrite.update(mwrite)

	def is_datafile(self, file):
		return self.file_re.search(file) is not None
	
	def process_datafile(self,file):
		f = open(file,'r')
		line = f.readline()
		while line:
			if 'xform' in line:
				self.process_transformation(f)
			line = f.readline()
		f.close()
			
	def process_transformation(self,file):
		xform_name = None
		xform_mread = 0.0
		xform_mwrite = 0.0
	
		lpid = None
	
		line = file.readline()
		while line:
			# New transformation encountered
			if 'xform' in line:
				file.seek(len(line) * -1, os.SEEK_CUR)
				break
			# Skip non-data lines
			if 'WARNING' in line:
				line = file.readline()
				continue

			#xform pid exe file bread nread bwrite nwrite nseek mode flags
			tok = line.split(' ',9)
			if len(tok) != 10:
				continue
			
			xform = tok[0]
			pid = tok[1]
			exe = os.path.basename(tok[2])
			fname = tok[3]
			mread = float(tok[4])/(1024*1024)
			mwrite = float(tok[6])/(1024*1024)
		
			# if new process was encountered
			if pid != lpid:
				if lpid is not None:
					self.update_exe(xform_name,exe_name,exe_mread,exe_mwrite) # process the old one
				exe_mread = 0.0
				exe_mwrite = 0.0
				lpid = pid
			
			xform_name = xform
			exe_name = exe
		
			# Skip non-files
			if fname in ['<stdin>','<stdout>','<stderr>','<socket>','<pipe>']:
				line = file.readline()
				continue
		
			# add up everything
			exe_mread += mread
			exe_mwrite += mwrite
			xform_mread += mread
			xform_mwrite += mwrite
		
			line = file.readline()
		#end of loop
		
		# process last exe
		if lpid is not None:
			self.update_exe(xform_name,exe_name,exe_mread,exe_mwrite)
	
		# process xform
		self.update_xform(xform_name,xform_mread,xform_mwrite)


if __name__ == '__main__':
	IOAnalysis().analyze()
