كيفية السيناريو GDB (مع Python)؟ مثال إضافة نقاط التوقف ، تشغيل ، ما نقطة التوقف التي وصلنا إليها؟
-
27-09-2019 - |
سؤال
أحاول إنشاء اختبار وحدة صغير مع GDB ، من أجل MCU مضمن يتم التحكم فيه بواسطة OpenOcd (وهذا يعطيني السيطرة على هدفي عبر خادم GDB).
لذلك أود أن أتم أتمتة هذا مع بعض البرمجة النصية لـ GDB.
أود أن أكتب نوعًا من البرنامج النصي لـ GDB أن هذا أكثر أو أقل يفعل هذا:
- أضف بضع نقاط التوقف
- ابدأ البرنامج
- عندما نتوقف ، أين توقف (احصل على معلومات الإطار)
- يترك.
أيه أفكار؟
مثال على كيفية القيام بذلك في Python GDB Scripting سيكون لطيفًا.
شكرا يوهان
ملحوظة:
دعنا نقول أن لدينا هذا الهيكل الأساسي ، أن الأمر أكثر أو أقل يذهب إلى test_failed () أو test_success () اعتمادًا على ما يعيده الوظيفة start_test ().
void test_failed() {
while(1);
}
void test_success() {
while(1);
}
int main(void) {
int status = start_test();
if( status > 0 ) {
test_failed();
}
test_success();
while(1);
}
للقيام بذلك يدويًا في GDB هو مضيق للغاية ،
(gdb) break test_success
Breakpoint 1 at 0x20: file src/main.c, line 9.
(gdb) break test_failed
Breakpoint 2 at 0x18: file src/main.c, line 5.
(gdb) cont
Continuing.
Breakpoint 1, test_success () at src/main.c:9
9 while(1);
(gdb) frame
#0 test_success () at src/main.c:9
9 while(1);
(gdb)
لذا فإن الخطوة التالية التي حاولت فيها هي إضافة أوامر GDB هذه إلى برنامج نصي بدء التشغيل GDB والذي بدا أكثر أو أقل هكذا.
break test_success
break test_failed
target remote localhost:3333
cont
frame
وابدأ معها
arm-none-eabi-gdb --batch --command=commands.gdb main.elf
وهذا النوع من الأعمال ، لكنه ليس لطيفًا جدًا. كيف أفعل هذا باستخدام البرامج النصية "الجديدة والرائعة" ، يبدو أن GDB يدعمها.
المحلول
إصدارات GDB الحديثة FYI قابلة للنص في Python. يمكنك استدعاء رمز Python من سطر أوامر GDB. هذا يفتح عالمًا جديدًا بالكامل ، والتحقق من الوثائق ذات الصلة. من تشغيل سطر الأوامر:
dnf/yum/apt-get install gdb-doc
info gdb extending python
إذا كنت لا تحب متصفح المعلومات المستندة إلى النص ، فإليك واحد (من بين العديد من؟) متصفح بديل ورسومي:
yelp 'info:gdb' # , go to "Extending"
هنا نموذج GDB-Python السيناريو. يعلق GDB بأول "your_program" التي تم العثور عليها.
#!/usr/bin/python
import subprocess
import string
def backquotes(cmdwords):
output = subprocess.Popen(cmdwords, stdout=subprocess.PIPE).communicate()[0]
return output.strip()
pid = backquotes(['pgrep', 'your_program'])
gdb.execute("attach " + str(pid))
نصائح أخرى
مثال مخفض أستخدمه حاليًا:
class DebugPrintingBreakpoint(gdb.Breakpoint):
debugging_IDs = frozenset({37, 153, 420})
def stop(self):
top = gdb.newest_frame()
someVector = top.read_var('aVectorVar')
# Access the begin() & end() pointer of std::vector in GNU Standard C++ lib
first = someVector['_M_impl']['_M_start']
last = someVector['_M_impl']['_M_finish']
values = []
while first != last:
values.append(int(first.dereference()['intID']))
first = first + 1
if not set(values) & debugging_IDs:
return False # skip: none of the items we're looking for can be found by ID in the vector on the stack
print("Found other accompanying IDs: {}".format(values))
return True # drop to gdb's prompt
# Ensure shared libraries are loaded already
gdb.execute("start")
# Set our breakpoint, which happens to reside in some shared lib, hence the "start" previously
DebugPrintingBreakpoint("source.cpp:42")
gdb.execute("continue")
يمكنك تنفيذ هذا البرنامج النصي من موجه GDB مثل هذا:
(gdb) source script.py
أو من سطر الأوامر:
$ gdb --command script.py ./executable.elf
انظر الكامل مستندات GDB Python API لمزيد من المعلومات.
حسنًا ، لقد وجدت الإجابة أثناء طرح السؤال ... وكان الأمر بسيطًا حقًا.
يجب ألا تستخدم كل من "-Command" و "-eval" في نفس الوقت إذا كنت تتوقع أن يتم تنفيذها بطلب محدد!
هناك طريقة أكثر انطلاقًا هي وضع كل شيء في ملف الأوامر. gdb وتجاهل -eval.
لذلك يصبح شيئًا من هذا القبيل:
arm-none-eabi-gdb --batch --command=commands.gdb main.elf
حيث تبدو الأوامر. gdb مثل هذا:
break test_success
break test_failed
target remote localhost:3333
cont
frame
ولكن من المحتمل أن يكون من أجمل القيام بذلك بشيء مثل Python بدلاً من ذلك.
أردت فقط أن ألاحظ شيئًا أجده مربكًا كلما عدت إلى هذا الموضوع (ملاحظة ، أنا حاليًا على Ubuntu 14.04 ، GNU GDB (Ubuntu 7.7.1-0ubuntu5 ~ 14.04.3) 7.7.1):
أولاً ، هناك مراجع حول كونها "من الممكن استدعاء gdb
كمترجم":
- 9. البرمجة النصية GDB - منحدرات inanity (http://tromey.com/blog/؟p=548)
- http://blog.scottt.tw/2012/01/exploring-gdb-python-api-with-ipython_31.html
... بمعنى ، يمكن للمرء أن يكتب ملف نصي البرنامج النصي مع خط Shebang #!/usr/bin/gbd -P
أو #!/usr/bin/gbd --python
, ، ثم اكتب رمز Python فيه ، ثم اجعله قابلاً للتنفيذ chmod +x pygdbscript
, ، ثم اركض ./pygdbscript
; ؛ ... ولكن كما في هذا المنشور:
...، انا حصلت gdb: unrecognized option '--python'
إذا حاولت أي شيء من هذا القبيل. يبدو أن هذا الخيار هو/كان ميزة في بعض فرع "Archer" gdb
?!
لذلك ، من أجل تشغيل نص Python في gdb
, ، هناك بالفعل طريقتان:
- قم بتسمية ملف البرنامج النصي الخاص بك بالتمديد
.py
; ؛ قلtest.py
هنا:
def Something():
print("hello from python")
Something()
gdb.execute("quit");
لاحظ ، في هذه الحالة ، تكتب فقط رمز الثعبان العادي ؛ وأنت لا تحتاج إلى import gdb
من أجل الوصول إلى gdb
هدف. هذا يمكنك الركض مع أي من:
gdb -x test.py
gdb -x=test.py
gdb --command test.py
gdb --command=test.py
gdb -command test.py
gdb -command=test.py
... والتي تبدو متكافئة ، كنتيجة لأي من هذه هي نفس النسخة المطبوعة ، من قبل gdb
يتم توجيهه للخروج من البرنامج النصي:
$ gdb -x=test.py
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1
...
For help, type "help".
Type "apropos word" to search for commands related to "word".
hello from python
ملاحظة هذا بالنسبة لهذه الحالة ، أيضا أسماء مثل test.gdb.py
سيتم تفسيرها على أنها نصوص بيثون نقية ، منذ ذلك الحين تنتهي .py
.
- اسم البرنامج النصي الخاص بك هل من شيء آخر - طالما أنه يفعل ليس انتهت ب
.py
امتداد؛ قلtest.pygdb
هنا:
python
def Something():
print("hello from python")
Something()
gdb.execute("quit");
end
في هذه الحالة، gdb
يفسر البرنامج النصي باعتباره أ gdb
السيناريو ، أي مع gdb
الأوامر - وهذا يعني ، أن رمز الثعبان الذي قد ترغب في الكتابة فيه هنا ، يجب أن تكون ملفوفة في "python
"كخط انطلاق و"end
"في نهاية رمز Python. مرة أخرى ، سيتم استدعاؤه مع أي من هذه المكالمات المكافئة:
gdb -x test.pygdb
gdb -x=test.pygdb
gdb --command test.pygdb
gdb --command=test.pygdb
gdb -command test.pygdb
gdb -command=test.pygdb
... وبعد ذلك يكون الإخراج هو نفسه كما في الحالة السابقة (لأنه نفس البرنامج النصي للثعبان الذي يعمل):
$ gdb -x test.pygdb
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1
...
hello from python
استجابةً لـ OP: إذا كان رمز C في OP موجود /tmp/myprog.c
- مع إضافة int start_test() { return rand() % 50; }
في الأعلى ، وإلا فلن يتم تجميعه - ويتم تجميعه مع gcc -g myprog.c -o myprog.exe
داخل /tmp/myprog.exe
; ؛ ثم يمكنك استخدام أ myprog.gdb.py
السيناريو مثل هذا:
# need to specify the executable file which we debug (in this case, not from command line)
# here `gdb` command `file` is used - it does not have a Python equivalent (https://sourceware.org/gdb/onlinedocs/gdb/Objfiles-In-Python.html#index-Objfile_002eframe_005ffilters)
# so we must use gdb.execute:
myexefile="/tmp/myprog.exe"
print("""
### myprog.gdb.py is running: """ + myexefile + """ - and adding breakpoints:
""")
gdb.execute("file " + myexefile)
gdb.execute("set pagination off")
ax = gdb.Breakpoint("test_success")
bx = gdb.Breakpoint("test_failed")
gdb.execute("run")
# here the program will break, so we can do:
print("""
### myprog.gdb.py after the break - current stack frame:
""")
current_frame_at_break = gdb.selected_frame()
print(current_frame_at_break) # instead of gdb.execute("frame")
print("""
### myprog.gdb.py - backtrace:
""")
gdb.execute("backtrace 2")
print("""
### myprog.gdb.py - go to frame that called current frame:
""")
parent_frame = current_frame_at_break.older()
print(parent_frame)
status_var = parent_frame.read_var("status")
print("status_var is: ", status_var)
... ثم قم بتشغيل هذا البرنامج النصي مع:
$ gdb -x myprog.gdb.py
GNU gdb (Ubuntu 7.7.1-0ubuntu5~14.04.3) 7.7.1
....
For help, type "help".
Type "apropos word" to search for commands related to "word".
### myprog.gdb.py is running: /tmp/myprog.exe - and adding breakpoints:
Breakpoint 1 at 0x400565: file myprog.c, line 8.
Breakpoint 2 at 0x40055f: file myprog.c, line 4.
Breakpoint 2, test_failed () at myprog.c:4
4 while(1);
### myprog.gdb.py after the break - current stack frame:
{stack=0x7fffffffdc70,code=0x40055b,!special}
### myprog.gdb.py - backtrace:
#0 test_failed () at myprog.c:4
#1 0x000000000040058c in main () at myprog.c:15
### myprog.gdb.py - go to frame that called current frame:
{stack=0x7fffffffdc90,code=0x400567,!special}
status_var is: 33
(gdb)
لاحظ أنه في نهاية هذا البرنامج النصي ، (gdb)
يبقى موجه تفاعلي ، ويمكنك استخدامه عادة هنا ؛ إذا لم تكن بحاجة إلى موجه تفاعلي ، فيمكنك القيام به gdb.execute("quit");
كما في البرامج النصية المذكورة أعلاه لإفراد gdb
للخروج بدلاً من ذلك في نهاية تنفيذ النص.
أيضًا ، للحصول على مثال على فئة نقطة التوقف الفرعية في GDB Python ، انظر كيفية طباعة السطر الحالي للمصدر عند نقطة الإيقاف في GDB ولا شيء آخر؟