كيفية السيناريو GDB (مع Python)؟ مثال إضافة نقاط التوقف ، تشغيل ، ما نقطة التوقف التي وصلنا إليها؟

StackOverflow https://stackoverflow.com/questions/4060565

سؤال

أحاول إنشاء اختبار وحدة صغير مع GDB ، من أجل MCU مضمن يتم التحكم فيه بواسطة OpenOcd (وهذا يعطيني السيطرة على هدفي عبر خادم GDB).

لذلك أود أن أتم أتمتة هذا مع بعض البرمجة النصية لـ GDB.

أود أن أكتب نوعًا من البرنامج النصي لـ GDB أن هذا أكثر أو أقل يفعل هذا:

  1. أضف بضع نقاط التوقف
  2. ابدأ البرنامج
  3. عندما نتوقف ، أين توقف (احصل على معلومات الإطار)
  4. يترك.

أيه أفكار؟

مثال على كيفية القيام بذلك في 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 كمترجم":

... بمعنى ، يمكن للمرء أن يكتب ملف نصي البرنامج النصي مع خط 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 ولا شيء آخر؟

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top