Luận văn Phương pháp phân tích mã nguồn và sinh dữ liệu kiểm thử cho các dự án C/C++

Luận văn đã trình bày một phương pháp phân tích mã nguồn và sinh dữ liệu kiểm thử tự động cho các dự án C/C++. Kết quả nghiên cứu của luận văn được hiện thực hóa trong công cụ CFT4Cpp. Tính đúng đắn và hiệu quả của phương pháp đề xuất đã được chứng minh trong thực nghiệm qua các so sánh với KLEE, PathCrawler, CUTE, CREST, CAUT. Hiện tại, kết quả nghiên cứu đã được trình bày trong hai hội nghị gồm NICS 2016 và SoICT 2017. Mục tiêu chính của luận văn hướng đến giải quyết vấn đề sinh dữ liệu kiểm thử đầu tiên trong kiểm thử kĩ thuật kiểm thử tự động định hướng, đề xuất phương pháp sinh tập dữ liệu kiểm thử số lượng nhỏ và đạt độ phủ tối đa. Thứ nhất, để giải quyết vấn đề sinh dữ liệu kiểm thử đầu tiên, phương pháp đề xuất tiến hành phân tích chương trình để sinh bộ dữ liệu kiểm thử đầu tiên thay vì sinh ngẫu nhiên. Bởi thế, quá trình thực thi dữ liệu kiểm thử đầu tiên sẽ gây ít lỗi hơn so với phương pháp sinh ngẫu nhiên, đặc biệt với chương trình thao tác với vùng nhớ như con trỏ, xâu, v.v. Thứ hai, để sinh số lượng bộ dữ liệu kiểm thử nhỏ mà đạt độ phủ tối đa, luận văn đề xuất thuật toán LDFS. Tư tưởng chính của thuật toán LDFS là sinh dữ liệu kiểm thử cho trường hợp càng đơn giản hết mức có thể để giảm thiểu chi phí phân tích mã nguồn mà vẫn đạt độ phủ tối đa. Cụ thể, tập dữ liệu kiểm thử được sinh thỏa mãn lặp vòng lặp tối đa một lần, tức là cả nhánh đúng và sai của câu lệnh điều kiện đều được đi qua. Sau bước này, nếu độ phủ đạt được chưa tối đa, LDFS tiến hành sinh dữ liệu kiểm thử thỏa mãn lặp vòng lặp tối đa 0 lần (tức chỉ đi theo nhánh sai của câu lệnh điều kiện), sau đó lặp 2 lần, 3 lần, v.v. đến số lần lặp tối đa được cấu hình từ trước. Quá trình sinh dữ liệu kiểm thử dừng lại khi độ phủ đạt được là tối đa, hoặc hết thời gian chạy, hoặc LDFS đạt đến số lần lặp tối đa của vòng lặp. Công cụ CFT4Cpp cần được tiếp tục phát triển để ngày càng hoàn thiện hơn. Thứ nhất, CFT4Cpp sẽ được nâng cấp để hỗ trợ đầy đủ cú pháp của các chuẩn C++98, C++03, C++11, và C++14. Để làm được điều này, kĩ thuật phân tích mã nguồn cần được cải tiến. Thứ hai, nhiều trường hợp trong dự án C/C++ cần được xử lý thêm như template, dynamic invocation, overloading, v.v. Thứ ba, thuật toán thực thi tượng trưng chưa giải quyết được vấn đề sử dụng bộ nhớ không tường minh khi sử45 dụng hàm liên quan bộ nhớ như memmove, strncat. Kĩ thuật thực thi tượng trưng hiện tại chưa mô phỏng đầy đủ và chính xác được thay đổi của các biến khi những câu lệnh kiểu này được gọi. Thứ tư, công cụ cần được cải tiến để sinh dữ liệu kiểm thử cho các độ phủ khác bên cạnh ba độ phủ đã hỗ trợ sẵn gồm độ phủ nhánh, độ phủ câu lệnh và độ phủ MC/DC. Cuối cùng, công cụ hướng tới hỗ trợ nhiều trình biên dịch khác nhau trên các nền tảng khác nhau.

pdf69 trang | Chia sẻ: yenxoi77 | Lượt xem: 635 | Lượt tải: 0download
Bạn đang xem trước 20 trang tài liệu Luận văn Phương pháp phân tích mã nguồn và sinh dữ liệu kiểm thử cho các dự án C/C++, để xem tài liệu hoàn chỉnh bạn click vào nút DOWNLOAD ở trên
g thêm bất phương trình/phương trình về điều kiện giá trị cấp phát cũng như trạng thái bảng biến tabelVar được cập nhật nếu câu lệnh đang phân tích là câu lệnh cấp phát (dòng 11, 12). Nói rõ ràng hơn, nếu biến con trỏ được cấp phát một vùng nhớ có kích thước không xác định (gọi là sizeOfAllocation) thì giá trị vùng nhớ đó phải luôn dương để đảm bảo tính đúng đắn của hệ ràng buộc, tức là sizeOfAllocation>=0. Do đó, bất phương trình sizeOfAllocation >=0 được bổ sung vào hệ ràng buộc PC để đảm bảo nghiệm (hay dữ liệu kiểm thử) không gây lỗi khi thực thi. Trường hợp kế tiếp, nếu câu lệnh stm là khai báo thì bảng biến tableVar bổ sung thêm biến đó vào danh sách biến hiện tại. Đồng thời cập nhật giá trị biến vừa khai báo nếu biến đó được khởi tạo giá trị (dòng 14, 15). Xét trường hợp câu lệnh stm là câu lệnh điều kiện, nếu giá trị câu lệnh stm luôn sai thì hệ ràng buộc luôn vô nghiệm (dòng 16-23). Thuật toán trả về hệ ràng buộc rỗng và kết thúc. Ngược lại, nếu câu lệnh stm luôn có nghiệm, ta bỏ qua câu lệnh đó và không bổ sung vào PC. Trường hợp cuối cùng, câu lệnh stm không thể xác định được tính luận lý, hệ ràng buộc PC lưu thêm câu lệnh điều kiện này. 23 Thuật toán 3.2. Thuật toán xây dựng hệ ràng buộc từ đường thi hành. Nếu câu lệnh stm là câu lệnh đánh dấu phạm vi, ta cập nhật bảng biến như sau (dòng 24-30). Trường hợp stm là dấu mở ngoặc nhọn, tức là bắt đầu thân hàm hoặc 24 thân khối lệnh điều khiển, ta đánh dấu tất cả các biến được khởi tạo từ câu lệnh này trở đi ở mức level + 1 (trong đó level là giá trị cục bộ hiện tại). Chú ý rằng biến toàn cục luôn có giá trị level cục bộ bằng 0 (kí hiệu GLO). Ngược lại, nếu stm là dấu kết thúc thân hàm/thân khối lệnh điều khiển, ta xóa toàn bộ biến trong tableVar ở có giá trị cục bộ cao nhất bởi vì những biến đó đã trở nên vô nghĩa. Sau khi tất cả mọi câu lệnh trong khối lệnh điều khiển được phân tích, hệ ràng buộc PC tiếp tục bổ sung thêm một vài ràng buộc phụ vào để nghiệm tránh gây lỗi khi thực thi (dòng 32-34). Ví dụ, giả sử hiện tại trong PC có một hệ ràng buộc tồn tại phép chia a/b. Để đảm bảo nghiệm không gây lỗi chia cho 0, ta cần bổ sung thêm ràng buộc b!=0 (dòng 33). Ví dụ khác, nếu chỉ số mảng là biểu thức trừu tượng S mà không phải hằng số, ta cần thêm ràng buộc S >= 0 (dòng 32). Thuật toán kết thúc khi mọi câu lệnh trong đường thi hành được phân tích, và hệ ràng buộc phụ được bổ sung; hoặc trong quá trình phân tích một câu lệnh điều kiện luôn sai được phát hiện. Trong trường hợp cuối cùng này, hệ ràng buộc trả về rỗng (vô nghiệm). Ví dụ, xét đường thi hành của đồ thị CFG phủ câu lệnh/nhánh trình bày trong Hình 3.3 đi qua nhánh đúng điều kiện (value[i] != -2 && tcnt < 10) và đi qua nhánh sai điều kiện (vcnt<0) như sau: Begin -> (int tcnt = 0, vcnt = 0, sum = 0, i = 0;) -> (value[i] != -2 && tcnt (vcnt (return sum/vcnt) -> End Hệ ràng buộc tương ứng với đường thi hành trên được mô tả như sau. Trong đó, điều kiện [1] được chuyển thành ràng buộc (1), điều kiện [2] được chuyển thành ràng buộc (2). { (𝑣𝑎𝑙𝑢𝑒[0] ! = −2)𝑎𝑛𝑑 (0 < 10) (𝟏) 0 < 0 (𝟐) 3.3.4. Giải hệ ràng buộc sử dụng bộ giải SMT-Solver Bước giải hệ ràng buộc là bước thứ hai trong hàm has_solution(). Phần này giải quyết bài toán: “Cho một hệ ràng buộc sinh từ đường thi hành, ta cần tìm một nghiệm thỏa mãn hệ ràng buộc này.” Hệ ràng buộc ta sinh từ đường thi hành được biểu diễn dạng trung tố, tuy nhiên các bộ giải SMT-Solver không hỗ trợ giải biểu thức dạng này. Bởi thế, những hệ 25 ràng buộc này cần biến đổi về chuẩn đầu vào SMT-Solver – dạng biểu thức biểu thức chuẩn SMT-Lib. Quy trình xây dựng hệ ràng buộc theo chuẩn SMT-Lib từ hệ ràng buộc biểu diễn ở dạng biểu thức trung tố (infix expression) sinh từ đường thi hành được trình bày trong Hình 3.6. Đầu tiên, hệ ràng buộc được biến đổi về dạng biểu thức hậu tố (postfix expression), sau đó chuyển sang dạng cây (tree expression). Từ cây, ta xây dựng biểu thức chuẩn SMT-Lib bằng cách duyệt cây đó. Hình 3.6. Quy trình xây dựng biểu thức SMT-Lib từ ràng buộc. Hình 3.7 trình bày quá trình xây dựng hệ ràng buộc chuẩn SMT-Lib từ hệ ràng buộc sinh từ kĩ thuật thực thi tượng trưng. Đầu tiên, hệ ràng buộc gốc này được chuyển về dạng biểu thức hậu tố “x 0 > y 1 < || !”. Sau đó, biểu thức hậu tố này được chuyển thành cây cấu trúc. Các đỉnh trong cây có thể tương ứng với một toán tử (operator), hoặc toán hạng (operand). Cuối cùng, cây được chuyển về biểu thức chuẩn SMT-Lib như sau “assert( not (or (> x 0) (< y 1)))”. Từ khóa “not” ứng với phép phủ định. Hình 3.7. Quá trình biến đổi hệ ràng buộc về chuẩn SMTLib. 26 3.4. Biên dịch và thực thi dữ liệu kiểm thử trong môi trường chạy Phần này trình bày hàm execute() tại dòng 10 của thuật toán LDFS. Hàm execute() biên dịch và thực thi dữ liệu kiểm thử (solution) để tính danh sách các câu lệnh thật sự được đi qua. Chú ý rằng, từ một đường thi hành sinh từ đồ thị CFG, một bộ dữ liệu kiểm thử cần được tìm kiếm thỏa mãn đi qua đường thi hành đó. Để tìm được bộ giá trị này, đường thi hành được biểu diễn về hệ ràng buộc bằng cách áp dụng kĩ thuật thực thi tượng trưng. Sau đó, SMT-Solver giải hệ ràng buộc này để sinh dữ liệu kiểm thử đi qua đường thi hành đó. Tuy nhiên, dữ liệu kiểm thử này chưa chắc sẽ đi qua đường thi hành ban đầu bởi vì hai lí do. Thứ nhất, quá trình thực thi tượng trưng là một bước phân tích mã nguồn khá phức tạp và có thể có nhiều sai sót. Thứ hai, trường hợp một câu lệnh chưa được hỗ trợ trong bước quá trình thực thi tượng trưng, hệ ràng buộc chỉ sinh ra từ câu lệnh đầu tiên đến trước câu lệnh hiện tại. Điều đó có nghĩa là, nghiệm của hệ ràng buộc không chắc chắn đi qua đường thi hành ban đầu mà chỉ đi qua một phần. Bởi vì hai lý do này, dữ liệu kiểm thử sinh ra cần được thực thi thật sự trong trình biên dịch để thu thập chính xác danh sách câu lệnh được đi qua. Bộ thực thi dữ liệu kiểm thử của một hàm (test driver) cung cấp cơ chế khởi tạo giá trị các biến truyền vào hàm và chạy chương trình dưới bộ giá trị đó. Về bản chất, bộ thực thi dữ liệu kiểm thử này là một hàm main() – điểm bắt đầu chạy chương trình. Trong hàm main() này, danh sách biến truyền vào hàm được khởi tạo. Sau đó, hàm cần kiểm thử được mô tả lời gọi ở cuối cùng hàm main(). Cuối cùng, bộ thực thi dữ liệu kiểm thử này được đặt trong dự án và chạy tự động. Sau khi bộ thực thi dữ liệu kiểm thử này chạy xong, danh sách các câu lệnh thực thi được thu thập. Kế tiếp, đồ thị CFG của hàm cần kiểm thử được cập nhật trạng thái độ phủ, cũng một trạng thái viếng thăm của các cạnh/câu lệnh/điều kiện con (dòng 11 thuật toán LDFS). Chú ý rằng, nếu độ phủ đồ thị CFG đạt được tại bước này đạt 100% thì quá trình sinh dữ liệu kiểm thử kết thúc. Ngược lại, sinh dữ liệu kiểm thử tiếp tục được sinh ra sao cho đi qua các cạnh/câu lệnh/điều kiện con chưa được viếng thăm. 27 3.5. Tối ưu hóa pha sinh dữ liệu kiểm thử Để tăng tốc thời gian sinh dữ liệu kiểm thử, nhiều kĩ thuật khác nhau được áp dụng có thể kể đến kĩ thuật đơn giản hóa hệ ràng buộc, kĩ thuật biên dịch và thực thi dữ liệu kiểm thử cải tiến. Tư tưởng các kĩ thuật này được trình bày sau đây. 3.5.1. Đơn giản hóa hệ ràng buộc Kích thước của hệ ràng buộc có thể khá lớn, và cấu trúc khá phức tạp làm tăng thời gian giải hệ ràng buộc. Điều đó dẫn đến bài toán tối ưu hệ ràng buộc trước khi sử dụng SMT-Solver để giải hệ ràng buộc đó. Phương pháp đề xuất áp dụng một vài kĩ thuật tối ưu hóa hệ ràng buộc sau đây để giảm thiểu thời gian sử dụng bộ giải SMT- Solver. Trong đó, hai kĩ thuật chính được sử dụng gồm kĩ thuật giải hệ ràng buộc tăng dần và kĩ thuật kiểm tra tính thỏa mãn dựa trên bộ nhớ đệm. Hiện tại, nhiều kĩ thuật tối ưu hệ ràng buộc đã được đề xuất. Kĩ thuật tối ưu đầu tiên đề xuất trong CUTE, EXE, KLEE, và CAUT thu gọn hệ ràng buộc cần giải bằng cách loại bỏ các hệ ràng buộc không cần thiết. Tư tưởng chính của kĩ thuật này là chỉ những hệ ràng buộc liên quan đến hệ ràng buộc phủ định cuối cùng được giải. Kĩ thuật này gọi là kĩ thuật giải hệ ràng buộc tăng dần (incremental solving technique). Kĩ thuật tối ưu hệ ràng buộc thứ hai đề xuất bởi Cristian Cadar và cộng sự được gọi là kĩ thuật kiểm tra tính thỏa mãn dựa trên bộ nhớ đệm (cache-based unsatisfiability check). Kĩ thuật này được áp dụng trong EXE and KLEE. Theo như tư tưởng kĩ thuật này, tất cả các hệ ràng buộc đã được giải đều được lưu lại trong bộ nhớ. Tính luận lý của hệ ràng buộc kế tiếp được xác định nhanh qua đánh giá tập các hệ ràng buộc trước đó. Cụ thể, giả sử trong bộ nhớ lưu hai hệ ràng buộc đã được giải gồm A, B. Trong đó, hệ ràng buộc A có nghiệm, hệ ràng buộc B vô nghiệm. Cho hệ ràng buộc C, tính luận lý hệ ràng buộc này được kiểm tra nhanh như sau. Nếu 𝐴 ⊂ 𝐶 thì hệ ràng buộc C có thể có nghiệm, và nghiệm của A có thể là một phần nghiệm trong C. Nếu 𝐵 ⊂ 𝐶 thì hệ ràng buộc C vô nghiệm, tức hệ ràng buộc C không cần đưa vào bộ giải SMT-Solver để tìm nghiệm. Bên cạnh hai phương pháp chính nêu trên, phương pháp đề xuất áp dụng một vài kĩ thuật đơn giản khác như kĩ thuật đơn giản hóa biểu thức (ví dụ: x+0 > 1 đơn giản hóa thành x >1), kĩ thuật suy biến nhanh giá trị biến (ví dụ: x+1=10 đơn giản 28 hóa thành x=9), kĩ thuật loại bỏ hệ ràng buộc hiển nhiên (ví dụ: x<10, x==5 đơn giản hóa thành x==5), v.v. 3.5.2. Tăng tốc thời gian biên dịch và thực thi dữ liệu kiểm thử Theo như phương pháp truyền thống, sau khi từng bộ dữ liệu kiểm thử sinh ra, một bộ thực thi dữ liệu kiểm thử đó được tạo. Sau đó, bộ thực thi dữ liệu kiểm thử này được biên dịch và chạy để lấy tập câu lệnh đi qua. Vì thế, nếu có n bộ dữ liệu kiểm thử sinh ra thì quá trình biên dịch và chạy được tiến hành n lần. Phương pháp đề xuất tăng tốc thời gian chạy các dữ liệu kiểm thử sinh ra bằng cách xây dựng bộ thực thi dữ liệu kiểm thử tổng quát cho mọi loại giá trị đầu vào. Bởi thế, quá trình biên dịch và chạy nêu trên có thể rút ngắn xuống 1 lần biên dịch và n lần chạy dữ liệu kiểm thử. Hình 3.8. Kĩ thuật tạo bộ thực thi ca kiểm thử tổng quát. Hình 3.8 trình bày quá trình xây dựng bộ thực thi dữ liệu kiểm thử tổng quát. Đầu vào gồm hàm cần kiểm thử đã được chèn câu lệnh đánh dấu (instrumented function) và tệp lưu dữ liệu kiểm thử (test data external file). Đầu ra là mã nguồn bộ thực thi dữ liệu kiểm thử tổng quát cho mọi loại giá trị đầu vào gồm biến kiểu cơ bản (số nguyên, số thực, kí tự), biến mảng, biến con trỏ, biến kiểu dẫn xuất (class, struct). Quá trình tạo bộ mã nguồn tổng quát gồm ba pha như sau. Trong pha đầu tiên, khung xương bộ thực thi dữ liệu kiểm thử được tạo chứa đầy đủ các hàm cần thiết cho hai pha sau (ví dụ: hàm đọc dữ liệu dữ liệu kiểm thử từ tệp, v.v.). Pha thứ hai tạo mã nguồn nạp dữ liệu biến kiểu cơ bản từ tệp lưu dữ liệu kiểm thử dựa trên khung mã nguồn tạo ở pha trước. Pha cuối cùng tạo mã nguồn đọc biến kiểu dẫn xuất từ tệp dữ liệu kiểm thử. Trong pha cuối này, tham số độ sâu depth xác định độ sâu tối đa khi duyệt danh sách liên kết để tránh trường hợp duyệt danh sách liên kết vô hạn. Một biến có tính danh sách liên kết khi biến đó có thuộc tính có kiểu là chính biến đó. 29 Kĩ thuật tăng tốc quá trình thực thi dữ liệu kiểm thử thể hiện ưu điểm rõ ràng khi số lượng bộ dữ liệu kiểm thử thỏa mãn tiêu chí độ phủ đủ lớn. Tất cả mọi dữ liệu kiểm thử sinh ra từ đường thi hành đều được lưu trong một tệp cố định ở ngoài. Sau đó, bộ thực thi dữ liệu kiểm thử tổng quát đọc tệp này để tự động khởi tạo giá trị các biến. 3.6. Xuất mã nguồn kiểm thử theo chuẩn Google Test Google Test4 là một famework được dùng rộng rãi trong các công ty phần mềm C/C++ để viết unit test. Sau khi tập dữ liệu kiểm thử được sinh ra bởi thuật toán LDFS, tập mã nguồn kiểm thử theo chuẩn Google Test được tạo ra tự động. Điều này đặc biệt có ý nghĩa vì lập trình viên không cần tốn thời gian viết unit test với tập dữ liệu kiểm thử. Từ đó chi phí phát triển phần mềm giảm đi, đặc biệt đối với các dự án lớn khi số lượng bộ dữ liệu kiểm thử lên tới hàng chục nghìn, thậm chí hàng trăm nghìn. Cấu trúc của một bộ mã nguồn kiểm thử theo chuẩn framework Google Test như sau. Phần đầu tiên sẽ nạp chỉ thị tiền xử lý header của framework này để có thể sử dụng các hàm hỗ trợ bởi Google Test. Phần thứ hai là danh sách các hàm, trong đó mỗi hàm tương ứng với một bộ dữ liệu kiểm thử. Cụ thể, nếu 3 bộ dữ liệu kiểm thử được sinh ra thì số lượng mã nguồn kiểm thử Google Test cần sinh tự động tương ứng bằng 3. Cấu trúc một hàm gồm ba phần. Phần đầu tiên chứa mã nguồn khởi tạo giá trị các biến với các giá trị lưu trong dữ liệu kiểm thử. Phần thứ hai là lời gọi đến hàm cần kiểm thử. Các tham số truyền vào hàm này là danh sách biến được ở tạo ở phần đầu tiên. Phần cuối cùng là phép so sánh đầu ra mong muốn (expected output) với đầu ra thực tế (real output). 4 https://github.com/google/Google Test 30 CÔNG CỤ VÀ THỰC NGHIỆM Chương này trình bày tổng quan kiến trúc công cụ CFT4Cpp được xây dựng dựa trên phương pháp đề xuất. Bên cạnh đó, thực nghiệm so sánh với KLEE, CAUT, PathCrawler, CREST theo tiêu chí độ phủ và số bộ dữ liệu kiểm thử để chứng minh tính hiệu quả phương pháp. Ngoài ra, thực nghiệm cũng thể hiện tính hiệu quả về tiêu chí thời gian thực thi dữ liệu kiểm thử so với phương pháp thực thi dữ liệu kiểm thử truyền thống. Cuối cùng, kết quả sinh tập dữ liệu kiểm thử kiểm tra tính đúng đắn vòng lặp được trình bày chi tiết trong thực nghiệm. 4.1. Giới thiệu công cụ kiểm thử tự động CFT4Cpp Để chứng minh tính hiệu quả của phương pháp đề xuất, công cụ CFT4Cpp5 được phát triển trên ngôn ngữ Java. Công cụ CFT4Cpp cung cấp một giải pháp sinh dữ liệu kiểm thử tự động khá hiệu quả cho các dự án viết bằng C/C++. Phiên bản hiện tại của CFT4Cpp hỗ trợ chạy trên môi trường Window đã cài sẵn JDK 8 trở lên và trình biên dịch MingW64. Hình 4.1. Kiến trúc công cụ CFT4Cpp. 5 31 Về cơ bản, công cụ có kiến trúc hai tầng gồm tầng Presentation và tầng Business. Tầng Presentation gồm mô-đun chịu trách nhiệm vẽ đồ thị CFG, mô-đun hiển thị cây cấu trúc của dự án đầu vào, và mô-đun xuất biên bản kiểm thử. Tầng Business gồm ba mô-đun chính như sau: tiền xử lý mã nguồn, sinh dữ liệu kiểm thử, và sinh bộ mã nguồn kiểm thử cho từng dữ liệu kiểm thử theo định dạng của Google Test. Tầng Business tương tác với bộ giải SMT-Solver Z3 và trình biên dịch MingW64. Trình biên dịch này được tích hợp sẵn trong DevCpp. Chức năng của công cụ được mô tả qua bài toán sinh dữ liệu kiểm thử cho hàm Divide. Hàm này nhận hai tham số đầu vào x và y. Nếu giá trị y bằng 0, hàm này trả về ngoại lệ std::exception. Ngược lại, hàm Divide trả về giá trị x/y. - Bước 1. Nạp dự án cần phân tích. Đầu tiên, người dùng đẩy dự án C/C++ cần phân tích vào công cụ CFT4Cpp (Hình 4.2). Dự án này có tên Sample_for_R1_2 do TSDV cung cấp để đánh giá tính hoàn thiện của công cụ CFT4Cpp. Công cụ tự động dựng cây cấu trúc dự án (phía bên trái). Khung hiển thị đồ thị dòng điều khiển ở chính giữa. Thông tin mã nguồn hàm đang kiểm thử, cũng như thông tin cấu hình được thể hiện ở khung bên phải. Hình 4.2. Giao diện chính của công cụ CFT4Cpp. 32 - Bước 2. Cấu hình. Trước khi sinh dữ liệu kiểm thử cho hàm Divide, người dùng cần cấu hình đường dẫn đến trình biên dịch MingW64 Hình 4.3. Giao diện bước cấu hình công cụ CFT4Cpp. - Bước 3. Sinh dữ liệu kiểm thử. Sau khi hoàn thành bước cấu hình trình biên dịch, người dùng tiến hành sinh dữ liệu kiểm thử cho hàm Divide (Hình 4.3). Thời gian sinh dữ liệu kiểm thử khoảng 5 giây, và thời gian này có thể nhanh hơn hoặc chậm hơn tùy theo cấu hình máy tính. Công cụ sinh ra hai dữ liệu kiểm thử cho hàm Divide gồm (x, y) = (558, 0), (663, 1). Độ phủ nhánh đạt được bằng 100%. Hình 4.4. Giao diện sinh dữ liệu kiểm thử cho hàm Divide. 33 - Bước 4. Xuất biên bản kiểm thử. Công cụ hỗ trợ người dùng xuất kết quả kiểm thử ra biên bản MS Excel, hoặc xuất mã nguồn kiểm thử cho từng giá trị dữ liệu kiểm thử sinh ở bước 3 (Hình 4.5). Mã nguồn kiểm thử tuân theo chuẩn của framework Google Test. Hình 4.5. Biên bản kiểm thử xuất bởi công cụ CFT4Cpp cho hàm Divide. 4.2. Thư viện hỗ trợ sử dụng trong công cụ kiểm thử tự động CFT4Cpp 4.2.1. Thư viện giải hệ ràng buộc Z3 Thư viện Z36 là một trong những thư viện giải hệ ràng buộc khá mạnh mẽ. Thư viện này hỗ trợ hai môi trường gồm Window và Ubuntu. Để giải hệ ràng buộc, ta chuyển hệ này về biểu thức chuẩn SMT-Lib. Sau đó, biểu thức SMT-Lib này được giải qua dòng lệnh sử dụng Z3. Giả sử biểu thức SMT-Lib này lưu trong D:/constraints.txt lưu tại ổ D, cú pháp gọi bộ giải Z3 như sau: [đường dẫn z3.exe] –smt2 D:/constraints.txt 6 https://github.com/Z3Prover/z3 34 Người dùng có thể giải hệ trực tuyến qua hệ thống Z3 cung cấp sẵn tại link https://rise4fun.com/Z3. Mã nguồn 4.1 minh họa một hệ ràng buộc theo chuẩn SMT- Lib. Hình 4.6 trình bày nghiệm hệ ràng buộc này khi chạy Z3 trên hệ thống trực tuyến nêu trên. Nếu hệ ràng buộc vô nghiệm, hệ thống trả về UNSAT. Ngược lại, hệ thống thông báo SAT cũng với nghiệm thể hiện ở dưới. Trường hợp hệ ràng buộc có vố số nghiệm, Z3 trả về một nghiệm bất kỳ. Trong ví dụ này, Z3 trả về một bộ giá trị ngẫu nhiên (x, y) = (0, -1). (declare-const x Int) (declare-const y Int) (assert (< x 1)) (assert (> x y)) (check-sat) (get-model) Mã nguồn 4.1. Minh họa một hệ ràng buộc theo chuẩn SMT-Lib. Hình 4.6. Minh họa kết quả giải hệ ràng buộc sử dụng Z3. 4.2.2. Thư viện phân tích mã nguồn CDT Cho đến thời điểm hiện tại, nhiều thư viện hỗ trợ khả năng phân tích mã nguồn C/C++ như CDT7, Clang8, v.v. Trong nghiên cứu này, tôi sử dụng thư viện CDT để phân tích cú pháp mã nguồn. 7 https://www.eclipse.org/cdt 8 https://clang.llvm.org 35 Khi đẩy mã nguồn vào CDT, đầu ra là cây AST ứng với mã nguồn đó. Mã nguồn đẩy vào có thể là một hàm, một câu lệnh, một tệp header, v.v. Các đỉnh trong cây AST này chưa có khả năng binding bởi vì thư viện này được sử dụng ngoài Eclipse. Chẳng hạn, ta đẩy vào mã nguồn có câu lệnh x = y + 1. Đầu ra AST ứng với câu lệnh này thể hiện rằng biến x có kiểu là IASTIdExpression mà không thể hiện rõ ràng biến này có khai báo “int x;”. Nếu muốn bắt được thông tin này, ta cần sử dụng thư viện CDT dưới dạng plug-in Eclipse. 4.2.3. Thư viện tính giá trị biểu thức Jeval Thư viện Jeval9 là một thư viện không thể thiếu khi xây dựng công cụ CFT4Cpp. Đầu vào thư viện này là một biểu thức. Đầu ra là giá trị biểu thức đó. Chẳng hạn, với đầu vào là “1+2+3” thì đầu ra Jeval trả về bằng 6. 4.3. Kết quả thực nghiệm Tác giả đã tiến hành thực nghiệm trên công cụ CFT4Cpp với các bộ dữ liệu thực tế để thể hiện tính hiệu quả của phương pháp đề xuất. Bốn công cụ được so sánh gồm KLEE, PathCrawler, CAUT, và CREST. Thực nghiệm được tiến hành trên máy Intel(R) Core(TM) i5-4200U CPU @1.6GHz - 2.3GHz với 4GB bộ nhớ. 4.3.1. So sánh số lượng bộ dữ liệu kiểm thử và độ phủ với KLEE, CAUT, CREST, PathCrawler Bảng 4.1. Thông tin các công cụ so sánh trong thực nghiệm Công cụ Phiên bản Ngôn ngữ hỗ trợ Môi trường hỗ trợ Trình biên dịch Phương pháp sinh KLEE 2016 C Ubuntu Clang EGT CAUT 2014 C Ubuntu GCC DSE CREST 2014 C Ubuntu GCC DSE PathCrawler 2012 C Web-based GCC DSE CFT4Cpp 2017 C/C++ Window MingW DSE 9 36 Bảng 4.1 trình bày các công cụ được so sánh với CFT4Cpp gồm KLEE [2], CAUT [12], PathCrawler [13] và CREST [1]. Công cụ KLEE áp dụng kĩ thuật EGT để sinh dữ liệu kiểm thử thỏa mãn hai tiêu chí gồm độ phủ tối đa và phát hiện các lỗi tiềm ẩn. Các công cụ còn lại sử dụng kĩ thuật kiểm thử tự động định hướng. Để đảm bảo tính khách quan, CFT4Cpp được so sánh với từng công cụ nêu trên với cùng nhóm cấu hình mà các công cụ này hỗ trợ. Bảng 4.2 trình bày thông tin hai nhóm thực nghiệm gồm: nhóm A (KLEE, PathCrawler), và nhóm B (CAUT, CREST). Các thông tin cấu hình gồm: khoảng của biến kí tự, khoảng của biến kiểu số, số phần tử của mảng, số lần lặp tối đa của vòng, và chiến thuật chọn đường thi hành. Nếu một cấu hình của công cụ đánh dấu “-“, tức là công cụ không hỗ trợ cài đặt loại thông tin đó. Chiến thuật chọn đường thi hành được chọn dựa trên bài báo tương ứng của từng công cụ thực nghiệm. Đó là những chiến thuật đã được chính minh tính hiệu quả trong các bài toán thực tế. Cụ thể, KLEE đưa ra khá nhiều chiến thuật chọn đường thi hành khác nhau, nhưng thực nghiệm chỉ ra chiến thuật hiệu quả nhất có tên nurs:md2u. Bảng 4.2. Thông tin cấu hình các ví dụ trong thực nghiệm Nhóm A Nhóm B KLEE PathCrawler CFT4Cpp CAUT CREST CFT4Cpp Khoảng biến kí tự [32..126] Phạm vi kiểu biến Khoảng biến kiểu số [-100..200] Phạm vi kiểu biến Kích thước mảng [0..10] 10 Số lần lặp tối đa - 11 11 - 11 11 Chiến thuật chọn đường thi hành nurs: md2u k-path LDFS pps cfg LDFS Bảng 4.2 mô tả thông tin các ví dụ tiến hành thực nghiệm. Tất cả những ví dụ này được lấy ngẫu nhiên trên github. Kí hiệu CC mô tả độ phức tạp của ví dụ được tính theo công thức Mc-Cabe. Kí hiệu LOC là số dòng mã nguồn theo tiêu chuẩn của Eclipse (không tính số dòng thừa, v.v.). Tất cả các hàm kiểm thử đều có vòng lặp, trừ hàm ArrayCmp. Một vài hàm có đệ quy như reverse, Fibonacci, v.v. 37 Bảng 4.3. Thông tin các hàm kiểm thử về tiêu chí độ phủ và số bộ dữ liệu kiểm thử Hàm Mô tả Hàm Mô tả CC LOC CC LOC uninit_var(int[3],int[3]) 7 12 concatenate(char[],char[]) 3 11 ArrayCmp(int,char*,char*) 4 7 concatenate_string(char*,char*) 3 8 check_prime(int) 4 7 reverse2(char*,int,int) 2 8 check_armstrong(long long) 4 14 copy_string(char*,char*) 2 6 power(int,int) 2 6 string_length(char*) 2 6 print_floyd(int) 3 10 Fibonacci(int) 3 4 find_minimum(int[],int) 3 9 find_maximum(int[],int) 3 9 linear_search1(long[],long,long) 3 6 add_digits(int) 2 6 linear_search2(long*,long,long) 3 6 reverse(long) 2 8 bubble_sort(long[],long) 4 8 Bảng 4.4 trình bày kết quả thực nghiệm của nhóm A gồm hai công cụ KLEE và PathCrawler. Kí hiệu num đại diện số lượng bộ dữ liệu kiểm thử sinh ra. Kí hiệu cov đại diện độ phủ đạt được với số bộ dữ liệu kiểm thử sinh ra đó. Trong tất cả những ví dụ này, CFT4Cpp đều đạt độ phủ tối đa với số bộ dữ liệu kiểm thử ít hơn hẳn so với hai công cụ còn lại. Trong ví dụ đầu tiên uninit_var, cả ba công cụ đều đạt độ phủ tối đa. Tuy nhiên, số bộ dữ liệu kiểm thử do CFT4Cpp sinh ra bằng 5, trong khi đó KLEE sinh tới 27 bộ dữ liệu kiểm thử và PathCrawler sinh lớn hơn 20 bộ dữ liệu kiểm thử. Điều này tương tự với các ví dụ power, print_floyd. Trong nhiều ví dụ, số bộ dữ liệu kiểm thử do KLEE sinh ra khá lớn (kí hiệu n/a). Lý giải cho vấn đề này là do mục tiêu sinh dữ liệu kiểm thử của KLEE không chỉ đạt độ phủ tối đa mà còn phát hiện lỗi. Vì thế, KLEE sẽ kiểm tra mọi trường hợp có thể xảy ra trong mã nguồn. 38 Điều đó dẫn đến số lượng bộ dữ liệu kiểm thử sinh bởi KLEE khá lớn gây khó khăn cho quản lý. Bảng 4.4. Kết quả so sánh công cụ CFT4Cpp với KLEE và PathCrawler No.k Hàm CFT4Cpp KLEE PathCrawler Số bộ dữ liệu kiểm thử Độ phủ Số bộ dữ liệu kiểm thử Độ phủ Số bộ dữ liệu kiểm thử Độ phủ 1 uninit_var 5 100 27 100 >20 100 2 ArrayCmp 3 100 82 100 1 16.67 3 check_prime 3 100 n/a 100 >20 66.67 4 check_armstrong 2 100 n/a 100 5 100 5 power 2 100 > 20 100 >20 100 6 print_floyd 2 100 n/a 100 >20 100 7 find_minimum 2 100 n/a 100 - - 8 linear_search1 2 100 n/a 100 1 25 9 linear_search2 2 100 n/a 100 2 25 10 bubble_sort 2 100 n/a 100 1 16.67 11 concatenate 1 100 n/a 100 - - 12 concatenate_string 1 100 n/a 100 - - 13 reverse2 2 100 8 100 1 50 14 copy_string 2 100 10 100 - - 15 string_length 2 100 10 100 - - 18 Fibonacci 2 100 n/a 100 - - 19 find_maximum 2 100 n/a 100 - - 20 add_digits 2 100 4 100 2 100 21 reverse 2 100 4 100 2 100 39 Điểm thứ hai từ kết quả thực nghiệm trong Bảng 4.4 là công cụ PathCrawler đạt kết quả không tốt trong nhiều ví dụ. Tiêu biểu như ví dụ ArrayCmp, linear_search1, v.v. công cụ đạt được độ phủ khá nhỏ. Thậm chí, trong ví dụ find_minimum, concatenate, v.v. công cụ PathCrawler không thể sinh được dữ liệu kiểm thử (kí hiệu bởi dấu “-“). Về vấn đề đầu tiên, nguyên nhân chính do công cụ PathCrawler chưa xử lý được nhiều trường hợp mã nguồn khi áp dụng kĩ thuật thực thi tượng trưng. Nguyên nhân của vấn đề thứ hai liên quan đến phương pháp kĩ thuật kiểm thử tự động định hướng. Trong phương pháp kĩ thuật kiểm thử tự động định hướng, dữ liệu kiểm thử đầu tiên được sinh ngẫu nhiên, và sau đó được đẩy vào chương trình để chạy. Nếu dữ liệu kiểm thử đầu tiên không chạy được, tức là chương trình có lỗi, quá trình sinh dữ liệu kiểm thử thất bại. Bảng 4.5. Kết quả so sánh công cụ CFT4Cpp với CREST và CAUT No.k Hàm CFT4Cpp CREST CAUT Số bộ dữ liệu kiểm thử Độ phủ Số bộ dữ liệu kiểm thử Độ phủ Số bộ dữ liệu kiểm thử Độ phủ 1 uninit_var 5 100 24 100 11 91.69 2 ArrayCmp 3 100 6 100 4 100 3 check_prime 3 100 5 100 3 83.331 4 check_armstrong 2 100 4 100 3 87.5 5 power 2 100 - - 2 100 6 print_floyd 2 100 - - - - 7 find_minimum 2 100 - - 4 100 8 linear_search1 2 100 3 100 2 100 9 linear_search2 2 100 3 100 2 100 10 bubble_sort 2 100 - - 3 100 11 concatenate 1 100 2 100 - - 40 12 concatenate_string 1 100 2 100 - - 13 reverse2 2 100 3 100 2 100 14 copy_string 2 100 2 100 1 100 15 string_length 2 100 2 100 1 100 18 Fibonacci 2 100 - - - - 19 find_maximum 2 100 - - 4 100 20 add_digits 2 100 2 100 1 100 21 reverse 2 100 2 100 1 100 Bảng 4.5 trình bày kết quả thực nghiệm khi so sánh công cụ CFT4Cpp với hai công cụ CREST và CAUT. Hai công cụ này đều áp dụng kĩ thuật kiểm thử tự động định hướng nên có những điểm yếu như công cụ PathCrawler nêu trên. Hai công cụ CREST và CAUT đều cho kết quả kiểm thử tốt hơn hẳn so với PathCrawler. Thứ nhất, trong ví dụ copy_string, string_length, add_digits, và reverse, công cụ CREST cho độ phủ 100% trong khi số lượng bộ dữ liệu kiểm thử bằng CFT4Cpp. Nguyên nhân chính bởi vì chiến thuật chọn đường thi hành của CREST tốt hơn PathCrawler nên số bộ dữ liệu kiểm thử nhỏ hơn hẳn. Thứ hai, công cụ CAUT thể hiện tính ưu điểm hơn trong các ví dụ copy_string, string_length, add_digits và reverse so với hai công cụ CFTCpp và CREST. Những hàm này đều chứa vòng lặp. CAUT chỉ cần sinh ra đúng một bộ dữ liệu kiểm thử mà vẫn đạt độ phủ tối đa. Lý giải cho vấn đề này là do tính may mắn khi CAUT sinh dữ liệu kiểm thử đầu tiên theo phương pháp ngẫu nhiên. Với dữ liệu kiểm thử đầu tiên “may mắn” này, độ phủ đạt được là 100%. Tuy nhiên, CAUT thất bại khi xử lý các hàm print_floyd, concatenate, v.v. 4.3.2. Sinh dữ liệu kiểm thử vòng lặp Phương pháp đề xuất LDFS ưu tiên sinh dữ liệu kiểm thử lặp khối lệnh điều khiển lặp (while...do, do..while, for) càng nhỏ càng tốt. Cụ thể, tập dữ liệu kiểm thử lặp vòng lặp 1 lần, 0 lần, 2 lần, v.v., đến lặp tối đa n lần được sinh ra theo thứ tự (n là giá trị cấu hình số lần lặp tối đa do người dùng định nghĩa). Một khi tập dữ liệu kiểm thử đạt độ phủ tối đa, quá trình sinh dữ liệu kiểm thử kết thúc mà không quan tâm 41 các lần lặp kế tiếp. Điều đó có nghĩa, tập dữ liệu kiểm thử sinh ra đạt tiêu chí độ phủ cao và số lượng ít. Thực tế, lỗi có thể phát sinh ở vòng lặp khi số lần lặp đủ lớn. Để đảm bảo chất lượng phần mềm, tập dữ liệu kiểm thử thỏa mãn một trong hai điều kiện sau cần được sinh ra gồm (i) độ phủ lớn và số lượng bộ dữ liệu kiểm thử nhỏ, (ii) kiểm thử vòng lặp. Tập dữ liệu kiểm thử thỏa mãn điều kiện đầu tiên được sinh bằng cách áp dụng LDFS. Phần này trình bày thực nghiệm sinh dữ liệu kiểm thử đánh giá chất lượng vòng lặp theo tiêu chí số lần lặp. Bảng 4.6. Sinh dữ liệu kiểm thử vòng lặp Ví dụ Mô tả Vòng lặp CC LOC Loại vòng lặp Vòng lặp kiểm thử Kiểu biên Số lần lặp tối đa Số lần lặp Số bộ dữ liệu kiểm thử Merge1 6 17 Vòng lặp nối tiếp i <3 && j < 3 Biên 6 0,1,2,5,6,7 1 i<3 Biên 3 0,1,2,3,4 1 j<3 Biên 3 0,1,2,3,4 1 check_ subsequence 6 13 Vòng lặp lồng nhau a[c]!= ‘\0’ Không xác định - 0,1,2,30 4 a[c]!=b[d] && b[d]!= ‘\0’ Không xác định - 0,1,2,26 4 find_ frequency 4 6 Vòng lặp đơn s[c]!=’0’ Không xác định - 0,1,2,50 4 Bảng 4.6 trình bày tập dữ liệu kiểm thử để kiểm tra tính đúng đắn vòng lặp. Ba loại vòng lặp sinh dữ liệu kiểm thử gồm vòng lặp đơn, vòng lặp lồng nhau (nested loop) và vòng lặp liên tiếp (concatenated loop). Trong bảng này, CC và LOC tương 42 ứng là độ phức tạp của hàm và số dòng mã nguồn được tính bằng cách sử dụng plugin metrics2∗∗ của Eclipse. Cột Vòng lặp kiểm thử mô tả khối lặp đang cần sinh dữ liệu kiểm thử để kiểm tra tính đúng đắn. Cột Kiểu biên mô tả loại khối lặp đang kiểm tra ở cột Vòng lặp kiểm thử. Nếu vòng lặp đang kiểm thử xác định được biên, số lần lặp tối đa thể hiện ở cột Số lần lặp tối đa, ngược lại đánh dấu “-“. Cột Số lần lặp mô tả số lượng lặp của vòng lặp đang kiểm thử để sinh dữ liệu kiểm thử. Cột cuối cùng Số bộ dữ liệu kiểm thử trình bày số lượng bộ dữ liệu kiểm thử sinh được thành công từ những lần lặp này. Ba ví dụ tiêu biểu ứng với ba loại vòng lặp được trình bày trong bảng. Hàm Merge1 ghép hai mảng thành một mảng tăng dần gồm 3 khối lệnh điều khiển while nối tiếp nhau (xem mã nguồn tại phụ lục). Do đó, tập dữ liệu kiểm thử cho từng khối lệnh điều khiển while được sinh ra để kiểm tra tính đúng đắn của vòng lặp. Xét khối while đầu tiên, điều kiện khối lệnh này xác định được số lần lặp tối đa bằng 6 (i<3 && j < 3, trong đó i, j khởi tạo bằng 0). Do đó, tập dữ liệu kiểm thử thỏa mãn lặp khối while đầu tiên này 0 lần (không lặp lần nào), 1 lần, 2 lần, 5 lần, 6 lần (số lần lặp tối đa), 7 lần (số lần lặp tối đa + 1) được sinh. Trong trường hợp này, chỉ có dữ liệu kiểm thử lặp 5 lần được sinh thành công. Những lần lặp còn lại không thể sinh dữ liệu kiểm thử thỏa mãn. 4.3.3. So sánh thời gian biên dịch và thực thi dữ liệu kiểm thử Bảng 4.7 so sánh thời gian biên dịch và thực thi dữ liệu kiểm thử giữa phương pháp cải tiến và phương pháp truyền thống. Tất cả mọi hàm đầu vào đều chứa biến kiểu dẫn xuất, và thuộc tính của những biến này có kiểu con trỏ. Tiêu chí độ phủ sử dụng trong bảng thực nghiệm này là độ phủ MC/DC. Xét ví dụ tiêu biểu calculateZodiac (mã nguồn ở phụ lục), đầu vào của hàm này là một đối tượng kiểu ngày tháng. Số nhánh thỏa mãn độ phủ điều kiện con trong ví dụ này lên tới 120 nhánh. Áp dụng phương pháp truyền thống, tổng số lần thực thi lên tới 74 lần, tổng thời gian biên dịch và chạy vượt quá 10 phút, độ phủ MC/DC đạt được hơn 90%. Trong đó, hơn nửa thời gian chạy là bước biên dịch 74 bộ thực thi dữ liệu kiểm thử này. Ngược lại, phương pháp cải tiến chỉ tiến hành biên dịch 74 dữ 43 liệu kiểm thử này duy nhất một lần. Kết quả, tổng thời gian biên dịch và chạy rút ngắn xuống 3 lần (còn hơn 267 giây). Bảng 4.7. Bảng so sánh thời gian biên dịch và thực thi dữ liệu kiểm thử giữa kĩ thuật cải tiến và kĩ thuật truyền thống Tên hàm Độ phủ Số lần thực thi Kĩ thuật truyền thống Kĩ thuật cải tiến Tổng thời gian (s) Biên dịch (s) Tổng thời gian (s) Biên dịch (s) calculateZodiac(Date) 90.8 74 656.82 60.15 267.19 2 struct_test1(SinhVien1) 100 4 12.5 76.22 5.62 44.48 class_test1(SinhVien) 75 4 11.92 88.22 4.05 65.19 StackLinkedList::push(Node*) 100 2 6.35 39.53 5.48 24.64 StackLinkedList::pop() 100 3 4.56 81.88 2.13 60.09 StackLinkedList::destroyList() 100 2 3.42 71.36 2.56 55.47 disp(Node*) 100 2 3.45 75.32 2.4 60.42 testDate0(Date) 100 2 23.4 97.39 12.01 94.92 44 KẾT LUẬN Luận văn đã trình bày một phương pháp phân tích mã nguồn và sinh dữ liệu kiểm thử tự động cho các dự án C/C++. Kết quả nghiên cứu của luận văn được hiện thực hóa trong công cụ CFT4Cpp. Tính đúng đắn và hiệu quả của phương pháp đề xuất đã được chứng minh trong thực nghiệm qua các so sánh với KLEE, PathCrawler, CUTE, CREST, CAUT. Hiện tại, kết quả nghiên cứu đã được trình bày trong hai hội nghị gồm NICS 2016 và SoICT 2017. Mục tiêu chính của luận văn hướng đến giải quyết vấn đề sinh dữ liệu kiểm thử đầu tiên trong kiểm thử kĩ thuật kiểm thử tự động định hướng, đề xuất phương pháp sinh tập dữ liệu kiểm thử số lượng nhỏ và đạt độ phủ tối đa. Thứ nhất, để giải quyết vấn đề sinh dữ liệu kiểm thử đầu tiên, phương pháp đề xuất tiến hành phân tích chương trình để sinh bộ dữ liệu kiểm thử đầu tiên thay vì sinh ngẫu nhiên. Bởi thế, quá trình thực thi dữ liệu kiểm thử đầu tiên sẽ gây ít lỗi hơn so với phương pháp sinh ngẫu nhiên, đặc biệt với chương trình thao tác với vùng nhớ như con trỏ, xâu, v.v. Thứ hai, để sinh số lượng bộ dữ liệu kiểm thử nhỏ mà đạt độ phủ tối đa, luận văn đề xuất thuật toán LDFS. Tư tưởng chính của thuật toán LDFS là sinh dữ liệu kiểm thử cho trường hợp càng đơn giản hết mức có thể để giảm thiểu chi phí phân tích mã nguồn mà vẫn đạt độ phủ tối đa. Cụ thể, tập dữ liệu kiểm thử được sinh thỏa mãn lặp vòng lặp tối đa một lần, tức là cả nhánh đúng và sai của câu lệnh điều kiện đều được đi qua. Sau bước này, nếu độ phủ đạt được chưa tối đa, LDFS tiến hành sinh dữ liệu kiểm thử thỏa mãn lặp vòng lặp tối đa 0 lần (tức chỉ đi theo nhánh sai của câu lệnh điều kiện), sau đó lặp 2 lần, 3 lần, v.v. đến số lần lặp tối đa được cấu hình từ trước. Quá trình sinh dữ liệu kiểm thử dừng lại khi độ phủ đạt được là tối đa, hoặc hết thời gian chạy, hoặc LDFS đạt đến số lần lặp tối đa của vòng lặp. Công cụ CFT4Cpp cần được tiếp tục phát triển để ngày càng hoàn thiện hơn. Thứ nhất, CFT4Cpp sẽ được nâng cấp để hỗ trợ đầy đủ cú pháp của các chuẩn C++98, C++03, C++11, và C++14. Để làm được điều này, kĩ thuật phân tích mã nguồn cần được cải tiến. Thứ hai, nhiều trường hợp trong dự án C/C++ cần được xử lý thêm như template, dynamic invocation, overloading, v.v. Thứ ba, thuật toán thực thi tượng trưng chưa giải quyết được vấn đề sử dụng bộ nhớ không tường minh khi sử 45 dụng hàm liên quan bộ nhớ như memmove, strncat. Kĩ thuật thực thi tượng trưng hiện tại chưa mô phỏng đầy đủ và chính xác được thay đổi của các biến khi những câu lệnh kiểu này được gọi. Thứ tư, công cụ cần được cải tiến để sinh dữ liệu kiểm thử cho các độ phủ khác bên cạnh ba độ phủ đã hỗ trợ sẵn gồm độ phủ nhánh, độ phủ câu lệnh và độ phủ MC/DC. Cuối cùng, công cụ hướng tới hỗ trợ nhiều trình biên dịch khác nhau trên các nền tảng khác nhau. 46 PHỤ LỤC // Hàm calculateAge struct Date { int date; int month; int year;}; int calculateZodiac(Date born) { int date1 = born.date; int month1 = born.month; int year1 = born.year; int t; if (((month1 == 3) && (date1 >= 21) && (date1 <= 31)) || ((month1 == 4) && (date1 <= 19))){ t = 1; } else if (((month1 == 4) && (date1 >= 20) && (date1 <= 30)) || ((month1 == 5) && (date1 <= 20))) { t = 2; } else if (((month1 == 5) && (date1 >= 21) && (date1 <= 31)) || ((month1 == 6) && (date1 <= 20))) { t = 3; } else if (((month1 == 6) && (date1 >= 21) && (date1 <= 30)) || ((month1 == 7) && (date1 <= 22))) { t = 4; } else if (((month1 == 7) && (date1 >= 23) && (date1 <= 31)) || ((month1 == 8) && (date1 <= 22))) { t = 5; } else if (((month1 == 8) && (date1 >= 23) && (date1 <= 31)) || ((month1 == 9) && (date1 <= 22))) { t = 6; } else if (((month1 == 9) && (date1 >= 23) && (date1 <= 30)) || ((month1 == 10) && (date1 <= 22))) { t = 7; } else if (((month1 == 10) && (date1 >= 23) && (date1 <= 31)) || ((month1 == 11) && (date1 <= 21))) { t = 8; } else if (((month1 == 11) && (date1 >= 22) && (date1 <= 31)) || ((month1 == 12) && (date1 <= 21))) { t = 9; } else if (((month1 == 12) && (date1 >= 22) && (date1 <= 30)) || ((month1 == 1) && (date1 <= 19))) { t = 10; } else if (((month1 == 1) && (date1 >= 20) && (date1 <= 31)) || ((month1 == 2) && (date1 <= 18))) { t = 11; } else if (((month1 == 2) && (date1 >= 19) && (date1 <= 29)) || ((month1 == 3) && (date1 <= 20))) { t = 12; } else t = -1; return t; } 47 // Hàm Merge1 // Should copy all the elements of ordered arrays t1 and t2 into the ordered array t3 // Link: pathcrawler-online.com void Merge1(int t1[3], int t2[3], int t3[6]) { int i = 0, j = 0, k = 0; while (i < 3 && j < 3) { if (t1[i] < t2[j]) { t3[k] = t1[i]; i++; } else { t3[k] = t2[j]; j++; } k++; } while (i < 3) { t3[k] = t1[i]; i++; k++; } while (j < 3) { t3[k] = t2[j]; j++; k++; } } // Hàm find_frequency // Find frequency of characters in a string // Link: void find_frequency(char s[], int count[]) { int c = 0; while (s[c] != '\0') { if (s[c] >= 'a' && s[c] <= 'z') count[s[c] - 'a']++; c++; } } 48 TÀI LIỆU THAM KHẢO [1]. J. Burnim and K. Sen. 2008. Heuristics for Scalable Dynamic Test Generation. In Proceedings of the 2008 23rd IEEE/ACM International Conference on Automated Software Engineering (ASE ’08). IEEE Computer Society, Washington, DC, USA, 443–446. [2]. Cristian Cadar, Daniel Dunbar, and Dawson Engler. 2008. KLEE: Unassisted and Automatic Generation of High-coverage Tests for Complex Systems Programs. In Proceedings of the 8th USENIX Conference on Operating Systems Design and Implementation (OSDI’08). USENIX Association, Berkeley, CA, USA, 209–224. [3]. Cristian Cadar and Koushik Sen. 2013. Symbolic Execution for Software Testing: Three Decades Later. Commun. ACM 56, 2 (Feb. 2013), 82–90. [4]. Leonardo De Moura and Nikolaj Bjørner. 2008. Z3: An Efficient SMT Solver. In Proceedings of the Theory and Practice of Software, 14th International Conference on Tools and Algorithms for the Construction and Analysis of Systems (TACAS’08/ETAPS’08). Springer-Verlag, Berlin, Heidelberg, 337– 340. [5]. Patrice Godefroid, Nils Klarlund, and Koushik Sen. 2005. DART: Directed Automated Random Testing. In Proceedings of the 2005 ACM SIGPLAN Conference on Programming Language Design and Implementation (PLDI ’05). ACM, New York, NY, USA, 213–223. [6]. James C. King. 1976. Symbolic Execution and Program Testing. Commun. ACM 19, 7 (July 1976), 385–394. [7]. Guodong Li, Indradeep Ghosh, and Sreeranga P. Rajan. 2011. KLOVER: A Symbolic Execution and Automatic Test Generation Tool for C++ Programs. In Proceedings of the 23rd International Conference on Computer Aided Verification (CAV’11). Springer-Verlag, Berlin, Heidelberg, 609–615. [8]. George C. Necula, Scott McPeak, Shree Prakash Rahul, and Westley Weimer. 2002. CIL: Intermediate Language and Tools for Analysis and Transformation of C Programs. In Proceedings of the 11th International Conference on Compiler Construction (CC ’02). Springer-Verlag, London, UK, UK, 213– 228. 49 [9]. Dirk Riehle. 1997. Composite Design Patterns. In Proceedings of the 12th ACM SIGPLAN Conference on Object-oriented Programming, Systems, Languages, and Applications (OOPSLA ’97). ACM, New York, NY, USA, 218–228. [10]. Koushik Sen, Darko Marinov, and Gul Agha. 2005. CUTE: A Concolic Unit Testing Engine for C. In Proceedings of the 10th European Software Engineering Conference Held Jointly with 13th ACM SIGSOFT International Symposium on Foundations of Software Engineering (ESEC/FSE-13). ACM, New York, NY, USA, 263–272. [11]. T. Su, G. Pu, B. Fang, J. He, J. Yan, S. Jiang, and J. Zhao. 2014. Automated CoverageDriven Test Data Generation Using Dynamic Symbolic Execution. In 2014 Eighth International Conference on Software Security and Reliability (SERE). 98–107. [12]. Z. Wang, X. Yu, T. Sun, G. Pu, Z. Ding, and J. Hu. 2009. Test Data Generation for Derived Types in C Program. In 2009 Third IEEE International Symposium on Theoretical Aspects of Software Engineering. 155–162. [13]. Nicky Williams, Bruno Marre, Patricia Mouy, and Muriel Roger. 2005. PathCrawler: Automatic Generation of Path Tests by Combining Static and Dynamic Analysis. In Proceedings of the 5th European Conference on Dependable Computing (EDCC’05). Springer-Verlag, Berlin, Heidelberg, 281–292 [14]. Cristian Cadar, Vijay Ganesh, Peter M. Pawlowski, David L. Dill, and Dawson R. Engler. 2008. EXE: Automatically Generating Inputs of Death. ACM Trans. Inf. Syst. Secur. 12, 2, Article 10 (Dec. 2008), 38 pages. D~I HOC QUOC GIA HA NQI TRU"ONG D~I HQC CONG NGHE: ------w ------ C<)NG HOA XA H<)I CHU NGHiA VIE:T NAM D(}c l~p- Tl! do- H~nh phuc *********** Ha N{Ji, ngay .. Z-thang 12 niim 2017 QUYETNGHJ CUA HQI DONG CHAM LU~N VAN TH~C Si Can Clr Quy€t djnh s6 1138/QD-DT, ngay 23 thang 11 nam 2017 cua Hi~u tru6ng truemg D~i hQC Cong ngh~ v€ vi~c thanh l~p H(>i dbng ch~m lu~n van th~c si cua h9c vien Nguy~n Due Anh, H(>i dbng ch~m lu~n van Th~c si da hQp vao 14h, thu 7, ngay 02 thang 12 nam 2017, Phong 309, Nha E3, Truo·ng D~i hQc Cong ngh~- DHQGHN. Ten d€ tai lu~n van: Phuong phap phan tich rna ngufin va sinh dfr li~u ki~m thlr cho cac dl}' an C/C++ Nganh: Cong ngh~ Thong tin Chuyen nganh: Ky thu~t phftn m~m Mas6: Sau khi nghe h9c vien trinh bay tom tAt lu~n van Th~c si, cac phim bi~n d9c nh~n xet, h9c vien tra lai cac cau hoi, H(>i dbng da hQp, trao d6i y ki€n va thfJng nh~t k€t lu~n: 1. V~ tinh cftp thi~t,Jinh th(ri S\f, y nghia ly lu~n va th\fC ti~n CUa d~ tai lu~n van: ...... {)i~ ... k. .... ~ .... v.tl:;, ....... U: ..... ti. .. ~~-~ ...... ~ ....... ll.~ ..... ~ ..... t.:~~ .. ~ ~ {/ '·7~ , .. 2. Vi bB Ct}.C, phuO"ng phap nghien CUU, tai li~U tham khao, ..... CUa lu~n van: .................. ·; ............................................... ) .. ...... ·o ..... ~ ............ ~- ................. ·;~ .................... , ........ ·p .... ~- ........ t~· .. . .. -=: .. /f,£. ... ~·······4····~····~--~·····~······,?16r; ...... ~f;;-]······ ::c:::::flt::::j:::;x.;;::::::::;;p;:::::~::::r::·~~:::::::;x:::;&~~:::::~::::: ~:::::::~-:::::zE/::·:~:::~:::.::J??::::::~:::::::i!1':::::·~:1:::--:::::::::::::::::::::·:::-::: 3. v~ k~t qua nghien cuu: =---ii:~--~----~-~---4···4'. ..... ~~---··;4 ... r-~-f···;£;·~-~·-····~---·· -~······~·-f:··-'·: .. ~ .......................................................................................................................................... . ·············~············;·)·················~··························;·············.:.:.·························/··;1·····················:;································· =--~ .... ,KJ.J..itl ..... h······-N·····w. ...... Oa_ ....... ~ ....... L ................................... . :;:~:::::::ZiJ:::::::~::::::~::::::::i :::z:;i::~:;;:_:::::::::::::::::::::::::::::::::::::::::::::::::::::::::::: 4. H~n ch~ ciia lu~n van (n~u co): ::::::::;za.:::;;z::::::~:::::::~::::~:::::::L::.t.::::::~:~:::::::::::::::::::::::::::::::::::::::::::: - .......... , ... ~······························f'····························································································r································r······· ~ ...... kk. ...... d.:. ..... ;;~ ...... ~ ....... ~ ...... ~~-······Ld: ...... ~~ . .A ..... ~ ...... ~d?. .... . I , - ;::::c;i,::::;;R;i::·::;.:::;t;i:::::;;.J.:::::~::::::~:::::::;z.::::;;.::::~:::::~&J.:'.:::::: 5. Danh gia chung va k~t lu~n: /) r ... ~ .... Mt!it. .... ~ ... u, ..... ~ ... cX.. .... uJ. ..... ~Y..::_~······~·-··-~····-~~­ .{;/,1.J .... fH:4. .... ~ ..... y~---·~·-······~····· ~-····~································································ :::::-::::::::::::::::::::::::::::::::::.::::::::::::-:::::::::::::::::::::::::::::::::::::::::::1::::::::::::::::::::::::::::::::::::::::::::::::::.:::::::::::::::::::::::::::: Lu~n van d~t f...~ . ./ 10 di€m. Quy€t ngh! nay OUQ"C ... :s:. I .s:: ... thanh vien cua H(>i a6ng nhftt tri thong qua. THU' KY H<)I DONG CHU TJCH H<)I DONG 11 T(Lu i!j /Jr( /-{ ~ ,__ XACNH~~CUACOS6DAOT~O :·r r !~ \ \Tl!'tT '\..T • '·r , , · Ur il ,l r"t.. · ~ ...... l "i "~ "·~ .t - H ~· n h ph U. c BAN NHAN XET PHAN BIEN LUAN VAN THAC Si . . . . HQ va ten can b<) phan bi~n: Le Quang Minh HQC haiTI, hQC vi: Chuyen nganh: May tinh va H~ th6ng tinh toan Co quan cong tac: Vi~n Cong ngh~ thong tin - DHQGHN HQ va ten hQc vien cao hQc: Nguy~n Drrc Anh Ti~n sy Ten d~ tai lu~n van: PhrrO'ng phap phan tich rna nguBn va sinh dfr li~u ki~m thrr cho cac dlf an C/C++ Chuyen nganh: Ky thu~t ph~n rn~rn Ma s6: 60.48.01.03 Y KIEN NH~N XET 1. Tinh cdp thiit, thai Sl:f, y ngh'ia khoa hQC va thvc tiln cita aJ tai lu(jn van. Ngon ngfr l~p trinh C/C++ vfrn la ngon ngfr trong han 70% cac dv an phat tri6n h~ thf>ng nhung, vi v~y vi~c nghien Clru cac ky thu~t ki6rn thtr cho cac dV an tren ngon ngfr C/C++ luon la rn<)t doi hoi d~t ra. Lu~n van nay t~p trung nghien c(ru v~ vfrn d~ phan tich rna ngu6n va sinh ca ki6rn thu tv d<)ng cho cac dv an C/C++, d~ tai nay c6 y nghia thai sv, khoa hQc, phil hqp v6i lu~n van th~c sy chuyen nganh ky thu~t ph~n rnern. 2. Sv khong trung l¢p cua a~ tai nghien ciru so vai cac cong trinh khoa h9c, lucjn win aa cong b6 a trong va ngoai nuac; tinh trung thvc, ro rang va ady au trong trich ddn tai li¢u tham khao. Lu~n van c6 sv t6ng hqp va phat tri6n tir nhi~u ngu6n tai li~u, c6 sv trich dfrn r5 rang. Cach thuc trich dfrn tai li~u nghiern tuc. Khong phat hi~n ra sv trilng l~p v6i cac lu~n van tru6c day. 3. Sv phil hQ'p gifra ten a~ tai vai n9i dung nghien CUu, cung nhu vai chuyen nganh va ma s6 aao tqo. Ten d~ tai va n<)i dung d~ tai phil hqp v6i nhau, phil hqp v6i chuyen nganh va rna s6 dao t~o. 4. D9 tin ccjy va tinh hi?n aqi cita phuang phap nghien CUu aa sir dZJng a~ hoan thanh lucjn van. D~ tai c6 sv trich dfrn tir nhi~u ngu6n tai li~u, cac tai li~u th6 hi~n sv phong phu, c~p nh~t phil hqp v6i llnh vvc cua d~ tai. Pi;. __ c(ru. 5. J(dt qzf(z nglzien cz!·u ;n(li CltLt /(lC giu: dc)ng gop mO'i cho s~r pluit trien chuyen ngc7nh: t/ong gop 1no·i ph~tc v~1 san xuci1. kinh rd. xa h(Ji, an ninh, qubc phong va t/o·i sbng. Y nghza khoa h9c, gia trf vet (19 1 in c4_v czla nhfing kit qua nghien cz'ru. Lu<;ln van trinh bay t6ng quat chung v~ l) thuy~t sinh ca ki~1n thu tg d<)ng d6i v&i dv an C/C++, d€ xuclt cac cai ti~n ti~p c<;ln theo huang sinh ca ki~m thu dftu tien d~ tir d6 se c6 s6 luqng ca ki~1n thu nho nhclt v&i d(> phu t6i da. Cac k~t qua nay da duqc thu nghi~1n va cong b6 trong ky y~u khoa hQc cua 2 H<)i nghi NICS 2016 va SoiCT 2017. 6. Uu aiim va nhu(JC aiim v~ n(Ji dung, bb Cl;lC va hinh thuc cua luqn van. Lu<;ln van nhin chung dap ung cac yeu cftu ca ban cua lu?n van th~c sy nganh Cong ngh~ thong tin, chuyen nganh Ky thu?t phftn m€m, tuy nhien v~n con m<)t s6 di~m nen luu y d~ chinh sua cho hoan thi~n han: - Hinh thuc: Dftu dong cftn thvt vao, khoang each trang phai theo dung quy dinh. Nhi€u cau van chua duqc chau chu6t, cvt lun ho~c khong r5 nghia; - Ten gQi cac chuang nen duqc d~t l~i cho r5 y nghia cua cac chuang, hi~n nay n~u chi nhin vao mvc lvc khong r5 lu?n van lam gi va cai ti~n gi; - Trong lu?n van xuclt hi~n cac tir lu?n an, kh6a lu?n t6t nghi~p, khong r5 la tac gia c6 sv nhftm l~n hay c6 sv copy 6 dau trong qua trinh vi~t lu?n van. 7. Kit luqn chung Lu?n van dap ung duqc yeu cftu cua m<)t lu?n van t6t nghi~p th~c sy, toi de nghi H<)i dbng cho phep hQc vien duqc phep bao v~ tru&c H<)i dbng. Ha N <)i, ngayl. thang 1(tnam lJ)) }- CAN BQ PHAN BI~N TS. Le Quang Minh C<)NG HOA xA H<)I CHU NGHiA VI~T NAM D(}c I~p - Tv do - H~nh phuc ===================== BAN NH~N xET PHAN BI~N LU~N VAN TH~C Si HQ va ten can b{) phan bi~n: Truang Ninh Thu~n HQc ham, hQc vi: PGS.TS Chuyen nganh: CNTT Ca quan c6ng tac: Truemg DH C6ng ngh~ - DHQGHN HQ va ten hQc vien cao hQc: Nguy~n Due Anh Tend~ tai lu~n van: Phuang phap phan tich rna ngu6n va sinh dfr li~u ki~m thu cho cac dv an C/C++ Y KIEN NH~N XET Ki~m thu d6ng vai tro quan trQng trong quy trinh phat tri~n phAn m~m, cho phep ki~m tra cac I6i c6 th~ xay ra trong chuang trinh phAn m~m. M{)t trong cac thach thuc cua vi~c ki~m thu la sinh dfr li~u dAu vao d~ vi~c ki~m thu chuang trinh hi~u qua. Lu~n van nghien Clru v~ phuang phap phan tich rna ngu6n va sinh dfr li~u ki~m thir cho cac d\f an C/C++ la m{)t d~ tai c6 y nghia khoa hQc va thvc ti~n cao. 1. N{)i dung lu~n van bao g6m 5 chuang: Chuang 1 neu phAn d~t vfin d~. Chuang nay nen chuy~n thanh Gi6i thi~u trong d6 c6 phAn d~t vfin d~, gi6i thi~u n{)i dung va cfiu true kh6a lu~n. Chuang 2 gi6i thi~u v~ ca sa ly thuy~t, tac gia trinh bay cac ki~n thuc vS ki~m thu. Chuang 3 trinh bay phuang phap sinh ca ki~m thir tir rna ngu6n chuang trinh C/C++ Chuang 4 gi6i thi~u c6ng C\} h6 trq va thvc nghi~m. Chuang 5 la phAn k~t lu~n cua lu~n van. 2. D6ng g6p cua lu~n van: - Tim hi~u v~ ki~m thu va phan tich rna ngu6n - Xay d\fllg phuang phap sinh ca ki~m thu tir rna chuang trinh C/C++ - Xay dvng c6ng cv sinh ca ki~m thu tv d{)ng 3. H~n ch~ cua lu~n van: - H~n ch~ v~ cfiu true, trinh bay, I6i chinh ta - Chua lam r5 vi~c sinh ca ki~m thu thS nao, hi~u qua cua cac ca ki~m thu sinh ra trong vi~c tim 16i chuang trinh ph~n mSm. 4. KSt lu~n chung: Lu~n van dap irng cac yeu c~u cua m<)t lu~n van th~c sy nganh CNTT, chuyen nganh KTPM. Ha N<)i, ngay 01 thang 12 nam 2017 :xAc NH~N CUA CO QUAN CONG TAC CAN B<) PHAN BI~N _/LI~17~ r'l~ •. / .--" jV'" '/r)

Các file đính kèm theo tài liệu này:

  • pdfluan_van_phuong_phap_phan_tich_ma_nguon_va_sinh_du_lieu_kiem.pdf
Luận văn liên quan