#!/usr/bin/python

# This script is used to find the hotcode in some tasks
# GPL
# Copyright(C) Hui Zhu (teawater@gmail.com), 2012

import gdb
import tempfile
import os
import signal
import sys
import traceback
import time

class hotcode_list:
	def __init__(self):
		self.function_list = {}
		self.file_list = {}
		self.line_list = {}
		self.function_list_line = {}
		self.file_list_line = {}
		self.num = 0

class task:
	def __init__(self, fid, user_dir):
		self.fid = fid
		self.user_dir = user_dir
		self.kernel = hotcode_list()
		self.user = hotcode_list()

debug_dir = "/usr/lib/debug/"
task_list = {}
no_task = False
kernel_hotcode_list = hotcode_list()

output_html = True
output_html_file = "./hotcode.html"
show_line_number_default = 20
show_line_number = show_line_number_default

#--------------------------------------------------------------------------------------------------
#For signal handler

from operator import itemgetter
def dict_sort(d, reverse=False):
	#proposed in PEP 265, using  the itemgetter
	return sorted(d.iteritems(), key=itemgetter(1), reverse=True)

def hotcode_show_code_list(string, code_list):
	if len(code_list) > 0:
		print "\t", string
		i = 1
		for c in dict_sort(code_list):
			print "\t", c[0], "\t\t", c[1]
			i += 1
			if i > show_line_number:
				break
		print

def hotcode_show():
	if no_task:
		hotcode_show_code_list("Hotest function", kernel_hotcode_list.function_list)
		hotcode_show_code_list("Hotest file", kernel_hotcode_list.file_list)
		hotcode_show_code_list("Hotest line", kernel_hotcode_list.line_list)
	else:
		for pid in task_list:
			print "task", str(pid), task_list[pid].user_dir
			print "Kernel hotcode:"
			hotcode_show_code_list("Hotest function", task_list[pid].kernel.function_list)
			hotcode_show_code_list("Hotest file", task_list[pid].kernel.file_list)
			hotcode_show_code_list("Hotest line", task_list[pid].kernel.line_list)
			print "User hotcode:"
			hotcode_show_code_list("Hotest function", task_list[pid].user.function_list)
			hotcode_show_code_list("Hotest file", task_list[pid].user.file_list)
			hotcode_show_code_list("Hotest line", task_list[pid].user.line_list)
			print

html_id = 0

def hotcode_list_to_output_html_fd_1(llist, tlist, fd):
	global html_id
	i = 1
	for c in dict_sort(llist):
		if tlist != None:
			fd.write('''<tr><td onclick='sh("'''+str(html_id)+'''");'>'''+str(c[0])+'''</td><td style=" width: 10%; text-align: right;">'''+str(c[1])+'''</td></tr>''')
			fd.write('''<tr><td style="text-align: center; display: none;" colspan="2" id="''' + str(html_id) + '''"><table style="width: 100%;" border="1" cellpadding="0" cellspacing="0"><tbody>''')
			for d in dict_sort(tlist[c[0]]):
				fd.write("<tr><td>" + str(d[0]) + '''</td><td style=" width: 10%; text-align: right;">''' + str(d[1]) + "</td></tr>")
			fd.write('</tbody></table>')
		else:
			fd.write('<tr><td>'+str(c[0])+'''</td><td style=" width: 10%; text-align: right;">'''+str(c[1])+'''</td></tr>''')
		i += 1
		html_id += 1
		if i > show_line_number:
			break

def hotcode_list_to_output_html_fd(hlist, fd):
	global html_id
	fd.write('''<tr><td style="text-align: center;" colspan="2">Hot functions list</td></tr>''')
	hotcode_list_to_output_html_fd_1(hlist.function_list, hlist.function_list_line, fd)

	fd.write('''<tr><td style="text-align: center;" colspan="2">Hot file list</td></tr>''')
	hotcode_list_to_output_html_fd_1(hlist.file_list, hlist.file_list_line, fd)

	fd.write('''<tr><td style="text-align: center;" colspan="2">Hot line list</td></tr>''')
	hotcode_list_to_output_html_fd_1(hlist.line_list, None, fd)

def hotcode_to_output_html_file():
	global html_id
	html_id = 0
	fd = open(output_html_file, "w")
	fd.write('''
<html><head><title>Hotcode</title>
<script>
<!--
function sh(id)
{
	if(document.getElementById(id).style.display=='none') {
		document.getElementById(id).style.display='block';
	}
	else {
		document.getElementById(id).style.display='none';
	}
}
-->
</script></head>
<body>
<div style="text-align: center;">This file is generated by KGTP (<a href="http://code.google.com/p/kgtp/">http://code.google.com/p/kgtp/</a>) in ''' + time.strftime("%Y-%m-%d %H:%M:%S", time.localtime()) + '''.</div>
<div style="text-align: center;">Click the function name or file name to see the detailed info.</div>''')
	if show_line_number > 0:
		fd.write('''<div style="text-align: center;">Just show top 20 of each list.</div>''')
	if no_task:
		fd.write('<br><br>')
		fd.write('''<table style="margin-left: auto; margin-right: auto;" border="1" cellpadding="2" cellspacing="0"><tbody>''')
		fd.write('''<tr><td><strong>Kernel space hotcode list</strong></td><td style=" width: 10%; text-align: right;">'''+str(kernel_hotcode_list.num)+'''</td></tr>''')
		hotcode_list_to_output_html_fd(kernel_hotcode_list, fd)
		fd.write('</tbody></table>')
	else:
		for pid in task_list:
			fd.write('<br><br>')
			fd.write('''<table style="margin-left: auto; margin-right: auto;" border="1" cellpadding="2" cellspacing="0"><tbody>''')
			fd.write('''<tr><td style="text-align: center;" colspan="2">pid:''' + str(pid) + " " + task_list[pid].user_dir + "</td></tr>")
			if trace_user:
				fd.write('''<tr><td style="text-align: center;" colspan="2">User space hotcode list </td></tr>''')
				fd.write('''<tr><td><strong>User space hotcode list</strong></td><td style=" width: 10%; text-align: right;">'''+str(task_list[pid].user.num)+'''</td></tr>''')
				hotcode_list_to_output_html_fd(task_list[pid].user, fd)
			if trace_kernel:
				if trace_user:
					fd.write('''<tr><td style="text-align: center;" colspan="2"></td></tr>''')
				fd.write('''<tr><td><strong>Kernel space hotcode list</strong></td><td style=" width: 10%; text-align: right;">'''+str(task_list[pid].kernel.num)+'''</td></tr>''')
				hotcode_list_to_output_html_fd(task_list[pid].kernel, fd)
			fd.write('</tbody></table>')
	fd.write('</body></html>')
	fd.close()
	print "Save", html_id, "entries."

def sigint_handler(num, e):
	if output_html:
		hotcode_to_output_html_file()
	else:
		hotcode_show()
	try:
		s = raw_input('Conitnue? [(y)es], (n)o:')
	except:
		s = 'y'
	finally:
		if s[0:1] != 'n' and s[0:1] != 'N':
			return;
	#gdb.execute("inferior 1")
	try:
		gdb.execute("tfind -1", True, False)
		gdb.execute("target remote /sys/kernel/debug/gtp", True, False)
		gdb.execute("set disconnected-tracing off", True, False)
	except:
		print "Try to stop GTP got error, please use command \"sudo rmmod gtp.ko\" stop it."
	exit(1);
#--------------------------------------------------------------------------------------------------
#init

def add_inferior():
	fid = gdb.execute("add-inferior", False, True)
	if fid.find("Added inferior ") != 0:
		return -1
	fid = int(fid[len("Added inferior "):])
	return fid

gdb.execute("set target-async on", True, False)
gdb.execute("set pagination off", True, False)
gdb.execute("set confirm off", True, False)
gdb.execute("set circular-trace-buffer on", True, False)
gdb.execute("set debug-file-directory "+debug_dir, True, False)
try:
	gdb.execute("kill", True, False)
except:
	pass

trace_user = True
trace_kernel = True
while 1:
	tmp = "both"
	try:
		tmp = raw_input('Which part of code you want trace? [(b)oth], (u)ser, (k)ernel:')
	except:
		continue
	if tmp[0:1] == 'U' or tmp[0:1] == 'u':
		trace_kernel = False
	elif tmp[0:1] == 'K' or tmp[0:1] == 'k':
		trace_user = False
	break

#Get which task pid why want to trace
print("Please input the pid of task that you want to trace - one per line.")
print("If not set any task, will trace all code in the Linux kernel.")
while 1:
	pid = -1
	try:
		pid = input('task pid (use empty to stop pid input):')
	except:
		pass
	if pid <= 0:
		break
	if pid in task_list:
		print("This pid already in the list.")
		continue
	user_dir = ""
	fid = 0
	if trace_user:
		try:
			orig_user_dir = user_dir = os.path.realpath("/proc/"+str(pid)+"/exe")
		except:
			#maybe this is the kernel task
			print "Cannot get the user code info of this pid, will not parse the user level code symbol"
			task_list[pid] = task(fid, user_dir)
			continue
		if os.path.exists(debug_dir+user_dir):
			user_dir = debug_dir+user_dir
		while 1:
			tmp = ""
			try:
				tmp = raw_input('Please input the debug binary of task if you want to change it ['+user_dir+']:')
			except:
				continue
			if tmp != "":
				user_dir = os.path.realpath(tmp)
			break
		if not os.path.exists(user_dir):
			print "Cannot get the user code info of this pid, will not parse the user level code symbol"
			task_list[pid] = task(fid, user_dir)
			continue
		print "Use "+user_dir+" as debug binary."
		fid = add_inferior()
		if fid < 0:
			print "Try to load task got error."
			continue
		gdb.execute("inferior "+str(fid))
		pfile = open("/proc/"+str(pid)+"/maps", "r")
		tmplist = pfile.read().split(os.linesep)
		pfile.close()
		for c in tmplist:
			c_list = c.split(" ")
			filename = c_list[-1].strip()
			if filename != orig_user_dir and os.path.exists(filename) and len(c_list) > 2 and len(c_list[1]) > 3 and c_list[1][2] == 'x':
				addr = "0x"+c_list[0][0:c.find('-')]
				gdb.execute("file "+filename)
				info_files = gdb.execute("info files", True, True)
				info_files_list = info_files.split(os.linesep)
				text_offset = "0x0"
				for line in info_files_list:
					line_list = line.split(" is ")
					if len(line_list) == 2 and line_list[1].strip() == ".text":
						line_list[0] = line_list[0].strip()
						text_offset = line_list[0][0:line_list[0].find(' - ')]
				print ("add-symbol-file "+filename+" ("+addr+"+"+text_offset+")")
				gdb.execute("add-symbol-file "+filename+" ("+addr+"+"+text_offset+")")
		gdb.execute("file "+user_dir)
		gdb.execute("inferior 1")
	task_list[pid] = task(fid, user_dir)

def get_addr_range_list(fun):
	buf = gdb.execute("info line "+fun, False, True)
	line_list = buf.split(os.linesep)
	ret = []
	begin = -1
	end = -1
	for line in line_list:
		addr_begin = line.find("starts at address ")
		if addr_begin >= 0:
			line = line[addr_begin + len("starts at address "):]
			addr_end = line.find(" <"+fun)
			if addr_end >= 0:
				begin = int(line[:addr_end], 0)
				line = line[addr_end + len(" <"+fun):]
		addr_begin = line.find("ends at ")
		if addr_begin >= 0:
			line = line[addr_begin + len("ends at "):]
			addr_end = line.find(" <"+fun)
			if addr_end > 0:
				end = int(line[:addr_end], 0)
				if begin != -1:
					ret.append([begin, end])
				begin = -1
				end = -1

	if len(ret) > 0:
		buf = gdb.execute("disassemble "+fun, False, True)
		line_list = buf.split(os.linesep)
		line_list.reverse()
		end = 0
		for line in line_list:
			addr_begin = line.find("0x")
			if addr_begin >= 0:
				line = line[addr_begin:]
				addr_end = line.find(" <+")
				if addr_end > 0:
					end = int(line[:addr_end], 0) + 1
					break
		if end != 0:
			offset = 0
			for c in ret:
				if c[1] < end:
					if offset == 0 or offset > (end - c[1]):
						offset = end - c[1]
			for c in ret:
				c[1] += offset

	return ret

def get_ignore_str(function):
	ret = ""
	try:
		s = raw_input('Do you want to ignore function \"'+function+'\"? [(y)es], (n)o:')
	except:
		s = 'y'
	if s[0:1] != 'n' and s[0:1] != 'N':
		r_list = get_addr_range_list(function)
		for r in r_list:
			if ret != "":
				ret += " && "
			else:
				ret += "&& ("
			#(regs->ip < r[0] || regs->ip > r[1])
			ret += "($p_ip < "+str(r[0])+" || $p_ip > "+str(r[1])+")"
		if ret != "":
			ret += ")"
	return ret

if len(task_list) == 0:
	trace_user = False
	trace_kernel = True
	no_task = True

try:
	s = raw_input('Which way you want to output hotcode info when ctrl-c? [(h)tml], (t)ty:')
except:
	s = 'h'
if s[0:1] == 't' or s[0:1] == 'T':
	output_html = False
else:
	output_html = True

if output_html:
	while 1:
		try:
			s = raw_input('Which file you want to save the html output? [' + output_html_file + ']:')
			if os.path.exists(s):
				if os.path.isfile(s):
					s = raw_input('File ' + s +' exist, do you want to over write it? (y)es, [(n)o]:')
					if s[0:1] != 'y' and s[0:1] != 'Y':
						continue
				else:
					print 'File ' + s +' exist, but it cannot be written.  Please choice another file.'
					continue
		except:
			continue
		if len(s) > 0:
			output_html_file = s
		break

try:
	show_line_number = input('Show line number (0 meas all)? ['+str(show_line_number)+']:')
except:
	show_line_number = show_line_number_default

#Set tracepoint
gdb.execute("target remote /sys/kernel/debug/gtp", True, False)

try:
	gdb.execute("tstop", True, False)
	gdb.execute("delete", True, False)
except:
	pass


def getmod():
	#following code is get from ../getmod.py
	#use the code directly because sys.argv = [''] inside GDB
	def format_file(name):
		tmp = ""
		for c in name:
			if c == "_":
				c = "-"
			tmp += c
		return tmp

	#Check if the target is available
	if str(gdb.selected_thread()) == "None":
		raise gdb.error("Please connect to Linux Kernel before use the script.")

	#Output the help
	print "Use GDB command \"set $mod_search_dir=dir\" to set an directory for search the modules."

	ignore_gtp_ko = gdb.parse_and_eval("$ignore_gtp_ko")
	if ignore_gtp_ko.type.code == gdb.TYPE_CODE_INT:
		ignore_gtp_ko = int(ignore_gtp_ko)
	else:
		ignore_gtp_ko = 1

	#Get the mod_search_dir
	mod_search_dir_list = []
	#Get dir from $mod_search_dir
	tmp_dir = gdb.parse_and_eval("$mod_search_dir")
	if tmp_dir.type.code == gdb.TYPE_CODE_ARRAY:
		tmp_dir = str(tmp_dir)
		tmp_dir = tmp_dir[1:len(tmp_dir)]
		tmp_dir = tmp_dir[0:tmp_dir.index("\"")]
		mod_search_dir_list.append(tmp_dir)
	#Get dir that same with current vmlinux
	tmp_dir = str(gdb.execute("info files", False, True))
	tmp_dir = tmp_dir[tmp_dir.index("Symbols from \"")+len("Symbols from \""):len(tmp_dir)]
	tmp_dir = tmp_dir[0:tmp_dir.index("\"")]
	tmp_dir = tmp_dir[0:tmp_dir.rindex("/")]
	mod_search_dir_list.append(tmp_dir)
	#Get the dir of current Kernel
	tmp_dir = "/lib/modules/" + str(os.uname()[2])
	if os.path.isdir(tmp_dir):
		mod_search_dir_list.append(tmp_dir)
	#Let user choice dir
	mod_search_dir = ""
	while mod_search_dir == "":
		for i in range(0, len(mod_search_dir_list)):
			print str(i)+". "+mod_search_dir_list[i]
		try:
			s = input('Select a directory for search the modules [0]:')
		except SyntaxError:
			s = 0
		except:
			continue
		if s < 0 or s >= len(mod_search_dir_list):
			continue
		mod_search_dir = mod_search_dir_list[s]

	mod_list_offset = long(gdb.parse_and_eval("((size_t) &(((struct module *)0)->list))"))
	mod_list = long(gdb.parse_and_eval("(&modules)"))
	mod_list_current = mod_list

	while 1:
		mod_list_current = long(gdb.parse_and_eval("((struct list_head *) "+str(mod_list_current)+")->next"))

		#check if need break the loop
		if mod_list == mod_list_current:
			break

		mod = mod_list_current - mod_list_offset

		#get mod_name
		mod_name = str(gdb.parse_and_eval("((struct module *)"+str(mod)+")->name"))
		mod_name = mod_name[mod_name.index("\"")+1:len(mod_name)]
		mod_name = mod_name[0:mod_name.index("\"")]
		if mod_name == "fglrx":
			contiue
		mod_name += ".ko"
		mod_name = format_file(mod_name)

		#get mod_dir_name
		mod_dir_name = ""
		for root, dirs, files in os.walk(mod_search_dir):
			for afile in files:
				tmp_file = format_file(afile)
				if tmp_file == mod_name:
					mod_dir_name = os.path.join(root,afile)
					break
			if mod_dir_name != "":
				break

		command = " "

		#Add module_core to command
		command += str(gdb.parse_and_eval("((struct module *)"+str(mod)+")->module_core"))

		#Add each sect_attrs->attrs to command
		#get nsections
		nsections = int(gdb.parse_and_eval("((struct module *)"+str(mod)+")->sect_attrs->nsections"))
		sect_attrs = long(gdb.parse_and_eval("(u64)((struct module *)"+str(mod)+")->sect_attrs"))
		for i in range(0, nsections):
			command += " -s"
			tmp = str(gdb.parse_and_eval("((struct module_sect_attrs *)"+str(sect_attrs)+")->attrs["+str(i)+"].name"))
			tmp = tmp[tmp.index("\"")+1:len(tmp)]
			tmp = tmp[0:tmp.index("\"")]
			command += " "+tmp
			tmp = str(gdb.parse_and_eval("((struct module_sect_attrs *)"+str(sect_attrs)+")->attrs["+str(i)+"].address"))
			command += " "+tmp

		if mod_dir_name == "":
			print "Cannot find out",mod_name,"from directory."
			print "Please use following command load the symbols from it:"
			print "add-symbol-file some_dir/"+mod_name+command
		else:
			if ignore_gtp_ko and mod_name == "gtp.ko":
				pass
			else:
				#print "add-symbol-file "+mod_dir_name+command
				gdb.execute("add-symbol-file "+mod_dir_name+command, False, False)

if trace_kernel:
	try:
		s = raw_input('Do you load the symbol from LKM? (y)es, [(n)o]:')
	except:
		s = 'n'
	if s[0:1] == 'y' or s[0:1] == 'Y':
		getmod()

cpu_number = int(gdb.parse_and_eval("$cpu_number"))
tempfilename = tempfile.mktemp()
tempfile = open(tempfilename, "w")
if no_task:
	ignore_str = ""
	#Setup first tracepoint
	ignore_str += get_ignore_str("arch_local_irq_enable")
	ignore_str += get_ignore_str("intel_idle")
	# GDB have bug with long conditon so close them
	#ignore_str += get_ignore_str("__do_softirq")
	#ignore_str += get_ignore_str("_raw_spin_unlock_irqrestore")

	tempfile.write("tvariable $p_ip\n")
	tempfile.write("tvariable $p_cs\n")
	tempfile.write("trace handle_irq\n")
	tempfile.write("commands\n")
	tempfile.write("teval $p_ip=(u64)regs->ip\n")
	tempfile.write("teval $p_cs=(u64)regs->cs\n")
	tempfile.write("end\n")
	#Setup second tracepoint
	tempfile.write("trace handle_irq\n")
	cond_str = " (($p_cs & 3) == 0)"
	tempfile.write("condition $bpnum "+cond_str+ignore_str+"\n")
	tempfile.write("commands\n")
	#tempfile.write("collect $no_self_trace\n")
	tempfile.write("collect $p_ip\n")
	tempfile.write("end\n")
	tempfile.write("trace smp_apic_timer_interrupt\n")
	tempfile.write("commands\n")
	tempfile.write("teval $p_ip=(u64)regs->ip\n")
	tempfile.write("teval $p_cs=(u64)regs->cs\n")
	tempfile.write("end\n")
	#Setup second tracepoint
	tempfile.write("trace smp_apic_timer_interrupt\n")
	cond_str = " (($p_cs & 3) == 0)"
	tempfile.write("condition $bpnum "+cond_str+ignore_str+"\n")
	tempfile.write("commands\n")
	#tempfile.write("collect $no_self_trace\n")
	tempfile.write("collect $p_ip\n")
	tempfile.write("end\n")
else:
	pid_str = ""
	for pid in task_list:
		if pid_str != "":
			pid_str += " || "
		else:
			pid_str += "("
		pid_str += "($current_task_pid == "+str(pid)+") "
	if pid_str != "":
		pid_str += ")"
	cond_str = ""
	if not trace_user:
		if pid_str != "":
			cond_str += " && "
		cond_str += " ((regs->cs & 3) == 0)"
	elif not trace_kernel:
		if pid_str != "":
			cond_str += "&&"
		cond_str += " ((regs->cs & 3) == 3)"
	tempfile.write("trace handle_irq\n")
	tempfile.write("condition $bpnum "+pid_str+cond_str+"\n")
	tempfile.write("commands\n")
	tempfile.write("collect regs->ip\n")
	if trace_user and trace_kernel:
		tempfile.write("collect regs->cs\n")
	tempfile.write("collect $current_task_pid\n")
	tempfile.write("end\n")
	tempfile.write("trace smp_apic_timer_interrupt\n")
	tempfile.write("condition $bpnum "+pid_str+cond_str+"\n")
	tempfile.write("commands\n")
	tempfile.write("collect regs->ip\n")
	if trace_user and trace_kernel:
		tempfile.write("collect regs->cs\n")
	tempfile.write("collect $current_task_pid\n")
	tempfile.write("end\n")
tempfile.close()
tempfile = open(tempfilename, "r")
print "Tracepoint command:"
print tempfile.read()
tempfile.close()
gdb.execute("source "+tempfilename, True, False)
os.remove(tempfilename)
gdb.execute("set disconnected-tracing on", True, False)
gdb.execute("tstart")
gdb.execute("kill", True, False)

signal.signal(signal.SIGINT, sigint_handler);
signal.siginterrupt(signal.SIGINT, False);

#Connect to pipe
gdb.execute("target tfile /sys/kernel/debug/gtpframe_pipe")

#--------------------------------------------------------------------------------------------------
#cycle

def add_line_to_list(line, line_list):
	if line in line_list:
		line_list[line] += 1
	else:
		line_list[line] = 1

#info[0] line_num, info[1] file_name, info[2] function_name
def add_info_to_code_list(info, code_list):
	line = str(info[1]) + ":" + str(info[0])
	#function_list
	if info[2] in code_list.function_list:
		code_list.function_list[info[2]] += 1
	else:
		code_list.function_list[info[2]] = 1
		code_list.function_list_line[info[2]] = {}
	add_line_to_list(line, code_list.function_list_line[info[2]])
	#file_list
	if info[1] in code_list.file_list:
		code_list.file_list[info[1]] += 1
	else:
		code_list.file_list[info[1]] = 1
		code_list.file_list_line[info[1]] = {}
	add_line_to_list(line, code_list.file_list_line[info[1]])
	#line_list
	add_line_to_list(line, code_list.line_list)
	#num
	code_list.num += 1

def task_list_add_line(is_user, pid, info):
	global task_list
	if no_task:
		add_info_to_code_list (info, kernel_hotcode_list)
	else:
		if is_user:
			add_info_to_code_list (info, task_list[pid].user)
		else:
			add_info_to_code_list (info, task_list[pid].kernel)

def get_line_from_sym(sym):
	sym = sym.rstrip(os.linesep)

	#Get line_num and file_name
	begin = sym.find("Line ")
	end = sym.find("\" starts at address")
	line_num = None
	file_name = None
	if begin >= 0 and end > 0 and begin + len("Line ") < end:
		line = sym[begin + len("Line "):end]
		line = line.split(" of \"")
		if len(line) == 2:
			line_num = line[0]
			file_name = line[1]
		sym = sym[end:]

	#Get function_name
	begin = sym.find("<")
	end = sym.find(">")
	if begin >= 0 and end > 0 and begin + 1 < end:
		function_name = sym[begin + 1:end]
		end = function_name.rfind("+")
		if end > 0:
			function_name = function_name[:end]
		sym = gdb.execute("info symbol "+function_name, True, True).rstrip(os.linesep)
		begin = sym.rfind(" of ")
		if begin > 0:
			begin += len(" of ")
			function_name = sym[begin:] + ":" + function_name
	else:
		function_name = None
	return (line_num, file_name, function_name)

if no_task:
	while 1:
		try:
			gdb.execute("tfind 0", False, True)
			sym = gdb.execute("info line *($p_ip - 1)", True, True)
			line = get_line_from_sym(sym)
			task_list_add_line(False, 0, line)
		except gdb.error, x:
			print("Drop one entry because:")
			for file, lineno, function, text in traceback.extract_tb(sys.exc_info()[2]):
				print file, lineno, function, text
		except gdb.MemoryError, x:
			print("Drop one entry because:")
			for file, lineno, function, text in traceback.extract_tb(sys.exc_info()[2]):
				print file, lineno, function, text
		try:
			gdb.execute("tfind 1", False, True)
		except:
			pass
else:
	while 1:
		try:
			gdb.execute("tfind 0", False, True)
			is_user = False
			pid = long(gdb.parse_and_eval("$current_task_pid"))
			if not pid in task_list:
				raise gdb.error ("Cannot find inferior for pid "+ str(pid) +", drop one entry.")
			if trace_user and (not trace_kernel or long(gdb.parse_and_eval("regs->cs & 3")) == 3):
				is_user = True
				ip = long(gdb.parse_and_eval("regs->ip - 1"))
				gdb.execute("inferior "+str(task_list[pid].fid), False, True)
				sym = gdb.execute("info line *"+str(ip), True, True)
			else:
				sym = gdb.execute("info line *(regs->ip - 1)", True, True)
			line = get_line_from_sym(sym)
			if is_user:
				gdb.execute("inferior 1", False, True)
			task_list_add_line(is_user, pid, line)
		except gdb.error, x:
			print("Drop one entry because:")
			for file, lineno, function, text in traceback.extract_tb(sys.exc_info()[2]):
				print file, lineno, function, text
			try:
				gdb.execute("inferior 1", False, True)
			except:
				pass
		except gdb.MemoryError, x:
			print("Drop one entry because:")
			for file, lineno, function, text in traceback.extract_tb(sys.exc_info()[2]):
				print file, lineno, function, text
			try:
				gdb.execute("inferior 1", False, True)
			except:
				pass
		try:
			gdb.execute("tfind 1", False, True)
		except:
			pass
