Mentions légales du service

Skip to content
Snippets Groups Projects
Commit 2efe9351 authored by Kevin Pouget's avatar Kevin Pouget
Browse files

docker filesystem for gdb.py tutorial

parents
Branches
No related tags found
No related merge requests found
Showing
with 1381 additions and 0 deletions
FROM finalduty/archlinux:monthly
#FROM kpouget/tuto-gdb.py
MAINTAINER Kevin Pouget <kpouget@imag.fr>
ENTRYPOINT ["bash"]
CMD ["init.sh"]
RUN pacman --noconfirm --needed -Sy gdb strace libdwarf make binutils gcc vim \
&& rm -rf /var/cache/pacman/pkg/ \
&& mkdir -p /var/cache/pacman/pkg/
RUN mkdir -p /home/gdb.py
WORKDIR /home/gdb.py
COPY home /home/gdb.py
COPY Dockerfile /home/gdb.py
RUN date > /home/gdb.py/.version
VOLUME /home/gdb.py
export PS1="\u@\w \\$ \[$(tput sgr0)\]"
alias ls="ls --color"
alias la="ls -a"
alias llh="ls -lh"
alias vi="vim"
export HISTFILE=/home/jfc/host/.bash_history
# no end-of-window enter-key-to-continue
set height 0
set width 0
# for docker
set disable-randomization off
set breakpoint pending on
# conveniant to debug python code
set python print-stack full
# may not be compatible with
set confirm off
# structures are not displayed inline
set print pretty
# yes !
set history filename ~/host/.gdb_history
set history save
# New commands for Python:
# (gdb) pp py_obj
# <> print(str(py_obj)) ...
#
# (gdb) ppd py_obj
# <> print(dir(py_obj))
python
import gdb
class pp(gdb.Command):
"""Python print its args"""
def __init__(self):
gdb.Command.__init__ (self, "pp", gdb.COMMAND_DATA, completer_class=gdb.COMPLETE_SYMBOL)
def invoke (self, arg, from_tty):
gdb.execute("python(print(%s))" % arg) # do it in GDB python env, not here
pp()
class ppd(gdb.Command):
"""Python print dir() of its args"""
def __init__(self):
gdb.Command.__init__ (self, "ppd", gdb.COMMAND_DATA, completer_class=gdb.COMPLETE_SYMBOL)
def invoke (self, arg, from_tty):
gdb.execute("python(print(dir(%s)))" % arg) # do it in GDB python env, not here
ppd()
end
### Tutorial slides
$HOME/presentation.pdf
### Exercise sheet
$HOME/exercices.md
### Dwarf stuff and discovery exerises
cd [host/]dwarf
make
vi prodconsum.dwarf
### Python stuff
cd [host/]python
GDB Python documentation: ./doc/index.html (Python-API.html)
https://sourceware.org/gdb/current/onlinedocs/gdb/Python-API.html
make section # builds the binary
make help # shows the available examples:
> run_fake run_return run_section run_step run_watch
# then
make run_section DEMO=y # runs the demo version of the python commands (demo/*)
make run_section [DEMO=n] # runs your version of the commands
CFLAGS := -g
all : prodconsum.dwarf
@echo
@echo "###"
@echo "### $< is ready."
@echo "###"
@echo
prodconsum : prodconsum.c
gcc ${CFLAGS} $< -o $@ -lpthread
prodconsum.dwarf : prodconsum
dwarfdump $< > $@
clean :
rm -f prodconsum prodconsum.dwarf
/*pthread example.
Copyright (c) 2006-2007 Richard Palethorpe
Permission is hereby granted, free of charge, to any person obtaining a copy of this
software and associated documentation files (the "Software"), to deal in the Software
without restriction, including without limitation the rights to use, copy, modify,
merge, publish, distribute, sublicense, and/or sell copies of the Software, and to
permit persons to whom the Software is furnished to do so, subject to the following
conditions:
The above copyright notice and this permission notice shall be included in all copies
or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR
IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
SOFTWARE
Website: richiejp.wordpress.com email: richiejp@gmail.com*/
#include <stdlib.h>
#include <stdio.h>
#include <pthread.h>
void* producer(void*);
void* consumer(void*);
/*This data structure contains information that needs to be shared between
threads. It is passed to the producer and consumer threads' starter functions.
The alternative to this is to use global variables.*/
struct Context{
pthread_cond_t* cond;
pthread_mutex_t* mutex;
/*This is used to hold a character while it is passed from one thread to
another. Only the thread which owns the mutex lock should access it.*/
char holder;
int error;
};
int main(){
volatile struct Context context;
context.cond = (pthread_cond_t*)malloc(sizeof(pthread_cond_t));
context.mutex = (pthread_mutex_t*)malloc(sizeof(pthread_mutex_t));
context.error = 0;
pthread_cond_init(context.cond, NULL);
pthread_mutex_init(context.mutex, NULL);
pthread_t prod;
pthread_t cons;
puts("createing first thread");
if(pthread_create(&prod, NULL, producer, (void*)&context) != 0){
puts("Could not create producer thread");
pthread_mutex_destroy(context.mutex);
pthread_cond_destroy(context.cond);
return(EXIT_FAILURE);
}
puts("createing second thread");
if(pthread_create(&cons, NULL, consumer, (void*)&context) != 0){
puts("Could not create consumer thread");
pthread_mutex_lock(context.mutex);
context.error = 1;
pthread_mutex_unlock(context.mutex);
pthread_cond_signal(context.cond);
pthread_join(prod, NULL);
pthread_mutex_destroy(context.mutex);
pthread_cond_destroy(context.cond);;
return(EXIT_FAILURE);
}
pthread_join(prod, NULL);
pthread_join(cons, NULL);
pthread_mutex_destroy(context.mutex);
pthread_cond_destroy(context.cond);
free(context.cond);
free(context.mutex);
return(EXIT_SUCCESS);
}
void* producer(void* _context){
puts("in producer");
struct Context* context = (struct Context*)_context;
char data[] = "Some data to send to the consumer\n";
pthread_mutex_lock(context->mutex);
int i;
for(i = 0; i < sizeof(data); i++){
if(!context->error){
context->holder = data[i];
pthread_cond_signal(context->cond);
pthread_cond_wait(context->cond, context->mutex);
}else{
pthread_mutex_unlock(context->mutex);
return NULL;
}
}
pthread_cond_signal(context->cond);
pthread_mutex_unlock(context->mutex);
return NULL;
}
void* consumer(void* _context){
puts("in consumer");
struct Context* context = (struct Context*)_context;
printf("Recieving data: ");
pthread_mutex_lock(context->mutex);
while(context->holder != '\0'){
if(!context->error){
putc((unsigned int)context->holder, stdout);
pthread_cond_signal(context->cond);
pthread_cond_wait(context->cond, context->mutex);
}else{
pthread_cond_signal(context->cond);
pthread_mutex_unlock(context->mutex);
return NULL;
}
}
pthread_cond_signal(context->cond);
pthread_mutex_unlock(context->mutex);
return NULL;
}
Scripting `gdb.py`
==================
Preliminary setup
-----------------
Prepare and launch the docker container:
HOST_DIR=/home/kevin/jdd_debug_data/ # absolute path required
docker run -it -v $HOST_DIR:/home/jcf/host -e GROUPID=$(id -g) -e USERID=$(id -u) --cap-add sys_ptrace kpouget/tuto-gdb.py
Then if you want to share the data with your host, work from `~/host`, otherwise stay in `~`:
cd ~/host # if you want to share the data with your host
Prepare the binaries:
cd dwarf/
make
cd ..
cd python
make
Discovering gdb-cli and gdb.py
------------------------------
Start GDB:
cd dwarf
gdb prodconsum
start
Go somewhere:
tbreak consumer
# stopped in a consumer thread
continue
# go to main thread
thread 1
# go to main frame
up
And now we play with gdb-cli:
print context
ptype context
print &context
And now we play with gdb.py.
`pp` is a custom command I made, it means python-print: `pi print(args)`.
`ppd` is also a custom command, it means pyton-print-dir: `pi print(dir(args))`
pp gdb.parse_and_eval("context")
ppd gdb.parse_and_eval("context")
Now we enter Python mode:
pi # or python-interactive
ctx = gdb.parse_and_eval("context")
ctx # <gdb.Value object at 0x7f1a91af3970> # ctx is a Python object abstracting the `context` variable:
err = ctx['error'] # <gdb.Value object at 0x7f1a91af38f0>
print(err)
Let's look at the types
print(err.type) # type int
dir(err.type) # sizeof
print(err.type.sizeof)
print(err.type.pointer()) # type int *
Let's play with some values:
# go back to the thread that hit the breakpoint
(gdb) info threads
(gdb) thread 3 # in my case
Then, back in pyton mode (`pi`):
_ctx = gdb.parse_and_eval("_context")
print(_ctx) # 0x7ffe6172f9f0
print(_ctx.type) # void *
Let's check the code:
gdb.execute("list") # to see the cod
122 void* consumer(void* _context){
123 puts("in consumer");
124 struct Context* context = (struct Context*)_context;
Variable `_context` is `void *`, that makes sense. But we can cast it into a `struct Context*`:
ctx_type = gdb.lookup_type("struct Context *") # doesn't work
ctx_type = gdb.lookup_type("struct Context") # works !
ctx_ptr_type = ctx_type.pointer()
print(ctx_ptr_type) # struct Context * # yes !
ctx = _ctx.cast(ctx_ptr_type).dereference()
print(ctx) # yes !
Just to try:
gdb.execute("set scheduler-locking on") # don't run anything else meanwhile
gdb.parse_and_eval("puts")("Hello world !")
gdb.execute("set scheduler-locking off")
A few other things:
gdb.execute("disassemble main")
for line in gdb.execute("disassemble main", to_string=True).split("\n"):
if 'callq' in line: print(line[:-1])
# or
frame = gdb.selected_frame()
frame.architecture().disassemble(frame.read_register("pc"))
frame.architecture().disassemble(int(frame.read_register("pc")))
frame.architecture().disassemble(int(frame.read_register("pc"))+4)
frame.architecture().disassemble(int(frame.read_register("pc"))+4*2+3)
# [{'length': 5, 'asm': 'callq 0x4008f0 <pthread_mutex_lock@plt>', 'addr': 4197358}]
Hooking into gdb.py
-------------------
We far we've seem you to run Python code in GDB cli, but not really how to script it.
Let's give it a try. Save the following code snippets into a file, and `source` them from GDB:
(gdb) source snippet-_____.py
First, we need new commands to interact with the user (`dwarf/snippet-command.py`):
import gdb
class MyCommand(gdb.Command):
def __init__(self):
gdb.Command.__init__(self, "command_tester", gdb.COMMAND_NONE)
def invoke (self, args, from_tty):
print("args: {}".format(args))
print("args well splited: {}".format(args))
for i, arg in enumerate(gdb.string_to_argv(args)):
print("{}) {}".format(i, arg))
MyCommand() # don't forget to instanciate your object
We also need ways to trigger code execution, for instance on breakpoints (`dwarf/snippet-breakpoint.py`):
import gdb
# (gdb) list prodconsum.c:104
# 102 int i;
# 103 for(i = 0; i < sizeof(data); i++){
# 104 if(!context->error){
class MyBreakpoint(gdb.Breakpoint):
def __init__ (self):
gdb.Breakpoint.__init__(self, "prodconsum.c:104",
internal=True)
self.silent = True
print("Custom breakpoint on {} set.".format(self.location))
def stop(self):
i = gdb.parse_and_eval("i") # type Value<int>
if int(i) != 9:
return False # don't stop
print("Stopped with i == 5.")
print("Letters sent by the producer so far:")
data = gdb.newest_frame().read_var("data") # type Value<char[]>
for idx in range(int(i)):
print("{}: {}".format(idx, chr(data[idx])))
return True # stop now
MyBreakpoint() # don't forget to instanciate your object
Finally, we'll try to insert code on gdb execution events (`dwarf/snippet-events.py`):
import gdb
def exit_handler(exited_event):
try:
print("EXITED: program terminated with code {}".format(exited_event.exit_code))
except AttributeError:
# Variable: ExitedEvent.exit_code
# An integer representing the exit code, if available, which the inferior has returned.
# (The exit code could be unavailable if, for example, gdb detaches from the inferior.)
# If the exit code is unavailable, the attribute does not exist.
print("EXITED: program detached (no return code)")
def new_objfile_handler(objfile_event):
print("NEW_OBJFILE: program loaded a library: {}".format(objfile_event.new_objfile.filename))
def stop_handler(stop_event):
print("STOP: program stopped because of a {}".format(stop_event.__class__.__name__))
gdb.events.exited.connect(exit_handler)
gdb.events.new_objfile.connect(new_objfile_handler)
gdb.events.stop.connect(stop_handler)
Try to run different processes with this debugger script and see how it behaves. To get a StopEvent that is not a BreakpointEvent, try:
(gdb) start
...
(gdb) print sleep(5)
^C
Program received signal SIGINT, Interrupt.
0x00007f312b916fe0 in __nanosleep_nocancel () from /usr/lib/libc.so.6
STOP: program stopped because of a SignalEvent
...
Now you're know the basics of GDB scripting ! Get back to the slides *Part 2*, to try to build new commands with real utilities!
The program is:
1. Section breakpoint
2. Return true breakpoint
3. Register watchpoint
4. Step into next call
5. Faking function execution
\ No newline at end of file
#!/bin/bash
cat <<EOF
##########
#
# Understanding, Scripting and Extending GDB
#
##########
#
##########
# Running an Archlinux container
##########
EOF
##### check env #####
if [ -z "$USERID" ]
then
echo "FATAL: USERID/GROUPID environment variables not set. Did you pass it to docker?"
echo " Please run docker as follows:"
cat <<EOF
HOST_DIR=$HOME/jcf_debug_data # create it first !
docker run -it -v \$HOST_DIR:/home/jcf/host -e GROUPID=\$(uid -g) -e USERID=\$(uid -u) --cap-add sys_ptrace kpouget/tuto-gdb.py
EOF
exec bash
fi
##### admin stuff #####
echo "root:root" | chpasswd && echo "INFO: root password set to 'root'."
groupadd --gid $GROUPID jcf --non-unique
useradd --uid $USERID --gid $GROUPID jcf
chown jcf:jcf /home/jcf/ -R
echo "INFO: Docker image version $(cat /home/jcf/.version)"
##### check strace #####
strace ls &>/dev/null
if [ $? -eq 1 ]
then
echo 'ERROR: ptrace not working. Did you pass --privileged or --cap-add sys_ptrace option to docker ?'
fi
##### prepare host #####
HOST_MNT=/home/jcf/host
if [ -z "$(ls -A $HOST_MNT/dwarf $HOST_MNT/python 2>/dev/null)" ]
then
echo "INFO: Running './prepare_host.sh' to populate $HOST_MNT host-shared directory."
su jcf -c ./prepare_host.sh
fi
echo "INFO: Read ~/README or ~/presentation.pdf for usage details"
exec su jcf
#! /usr/bin/bash
HOST_MNT=/home/jcf/host
mkdir -p $HOST_MNT
if [ "$(ls -A $HOST_MNT/dwarf $HOST_MNT/python 2>/dev/null)" ]
then
echo ERROR: host directory not empty. Edit and remove this test to continue # :)
exit
fi
cp dwarf ~/host -r
cp python ~/host -r
cp .gdbinit ~/host/python/gdbinit
echo INFO: Tutorial files copied to ~/host.
File added
CFLAGS := -g
% : %.c
gcc ${CFLAGS} $< -o $@
#
# Set demo to y to run the demonstration version of the code
#
DEMO ?= n
ifeq ($(DEMO),y)
PY_DIR := demo
GDB_MSG := USING DEMONSTRATION CODE
else
PY_DIR := .
GDB_MSG := USING YOUR CODE
endif
.PHONY: help all clean
BINARY = section
all: ${BINARY}
clean :
rm -f ${BINARY}
PREFIX="run_"
help: # list targets starting with $PREFIX
@echo "set DEMO=n|y to run your code (n) or the demo one (y)"
@$(MAKE) -pRrq -f $(lastword $(MAKEFILE_LIST)) 2>/dev/null \
| awk -v RS= -F: '/^# File/,/^# Finished Make data base/ {if ($$1 !~ "^[#.]") {print $$1}}' \
| sort \
| egrep -v -e '^[^[:alnum:]]' -e '^$@$$' \
| grep ${PREFIX} \
| xargs
###########
run_section: ${BINARY}
gdb ${BINARY} -ex "source $(PY_DIR)/section.py" -ex "echo \n$(GDB_MSG)\n" \
-ex "echo \nTEST: break_section start_profiling stop_profiling run\nTEST: run\n\n"
run_return: ${BINARY}
gdb ${BINARY} -ex "source $(PY_DIR)/break_return.py" -ex "echo \n$(GDB_MSG)\n" \
-ex "echo \nTEST: break_return run 1\nTEST: run\n\n"
run_watch: ${BINARY}
gdb ${BINARY} -ex "source $(PY_DIR)/reg_watchpoint.py" -ex "echo \n$(GDB_MSG)\n" \
-ex "echo \nTEST: start\nTEST: reg_watch eax main void *\nTEST: cont\n\n"
run_step: ${BINARY}
gdb ${BINARY} -ex "source $(PY_DIR)/step_to_next_call.py" -ex "echo \n$(GDB_MSG)\n" \
-ex "echo \nTEST: start\nTEST: next * 2\nTEST: step-into-next-call OR step-before-next-call\n\n" \
run_fake: ${BINARY}
gdb ${BINARY} -ex "source $(PY_DIR)/fake_function.py" -ex "echo \n$(GDB_MSG)\n" \
-ex "echo \nTEST: skip_function run OR fake_run_function\nTEST: run\n\n"
###############
import logging as log
import gdb
gdb.execute("source common.py") # setup path for my_gdb
import my_gdb
import my_gdb.my_archi
class FunctionReturnBreakpoint(my_gdb.FunctionBreakpoint):
def __init__ (self, fct, expected):
my_gdb.FunctionBreakpoint.__init__(self, fct)
self.expected = expected
def prepare_before(self):
return (False, # don't stop here
True, # stop at return to execute self.prepare_after
{}) # don't transmit anything to prepare_after::data
def prepare_after(self, data):
if not self.expected: # no expected value, so stop anyway
return True
ret = my_gdb.my_archi.return_value(self.expected.type) # cast return value to its expected type
...
class BreakReturn_cmd(gdb.Command):
def __init__ (self):
gdb.Command.__init__(self, "break_return", gdb.COMMAND_OBSCURE)
def invoke (self, args, from_tty):
fct, _, val = args.partition(" ")
...
print("Breakpoint set on function '{}' if it returns '{}'".format(fct, val))
BreakReturn_cmd()
import sys, os
sys.path.append(os.getcwd())
###############
import logging as log
import gdb
gdb.execute("source common.py")
import my_gdb
import my_gdb.my_archi
class FunctionReturnBreakpoint(my_gdb.FunctionBreakpoint):
def __init__ (self, fct, expected):
my_gdb.FunctionBreakpoint.__init__(self, fct)
self.expected = expected
def prepare_before(self):
return (False, # don't stop here
True, # stop at return to execute self.prepare_after
{}) # don't transmit anything to prepare_after::data
def prepare_after(self, data):
if not self.expected: # no expected value, so stop anyway
return True
ret = my_gdb.my_archi.return_value(self.expected.type) # cast return value to its expected type
log.info("Test {} return value ({}) against expected value ({})".format(self.location, ret, self.expected))
do_stop = ret == self.expected
if do_stop:
print("\nStopped after finding '{}' return value = {} in {}.".format(self.location, ret, my_gdb.my_archi.return_regname()))
gdb.execute("where 1") # prints the current frame
return do_stop
class BreakReturn_cmd(gdb.Command):
def __init__ (self):
gdb.Command.__init__(self, "break_return", gdb.COMMAND_OBSCURE)
def invoke (self, args, from_tty):
fct, _, val = args.partition(" ")
value = gdb.parse_and_eval(val) if val else None
FunctionReturnBreakpoint(fct, value)
print("Breakpoint set on function '{}' if it returns '{}'".format(fct, val))
BreakReturn_cmd()
###############
import logging as log
gdb.execute("source common.py")
import my_gdb
import my_gdb.my_archi
class SkipFunctionBreakpoint(gdb.Breakpoint):
def __init__ (self, fct):
gdb.Breakpoint.__init__(self, fct, internal=True)
self.silent = True
def stop(self):
gdb.execute("return")
return False
class SkipFunction_cmd(gdb.Command):
def __init__ (self):
gdb.Command.__init__(self, "skip_function", gdb.COMMAND_OBSCURE)
def invoke (self, args, from_tty):
SkipFunctionBreakpoint(args)
print("Skip breakpoint set on function '{}'".format(args))
SkipFunction_cmd()
###############################
class FakeRunFunctionBreakpoint(gdb.Breakpoint):
def __init__ (self):
gdb.Breakpoint.__init__(self, "run", internal=True)
self.silent = True
def stop(self):
i = int(gdb.newest_frame().read_var("i"))
if i % 10 == 0:
gdb.execute("call bug({})".format(i))
gdb.execute("return")
return False
class FakeRunFunction_cmd(gdb.Command):
def __init__ (self):
gdb.Command.__init__(self, "fake_run_function", gdb.COMMAND_OBSCURE)
def invoke (self, args, from_tty):
FakeRunFunctionBreakpoint()
print("Fake function run execution".format(args))
FakeRunFunction_cmd()
import gdb
import logging as log
gdb.execute("source common.py")
import my_gdb
class RegWatch_cmd(gdb.Command):
def __init__ (self):
gdb.Command.__init__(self, "reg_watch", gdb.COMMAND_OBSCURE)
def invoke (self, args, from_tty):
try:
args = args.split(" ")
reg, fct = args[:2]
format = " ".join(args[2:])
except Exception:
print("Usage: reg_watch register function [format]")
return
try:
if not gdb.lookup_symbol(fct)[0]:
print("Error: cannot find symbol '{}'".format(fct))
return
except gdb.error: # No frame selected.
print("Error: Please start the execution before setting reg watchpoints")
return
disa = gdb.execute("disassemble {}".format(fct), to_string=True)
watchpoints = []
for line in disa.replace("=> ", "").split("\n"):
addr, _, rest = line.strip().partition(" ")
if not "%{}".format(reg) in rest:
continue
watchpoints.append(RegWatch_bpt(addr, line, reg[1:], format))
print("{} watchpoints added in function {}".format(len(watchpoints), fct))
class RegWatch_bpt(gdb.Breakpoint):
def __init__(self, addr, line, reg, format):
gdb.Breakpoint.__init__(self, "*{}".format(addr), internal=True)
self.silent = True
self.line = line
self.reg = reg
self.format = format
def stop(self):
# before_val = str(gdb.newest_frame().read_register(self.reg))
before_val = gdb.parse_and_eval("({}) ${}".format(self.format, self.reg))
print("before: ({}) {}".format(self.format, before_val))
print(self.line)
def after():
gdb.execute("nexti", to_string=True)
# after_val = str(gdb.newest_frame().read_register(self.reg))
after_val = gdb.parse_and_eval("({}) ${}".format(self.format, self.reg))
if after_val == before_val: after_val = "<unchanged>"
else: after_val = "({}) {}".format(self.format, after_val)
print("after: {}".format(after_val))
my_gdb.before_prompt(after)
return True
RegWatch_cmd()
import gdb
# https://sourceware.org/gdb/current/onlinedocs/gdb/Breakpoints-In-Python.html#Breakpoints-In-Python
class StartStopBreakpoint(gdb.Breakpoint):
def __init__(self, loc, section_bpt, is_start):
gdb.Breakpoint.__init__(self, loc, internal=True)
self.silent = True
self.is_start = is_start
self.section_bpt = section_bpt
def stop(self):
self.section_bpt.in_section = self.is_start
# and/or
self.section_bpt.enabled = not self.is_start
return False # never stop here
class SectionBreakpoint(gdb.Breakpoint):
def __init__(self, location):
gdb.Breakpoint.__init__(self, location, internal=True)
self.silent = True
self.in_section = False
def stop(self):
if self.in_section:
return False # ignore hit in section
else:
print("Section breakpoint hit outside of section")
def double_check(): # just to be sure ...
main_frame = gdb.newest_frame().older()
if main_frame.read_var("i") != main_frame.read_var("bad"):
print("---> i != bad ??? :(")
double_check()
sal = gdb.newest_frame().find_sal()
print("Stopped in {}:{}".format(sal.symtab,sal.line))
gdb.execute("list {},{}".format(sal.line, sal.line)) # prints only the current line
return True # hit outside of section, do stop
class BreakSection_cmd(gdb.Command):
def __init__ (self):
gdb.Command.__init__(self, "break_section", gdb.COMMAND_OBSCURE)
def invoke (self, args, from_tty):
start, stop, run = args.split(" ")
section_bpt = SectionBreakpoint(run)
StartStopBreakpoint(start, section_bpt, is_start=True)
StartStopBreakpoint(stop, section_bpt, is_start=False)
print("Section breakpoint set on start={} stop={} run={}".format(start, stop, run))
BreakSection_cmd()
import gdb
SILENT_STEPS = True
def addr2num(addr):
try:
return int(addr) # Python 3
except:
return long(addr) # Python 2
def callstack_depth():
depth = 1
frame = gdb.newest_frame()
while frame is not None:
frame = frame.older()
depth += 1
return depth
class StepBeforeNextCall (gdb.Command):
def __init__ (self):
super (StepBeforeNextCall, self).__init__ ("step-before-next-call",
gdb.COMMAND_OBSCURE)
def invoke (self, arg, from_tty):
arch = gdb.selected_frame().architecture()
while True:
current_pc = addr2num(gdb.selected_frame().read_register("pc"))
disa = arch.disassemble(current_pc)[0]
if "call" in disa["asm"]: # or startswith ?
break
gdb.execute("stepi", to_string=SILENT_STEPS)
print("step-before-next-call: next instruction is a call.")
print("{}: {}".format(hex(int(disa["addr"])), disa["asm"]))
class StepIntoNextCall (gdb.Command):
def __init__ (self):
super (StepIntoNextCall, self).__init__ ("step-into-next-call",
gdb.COMMAND_OBSCURE)
def invoke (self, arg, from_tty):
start_depth = current_depth = callstack_depth()
# step until we're one step deeper
while current_depth == start_depth:
gdb.execute("step", to_string=SILENT_STEPS)
current_depth = callstack_depth()
# display information about the two top frames
print("Stepped into function {}\n".format(gdb.newest_frame().name()))
gdb.execute("frame 0")
gdb.execute("frame 1")
StepIntoNextCall()
StepBeforeNextCall()
<html lang="en">
<head>
<title>Architectures In Python - Debugging with GDB</title>
<meta http-equiv="Content-Type" content="text/html">
<meta name="description" content="Debugging with GDB">
<meta name="generator" content="makeinfo 4.13">
<link title="Top" rel="start" href="index.html#Top">
<link rel="up" href="Python-API.html#Python-API" title="Python API">
<link rel="prev" href="Lazy-Strings-In-Python.html#Lazy-Strings-In-Python" title="Lazy Strings In Python">
<link href="http://www.gnu.org/software/texinfo/" rel="generator-home" title="Texinfo Homepage">
<!--
Copyright (C) 1988-2016 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with the
Invariant Sections being ``Free Software'' and ``Free Software Needs
Free Documentation'', with the Front-Cover Texts being ``A GNU Manual,''
and with the Back-Cover Texts as in (a) below.
(a) The FSF's Back-Cover Text is: ``You are free to copy and modify
this GNU Manual. Buying copies from GNU Press supports the FSF in
developing GNU and promoting software freedom.''
-->
<meta http-equiv="Content-Style-Type" content="text/css">
<style type="text/css"><!--
pre.display { font-family:inherit }
pre.format { font-family:inherit }
pre.smalldisplay { font-family:inherit; font-size:smaller }
pre.smallformat { font-family:inherit; font-size:smaller }
pre.smallexample { font-size:smaller }
pre.smalllisp { font-size:smaller }
span.sc { font-variant:small-caps }
span.roman { font-family:serif; font-weight:normal; }
span.sansserif { font-family:sans-serif; font-weight:normal; }
--></style>
</head>
<body>
<div class="node">
<a name="Architectures-In-Python"></a>
<p>
Previous:&nbsp;<a rel="previous" accesskey="p" href="Lazy-Strings-In-Python.html#Lazy-Strings-In-Python">Lazy Strings In Python</a>,
Up:&nbsp;<a rel="up" accesskey="u" href="Python-API.html#Python-API">Python API</a>
<hr>
</div>
<h5 class="subsubsection">23.2.2.32 Python representation of architectures</h5>
<p><a name="index-Python-architectures-2297"></a>
<span class="sc">gdb</span> uses architecture specific parameters and artifacts in a
number of its various computations. An architecture is represented
by an instance of the <code>gdb.Architecture</code> class.
<p>A <code>gdb.Architecture</code> class has the following methods:
<div class="defun">
&mdash; Function: <b>Architecture.name</b> ()<var><a name="index-Architecture_002ename-2298"></a></var><br>
<blockquote><p>Return the name (string value) of the architecture.
</p></blockquote></div>
<div class="defun">
&mdash; Function: <b>Architecture.disassemble</b> (<var>start_pc </var><span class="roman">[</span><var>, end_pc </var><span class="roman">[</span><var>, count</var><span class="roman">]]</span>)<var><a name="index-Architecture_002edisassemble-2299"></a></var><br>
<blockquote><p>Return a list of disassembled instructions starting from the memory
address <var>start_pc</var>. The optional arguments <var>end_pc</var> and
<var>count</var> determine the number of instructions in the returned list.
If both the optional arguments <var>end_pc</var> and <var>count</var> are
specified, then a list of at most <var>count</var> disassembled instructions
whose start address falls in the closed memory address interval from
<var>start_pc</var> to <var>end_pc</var> are returned. If <var>end_pc</var> is not
specified, but <var>count</var> is specified, then <var>count</var> number of
instructions starting from the address <var>start_pc</var> are returned. If
<var>count</var> is not specified but <var>end_pc</var> is specified, then all
instructions whose start address falls in the closed memory address
interval from <var>start_pc</var> to <var>end_pc</var> are returned. If neither
<var>end_pc</var> nor <var>count</var> are specified, then a single instruction at
<var>start_pc</var> is returned. For all of these cases, each element of the
returned list is a Python <code>dict</code> with the following string keys:
<dl>
<dt><code>addr</code><dd>The value corresponding to this key is a Python long integer capturing
the memory address of the instruction.
<br><dt><code>asm</code><dd>The value corresponding to this key is a string value which represents
the instruction with assembly language mnemonics. The assembly
language flavor used is the same as that specified by the current CLI
variable <code>disassembly-flavor</code>. See <a href="Machine-Code.html#Machine-Code">Machine Code</a>.
<br><dt><code>length</code><dd>The value corresponding to this key is the length (integer value) of the
instruction in bytes.
</dl>
</p></blockquote></div>
</body></html>
<html lang="en">
<head>
<title>Basic Python - Debugging with GDB</title>
<meta http-equiv="Content-Type" content="text/html">
<meta name="description" content="Debugging with GDB">
<meta name="generator" content="makeinfo 4.13">
<link title="Top" rel="start" href="index.html#Top">
<link rel="up" href="Python-API.html#Python-API" title="Python API">
<link rel="next" href="Exception-Handling.html#Exception-Handling" title="Exception Handling">
<link href="http://www.gnu.org/software/texinfo/" rel="generator-home" title="Texinfo Homepage">
<!--
Copyright (C) 1988-2016 Free Software Foundation, Inc.
Permission is granted to copy, distribute and/or modify this document
under the terms of the GNU Free Documentation License, Version 1.3 or
any later version published by the Free Software Foundation; with the
Invariant Sections being ``Free Software'' and ``Free Software Needs
Free Documentation'', with the Front-Cover Texts being ``A GNU Manual,''
and with the Back-Cover Texts as in (a) below.
(a) The FSF's Back-Cover Text is: ``You are free to copy and modify
this GNU Manual. Buying copies from GNU Press supports the FSF in
developing GNU and promoting software freedom.''
-->
<meta http-equiv="Content-Style-Type" content="text/css">
<style type="text/css"><!--
pre.display { font-family:inherit }
pre.format { font-family:inherit }
pre.smalldisplay { font-family:inherit; font-size:smaller }
pre.smallformat { font-family:inherit; font-size:smaller }
pre.smallexample { font-size:smaller }
pre.smalllisp { font-size:smaller }
span.sc { font-variant:small-caps }
span.roman { font-family:serif; font-weight:normal; }
span.sansserif { font-family:sans-serif; font-weight:normal; }
--></style>
</head>
<body>
<div class="node">
<a name="Basic-Python"></a>
<p>
Next:&nbsp;<a rel="next" accesskey="n" href="Exception-Handling.html#Exception-Handling">Exception Handling</a>,
Up:&nbsp;<a rel="up" accesskey="u" href="Python-API.html#Python-API">Python API</a>
<hr>
</div>
<h5 class="subsubsection">23.2.2.1 Basic Python</h5>
<p><a name="index-python-stdout-1774"></a><a name="index-python-pagination-1775"></a>At startup, <span class="sc">gdb</span> overrides Python's <code>sys.stdout</code> and
<code>sys.stderr</code> to print using <span class="sc">gdb</span>'s output-paging streams.
A Python program which outputs to one of these streams may have its
output interrupted by the user (see <a href="Screen-Size.html#Screen-Size">Screen Size</a>). In this
situation, a Python <code>KeyboardInterrupt</code> exception is thrown.
<p>Some care must be taken when writing Python code to run in
<span class="sc">gdb</span>. Two things worth noting in particular:
<ul>
<li><span class="sc">gdb</span> install handlers for <code>SIGCHLD</code> and <code>SIGINT</code>.
Python code must not override these, or even change the options using
<code>sigaction</code>. If your program changes the handling of these
signals, <span class="sc">gdb</span> will most likely stop working correctly. Note
that it is unfortunately common for GUI toolkits to install a
<code>SIGCHLD</code> handler.
<li><span class="sc">gdb</span> takes care to mark its internal file descriptors as
close-on-exec. However, this cannot be done in a thread-safe way on
all platforms. Your Python programs should be aware of this and
should both create new file descriptors with the close-on-exec flag
set and arrange to close unneeded file descriptors before starting a
child process.
</ul>
<p><a name="index-python-functions-1776"></a><a name="index-python-module-1777"></a><a name="index-gdb-module-1778"></a><span class="sc">gdb</span> introduces a new Python module, named <code>gdb</code>. All
methods and classes added by <span class="sc">gdb</span> are placed in this module.
<span class="sc">gdb</span> automatically <code>import</code>s the <code>gdb</code> module for
use in all scripts evaluated by the <code>python</code> command.
<p><a name="index-gdb_002ePYTHONDIR-1779"></a>
<div class="defun">
&mdash; Variable: <b>gdb.PYTHONDIR</b><var><a name="index-gdb_002ePYTHONDIR-1780"></a></var><br>
<blockquote><p>A string containing the python directory (see <a href="Python.html#Python">Python</a>).
</p></blockquote></div>
<p><a name="index-gdb_002eexecute-1781"></a>
<div class="defun">
&mdash; Function: <b>gdb.execute</b> (<var>command </var><span class="roman">[</span><var>, from_tty </var><span class="roman">[</span><var>, to_string</var><span class="roman">]]</span>)<var><a name="index-gdb_002eexecute-1782"></a></var><br>
<blockquote><p>Evaluate <var>command</var>, a string, as a <span class="sc">gdb</span> CLI command.
If a GDB exception happens while <var>command</var> runs, it is
translated as described in <a href="Exception-Handling.html#Exception-Handling">Exception Handling</a>.
<p>The <var>from_tty</var> flag specifies whether <span class="sc">gdb</span> ought to consider this
command as having originated from the user invoking it interactively.
It must be a boolean value. If omitted, it defaults to <code>False</code>.
<p>By default, any output produced by <var>command</var> is sent to
<span class="sc">gdb</span>'s standard output (and to the log output if logging is
turned on). If the <var>to_string</var> parameter is
<code>True</code>, then output will be collected by <code>gdb.execute</code> and
returned as a string. The default is <code>False</code>, in which case the
return value is <code>None</code>. If <var>to_string</var> is <code>True</code>, the
<span class="sc">gdb</span> virtual terminal will be temporarily set to unlimited width
and height, and its pagination will be disabled; see <a href="Screen-Size.html#Screen-Size">Screen Size</a>.
</p></blockquote></div>
<p><a name="index-gdb_002ebreakpoints-1783"></a>
<div class="defun">
&mdash; Function: <b>gdb.breakpoints</b> ()<var><a name="index-gdb_002ebreakpoints-1784"></a></var><br>
<blockquote><p>Return a sequence holding all of <span class="sc">gdb</span>'s breakpoints.
See <a href="Breakpoints-In-Python.html#Breakpoints-In-Python">Breakpoints In Python</a>, for more information. In <span class="sc">gdb</span>
version 7.11 and earlier, this function returned <code>None</code> if there
were no breakpoints. This peculiarity was subsequently fixed, and now
<code>gdb.breakpoints</code> returns an empty sequence in this case.
</p></blockquote></div>
<p><a name="index-gdb_002eparameter-1785"></a>
<div class="defun">
&mdash; Function: <b>gdb.parameter</b> (<var>parameter</var>)<var><a name="index-gdb_002eparameter-1786"></a></var><br>
<blockquote><p>Return the value of a <span class="sc">gdb</span> <var>parameter</var> given by its name,
a string; the parameter name string may contain spaces if the parameter has a
multi-part name. For example, &lsquo;<samp><span class="samp">print object</span></samp>&rsquo; is a valid
parameter name.
<p>If the named parameter does not exist, this function throws a
<code>gdb.error</code> (see <a href="Exception-Handling.html#Exception-Handling">Exception Handling</a>). Otherwise, the
parameter's value is converted to a Python value of the appropriate
type, and returned.
</p></blockquote></div>
<p><a name="index-gdb_002ehistory-1787"></a>
<div class="defun">
&mdash; Function: <b>gdb.history</b> (<var>number</var>)<var><a name="index-gdb_002ehistory-1788"></a></var><br>
<blockquote><p>Return a value from <span class="sc">gdb</span>'s value history (see <a href="Value-History.html#Value-History">Value History</a>). The <var>number</var> argument indicates which history element to return.
If <var>number</var> is negative, then <span class="sc">gdb</span> will take its absolute value
and count backward from the last element (i.e., the most recent element) to
find the value to return. If <var>number</var> is zero, then <span class="sc">gdb</span> will
return the most recent element. If the element specified by <var>number</var>
doesn't exist in the value history, a <code>gdb.error</code> exception will be
raised.
<p>If no exception is raised, the return value is always an instance of
<code>gdb.Value</code> (see <a href="Values-From-Inferior.html#Values-From-Inferior">Values From Inferior</a>).
</p></blockquote></div>
<p><a name="index-gdb_002eparse_005fand_005feval-1789"></a>
<div class="defun">
&mdash; Function: <b>gdb.parse_and_eval</b> (<var>expression</var>)<var><a name="index-gdb_002eparse_005fand_005feval-1790"></a></var><br>
<blockquote><p>Parse <var>expression</var>, which must be a string, as an expression in
the current language, evaluate it, and return the result as a
<code>gdb.Value</code>.
<p>This function can be useful when implementing a new command
(see <a href="Commands-In-Python.html#Commands-In-Python">Commands In Python</a>), as it provides a way to parse the
command's argument as an expression. It is also useful simply to
compute values, for example, it is the only way to get the value of a
convenience variable (see <a href="Convenience-Vars.html#Convenience-Vars">Convenience Vars</a>) as a <code>gdb.Value</code>.
</p></blockquote></div>
<p><a name="index-gdb_002efind_005fpc_005fline-1791"></a>
<div class="defun">
&mdash; Function: <b>gdb.find_pc_line</b> (<var>pc</var>)<var><a name="index-gdb_002efind_005fpc_005fline-1792"></a></var><br>
<blockquote><p>Return the <code>gdb.Symtab_and_line</code> object corresponding to the
<var>pc</var> value. See <a href="Symbol-Tables-In-Python.html#Symbol-Tables-In-Python">Symbol Tables In Python</a>. If an invalid
value of <var>pc</var> is passed as an argument, then the <code>symtab</code> and
<code>line</code> attributes of the returned <code>gdb.Symtab_and_line</code> object
will be <code>None</code> and 0 respectively.
</p></blockquote></div>
<p><a name="index-gdb_002epost_005fevent-1793"></a>
<div class="defun">
&mdash; Function: <b>gdb.post_event</b> (<var>event</var>)<var><a name="index-gdb_002epost_005fevent-1794"></a></var><br>
<blockquote><p>Put <var>event</var>, a callable object taking no arguments, into
<span class="sc">gdb</span>'s internal event queue. This callable will be invoked at
some later point, during <span class="sc">gdb</span>'s event processing. Events
posted using <code>post_event</code> will be run in the order in which they
were posted; however, there is no way to know when they will be
processed relative to other events inside <span class="sc">gdb</span>.
<p><span class="sc">gdb</span> is not thread-safe. If your Python program uses multiple
threads, you must be careful to only call <span class="sc">gdb</span>-specific
functions in the <span class="sc">gdb</span> thread. <code>post_event</code> ensures
this. For example:
<pre class="smallexample"> (gdb) python
&gt;import threading
&gt;
&gt;class Writer():
&gt; def __init__(self, message):
&gt; self.message = message;
&gt; def __call__(self):
&gt; gdb.write(self.message)
&gt;
&gt;class MyThread1 (threading.Thread):
&gt; def run (self):
&gt; gdb.post_event(Writer("Hello "))
&gt;
&gt;class MyThread2 (threading.Thread):
&gt; def run (self):
&gt; gdb.post_event(Writer("World\n"))
&gt;
&gt;MyThread1().start()
&gt;MyThread2().start()
&gt;end
(gdb) Hello World
</pre>
</blockquote></div>
<p><a name="index-gdb_002ewrite-1795"></a>
<div class="defun">
&mdash; Function: <b>gdb.write</b> (<var>string </var><span class="roman">[</span><var>, stream</var>])<var><a name="index-gdb_002ewrite-1796"></a></var><br>
<blockquote><p>Print a string to <span class="sc">gdb</span>'s paginated output stream. The
optional <var>stream</var> determines the stream to print to. The default
stream is <span class="sc">gdb</span>'s standard output stream. Possible stream
values are:
<a name="index-STDOUT-1797"></a>
<a name="index-gdb_002eSTDOUT-1798"></a>
<dl><dt><code>gdb.STDOUT</code><dd><span class="sc">gdb</span>'s standard output stream.
<p><a name="index-STDERR-1799"></a><a name="index-gdb_002eSTDERR-1800"></a><br><dt><code>gdb.STDERR</code><dd><span class="sc">gdb</span>'s standard error stream.
<p><a name="index-STDLOG-1801"></a><a name="index-gdb_002eSTDLOG-1802"></a><br><dt><code>gdb.STDLOG</code><dd><span class="sc">gdb</span>'s log stream (see <a href="Logging-Output.html#Logging-Output">Logging Output</a>).
</dl>
<p>Writing to <code>sys.stdout</code> or <code>sys.stderr</code> will automatically
call this function and will automatically direct the output to the
relevant stream.
</p></blockquote></div>
<p><a name="index-gdb_002eflush-1803"></a>
<div class="defun">
&mdash; Function: <b>gdb.flush</b> ()<var><a name="index-gdb_002eflush-1804"></a></var><br>
<blockquote><p>Flush the buffer of a <span class="sc">gdb</span> paginated stream so that the
contents are displayed immediately. <span class="sc">gdb</span> will flush the
contents of a stream automatically when it encounters a newline in the
buffer. The optional <var>stream</var> determines the stream to flush. The
default stream is <span class="sc">gdb</span>'s standard output stream. Possible
stream values are:
<a name="index-STDOUT-1805"></a>
<a name="index-gdb_002eSTDOUT-1806"></a>
<dl><dt><code>gdb.STDOUT</code><dd><span class="sc">gdb</span>'s standard output stream.
<p><a name="index-STDERR-1807"></a><a name="index-gdb_002eSTDERR-1808"></a><br><dt><code>gdb.STDERR</code><dd><span class="sc">gdb</span>'s standard error stream.
<p><a name="index-STDLOG-1809"></a><a name="index-gdb_002eSTDLOG-1810"></a><br><dt><code>gdb.STDLOG</code><dd><span class="sc">gdb</span>'s log stream (see <a href="Logging-Output.html#Logging-Output">Logging Output</a>).
</dl>
<p>Flushing <code>sys.stdout</code> or <code>sys.stderr</code> will automatically
call this function for the relevant stream.
</p></blockquote></div>
<p><a name="index-gdb_002etarget_005fcharset-1811"></a>
<div class="defun">
&mdash; Function: <b>gdb.target_charset</b> ()<var><a name="index-gdb_002etarget_005fcharset-1812"></a></var><br>
<blockquote><p>Return the name of the current target character set (see <a href="Character-Sets.html#Character-Sets">Character Sets</a>). This differs from <code>gdb.parameter('target-charset')</code> in
that &lsquo;<samp><span class="samp">auto</span></samp>&rsquo; is never returned.
</p></blockquote></div>
<p><a name="index-gdb_002etarget_005fwide_005fcharset-1813"></a>
<div class="defun">
&mdash; Function: <b>gdb.target_wide_charset</b> ()<var><a name="index-gdb_002etarget_005fwide_005fcharset-1814"></a></var><br>
<blockquote><p>Return the name of the current target wide character set
(see <a href="Character-Sets.html#Character-Sets">Character Sets</a>). This differs from
<code>gdb.parameter('target-wide-charset')</code> in that &lsquo;<samp><span class="samp">auto</span></samp>&rsquo; is
never returned.
</p></blockquote></div>
<p><a name="index-gdb_002esolib_005fname-1815"></a>
<div class="defun">
&mdash; Function: <b>gdb.solib_name</b> (<var>address</var>)<var><a name="index-gdb_002esolib_005fname-1816"></a></var><br>
<blockquote><p>Return the name of the shared library holding the given <var>address</var>
as a string, or <code>None</code>.
</p></blockquote></div>
<p><a name="index-gdb_002edecode_005fline-1817"></a>
<div class="defun">
&mdash; Function: <b>gdb.decode_line</b> <span class="roman">[</span><var>expression</var><span class="roman">]</span><var><a name="index-gdb_002edecode_005fline-1818"></a></var><br>
<blockquote><p>Return locations of the line specified by <var>expression</var>, or of the
current line if no argument was given. This function returns a Python
tuple containing two elements. The first element contains a string
holding any unparsed section of <var>expression</var> (or <code>None</code> if
the expression has been fully parsed). The second element contains
either <code>None</code> or another tuple that contains all the locations
that match the expression represented as <code>gdb.Symtab_and_line</code>
objects (see <a href="Symbol-Tables-In-Python.html#Symbol-Tables-In-Python">Symbol Tables In Python</a>). If <var>expression</var> is
provided, it is decoded the way that <span class="sc">gdb</span>'s inbuilt
<code>break</code> or <code>edit</code> commands do (see <a href="Specify-Location.html#Specify-Location">Specify Location</a>).
</p></blockquote></div>
<div class="defun">
&mdash; Function: <b>gdb.prompt_hook</b> (<var>current_prompt</var>)<var><a name="index-gdb_002eprompt_005fhook-1819"></a></var><br>
<blockquote><p><a name="prompt_005fhook"></a>If <var>prompt_hook</var> is callable, <span class="sc">gdb</span> will call the method
assigned to this operation before a prompt is displayed by
<span class="sc">gdb</span>.
<p>The parameter <code>current_prompt</code> contains the current <span class="sc">gdb</span>
prompt. This method must return a Python string, or <code>None</code>. If
a string is returned, the <span class="sc">gdb</span> prompt will be set to that
string. If <code>None</code> is returned, <span class="sc">gdb</span> will continue to use
the current prompt.
<p>Some prompts cannot be substituted in <span class="sc">gdb</span>. Secondary prompts
such as those used by readline for command input, and annotation
related prompts are prohibited from being changed.
</p></blockquote></div>
</body></html>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment