2010-04-18 33 views
39

Tôi đang phát triển các phần mở rộng C từ quảng cáo python tôi nhận được một số segfaults (không thể tránh khỏi trong quá trình phát triển ...).python truy tìm lỗi phân đoạn

Tôi đang tìm cách hiển thị dòng mã nào xảy ra (một ý tưởng giống như truy tìm từng dòng mã), cách tôi có thể thực hiện điều đó?

Trả lời

32

Dưới đây là một cách để đầu ra số tên tập tin và dòng mỗi dòng của Python mã của bạn chạy:

import sys 

def trace(frame, event, arg): 
    print "%s, %s:%d" % (event, frame.f_code.co_filename, frame.f_lineno) 
    return trace 

def test(): 
    print "Line 8" 
    print "Line 9" 

sys.settrace(trace) 
test() 

Output:

call, test.py:7 
line, test.py:8 
Line 8 
line, test.py:9 
Line 9 
return, test.py:9 

(Bạn có thể muốn viết dấu vết đầu ra cho một tệp, tất nhiên.)

+0

Tính năng này có hoạt động với các tiện ích mở rộng C không? –

+1

@MadPhysicist: Nó sẽ không in số dòng mã C của bạn, nếu đó là ý của bạn. :-) Nó sẽ in số dòng của mã Python gọi vào mã C của bạn. – RichieHindle

+0

Đó là những gì tôi muốn nói. Tôi tìm thấy câu hỏi ban đầu thú vị vì tôi đã có cùng một vấn đề. Segfault hóa ra là vì mã C của tôi đã chèn một phần tử NULL vào một PyList_Object. Nó biểu hiện chính nó ở phía Python khi tôi cố gắng lặp qua danh sách. Bạn không chắc chắn một trình gỡ lỗi python có thể đã giúp nhiều trong trường hợp đó. –

54

Nếu bạn đang sử dụng Linux, hãy chạy python theo gdb

gdb python 
(gdb) run /path/to/script.py 
## wait for segfault ## 
(gdb) backtrace 
## stack trace of the c code 
+7

Nếu bạn đã có một tệp lõi, bạn có thể sử dụng 'gdb python core' (hoặc bất kỳ tệp lõi nào được gọi). Nếu bạn đang sử dụng OSX, các vùng lõi (không được tạo theo mặc định; xem 'ulimit -c') được lưu trữ trong thư mục'/cores'. –

+0

Tôi thực sự muốn đây là câu trả lời đầu tiên cho tôi khi đọc tất cả những người khác đã dành thời gian đáng kể tôi muốn có chi tiêu chạy xuống lỗi của tôi. – sage

+0

Nếu bạn nhận được một segfault trong khi chạy một thử nghiệm đơn vị Python như 'python -m unittest my.module.tests.mytest', sau đó chuyển đổi' -m' gây nhầm lẫn 'gdb'. Gọi bằng cách sử dụng tùy chọn '--args' như sau:' gdb --args python -m unittest my.module.tests.mytest' –

15

Segfaults từ phần mở rộng C thường là kết quả của việc không tăng số lượng tham chiếu khi bạn tạo tham chiếu mới cho đối tượng. Điều đó làm cho chúng rất khó để theo dõi khi segfault xảy ra chỉ sau khi tham chiếu cuối cùng được loại bỏ khỏi đối tượng, và thậm chí sau đó thường chỉ khi một số đối tượng khác đang được cấp phát.

Bạn không nói số lượng mã mở rộng C bạn đã viết cho đến thời điểm này, nhưng nếu bạn chỉ mới bắt đầu xem xét liệu bạn có thể sử dụng một trong hai ctypes hoặc Cython. Ctypes có thể không đủ linh hoạt cho nhu cầu của bạn, nhưng bạn sẽ có thể liên kết tới bất kỳ thư viện C nào với Cython và có tất cả số lượng tham chiếu được duy trì cho bạn một cách tự động.

Điều đó không phải lúc nào cũng đủ: nếu đối tượng Python của bạn và bất kỳ đối tượng C bên dưới nào có tuổi thọ khác nhau, bạn vẫn có thể gặp sự cố, nhưng nó đơn giản hóa mọi thứ một cách đáng kể.

+2

Ngoài ra, đặt NULL ở những nơi chúng không thuộc về. –

3

Có phần mở rộng python không có giấy tờ cho gdb.

Từ nguồn lấy Python Tools/gdb/libpython.py (không được bao gồm trong cài đặt bình thường).

Đặt này trong sys.path

Sau đó:

# gdb /gps/python2.7_x64/bin/python coredump 
... 
Core was generated by `/usr/bin/python script.py'. 
Program terminated with signal 11, Segmentation fault. 
#0 call_function (oparg=<optimized out>, pp_stack=0x7f9084d15dc0) at Python/ceval.c:4037 
... 
(gdb) python 
>import libpython 
> 
>end 
(gdb) bt 
#0 call_function (oparg=<optimized out>, pp_stack=0x7f9084d15dc0) at Python/ceval.c:4037 
#1 PyEval_EvalFrameEx ([email protected]= 
    Frame 0x7f9084d20ad0, 
    for file /usr/lib/python2.7/site-packages/librabbitmq/__init__.py, line 220, 
    in drain_events (self=<Connection(channels={1: <Channel(channel_id=1, connection=<...>, is_open=True, connect_timeout=4, _default_channel=<....(truncated), [email protected]=0) at Python/ceval.c:2681 
... 
(gdb) py-list 
218   else: 
219    timeout = float(timeout) 
>220   self._basic_recv(timeout) 
221 
222  def channel(self, channel_id=None): 

Như bạn có thể thấy bây giờ chúng ta có tầm nhìn vào Python chồng tương ứng với chuỗi gọi CPython.

Một số hãy cẩn thận:

  • Phiên bản của bạn gdb cần lớn hơn 7 và nó cần phải được biên soạn với --with-python
  • gdb nhúng python (bằng cách liên kết để libpython), nó không chạy nó trong một subshell. Điều này có nghĩa rằng nó có thể không nhất thiết phải phù hợp với phiên bản của python đó là trên $PATH.
  • Bạn cần tải xuống libpython.py từ bất kỳ phiên bản nào của nguồn Python khớp với bất kỳ nội dung nào gdb được liên kết.
  • Bạn có thể phải chạy gdb dưới dạng gốc - nếu bạn có thể cần phải thiết lập sys.path để khớp với mã bạn đang gỡ lỗi.

Nếu bạn không thể sao chép libpython.py vào sys.path sau đó bạn có thể thêm vị trí của nó để sys.path như thế này:

(gdb) python 
>import sys 
>sys.path.append('/path/to/containing/dir/') 
>import libpython 
> 
>end 

này hơi kém tài liệu trong python dev docs, the fedora wikithe python wiki

Nếu bạn có cũ hơn gdb hoặc không thể làm việc này cũng có một số gdbinit trong nguồn Python mà bạn có thể sao chép sang ~/.gdbinit w hich thêm một số chức năng tương tự

0

Tôi đến đây tìm kiếm giải pháp cho cùng một vấn đề và không có câu trả lời nào khác giúp tôi. Trợ giúp là faulthandler và bạn có thể cài đặt nó trong Python 2.7 chỉ bằng cách sử dụng pip install.

faulthandler được giới thiệu với Python chỉ trong phiên bản 3.3, được phát hành vào tháng 9 năm 2012, sau khi hầu hết các câu trả lời khác ở đây được viết.