mirror of
				https://github.com/python/cpython.git
				synced 2025-10-31 13:41:24 +00:00 
			
		
		
		
	 e61fa0a1e4
			
		
	
	
		e61fa0a1e4
		
	
	
	
	
		
			
			* builtin.py: b/w compat for builtin -> __builtin__ name change * string.py: added atof() and atol() and corresponding exceptions * test_types.py: added test for list sort with user comparison function
		
			
				
	
	
		
			391 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
			
		
		
	
	
			391 lines
		
	
	
	
		
			9.9 KiB
		
	
	
	
		
			Python
		
	
	
		
			Executable file
		
	
	
	
	
| #
 | |
| # Class for profiling python code.
 | |
| # Author: Sjoerd Mullender
 | |
| # Hacked somewhat by: Guido van Rossum
 | |
| #
 | |
| # See the accompanying document profile.doc for more information.
 | |
| 
 | |
| import sys
 | |
| import codehack
 | |
| import os
 | |
| import string
 | |
| import fpformat
 | |
| import marshal
 | |
| 
 | |
| class Profile:
 | |
| 
 | |
| 	def init(self):
 | |
| 		self.timings = {}
 | |
| 		self.debug = None
 | |
| 		self.call_level = 0
 | |
| 		self.profile_func = None
 | |
| 		self.profiling = 0
 | |
| 		return self
 | |
| 
 | |
| 	def profile(self, funcname):
 | |
| 		if not self.profile_func:
 | |
| 			self.profile_func = {}
 | |
| 		self.profile_func[funcname] = 1
 | |
| 
 | |
| 	def trace_dispatch(self, frame, event, arg):
 | |
| 		if event == 'call':
 | |
| 			funcname = codehack.getcodename(frame.f_code)
 | |
| 			if self.profile_func and not self.profiling:
 | |
| 				if self.profile_func.has_key(funcname):
 | |
| 					return
 | |
| 				self.profiling = 1
 | |
| 			t = os.times()
 | |
| 			t = t[0] + t[1]
 | |
| 			if frame.f_locals.has_key('__key'):
 | |
| 				key = frame.f_locals['__key']
 | |
| 			else:
 | |
| 				lineno = codehack.getlineno(frame.f_code)
 | |
| 				filename = frame.f_code.co_filename
 | |
| 				key = filename + ':' + `lineno` + '(' + funcname + ')'
 | |
| 				frame.f_locals['__key'] = key
 | |
| 			self.call_level = depth(frame)
 | |
| 			self.cur_frame = frame
 | |
| 			pframe = frame.f_back
 | |
| 			if self.debug:
 | |
| 				s0 = 'call: ' + key + ' depth: ' + `self.call_level` + ' time: ' + `t`
 | |
| 			if pframe:
 | |
| 				if pframe.f_locals.has_key('__key'):
 | |
| 					pkey = pframe.f_locals['__key']
 | |
| 				else:
 | |
| 					pkey = pframe.f_code.co_filename + \
 | |
| 						  ':' + \
 | |
| 						  `codehack.getlineno(pframe.f_code)` \
 | |
| 						  + '(' + \
 | |
| 						  codehack.getcodename(pframe.f_code) \
 | |
| 						  + ')'
 | |
| 					pframe.f_locals['__key'] = pkey
 | |
| 				if self.debug:
 | |
| 					s1 = 'parent: ' + pkey
 | |
| 				if pframe.f_locals.has_key('__start_time'):
 | |
| 					st = pframe.f_locals['__start_time']
 | |
| 					nc, tt, ct, callers, callees = \
 | |
| 						self.timings[pkey]
 | |
| 					if self.debug:
 | |
| 						s1 = s1+' before: st='+`st`+' nc='+`nc`+' tt='+`tt`+' ct='+`ct`
 | |
| 					if callers.has_key(key):
 | |
| 						callers[key] = callers[key] + 1
 | |
| 					else:
 | |
| 						callers[key] = 1
 | |
| 					if self.debug:
 | |
| 						s1 = s1+' after: st='+`st`+' nc='+`nc`+' tt='+`tt+(t-st)`+' ct='+`ct`
 | |
| 					self.timings[pkey] = nc, tt + (t - st), ct, callers, callees
 | |
| 			if self.timings.has_key(key):
 | |
| 				nc, tt, ct, callers, callees = self.timings[key]
 | |
| 			else:
 | |
| 				nc, tt, ct, callers, callees = 0, 0, 0, {}, {}
 | |
| 			if self.debug:
 | |
| 				s0 = s0+' before: nc='+`nc`+' tt='+`tt`+' ct='+`ct`
 | |
| 				s0 = s0+' after: nc='+`nc+1`+' tt='+`tt`+' ct='+`ct`
 | |
| 			if pframe:
 | |
| 				if callees.has_key(pkey):
 | |
| 					callees[pkey] = callees[pkey] + 1
 | |
| 				else:
 | |
| 					callees[pkey] = 1
 | |
| 			self.timings[key] = nc + 1, tt, ct, callers, callees
 | |
| 			frame.f_locals['__start_time'] = t
 | |
| 			if self.debug:
 | |
| 				print s0
 | |
| 				print s1
 | |
| 			return
 | |
| 		if event == 'return':
 | |
| 			if self.profile_func:
 | |
| 				if not self.profiling:
 | |
| 					return
 | |
| 				if self.profile_func.has_key( \
 | |
| 					codehack.getcodename(frame.f_code)):
 | |
| 					self.profiling = 0
 | |
| 			self.call_level = depth(frame)
 | |
| 			self.cur_frame = frame
 | |
| 			pframe = frame.f_back
 | |
| 			if self.debug:
 | |
| 				s0 = 'return: '
 | |
| 			else:
 | |
| 				s0 = None
 | |
| 			self.handle_return(pframe, frame, s0)
 | |
| 			return
 | |
| 		if event == 'exception':
 | |
| 			if self.profile_func and not self.profiling:
 | |
| 				return
 | |
| 			call_level = depth(frame)
 | |
| 			if call_level < self.call_level:
 | |
| 				if call_level <> self.call_level - 1:
 | |
| 					print 'heh!',call_level,self.call_level
 | |
| 				if self.debug:
 | |
| 					s0 = 'exception: '
 | |
| 				else:
 | |
| 					s0 = None
 | |
| 				self.handle_return(self.cur_frame, frame, s0)
 | |
| 			self.call_level = call_level
 | |
| 			self.cur_frame = frame
 | |
| 			return
 | |
| 		print 'profile.Profile.dispatch: unknown debugging event:',
 | |
| 		print `event`
 | |
| 		return
 | |
| 
 | |
| 	def handle_return(self, pframe, frame, s0):
 | |
| 		t = os.times()
 | |
| 		t = t[0] + t[1]
 | |
| 		if frame.f_locals.has_key('__key'):
 | |
| 			key = frame.f_locals['__key']
 | |
| 		else:
 | |
| 			funcname = codehack.getcodename(frame.f_code)
 | |
| 			lineno = codehack.getlineno(frame.f_code)
 | |
| 			filename = frame.f_code.co_filename
 | |
| 			key = filename + ':' + `lineno` + '(' + funcname + ')'
 | |
| 			frame.f_locals['__key'] = key
 | |
| 		if self.debug:
 | |
| 			s0 = s0 + key + ' depth: ' + `self.call_level` + ' time: ' + `t`
 | |
| 		if pframe:
 | |
| 			if pframe.f_locals.has_key('__key'):
 | |
| 				pkey = pframe.f_locals['__key']
 | |
| 			else:
 | |
| 				funcname = codehack.getcodename(frame.f_code)
 | |
| 				lineno = codehack.getlineno(frame.f_code)
 | |
| 				filename = frame.f_code.co_filename
 | |
| 				pkey = filename + ':' + `lineno` + '(' + funcname + ')'
 | |
| 				pframe.f_locals['__key'] = pkey
 | |
| 			if self.debug:
 | |
| 				s1 = 'parent: '+pkey
 | |
| 			if pframe.f_locals.has_key('__start_time') and \
 | |
| 				  self.timings.has_key(pkey):
 | |
| 				st = pframe.f_locals['__start_time']
 | |
| 				nc, tt, ct, callers, callees = \
 | |
| 					self.timings[pkey]
 | |
| 				if self.debug:
 | |
| 					s1 = s1+' before: st='+`st`+' nc='+`nc`+' tt='+`tt`+' ct='+`ct`
 | |
| 					s1 = s1+' after: st='+`t`+' nc='+`nc`+' tt='+`tt`+' ct='+`ct+(t-st)`
 | |
| 				self.timings[pkey] = \
 | |
| 					nc, tt, ct + (t - st), callers, callees
 | |
| 				pframe.f_locals['__start_time'] = t
 | |
| 		if self.timings.has_key(key):
 | |
| 			nc, tt, ct, callers, callees = self.timings[key]
 | |
| 		else:
 | |
| 			nc, tt, ct, callers, callees = 0, 0, 0, {}, {}
 | |
| 		if frame.f_locals.has_key('__start_time'):
 | |
| 			st = frame.f_locals['__start_time']
 | |
| 		else:
 | |
| 			st = t
 | |
| 		if self.debug:
 | |
| 			s0 = s0+' before: st='+`st`+' nc='+`nc`+' tt='+`tt`+' ct='+`ct`
 | |
| 			s0 = s0+' after: nc='+`nc`+' tt='+`tt+(t-st)`+' ct='+`ct+(t-st)`
 | |
| 			print s0
 | |
| 			print s1
 | |
| 		self.timings[key] = \
 | |
| 			nc, tt + (t - st), ct + (t - st), callers, callees
 | |
| 
 | |
| 	def print_stats(self):
 | |
| 		# Print in reverse order by ct
 | |
| 		print_title()
 | |
| 		list = []
 | |
| 		for key in self.timings.keys():
 | |
| 			nc, tt, ct, callers, callees = self.timings[key]
 | |
| 			if nc == 0:
 | |
| 				continue
 | |
| 			list.append(ct, tt, nc, key)
 | |
| 		list.sort()
 | |
| 		list.reverse()
 | |
| 		for ct, tt, nc, key in list:
 | |
| 			print_line(nc, tt, ct, os.path.basename(key))
 | |
| 
 | |
| 	def dump_stats(self, file):
 | |
| 		f = open(file, 'w')
 | |
| 		marshal.dump(self.timings, f)
 | |
| 		f.close()
 | |
| 
 | |
| 	# The following two methods can be called by clients to use
 | |
| 	# a profiler to profile a statement, given as a string.
 | |
| 	
 | |
| 	def run(self, cmd):
 | |
| 		import __main__
 | |
| 		dict = __main__.__dict__
 | |
| 		self.runctx(cmd, dict, dict)
 | |
| 	
 | |
| 	def runctx(self, cmd, globals, locals):
 | |
| 		sys.setprofile(self.trace_dispatch)
 | |
| 		try:
 | |
| 			exec(cmd + '\n', globals, locals)
 | |
| 		finally:
 | |
| 			sys.setprofile(None)
 | |
| 
 | |
| 	# This method is more useful to profile a single function call.
 | |
| 
 | |
| 	def runcall(self, func, *args):
 | |
| 		sys.setprofile(self.trace_dispatch)
 | |
| 		try:
 | |
| 			apply(func, args)
 | |
| 		finally:
 | |
| 			sys.setprofile(None)
 | |
| 
 | |
| 
 | |
| def depth(frame):
 | |
| 	d = 0
 | |
| 	while frame:
 | |
| 		d = d + 1
 | |
| 		frame = frame.f_back
 | |
| 	return d
 | |
| 
 | |
| class Stats:
 | |
| 	def init(self, file):
 | |
| 		f = open(file, 'r')
 | |
| 		self.stats = marshal.load(f)
 | |
| 		f.close()
 | |
| 		self.stats_list = None
 | |
| 		return self
 | |
| 
 | |
| 	def print_stats(self):
 | |
| 		print_title()
 | |
| 		if self.stats_list:
 | |
| 			for i in range(len(self.stats_list)):
 | |
| 				nc, tt, ct, callers, callees, key = \
 | |
| 					self.stats_list[i]
 | |
| 				print_line(nc, tt, ct, key)
 | |
| 		else:
 | |
| 			for key in self.stats.keys():
 | |
| 				nc, tt, ct, callers, callees = self.stats[key]
 | |
| 				print_line(nc, tt, ct, key)
 | |
| 
 | |
| 	def print_callers(self):
 | |
| 		if self.stats_list:
 | |
| 			for i in range(len(self.stats_list)):
 | |
| 				nc, tt, ct, callers, callees, key = \
 | |
| 					self.stats_list[i]
 | |
| 				print key,
 | |
| 				for func in callers.keys():
 | |
| 					print func+'('+`callers[func]`+')',
 | |
| 				print
 | |
| 		else:
 | |
| 			for key in self.stats.keys():
 | |
| 				nc, tt, ct, callers, callees = self.stats[key]
 | |
| 				print key,
 | |
| 				for func in callers.keys():
 | |
| 					print func+'('+`callers[func]`+')',
 | |
| 				print
 | |
| 
 | |
| 	def print_callees(self):
 | |
| 		if self.stats_list:
 | |
| 			for i in range(len(self.stats_list)):
 | |
| 				nc, tt, ct, callers, callees, key = \
 | |
| 					self.stats_list[i]
 | |
| 				print key,
 | |
| 				for func in callees.keys():
 | |
| 					print func+'('+`callees[func]`+')',
 | |
| 				print
 | |
| 		else:
 | |
| 			for key in self.stats.keys():
 | |
| 				nc, tt, ct, callers, callees = self.stats[key]
 | |
| 				print key,
 | |
| 				for func in callees.keys():
 | |
| 					print func+'('+`callees[func]`+')',
 | |
| 				print
 | |
| 
 | |
| 	def sort_stats(self, field):
 | |
| 		stats_list = []
 | |
| 		for key in self.stats.keys():
 | |
| 			t = self.stats[key]
 | |
| 			nt = ()
 | |
| 			for i in range(len(t)):
 | |
| 				if i == field:
 | |
| 					nt = (t[i],) + nt[:]
 | |
| 				else:
 | |
| 					nt = nt[:] + (t[i],)
 | |
| 			if field == -1:
 | |
| 				nt = (key,) + nt
 | |
| 			else:
 | |
| 				nt = nt + (key,)
 | |
| 			stats_list.append(nt)
 | |
| 		stats_list.sort()
 | |
| 		self.stats_list = []
 | |
| 		for i in range(len(stats_list)):
 | |
| 			t = stats_list[i]
 | |
| 			if field == -1:
 | |
| 				nt = t[1:] + t[0:1]
 | |
| 			else:
 | |
| 				nt = t[1:]
 | |
| 				nt = nt[:field] + t[0:1] + nt[field:]
 | |
| 			self.stats_list.append(nt)
 | |
| 
 | |
| 	def reverse_order(self):
 | |
| 		self.stats_list.reverse()
 | |
| 
 | |
| 	def strip_dirs(self):
 | |
| 		newstats = {}
 | |
| 		for key in self.stats.keys():
 | |
| 			nc, tt, ct, callers, callees = self.stats[key]
 | |
| 			newkey = os.path.basename(key)
 | |
| 			newcallers = {}
 | |
| 			for c in callers.keys():
 | |
| 				newcallers[os.path.basename(c)] = callers[c]
 | |
| 			newcallees = {}
 | |
| 			for c in callees.keys():
 | |
| 				newcallees[os.path.basename(c)] = callees[c]
 | |
| 			newstats[newkey] = nc, tt, ct, newcallers, newcallees
 | |
| 		self.stats = newstats
 | |
| 		self.stats_list = None
 | |
| 
 | |
| def print_title():
 | |
| 	print string.rjust('ncalls', 8),
 | |
| 	print string.rjust('tottime', 8),
 | |
| 	print string.rjust('percall', 8),
 | |
| 	print string.rjust('cumtime', 8),
 | |
| 	print string.rjust('percall', 8),
 | |
| 	print 'filename:lineno(function)'
 | |
| 
 | |
| def print_line(nc, tt, ct, key):
 | |
| 	print string.rjust(`nc`, 8),
 | |
| 	print f8(tt),
 | |
| 	if nc == 0:
 | |
| 		print ' '*8,
 | |
| 	else:
 | |
| 		print f8(tt/nc),
 | |
| 	print f8(ct),
 | |
| 	if nc == 0:
 | |
| 		print ' '*8,
 | |
| 	else:
 | |
| 		print f8(ct/nc),
 | |
| 	print key
 | |
| 
 | |
| def f8(x):
 | |
| 	return string.rjust(fpformat.fix(x, 3), 8)
 | |
| 
 | |
| # simplified user interface
 | |
| def run(statement, *args):
 | |
| 	prof = Profile().init()
 | |
| 	try:
 | |
| 		prof.run(statement)
 | |
| 	except SystemExit:
 | |
| 		pass
 | |
| 	if len(args) == 0:
 | |
| 		prof.print_stats()
 | |
| 	else:
 | |
| 		prof.dump_stats(args[0])
 | |
| 
 | |
| # test command with debugging
 | |
| def debug():
 | |
| 	prof = Profile().init()
 | |
| 	prof.debug = 1
 | |
| 	try:
 | |
| 		prof.run('import x; x.main()')
 | |
| 	except SystemExit:
 | |
| 		pass
 | |
| 	prof.print_stats()
 | |
| 
 | |
| # test command
 | |
| def test():
 | |
| 	run('import x; x.main()')
 | |
| 
 | |
| # print help
 | |
| def help():
 | |
| 	for dirname in sys.path:
 | |
| 		fullname = os.path.join(dirname, 'profile.doc')
 | |
| 		if os.path.exists(fullname):
 | |
| 			sts = os.system('${PAGER-more} '+fullname)
 | |
| 			if sts: print '*** Pager exit status:', sts
 | |
| 			break
 | |
| 	else:
 | |
| 		print 'Sorry, can\'t find the help file "profile.doc"',
 | |
| 		print 'along the Python search path'
 |