Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
10 changes: 7 additions & 3 deletions configure
Original file line number Diff line number Diff line change
Expand Up @@ -5279,8 +5279,8 @@ fi
{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if python3 is available" >&5
printf %s "checking if python3 is available... " >&6; }
# If Python returns True, that is converted to '1', which is Failure for shell:
PYTHON=`which python`
PYTHON3=`which python3`
PYTHON=`which python 2> /dev/null`
PYTHON3=`which python3 2> /dev/null`
if [ "$PYTHON" != "" ] && \
$PYTHON -c 'import sys; sys.exit(sys.version_info.major < 3)'; then
has_python=yes
Expand Down Expand Up @@ -5360,9 +5360,13 @@ PYTHON_VERSION_MINOR=`$PYTHON -c 'import sys; print("%d" % (sys.version_info.min

# If Python returns True, True is converted to '1', which is Failure for shell:
# FIXME: We should test if gdb.Frame.level is present, for GDB's need to patch.
# This came into GDB somewhere from GDB-10 to GDB-12.
# And capture_output flag came into Python3 in python 3.7.
# CentOS 8 still uses outdated Python and GDB.
if $PYTHON -c 'import sys; sys.exit('$GDB_VERSION' >= 12 and '$PYTHON_VERSION_MINOR' >= 7)'; then
echo '*** Python or GDB Python API is older: patching Python code'
patch -p1 < mcmini-oldsys.patch
echo '*** There may be some smaller missing features for mcminig-gdb.'
# patch -p1 < mcmini-oldsys.patch
fi

{ printf "%s\n" "$as_me:${as_lineno-$LINENO}: checking if process_vm_readv/process_vm_writev (CMA) available" >&5
Expand Down
4 changes: 2 additions & 2 deletions configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -53,8 +53,8 @@ fi

AC_MSG_CHECKING([if python3 is available])
# If Python returns True, that is converted to '1', which is Failure for shell:
PYTHON=`which python`
PYTHON3=`which python3`
PYTHON=`which python 2> /dev/null`
PYTHON3=`which python3 2> /dev/null`
if [[ "$PYTHON" != "" ]] && \
$PYTHON -c 'import sys; sys.exit(sys.version_info.major < 3)'; then
has_python=yes
Expand Down
10 changes: 8 additions & 2 deletions gdbinit
Original file line number Diff line number Diff line change
Expand Up @@ -48,7 +48,13 @@ continue
tbreak main
continue

# The automated script is done. It's now safe to turn pagination on.
set pagination on

# Print Python-based GDB commands:
help user-defined
echo \n\ \ *** Type 'mcmini help' for usage. ***\n
echo \ \ (Do 'set print address off' for less verbose output.)\n\n
printf "\n****************************************************************************\n\
* *** Type 'mcmini help' for usage. *** *\n\
* (Do 'set print address off' for less verbose output.) *\n\
* CONSIDER USING: ctrl-Xa ('ctrl-X' and 'a'), ctrl-Xo, 'winheight src -5' *\n\
****************************************************************************\n\n"
183 changes: 155 additions & 28 deletions gdbinit_commands.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,20 @@
assert "MCMINI_ARGS" in os.environ
mcmini_args = os.getenv("MCMINI_ARGS")
del os.environ["MCMINI_ARGS"]
if "-p0" not in mcmini_args and "'-p' '0'" not in mcmini_args:

def insert_extra(args, extra):
args = args.split()
last_flag_idx = max([ idx for (idx, word) in enumerate(args)
if word.startswith('-') or word.startswith("'-") ],
default = 0)
last_word = args[last_flag_idx].replace("'", "")
if last_word in [ "--max-depth-per-thread", "--print-at-trace"] or \
len(last_word) == 2 and last_word[1] in ['m', 'p']:
last_flag_idx += 1
args = args[:last_flag_idx+1] + extra.split() + args[last_flag_idx+1:]
return ' '.join(args)

if "-p0" not in mcmini_args.split() and "'-p' '0'" not in mcmini_args:
# If "-p0" not in the mcmini arguments, then get the trace sequence first.
# We will then add "-p 0 -p <traceSeq>" to the command line before giving
# control to gdb.
Expand All @@ -72,7 +85,7 @@
exec_file = exec_file.split('"')[1]
# FIXME: If it needs '-m', it won't print. Check if max-limit reaached,
# and then suggest to user to add '-m'.
cmd = exec_file + " -v -q " + mcmini_args
cmd = exec_file + " -v -q -p0 " + mcmini_args
print("** Generating trace sequence for:\n " + cmd)
print(" (This may take a while ...)")
mcmini_output = subprocess.run(cmd, shell=True, capture_output=True,
Expand Down Expand Up @@ -104,8 +117,33 @@
" can't compute trace sequence")
gdb.execute("quit")
extra_args = " -p 0 -p'" + trace_seq + "' "
mcmini_args = (mcmini_args.rsplit(maxsplit=1)[0] + extra_args +
mcmini_args.rsplit(maxsplit=1)[1])

mcmini_args = mcmini_args.split()
# Strip "'" if it surrounds an arg with no spaces:
for i in range(len(mcmini_args)):
if len(mcmini_args[i].strip("'"))+2 == len(mcmini_args[i]):
mcmini_args[i] = mcmini_args[i].strip("'")
# Remove any old prefixes: '-p 0,0, ...' (or variations)
for i in range(len(mcmini_args)):
if "-p" in mcmini_args[i] and "'" in mcmini_args[i] or \
i > 0 and mcmini_args[i-1] == "-p" and "'" in mcmini_args[i]:
j = i if "-p" in mcmini_args[i] else i-1
mcmini_args[j] = ""
for k in range(j+1, len(mcmini_args)):
tmp = mcmini_args[k]
mcmini_args[k] = ""
if "'" in tmp: # If this is the matching "'":
break
# Now, rejoin the edited words in mcmini_args
def delete_one(elt, args):
if len([1 for arg in args if arg == elt]):
args[args.index(elt)] = ""
delete_one('-f', mcmini_args)
delete_one('-q', mcmini_args)
delete_one('-v', mcmini_args)
mcmini_args = ' '.join([arg for arg in mcmini_args if arg != ""])

mcmini_args = insert_extra(mcmini_args, extra_args)
print("** Running: " + exec_file + "-gdb " + mcmini_args)
print("** Note: In order to replay this trace,\n" +
" it is faster to directly run the above command line.\n")
Expand Down Expand Up @@ -178,8 +216,7 @@ def select_user_frame():
frame.select()
if is_tui_active():
# We've selected the frame, but the GDB 'frame' cmd will now tell the TUI.
# For forcing TUI redisplay, alternatives to the GDB 'frame' cmd might be
# "tui disable; tui enable", or GDB "update" cmd, or GDB "down; up".
# For forcing TUI redisplay.
# FIXME: Don't do this if the TUI already knows about our frame.
# For example, 'mcmini where/print' doesn't need to change it.
gdb.execute("frame " + str( gdb.selected_frame().level() ))
Expand Down Expand Up @@ -230,10 +267,16 @@ def continue_until(function, thread_id=None):
bkpt_exit.silent = True
if thread_id:
bkpt.thread = thread_id
while bkpt.hit_count == 0:
exit_was_hit = False
while bkpt.hit_count == 0 and not exit_was_hit:
if bkpt_exit.hit_count > 0:
return
mcmini_execute("continue")
exit_was_hit = True
gdb.execute("inferior 1")
finish()
gdb.execute("inferior 1")
finish()
else:
mcmini_execute("continue")
bkpt.delete()
bkpt_exit.delete()

Expand All @@ -246,6 +289,85 @@ def finish():
# NOTE: gdb.Breakpoint.stop() can be defined to do anything arbitrary when
# reaching the breakpoint, such as print a message.

# ===========================================================
# Redirect output: gdb-msg -> /dev/null; McMini -> mcprintf_redirect()

dup_stdout = -1 # uninitialized
output = "REDIRECT UNINTIALIZED"
user_inferior = -1

def redirect_prolog(inferior=1):
# NOTE: This doesn't work for TUI; they output to curses, not stdout. :-(
# FIXME: But we can do tui-disable; update; tui-enable to get around it
# We need to capture McMini output and re-print it in tui-enabled in that case.
# TODO: For TUI, prolog should have cmd and args, disable, execute enable, print '(gdb) cmd line; output', enable
# TODO: Could maybe also print last_output before printing output if desired.
# Replace original stdout/stderr by /dev/null
# Turn pagination off; GDB junk should not go to paginated stream.
cur_pagination = gdb.parameter("pagination")
gdb.set_parameter("pagination", "off")
# GDB 'inferior XXX' normally tries to print filename, and errors and
# and sends to stderr Stop trying to print filename. This prevents that.
cur_frame_info = gdb.execute("show print frame-info", to_string=True)
cur_frame_info = cur_frame_info.split('"')[1]
gdb.execute("set print frame-info location")
### if not is_tui_active() and not tui_was_initiated:
### gdb.execute("tui enable")
### gdb.execute("tui disable")
dup_stdout = os.dup(1)
os.close(1)
cur_stdout = os.open('/dev/null', os.O_WRONLY)
assert cur_stdout == 1
# GDB messages will now go to /dev/null
user_inferior = gdb.selected_inferior().num
# FIXME: We need 'inferior 1' to call 'mcprintf_redirect()'
# But 'inferior 1' cmd sends junk msg to stderr.
# If we temporarily set stderr to /dev/null, as with stdout,
# then GDB freezes after doing 'mcmini printTransitions' twice.
gdb.execute("inferior 1") # inferior 1 is scheduler process
gdb.execute("call mcprintf_redirect()")
gdb.execute("inferior " + str(user_inferior))
# return context
return (dup_stdout, user_inferior, inferior, cur_pagination, cur_frame_info)

def redirect_epilog(context, print_hack = False):
(dup_stdout, user_inferior, inferior, cur_pagination, cur_frame_info) =context
select_user_frame()
gdb.execute("inferior " + str(inferior)) # inferior 1 is scheduler process
gdb.execute("call mcprintf_stop_redirect()")
output = gdb.parse_and_eval("mcprintf_redirect_output").string()
if user_inferior not in [inf.num for inf in gdb.inferiors()]:
user_inferior = [inf.num for inf in gdb.inferiors()][-1]
if user_inferior == 1: print("WARNING: program exited??")
gdb.execute("inferior " + str(user_inferior))
select_user_frame()
if is_tui_active():
gdb.execute("frame " + str(gdb.selected_frame().level()))
# Return to original stdout/stderr
# FIXME: When is_tui_active(), we can't replace stdout
os.close(1)
os.dup2(dup_stdout, 1)
os.close(dup_stdout)
# It's now safe to print
gdb.flush()
gdb.set_parameter("pagination", "on" if cur_pagination else "off")
# We need this hack because GDB TUI doesn't erase part of first line.
if is_tui_active() and print_hack:
print(" === ")
output = "*** " + output
print(output)
## gdb.execute('printf "' + output.replace("\n", "\\n") + '"')
## gdb.write(output)
gdb.flush()
gdb.execute("set print frame-info " + cur_frame_info)
gdb.execute("inferior" + str(inferior))
if is_tui_active():
# BUG: Doing: mcmini forward 6; mcmini printTransitionss; ^Xa; up-arrow
# then sets TUI src window to "No source available".
# If we type this below manually, it refreshes, but not under Python.
gdb.execute("frame " + str( gdb.selected_frame().level() ))
gdb.execute("refresh")

# ===========================================================
# Set up McMini commands

Expand Down Expand Up @@ -281,7 +403,7 @@ def __init__(self):
* For details of 'mcmini' commands, type 'help user-defined' and the online *
* McMini manual. *
* *
* CONSIDER USING ctrl-Xa ('ctrl-X' and 'a') TO TOGGLE SOURCE DISPLAY ON OR OFF.*
* CONSIDER USING: ctrl-Xa ('ctrl-X' and 'a'), ctrl-Xo, 'winheight src -5' *
********************************************************************************
"""
)
Expand All @@ -306,15 +428,15 @@ def __init__(self):
def invoke(self, args, from_tty):
has_exited = False
if gdb.newest_frame().name() in ["__GI__exit", "_exit"]:
gdb.execute("continue")
has_exited = True
current_inferior = gdb.selected_inferior().num
gdb.execute("inferior 1") # inferior 1 is scheduler process
gdb.execute("call programState->printTransitionStack()")
gdb.execute("call programState->printNextTransitions()")
if not has_exited:
gdb.execute("inferior " + str(current_inferior))
select_user_frame()
context = redirect_prolog()
gdb.execute("call programState->printTransitionStack()")
gdb.execute("call programState->printNextTransitions()")
## gdb.execute("set scheduler-locking off")
redirect_epilog(context, print_hack=True)
else:
print("Process has exited")
printTransitionsCmd()

class printPendingTransitionsCmd(gdb.Command):
Expand All @@ -324,11 +446,12 @@ def __init__(self):
"mcmini printPendingTransitions", gdb.COMMAND_USER
)
def invoke(self, args, from_tty):
current_inferior = gdb.selected_inferior().num
context = redirect_prolog()
user_inferior = gdb.selected_inferior().num
gdb.execute("inferior 1") # inferior 1 is scheduler process
gdb.execute("call programState->printNextTransitions()")
gdb.execute("inferior " + str(current_inferior))
select_user_frame()
gdb.execute("inferior " + str(user_inferior))
redirect_epilog(context)
printPendingTransitionsCmd()

import re
Expand Down Expand Up @@ -391,7 +514,8 @@ def invoke(self, args, from_tty):
if gdb.selected_inferior().num == 1 and gdb.inferiors()[-1].num > 0:
gdb.execute("inferior " + str(gdb.inferiors()[-1].num))
if gdb.selected_inferior().num == 1:
print("No target available. Did it exit?")
print("\n*** McMini target process has exited." +
" Suggestion: 'mcmini printTransitions'")
return
if iterations == 0:
print_current_frame_verbose()
Expand All @@ -405,15 +529,16 @@ def invoke(self, args, from_tty):
pass
continue_until("mc_shared_sem_wait_for_scheduler_done")
# FIXME: There can be many aliases for "_exit". We should use address.
if gdb.newest_frame().name() != "__GI__exit":
finish()
else:
if gdb.selected_inferior().pid == 0 or \
gdb.newest_frame().name() == "__GI__exit":
# FIXME: Stop scheduler from exiting, so that 'mcmini back' works.
gdb.execute("inferior " + str(gdb.inferiors()[-1].num))
gdb.execute("set unwindonsignal on")
if gdb.selected_inferior().pid != 0:
gdb.execute("inferior " + str(gdb.inferiors()[-1].num))
gdb.execute("set unwindonsignal on")
print("\n*** McMini scheduler has exited." +
" Suggestion: 'mcmini printTransitions'")
return
finish()
transitionId += 1
if "quiet" not in args:
select_user_frame()
Expand Down Expand Up @@ -447,6 +572,7 @@ def invoke(self, args, from_tty):
print("ERROR: Trying to go back past beginning:" +
" transitionId=%d; count=%d" % (transitionId, count))
return
context = redirect_prolog()
transitionId = 0
inferior_num = gdb.selected_inferior().num
if gdb.selected_inferior().num != 1: gdb.execute("inferior 1")
Expand All @@ -465,6 +591,7 @@ def invoke(self, args, from_tty):
continue_until("mc_shared_sem_wait_for_scheduler_done")
# We're now at the beginning of the trace (user: "mcmini_main") constructor.
continue_until("main")
redirect_epilog(context)
# We're now at the beginning of the trace (user: "main").
# After this, stap at mc_shared_sem_wait_for_scheduler_done for transition.
gdb.execute("mcmini forward " + str(iterationsForward))
Expand Down Expand Up @@ -608,13 +735,13 @@ def invoke(self, args, from_tty):
print("Breakpoint added at next visible operation in scheduler process.")
gdb.execute("break mc_run_thread_to_next_visible_operation(unsigned long)")
### These commented commands will go away, when it's clear it's not needed.
# current_inferior = gdb.selected_inferior().num
# user_inferior = gdb.selected_inferior().num
# gdb.execute("inferior 1") # Set inferior to scheduler
# scheduler_call_frame_fnc = "mc_shared_sem_wait_for_thread"
# gdb.execute("break " + scheduler_call_frame_fnc)
# This next command forces a GDB-internal bug in gdb-12.0
# gdb.FinishBreakpoint().__init__(find_call_frame_fnc(scheduler_call_frame_fnc))
# gdb.execute("inferior " + str(current_inferior))
# gdb.execute("inferior " + str(user_inferior))
gdb.execute("inferior 1")
gdb.execute("set print address on")
gdb.execute("set detach-on-fork on")
Expand Down
6 changes: 6 additions & 0 deletions mcmini-gdb.in
Original file line number Diff line number Diff line change
@@ -1,6 +1,12 @@
#!/bin/sh

export MCMINI_ROOT=@PWD@
if test ! -e $MCMINI_ROOT/mcmini; then
echo "$MCMINI_ROOT/mcmini not found"
echo "Was 'make' run during McMini install?"
echo ""
exit 1
fi
if test "$1" = ""; then
echo "USAGE: $0 [OPTIONS] TARGET_FILE"
$MCMINI_ROOT/mcmini
Expand Down
Loading