Tip:
Highlight text to annotate it
X
[Powered by Google Translate] [Valgrind
[Nate hardison, Đại học Harvard]
Đây là CS50, CS50.TV]
Một số các lỗi khó khăn nhất trong các chương trình C
đến từ sự quản lý yếu kém của bộ nhớ.
Có một số lượng lớn các cách để vít những thứ lên,
bao gồm cả việc phân bổ số tiền sai của bộ nhớ,
quên để khởi tạo các biến,
văn bản trước hoặc sau khi kết thúc của một bộ đệm,
và giải phóng giữ nhiều lần bộ nhớ.
Các triệu chứng bao gồm từ tai nạn liên tục
giá trị bí ẩn ghi đè,
thường tại các địa điểm và thời gian xa khỏi các lỗi ban đầu.
Truy tìm các vấn đề quan sát trở lại nguyên nhân gốc rễ cơ bản
thể được thử thách,
nhưng may mắn thay có một chương trình hữu ích được gọi là Valgrind
có thể làm rất nhiều để giúp đỡ.
>> Bạn chạy một chương trình theo Valgrind để cho phép
mở rộng kiểm tra cấp phát bộ nhớ heap và truy cập.
Khi Valgrind phát hiện một vấn đề, nó sẽ cho bạn ngay lập tức,
thông tin trực tiếp cho phép bạn
dễ dàng tìm thấy và sửa chữa vấn đề.
Valgrind báo cáo về các vấn đề bộ nhớ ít nguy hiểm,
chẳng hạn như rò rỉ bộ nhớ, cấp phát bộ nhớ heap,
và quên để giải phóng nó.
Như Clang trình biên dịch, của chúng tôi, trong trình gỡ lỗi của chúng tôi, GDB,
Valgrind là phần mềm miễn phí, và nó được cài đặt trên thiết bị.
Valgrind chạy vào thực thi nhị phân của bạn,
c của bạn hoặc mã h. các tập tin nguồn,
vì vậy hãy chắc chắn rằng bạn đã biên soạn một bản sao up-to-ngày của chương trình của bạn
sử dụng Clang hoặc Kiếm.
Sau đó, chạy chương trình của bạn dưới Valgrind có thể
đơn giản như chỉ cần đặt trước lệnh chương trình tiêu chuẩn với Valgrind từ,
mà khởi Valgrind và chạy các chương trình bên trong của nó.
Khi bắt đầu, Valgrind hiện một số phức tạp
jiggering để cấu hình thực thi cho các kiểm tra bộ nhớ,
vì vậy nó có thể mất một chút để có được và chạy.
Chương trình sau đó sẽ thực hiện bình thường, có thể là chậm hơn rất nhiều,
và khi nó kết thúc, Valgrind sẽ in một bản tóm tắt của việc sử dụng bộ nhớ của nó.
Nếu mọi việc suôn sẻ, nó sẽ giống như thế này:
Trong trường hợp này, / clean_program.
là đường dẫn đến chương trình tôi muốn chạy.
Và trong khi điều này không có bất kỳ đối số,
nếu nó đã làm tôi muốn chỉ tack đến cuối của lệnh như bình thường.
Sạch chương trình chỉ là một chương trình nhỏ ngớ ngẩn tôi đã tạo
phân bổ không gian cho một khối ints trên heap,
đặt một số giá trị bên trong của họ, và giải phóng toàn bộ khối.
Đây là những gì bạn đang chụp, không có lỗi và không có rò rỉ.
>> Một số liệu khác quan trọng là tổng số byte được phân bổ.
Tùy thuộc vào chương trình, nếu phân bổ của bạn là trong MB hoặc cao hơn,
có lẽ bạn đang làm sai điều gì.
Bạn không cần thiết lưu trữ bản sao?
Bạn đang sử dụng các đống cho việc lưu trữ, khi nào nó sẽ được tốt hơn để sử dụng ngăn xếp?
Vì vậy, lỗi bộ nhớ có thể thực sự ác.
Những người công khai hơn gây ra tai nạn ngoạn mục,
nhưng thậm chí sau đó nó vẫn có thể rất khó xác định
chính xác những gì đã dẫn đến vụ tai nạn.
Ngấm ngầm, một chương trình với một lỗi bộ nhớ
vẫn có thể biên dịch sạch
và dường như vẫn có thể làm việc một cách chính xác
bởi vì bạn quản lý để có được may mắn nhất của thời gian.
Sau khi một số "kết quả thành công,
bạn chỉ có thể nghĩ rằng một vụ tai nạn là sự may mắn của máy tính,
nhưng máy tính không bao giờ sai.
>> Chạy Valgrind có thể giúp bạn tìm ra nguyên nhân gây ra lỗi bộ nhớ có thể nhìn thấy
cũng như tìm ẩn lỗi bạn thậm chí không biết về.
Mỗi lần Valgrind phát hiện một vấn đề, nó in thông tin về những gì quan sát được nó.
Mỗi sản phẩm là khá ngắn gọn -
đường nguồn của lệnh vi phạm, những gì vấn đề là,
và một ít thông tin về bộ nhớ liên quan -
nhưng thường nó đủ thông tin để hướng sự chú ý của bạn đến đúng nơi.
Dưới đây là một ví dụ của Valgrind chạy trên một chương trình lỗi
mà không một chi không hợp lệ của bộ nhớ heap.
Chúng tôi thấy không có lỗi hoặc cảnh báo trong biên soạn.
Uh-oh, bản tóm tắt báo lỗi nói rằng có hai lỗi
hai không hợp lệ đọc của kích thước 4 - byte, đó là.
Xấu cả hai lần đọc xảy ra trong các chức năng chính của invalid_read.c,
người đầu tiên trên dòng 16 và thứ hai trên dòng 19.
Chúng ta hãy nhìn vào mã.
Hình như cuộc gọi đầu tiên printf cố gắng để đọc một int quá khứ kết thúc của khối bộ nhớ của chúng tôi.
Nếu chúng ta nhìn lại tại đầu ra của Valgrind,
chúng ta thấy rằng Valgrind nói với chúng tôi chính xác điều đó.
Địa chỉ chúng tôi đang cố gắng để đọc bắt đầu 0 byte
quá khứ kết thúc của khối có kích thước 16 byte -
bốn 32-bit ints mà chúng tôi phân bổ.
Đó là, các địa chỉ chúng tôi đã cố gắng để đọc bắt đầu vào cuối của khối của chúng tôi,
cũng giống như chúng ta thấy trong cuộc gọi printf xấu của chúng tôi.
Bây giờ, không hợp lệ lần đọc có thể không có vẻ như là lớn của một thỏa thuận,
nhưng nếu bạn đang sử dụng dữ liệu đó để kiểm soát dòng chảy của chương trình của bạn -
ví dụ, như một phần của tuyên bố nếu hay vòng lặp -
sau đó mọi thứ có thể âm thầm xấu đi.
Xem làm thế nào tôi có thể chạy chương trình invalid_read
và không có gì khác thường xảy ra.
Scary, huh?
>> Bây giờ, hãy nhìn vào các loại một số chi tiết của các lỗi mà bạn có thể gặp phải trong mã của bạn,
và chúng tôi sẽ xem Valgrind phát hiện chúng.
Chúng tôi chỉ thấy một ví dụ về một invalid_read,
vì vậy bây giờ chúng ta hãy kiểm tra một invalid_write.
Một lần nữa, không có lỗi hoặc cảnh báo trong biên soạn.
Được rồi, Valgrind nói rằng có hai lỗi trong chương trình này -
và invalid_write và invalid_read một.
Hãy kiểm tra mã này.
Hình như chúng ta đã có một ví dụ cổ điển strlen cộng 1 lỗi.
Mã không malloc thêm một byte của không gian
cho các nhân vật / 0,
vì vậy khi str bản sao đi để viết nó ở ssubstrlen "CS50 đá!"
nó đã viết 1 byte quá khứ kết thúc của khối của chúng tôi.
Invalid_read đến khi chúng tôi thực hiện cuộc gọi của chúng tôi để printf.
Printf kết thúc đọc bộ nhớ không hợp lệ khi nó đọc / 0 ký tự
vì nó trông vào cuối của chuỗi này E nó in ấn.
Tuy nhiên, không ai trong số này trốn thoát Valgrind.
Chúng ta thấy rằng nó bắt invalid_write như một phần của bản sao str
trên dòng 11 của chính invalid_read là một phần của printf.
Đá, Valgrind.
Một lần nữa, điều này có thể không có vẻ như là một việc lớn.
Chúng ta có thể chạy chương trình này hơn và hơn bên ngoài Valgrind
và không thấy bất kỳ triệu chứng lỗi.
>> Tuy nhiên, chúng ta hãy nhìn vào một biến thể nhẹ này để xem
làm thế nào những điều có thể nhận được thực sự xấu.
Vì vậy, các cấp, chúng ta đang lạm dụng nhiều điều hơn một chút trong mã này.
Chúng tôi chỉ phân bổ không gian trên heap cho hai chuỗi
chiều dài của CS50 đá,
thời gian này, nhớ / 0 ký tự.
Nhưng sau đó chúng tôi ném trong một chuỗi siêu dài vào khối nhớ
rằng S được trỏ đến.
Sẽ có ảnh hưởng trên các khối nhớ rằng T điểm?
Vâng, nếu T điểm tới bộ nhớ mà chỉ tiếp giáp với S,
ngay sau khi,
sau đó chúng ta có thể viết trên một phần của T.
Hãy chạy mã này.
Hãy nhìn vào những gì đã xảy ra.
Các chuỗi chúng tôi lưu trữ trong các khối đống của chúng tôi cả hai dường như đã in ra một cách chính xác.
Dường như không có gì sai cả.
Tuy nhiên, chúng ta hãy quay trở lại mã của chúng tôi và
nhận xét ra đường, nơi chúng tôi sao chép CS50 đá
vào khối bộ nhớ thứ hai, chỉ bởi t.
Bây giờ, khi chúng tôi chạy mã này, chúng ta nên
chỉ nhìn thấy các nội dung của khối bộ nhớ đầu tiên in ra.
Whoa, mặc dù chúng tôi đã không str bản sao
bất kỳ ký tự vào khối đống thứ hai, là chỉ bởi T,
chúng tôi nhận được một in ra.
Thật vậy, các chuỗi, chúng tôi nhồi vào khối đầu tiên của chúng tôi
tràn ngập khối đầu tiên và vào khối thứ hai,
làm cho tất cả mọi thứ có vẻ bình thường.
Valgrind, mặc dù, cho chúng ta biết câu chuyện có thật.
Hiện chúng tôi đi.
Tất cả những người không hợp lệ đọc và viết.
>> Hãy nhìn vào một ví dụ về một loại lỗi khác.
Ở đây chúng tôi làm điều gì đó chứ không phải bất hạnh.
Chúng tôi lấy không gian cho một int trên heap,
và chúng ta khởi tạo một con trỏ int - p - để trỏ đến không gian đó.
Tuy nhiên, trong khi con trỏ của chúng tôi được khởi tạo,
các dữ liệu mà nó trỏ đến bất cứ điều gì rác trong một phần của heap.
Vì vậy, khi chúng ta nạp dữ liệu đó vào int i,
chúng tôi kỹ thuật khởi tạo i,
nhưng chúng tôi làm như vậy với các dữ liệu rác.
Các cuộc gọi đến khẳng định, mà là một tiện dụng gỡ lỗi vĩ mô
được định nghĩa trong thư viện khẳng định aptly tên,
sẽ hủy bỏ chương trình nếu điều kiện thử nghiệm của nó không thành công.
Nghĩa là, nếu tôi không phải là 0.
Tùy thuộc vào những gì đã được trong không gian đống, chỉ bằng p,
chương trình này có thể làm việc và đôi khi thất bại tại các thời điểm khác.
Nếu nó hoạt động, chúng tôi chỉ nhận được may mắn.
Trình biên dịch sẽ không bắt lỗi này, nhưng Valgrind sẽ chắc chắn.
Hiện chúng tôi thấy thông báo lỗi xuất phát từ việc sử dụng của chúng tôi rằng các dữ liệu rác.
>> Khi bạn phân bổ bộ nhớ heap nhưng không deallocate nó hoặc giải phóng nó,
đó được gọi là bị rò rỉ.
Đối với một chương trình nhỏ, ngắn ngủi, chạy và ngay lập tức thoát ra,
rò rỉ là vô hại,
nhưng đối với một dự án của kích thước lớn hơn và / hoặc kéo dài tuổi thọ,
thậm chí là một rò rỉ nhỏ có thể hợp chất thành một cái gì đó lớn.
Đối với CS50, chúng tôi mong đợi bạn
chăm sóc giải phóng tất cả các bộ nhớ heap mà bạn phân bổ,
vì chúng tôi muốn bạn để xây dựng các kỹ năng để xử lý đúng quy trình hướng dẫn sử dụng
yêu cầu của C.
Để làm như vậy, chương trình của bạn cần phải có một chính xác
một-một sự tương ứng giữa malloc và các cuộc gọi miễn phí.
May mắn thay, Valgrind có thể giúp bạn với rò rỉ bộ nhớ.
Đây là một chương trình bị rò rỉ được gọi là leak.c giao
không gian trên heap, viết cho nó, nhưng không giải phóng nó.
Chúng tôi biên dịch nó với Thực hiện và chạy nó dưới Valgrind,
và chúng ta thấy rằng, trong khi chúng ta không có lỗi bộ nhớ,
chúng tôi có một bị rò rỉ.
Có 16 byte chắc chắn bị mất,
điều đó có nghĩa là con trỏ tới bộ nhớ mà không phải là trong phạm vi khi chương trình đã thoát.
Bây giờ, Valgrind không cung cấp cho chúng tôi một tấn thông tin về rò rỉ,
nhưng nếu chúng ta tuân theo lưu ý nhỏ này mà nó mang lại cho xuống phía dưới cùng của báo cáo của mình
chạy lại với rò rỉ kiểm tra = đầy đủ
để xem chi tiết đầy đủ của bộ nhớ bị rò rỉ,
chúng tôi sẽ nhận được thêm thông tin.
Bây giờ, trong phần tóm tắt đống,
Valgrind cho chúng ta biết nơi mà bộ nhớ bị mất đã được cấp ban đầu.
Cũng như chúng ta biết từ trong mã nguồn,
Valgrind thông báo với chúng tôi rằng chúng tôi bị rò rỉ bộ nhớ
được phân bổ với một cuộc gọi đến malloc vào dòng 8 của leak.c
trong các chức năng chính.
Khá tiện lợi.
>> Valgrind phân loại rò rỉ bằng cách sử dụng các thuật ngữ này:
Chắc chắn bị mất - đây là cấp phát bộ nhớ heap
mà chương trình không còn có một con trỏ.
Valgrind biết rằng bạn đã có một lần con trỏ, nhưng đã bị mất theo dõi của nó.
Bộ nhớ này chắc chắn là bị rò rỉ.
Gián tiếp bị mất - đây là cấp phát bộ nhớ heap
mà các con trỏ chỉ vào nó cũng bị mất.
Ví dụ, nếu bạn bị mất con trỏ của bạn để nút đầu tiên của một danh sách liên kết,
sau đó là nút đầu tiên chắc chắn sẽ bị mất,
trong khi bất kỳ các nút tiếp theo sẽ được gián tiếp bị mất.
Có thể bị mất - đây là cấp phát bộ nhớ heap
mà Valgrind không thể chắc chắn liệu có là một con trỏ hay không.
Vẫn có thể truy cập là cấp phát bộ nhớ heap
mà chương trình vẫn có một con trỏ ở lối ra,
mà thường có nghĩa là một biến toàn cầu trỏ đến nó.
Để kiểm tra cho các rò rỉ, bạn cũng sẽ phải bao gồm các tùy chọn
- Vẫn có thể truy cập = có
trong invocation của bạn Valgrind.
>> Những trường hợp khác nhau có thể yêu cầu các chiến lược khác nhau để làm sạch chúng,
nhưng rò rỉ nên loại bỏ.
Thật không may, sửa chữa rò rỉ có thể là khó khăn để làm,
kể từ khi các cuộc gọi không chính xác để miễn phí có thể thổi lên chương trình của bạn.
Ví dụ, nếu chúng ta nhìn vào invalid_free.c,
chúng ta thấy một ví dụ về deallocation trí nhớ kém.
Những gì nên được một cuộc gọi duy nhất để giải phóng toàn bộ khối
bộ nhớ chỉ bởi int_block,
thay vì trở thành một nỗ lực để giải phóng từng phần int có kích thước
bộ nhớ riêng.
Điều này sẽ không hư hại.
Boom! Thật là một lỗi.
Điều này chắc chắn là không tốt.
Nếu bạn đang mắc kẹt với các loại hình báo lỗi, mặc dù, và bạn không biết nơi để tìm,
rơi trở lại người bạn tốt nhất của bạn mới.
Bạn đoán nó - Valgrind.
Valgrind, như mọi khi, biết chính xác những gì.
Các tính alloc và miễn phí không phù hợp.
Chúng tôi đã có 1 alloc và 4 giải phóng.
Và Valgrind cũng cho chúng ta biết nơi mà các cuộc gọi miễn phí đầu tiên xấu -
đã kích hoạt blowup là đến từ -
dòng 16.
Như bạn thấy, các cuộc gọi xấu để giải phóng thực sự tồi tệ,
vì vậy chúng tôi khuyên bạn nên để cho chương trình của bạn bị rò rỉ
trong khi bạn đang làm việc trên nhận được các chức năng chính xác.
Bắt đầu tìm kiếm các chỗ rò rỉ chỉ sau khi chương trình của bạn đang làm việc đúng,
mà không có bất kỳ lỗi nào khác.
>> Và đó là tất cả chúng tôi đã có cho video này.
Bây giờ bạn còn chờ gì nữa?
Đi chạy Valgrind về các chương trình của bạn ngay bây giờ.
Tên là Nate hardison. Đây là CS50. [CS50.TV]