All pastes #1061507 Raw Edit

Anonymous

public text v1 · immutable
#1061507 ·published 2008-07-03 18:22 UTC
rendered paste body
# mndb.gdbinit
#
# Copyright (C) Chris Toshok, 2006
# Some rights reserved (guess which ones!)
#
# I got tired of waiting for a full featured debugger when all I
# really need is to set breakpoints, have the program stop at those
# breakpoints, and let me examine state.
# 
# Stepping, while useful, is definitely not as important as basic
# breakpoint/print functionality.
#
# This version of mndb.gdbinit supports breakpoints, but only at the
# start of a method.  Not based on file and line numbers, there's no
# overload resolution.  It's pretty simple, and quite beautiful if you
# can wrap your head around how nasty gdb's macro syntax is (and how
# broken its execution environment is).
#
# enjoy


# type/data inspection
# :   mptype
# :   mprint

define mptype
	# assumes args are: namespace class
	set $l = (GList*)$debug_handles
	set $mono_class = (MonoClass*)0

	while ($l != 0)
		set $mono_class = mono_class_from_name (((MonoDebugHandle*)$l->data)->image, $arg0, $arg1)
		if ($mono_class != 0)
			loop_break
		else
			set $l = $l->next
		end
	end

	if ($mono_class != 0)
		printf "%s\n", mono_type_get_name_full (mono_class_get_type ($mono_class), MONO_TYPE_NAME_FORMAT_ASSEMBLY_QUALIFIED)
	else
		printf "no such type found in loaded assemblies: %s.%s\n", $arg0, $arg1
	end
end

# thread support
# :   mthread

# stack manipulation
# :   mup
# :   mdown
# :   mframe
# :   mwhere/mbt

define mdown
	down-silently
	mndb_print_frame
end

define mup
	up-silently
	mndb_print_frame
end

define mwhere
	# save off our current $esp
	set $saved_esp = $esp
	set $saved_frame_index = 0

	# save off our endpoint
	up-silently 5000
	set $topmost_esp = $esp	

	select-frame 0

	set $i = 0
	set $last_esp = $esp
	while ($esp != $topmost_esp)
		if ($saved_esp == esp)
			set $saved_frame_index = $i
		end
		set $foo = mono_pmip ($pc)
		if ($foo == 0x00)
			frame
		else
			printf "#%d  0x%X in%s\n", $i, $pc, $foo
		end
		up-silently
		if ($last_esp == $esp)
			loop_break
		end
		set $last_esp = $esp
		set $i = $i + 1
	end

	select-frame $saved_frame_index

# does this work for anyone?  not for me.. - toshok
# set $mono_thread = mono_thread_current ()
# if ($mono_thread == 0x00)
#   printf "No mono thread associated with this thread\n"
# else
#   set $ucp = malloc (sizeof (ucontext_t))
#   call (void) getcontext ($ucp)
#   call (void) mono_print_thread_dump ($ucp)
#   call (void) free ($ucp)
# end
end

# execution
# :   mrun
# :   mstart
# :   mcontinue
# :   mnext
# :   mstep

define mrun
	if ($started == 1)
		printf "mndb doesn't support restarting.  tough cookies.\n"
	else
		set $started = 1
		set $stop_at_main = 0
		continue
	end
end

define mstart
	if ($started == 1)
		printf "mndb doesn't support restarting.  tough cookies.\n"
	else
		set $started = 1
		set $stop_at_main = 1
		continue
	end
end

define mcontinue
	continue
end

define mnext
	set $jit_info = mono_jit_info_table_find (mono_domain_get (), $pc)
	set $mono_method = mono_jit_info_get_method ($jit_info)

	mndb_find_debug_handle_for_method $mono_method

	mndb_get_debug_method_info $mono_method $mono_handle

	set $jit_debug_info = mono_debug_find_method ($debug_method_info, mono_domain_get ())

	set $native_offset = $pc - $jit_debug_info->code_start

	set $source_location = mono_debug_lookup_source_location ($mono_method, $native_offset, mono_domain_get ())

	#print *$jit_debug_info
	#print *$source_location

	printf "Current native offset = %d, il offset = %d\n", $native_offset, $source_location->il_offset

	# print out the current jit debug info line number table (native/il offsets)
	#set $i = 0
	#while ($i < $jit_debug_info->num_line_numbers)
	#	printf "jit_debug_info->line_numbers[%d] = %d, %d\n", $i, $jit_debug_info->line_numbers[$i].il_offset, $jit_debug_info->line_numbers[$i].native_offset
	#	set $i = $i + 1
	#end

	set $i = 0
	while ($i < $jit_debug_info->num_line_numbers)
		if ($source_location->il_offset < $jit_debug_info->line_numbers[$i].il_offset)
			loop_break
		end
		set $i = $i + 1
	end
	if ($i == $jit_debug_info->num_line_numbers)
		printf "couldn't find line number information for current native offset\n"
	else
		if ($i == ($jit_debug_info->num_line_numbers - 1))
			printf "mndb currently doesn't let you return from a method by stepping\n"
			# one would think this would work, but it doesn't
			# tbreak *(void**)($ebp + 4)
			# continue
		else
			set $bp_addr = $jit_debug_info->code_start + $jit_debug_info->line_numbers[$i+1].native_offset 
			#printf "setting new breakpoint at 0x%0x, native offset %d\n", $bp_addr, $bp_addr - $jit_debug_info->code_start
			tbreak *$bp_addr
			continue
		end
	end
end

define mstep
	printf "not yet...\n"
end

# breakpoints
# :   mbreak
# :   mdelete (can we even implement this?   not likely...)

define mbreak
	set $method_name = (char*)$arg0

	set $method_desc = mono_method_desc_new ($method_name, 1)

	if ($method_desc == 0)
		printf "Couldn't find method description\n"
	else
		# this looks an awful lot like mndb_find_debug_handle_for_method, but
		# we're also trying to locate the method info, so...

		set $l = (GList*)$debug_handles
		set $handle = (MonoDebugHandle*)0
		set $mono_method = (MonoMethod*)0

		while ($l != 0)
			set $handle = (MonoDebugHandle*)$l->data
			set $mono_method = mono_method_desc_search_in_image ($method_desc, $handle->image)
			if ($mono_method != 0)
				#printf "Found method in %s\n", $handle->image_file
				loop_break
			else
				set $l = $l->next
			end
		end

		if ($mono_method == 0)
			printf "Couldn't find method\n"
		else
			mndb_insert_breakpoint $mono_method $handle
		end
	end
end

define mndb_get_debug_method_info
	set $mono_method = $arg0
	set $handle = $arg1

	# we have to disable breakpoints here, since
	# our gdb commands can't re-enter, and calling into
	# mono_debug_symfile_lookup_method causes the debugger
	# notification function to be called (with REFRESH_SYMTABS)
	disable
	set $debug_method_info = mono_debug_symfile_lookup_method ($handle, $mono_method)
	if ($debug_method_info == 0)
		printf "expect a crash, could not find debug method info for %s\n", mono_method_full_name ($mono_method, 1)
	end
	enable
end

define mndb_insert_breakpoint
	set $mono_method = $arg0
	set $handle = $arg1
	set $mono_domain = mono_domain_get ()

	mndb_get_debug_method_info $mono_method $handle

	set $debug_jit_info = mono_debug_find_method ($debug_method_info, $mono_domain)

	if ($debug_jit_info == 0)
		printf "adding pending breakpoint for method %s\n", mono_method_full_name ($mono_method, 1)
		set $rv = mono_debugger_insert_breakpoint_full ($method_desc)
	else
		set $bp_addr = $debug_jit_info->code_start + $debug_jit_info->prologue_end
		printf "Setting breakpoint on method %s at 0x%x\n", mono_method_full_name ($mono_method, 1), $bp_addr
		break *$bp_addr
	end
end

define mndb_print_frame
	set $foo = mono_pmip ($pc)
	if ($foo == 0x00)
		frame
	else
		printf "0x%x in %s\n", $pc, $foo
	end
end

############################################################
# the ugly bits of glue to hook up to mono

set $started = 0
set $reached_main = 0
set $stop_at_main = 0
set $debug_handles = 0

# we generate a lot of spew
set pagination off

# and mono generates a lot of signals
handle SIGXCPU SIG33 SIGPWR nostop noprint
handle SIGTRAP noprint nostop nopass

watch mono_debugger_notification_function
run
delete 1

break *mono_debugger_notification_function
commands
	silent
	handle_mono_debugger_notification
end

printf "\n\n\n\n\n\n\n"
printf "Welcome to MNDB, the Mono Native Debugger\n"
printf "Version 0.002\n"
printf "To get started, type `mrun' or `mstart'\n"

# just for kicks (there's a trailing space)
set prompt (mndb) 


# these prints out the stack frame (if it's a mono frame, anyway)
# when we need to

# let the user use up/down as well as mup/mdown.  if we up/down to a
# mono frame, print out the location
define hookpost-up
	if ($reached_main)
		if (mono_pmip ($pc) != 0)
			mndb_print_frame
		end
	end
end

define hookpost-down
	if ($reached_main)
		if (mono_pmip ($pc) != 0)
			mndb_print_frame
		end
	end
end

define hook-stop
	if ($reached_main)
		if (mono_pmip ($pc) != 0)
			mndb_print_frame
		end
	end
end

define mndb_find_debug_handle_for_method
	set $l = (GList*)$debug_handles
	set $mono_handle = (MonoDebugHandle*)0

	while ($l != 0)
		set $iter_handle = (MonoDebugHandle*)$l->data
		if ($mono_method->klass->image == $iter_handle->image)
			set $mono_handle = $iter_handle
			loop_break
		else
			set $l = $l->next
		end
	end
end

define handle_mono_debugger_notification
	# this is gross, but since the function is generated a runtime
        # gdb doesn't have symbol info for it, and so we can't just
        # look at the event argument.  we have to grovel on the stack.
	set $event = *(guint32*)($esp + 4)
	set $data = *(guint32*)($esp + 12)
	set $arg = *(guint32*)($esp + 20)

#	print (MonoDebuggerEvent)$event
#	print (void*)$data

	if ($event == MONO_DEBUGGER_EVENT_INITIALIZE_MANAGED_CODE)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_INITIALIZE_CORLIB)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_LOAD_MODULE)
		set $debug_handle = (MonoDebugHandle*)$data
		printf "symbol file added to runtime: %s\n", $debug_handle->image_file
		set $debug_handles = g_list_prepend ($debug_handles, $debug_handle)
		return
		continue
        else
	if ($event == MONO_DEBUGGER_EVENT_UNLOAD_MODULE)
		# XXX remove it from our list of debug handles
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_JIT_BREAKPOINT)
		printf "JIT_BREAKPOINT\n"
		set $mono_method = (MonoMethod*)$data

		mndb_find_debug_handle_for_method $mono_method

		mndb_insert_breakpoint $mono_method $mono_handle

		#call (void) mono_debugger_remove_breakpoint ((int)$arg)

		printf "DONE\n"
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_INITIALIZE_THREAD_MANAGER)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_ACQUIRE_GLOBAL_THREAD_LOCK)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_RELEASE_GLOBAL_THREAD_LOCK)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_WRAPPER_MAIN)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_MAIN_EXITED)
		set $reached_main = 0
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_UNHANDLED_EXCEPTION)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_THREAD_CREATED)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_THREAD_CLEANUP)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_GC_THREAD_CREATED)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_GC_THREAD_EXITED)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_THROW_EXCEPTION)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_HANDLE_EXCEPTION)
		return
		continue
	else
        if ($event == MONO_DEBUGGER_EVENT_REACHED_MAIN)
		set $reached_main = 1
		return
		if ($stop_at_main == 0)
			continue
		end
	else
	if ($event == MONO_DEBUGGER_EVENT_FINALIZE_MANAGED_CODE)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_DOMAIN_CREATE)
		return
		continue
	else
	if ($event == MONO_DEBUGGER_EVENT_DOMAIN_UNLOAD)
		return
		continue
	else
		printf "unrecognized event code %d\n", $event
	end
	end
	end
	end
	end
	end
	end
	end
	end
	end
	end
	end
	end
	end
	end
	end
	end
	end
	end
	end
	end
end