1. Trang chủ
  2. » Luận Văn - Báo Cáo

NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm

64 1,3K 16
Tài liệu đã được kiểm tra trùng lặp

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

THÔNG TIN TÀI LIỆU

Thông tin cơ bản

Tiêu đề Nghiên cứu kỹ Thuật Phân Tích Chương Trình Tĩnh Trong Việc Nâng Cao Chất Lượng Phần Mềm
Trường học Trường Đại học Công nghệ, Đại học Quốc gia Hà Nội
Chuyên ngành Kỹ thuật phần mềm
Thể loại Luận văn tốt nghiệp
Năm xuất bản 2023
Thành phố Hà Nội
Định dạng
Số trang 64
Dung lượng 4,16 MB

Các công cụ chuyển đổi và chỉnh sửa cho tài liệu này

Nội dung

Sự tiến hóa nhanh chóng của các thiết bị phần cứng trong hơn 30 năm qua đã đưa đến hệ quả về sự phát triển theo cấp số nhân của kích cỡ các chương trình phần mềm chạy trên đó. Quy mô của những ứng dụng cực lớn này (khoảng từ 1 tới 40 triệu dòng mã lệnh) vẫn tiếp tục gia tăng trong thời gian tới. Những phần mềm như thế cần phải được thiết kế với chi phí vừa phải trong khi vẫn phải bảo trì, nâng cấp trong toàn bộ vòng đời của chúng, tầm 20 năm. Một thực tế là quy mô và hiệu quả của những nhóm lập trình và bảo trì chúng không thể tăng theo tỉ lệ như vậy. Với hoàn cảnh đó, tỉ lệ giả định 1 lỗi trong 1000 dòng lệnh đối với những phần mềm như vậy là quá lạc quan và sẽ không thể đạt được trong những hệ thống đòi hỏi độ an toàn cực cao. Do đó, vấn đề về độ tin cậy của phần mềm (software reliability) chắc chắn là một mối quan tâm và thách thức đối với xã hội hiện đại ngày càng phụ thuộc vào các dịch vụ do máy tính đem lại. Nhiều kỹ thuật kiểm chứng phần mềm (software verification) và các công cụ hỗ trợ đi kèm đã được phát triển để thực thi hoặc giả lập chương trình trên nhiều môi trường khác nhau. Tuy nhiên, gỡ rối mã dịch hoặc giả lập mô hình của mã nguồn các chương trình không thể mở rộng quy mô và thường chỉ xét được mức độ bao phủ hạn chế các hành vi động của chương trình. Các phương pháp hình thức trong kiểm chứng chương trình (formal methods) cố gắng chứng minh một cách tự động rằng chương trình sẽ thực thi đúng đắn trên mọi môi trường được đặc tả. Mảng nghiên cứu này bao gồm các phương pháp suy dẫn (deductive methods), kiểm chứng mô hình (model checking), định kiểu chương trình (program typing) và phân tích chương trình tĩnh (static program analysis). Ba nhóm đầu tập trung vào việc kiểm chứng phần mềm tại mức mô hình, trong khi nhóm cuối cùng xử lý phần mềm tại mức mã nguồn. Phân tích chương trình tĩnh thu hút sự quan tâm nhất do nền tảng lý thuyết hình thức của nó cũng như mục đích của nó đối với các ứng dụng của nó trong thực tế. Kỹ thuật này phát hiện tính chấthành vi của một chương trình mà không yêu cầu chạy chương trình đó. Ngoài ra, một số lỗi chương trình như việc khởi tạosử dụng biến chương trình, biến con trỏ NULL,... có thể được phát hiện bởi kỹ thuật này. Mục tiêu chính của luận văn là cập nhật được những xu thế trên thế giới trong lĩnh vực phân tích chương trình và cải tiến những kỹ thuật này. Cụ thể, luận văn tập trung vào nghiên cứu kỹ thuật phân tích chương trình dựa trên đồ thị luồng dữ liệu để nâng cao chất lượng phần mềm. Tiến hành thực nghiệm trên công cụ SOOT, một công cụ mã nguồn mở phân tích chương trình viết bằng Java trên môi trường tích hợp phát triển Eclipse. Cấu trúc của luận văn bao gồm: Chương 1 giới thiệu tổng quan về phân tích chương trình tĩnh. Trong chương này, trình bày định nghĩa kỹ thuật phân tích chương trình tĩnh, ứng dụng kỹ thuật phân tích, điểm mạnh và những hạn chế của kỹ thuật phân tích tĩnh. Và phần chính trong Chương 1 là phần kiến thức nền tảng chính sử dụng trong kỹ thuật phân tích chương trình tĩnh. Tiếp theo là Chương 2 trình bày về phân tích luồng dữ liệu. Cụ thể, trình bày phương pháp phân tích luồng dữ liệu trong một hàm không có chứa lời gọi hàm (nội thủ tục) và phân tích luồng dữ liệu đồ thị luồng dữ liệu cho cả chương trình với lời gọi hàm (liên thủ tục). Chương 3 trình bày phần thực nghiệm với SOOT một công cụ nguồn mở để phân tích chương trình viết bằng Java trên môi trường tích hợp phát triển là Eclipse. Cuối cùng là phần kết luận và các tài liệu tham khảo.

Trang 1

2.1 Phân tích luồng dữ liệu nội thủ tục 2.42.1.1 Phân tích quay lại (backward) 2.52.1.2 Phân tích chuyển tiếp (forward) 3.12.2 Phân tích luồng dữ liệu liên thủ tục 4.02.2.1 Xây dựng đồ thị luồng dữ liệu 4.12.2.2 Tính cảm ngữ cảnh (context sensitivity) 4.32.2.3 Ứng dụng phân tích luồng dữ liệu liên thủ tục 4.5

Trang 2

3 Thực nghiệm 47

3.1 Tổng quan về SOOT 4.73.1.1 Giới thiệu 4.73.2 Phân tích chương trình cùng với SOOT trong Eclipse 4.8

Trang 3

DANH MỤC CÁC KÝ HIỆU VÀ CHỮ VIẾT TẮT

Trang 4

Danh sách hình vẽ

1.1 CFG cho các lệnh cơ bản

1.31.2 CFG cho các lệnh tuần tự

1.31.3 CFG cho các lệnh if, if-else

1.31.4 CFG cho các lệnh while, for

1.41.5 CFG của chương trình tính giai thừa

1.51.6 Biểu đồ Hasse biểu diễn Dàn

1.61.7 Các biểu đồ Hasse là Dàn

1.71.8 Các biểu đồ Hasse không phải là Dàn

1.71.9 Phép toán cộng dàn

1.91.10 Phép toán nâng (lift) dàn

1.9

1.11 Phép toán lift của các tập tạo thành dàn 2.0

2.1 Dàn cho chương trình phân tích tính sống của biến

2.62.2 Dàn cho chương trình phân tích biểu thức bận rộn

2.92.3 CFG của chương trình phân tích biểu thức bận rộn

3.02.4 Dàn cho chương trình phân tích biểu thức có sẵn

3.42.5 CFG của chương trình phân tích biểu thức có sẵn

3.42.6 CFG của chương trình phân tích định nghĩa tới được 3.8

2.7 Đồ thị def-use định nghĩa tới được của các biến của chương trình 4.0

2.8 Ví dụ CFG tổng quát cho chương trình có chứa lời gọi hàm 4.22.9 CFG của chương trình phân tích liên thủ tục 4.32.10 CFG đơn biến

4.42.11 CFG đa biến

4.53.1 Tổng quan về luồng làm việc của SOOT [10]

4.83.2 Phương thức copy() và merge() trong SOOT

4.93.3 Kết quả phân tích với SOOT

5.1

Trang 5

MỞ ĐẦU

Sự tiến hóa nhanh chóng của các thiết bị phần cứng trong hơn 30 năm qua đãđưa đến hệ quả về sự phát triển theo cấp số nhân của kích cỡ các chương trìnhphần mềm chạy trên đó Quy mô của những ứng dụng cực lớn này (khoảng từ 1tới 40 triệu dòng mã lệnh) vẫn tiếp tục gia tăng trong thời gian tới Những phầnmềm như thế cần phải được thiết kế với chi phí vừa phải trong khi vẫn phải bảotrì, nâng cấp trong toàn bộ vòng đời của chúng, tầm 20 năm Một thực tế là quy

mô và hiệu quả của những nhóm lập trình và bảo trì chúng không thể tăng theo

tỉ lệ như vậy Với hoàn cảnh đó, tỉ lệ giả định 1 lỗi trong 1000 dòng lệnh đối vớinhững phần mềm như vậy là quá lạc quan và sẽ không thể đạt được trong những

hệ thống đòi hỏi độ an toàn cực cao Do đó, vấn đề về độ tin cậy của phần mềm

(software reliability) chắc chắn là một mối quan tâm và thách thức đối với xã

hội hiện đại ngày càng phụ thuộc vào các dịch vụ do máy tính đem lại Nhiều kỹ

thuật kiểm chứng phần mềm (software verification) và các công cụ hỗ trợ đi kèm

đã được phát triển để thực thi hoặc giả lập chương trình trên nhiều môi trườngkhác nhau Tuy nhiên, gỡ rối mã dịch hoặc giả lập mô hình của mã nguồn cácchương trình không thể mở rộng quy mô và thường chỉ xét được mức độ baophủ hạn chế các hành vi động của chương trình Các phương pháp hình thức

trong kiểm chứng chương trình (formal methods) cố gắng chứng minh một cách

tự động rằng chương trình sẽ thực thi đúng đắn trên mọi môi trường được đặc tả

Mảng nghiên cứu này bao gồm các phương pháp suy dẫn (deductive methods), kiểm chứng mô hình (model checking), định kiểu chương trình (program typing)

và phân tích chương trình tĩnh (static program analysis) Ba nhóm đầu tập trung

vào việc kiểm chứng phần mềm tại mức mô hình, trong khi nhóm cuối cùng xử

lý phần mềm tại mức mã nguồn Phân tích chương trình tĩnh thu hút sự quantâm nhất do nền tảng lý thuyết hình thức của nó cũng như mục đích của nó đốivới các ứng dụng của nó trong thực tế Kỹ thuật này phát hiện tính chất/hành vicủa một chương trình mà không yêu cầu chạy chương trình đó Ngoài ra, một sốlỗi chương trình như việc khởi tạo/sử dụng biến chương trình, biến con trỏNULL, có thể được phát hiện bởi kỹ thuật này

Mục tiêu chính của luận văn là cập nhật được những xu thế trên thế giới tronglĩnh vực phân tích chương trình và cải tiến những kỹ thuật này Cụ thể, luận văntập trung vào nghiên cứu kỹ thuật phân tích chương trình dựa trên đồ thị luồng

dữ liệu để nâng cao chất lượng phần mềm Tiến hành thực nghiệm trên công cụ

Trang 6

SOOT, một công cụ mã nguồn mở phân tích chương trình viết bằng Java trênmôi trường tích hợp phát triển Eclipse.

Cấu trúc của luận văn bao gồm: Chương 1 giới thiệu tổng quan về phân tíchchương trình tĩnh Trong chương này, trình bày định nghĩa kỹ thuật phân tíchchương trình tĩnh, ứng dụng kỹ thuật phân tích, điểm mạnh và những hạn chếcủa kỹ thuật phân tích tĩnh Và phần chính trong Chương 1 là phần kiến thức nềntảng chính sử dụng trong kỹ thuật phân tích chương trình tĩnh Tiếp theo làChương

2 trình bày về phân tích luồng dữ liệu Cụ thể, trình bày phương pháp phân tíchluồng dữ liệu trong một hàm không có chứa lời gọi hàm (nội thủ tục) và phântích luồng dữ liệu đồ thị luồng dữ liệu cho cả chương trình với lời gọi hàm (liênthủ tục) Chương 3 trình bày phần thực nghiệm với SOOT - một công cụ nguồn

mở để phân tích chương trình viết bằng Java trên môi trường tích hợp pháttriển là Eclipse Cuối cùng là phần kết luận và các tài liệu tham khảo

Trang 7

Chương 1

Giới thiệu

1.1 Giới thiệu về phân tích chương trình

Phân tích chương trình tĩnh là kỹ thuật xác định tính chất/hành vi của mộtchương trình mà không cần phải chạy chương trình đó Phân tích tĩnh được xây

dựng dựa trên lý thuyết diễn giải trừu tượng (abstract interpretation) [5, 6] để

chứng minh tính chính xác của các phân tích liên quan đến ngữ nghĩa của mộtngôn ngữ lập trình

Có rất nhiều câu hỏi thú vị mà có thể được hỏi về một chương trình hoặc các

điểm (point) riêng lẻ trong chương trình như:

• Chương trình có dừng hay không?

• Độ lớn có thể của vùng nhớ (heap) trong khi chạy?

• Đầu ra (output) có thể là gì?

• Biến x có luôn luôn cùng giá trị không?

• Giá trị của x sẽ được đọc trong tương lai?

• Con trỏ p null?

• Biến x đã được khởi tạo trước khi đọc không?

• v.v

Theo lý thuyết Rice [14], tất cả các câu hỏi trên về hành vi của chương trình là

không thể quyết định/chứng minh được (undecidable).

Thay vì mức mô hình như nhiều phương pháp hình thức, luận văn hướng tớiviệc phân tích chương trình tĩnh Cụ thể, luận văn trình bày một kỹ thuật để cải

Trang 8

tiến mã chương trình và phát hiện các lỗi tiềm năng bằng việc phân tích chươngtrình tĩnh dựa trên phân tích luồng dữ liệu.

1.2 Điểm mạnh và điểm yếu

Phân tích chương trình tĩnh có những ưu điểm sau:

• Chỉ ra lỗi tại vị trí chính xác trong chương trình

• Dễ dàng thực hiện bởi những chuyên gia kiểm định chất lượng phần mềm hiểu rõ về mã nguồn

• Khoảng thời gian ngắn từ lúc phát hiện tới khi sửa lỗi

• Có thể tự động hóa nhanh (thông qua các bộ công cụ hỗ trợ ví dụ: SOOT,Astree, TVLA, )

• Lỗi được phát hiện sớm trong qui trình phát triển phần mềm nên chi phí sửalỗi thấp

Tuy nhiên, điểm yếu của kỹ thuật này xuất hiện khi tại một câu lệnh xuất hiệnnhững tham chiếu, ràng buộc nằm ngoài phạm vi suy luận biểu trưng của chươngtrình Hạn chế này là bản chất của việc phân tích tĩnh - không chạy với dữ liệu cụthể Một số điểm yếu không khắc phục được:

• Mất thời gian nếu phải thực hiện bằng tay

• Việc tự động hóa chỉ hướng vào một ngôn ngữ lập trình (ví dụ: SOOT chỉ kiểm tra mã nguồn chương trình viết bằng ngôn ngữ Java)

• Thiếu nhân lực có thể hiểu và phân tích được chương trình

• Có thể sinh ra nhiều lời cảnh báo lỗi không chính xác

• Không phát hiện được lỗi chỉ xuất hiện khi chạy chương trình (run-time ror)

er-1.3 Các công nghệ phân tích chương trình tĩnh

Những kỹ thuật phân tích chương trình tĩnh đã và đang thu hút nhiều nghiêncứu trên thế giới, hiện có nhiều kỹ thuật nhưng tựu chung có thể phân theo 4nhóm chính như sau:

Trang 9

Thứ nhất, kỹ thuật phân tích chương trình tĩnh dựa trên phân tích luồng dữ

liệu (data flow analysis) [11, 15] Phân tích luồng dữ liệu là một quá trình thu

thập thông tin về dữ liệu trong các đoạn mã đó được thực thi trong thực tế trongchương trình mà không cần phải chạy đoạn mã đó Tuy nhiên, phân tích luồng

dữ liệu không sử dụng thao tác dựa trên ngữ nghĩa Phân tích luồng dữ liệu làmột cách rất hiệu quả và khả thi trong việc phát hiện lỗi chương trình và tối ưuhóa trong các trình biên dịch

Thứ hai, nhóm kỹ thuật liên quan tới xấp xỉ ngữ nghĩa được gọi là diễn giải

trừu tượng (abstract interpretation) [5, 6] Kỹ thuật diễn giải trừu tượng dựa trên

nguyên tắc xấp xỉ ngữ nghĩa của chương trình khi kiểm tra đối chiếu sự thỏa mãnđặc tả Kỹ thuật này trích ra từ một ngữ nghĩa chuẩn (standard semantics) đượcmột ngữ nghĩa trừu tượng đã xấp xỉ và tính toán được (approximate andcomputable abstract semantics) Quá trình chuyển này không hoàn toàn tựđộng mà có thể cần sự tương tác với người dùng Trong thực tế, kỹ thuật diễngiải trừu tượng có một thành phần là bộ sinh (generator) ngữ nghĩa trừu tượngđọc mã nguồn chương trình và tạo ra các ràng buộc hoặc hệ các phương trình cầnđược giải bởi máy tính thông qua một thành phần khác là bộ giải (solver) Mộtphương pháp phổ biến là dùng hàm lặp khi giải Việc tìm nghiệm thông qua hàmlặp có hạn chế về mặt thời gian (phương pháp không hội tụ sau vô hạn lần lặp).Các kỹ thuật liên quan tới việc tăng tốc hội tụ cũng được nghiên cứu

Thứ ba, nhóm kỹ thuật liên quan tới mô hình được gọi là kỹ thuật kiểm

chứng mô hình (Model checking) [4] Mô hình (model) của một hệ thống là

một cách biểu hiện ở mức trừu tượng cao hơn của hệ thống bằng cách lược bỏnhững phần quá chi tiết mà vẫn giữ lại những thông tin cần thiết về hệ thốngđang được xem xét Trong lĩnh vực phần mềm, kiểm chứng mô hình là cáchkiểm tra xem liệu mô hình của một hệ thống (phần cứng hay phần mềm) thỏamãn một tính chất nào đó hay không Những đặc tả tính chất đó thường lànhững tính chất an toàn như khả năng không tồn tại những khóa chết (deadlock)hoặc rơi vào những trạng thái nguy hiểm tạo sự cố cho hệ thống Nếu một hệthống không thoả mãn một tính chất thì kiểm chứng mô hình sẽ đưa ra phản ví

dụ với một xâu các trạng thái và sự kiện liên quan bắt đầu từ trạng thái ban đầutới trạng thái lỗi của mô hình

Cuối cùng, kỹ thuật phân tích biểu trưng (symbolic analysis) [18] Kỹ thuật

này là phân tích tĩnh mã nguồn tĩnh, xây dựng các luồng rẽ nhánh trong chương

trình dựa trên các nút Tại các nút tương ứng sẽ là tập hợp các ràng buộc

Trang 10

(con-straints) của dữ liệu, biến, tham số Tại nút khởi tạo chương trình, tập hợp cácràng buộc là rỗng Càng đi sâu xuống các nhánh nhỏ, tại các nút con, tập hợpràng buộc sẽ được tạo ra từ tập hợp ràng buộc tại nút ngay phía trên cộng vớiđiều kiện giữa các biến số để có thể rẽ từ nút trên vào nút dưới trong luồng chảychương trình Điểm đặc biệt của kỹ thuật này là các tham số hoàn toàn được thểhiện bằng ký tự biểu trưng, chứ không phải giá trị cụ thể Ý tưởng của phươngpháp này là để kiểm thử một nhánh trong chương trình, điều kiện tiên quyết là

dữ liệu tại đầu vào phải thỏa mãn tập hợp các ràng buộc tại nút bắt đầu nhánh đó.Việc giải các ràng buộc gắn với một nút được thực hiện bởi các bộ công cụ sẵn

có gọi là giải ràng buộc (constraint solver) dựa trên SMT (Satisfiability ModuloTheories) hay SAT (Satisfiability Testing)

Hai nhóm đầu tập trung vào việc nâng cao chất lượng phần mềm tại mức mãnguồn, trong khi hai nhóm sau xử lý phần mềm tại mức trừu tượng cao hơn – môhình Luận văn sẽ tập trung vào xu thế thứ nhất, đó là kiểu phân tích luồng dữliệu dựa trên đồ thị luồng dữ liệu

1.4 Nền tảng

1.4.1 Đồ thị luồng điều khiển

Đồ thị luồng điều khiển (Control Flow Graph-CFG) là một đồ thị có hướng,trong đó các nút (node) tương ứng là các điểm (point) chương trình và các cạnhthể hiện cho luồng điểu khiển Một CFG luôn luôn có một điểm của đầu vào, ký

hiệu là entry, và một điểm của đầu ra, ký hiệu là exit Ngoài ra, nếu v là một nút

trong CFG thì những ký hiệu pred(v) là tập các nút kế trước (predecessor) và

succ(v) là tập các nút kế sau (successor).

CFG cho các lệnh

• Các lệnh cơ bản

Các lệnh đơn giản mà CFG có thể khởi tạo liên quan đến Những CFG cơ bảntrong ngôn ngữ lập trình Java như là các phép gán (id = E;), output(printf(E);), lệnh return (return;), và khai báo biến (ví dụ int f;) được môt

tả trong Hình 1.1 bên dưới:

Trang 11

id = E printf(E) return E int id

• Các lệnh cấu trúc điều khiển

Cấu trúc điều khiển được minh họa bởi đồ thị quy nạp: Các lệnh if, if-else

if(E) S; if(E) S1; else S2;

Hình 1.3: CFG cho các lệnh if, if-else.

Trang 12

Các lệnh while, for

E1

E2 E

S S

E3

while(E) S; for(E1; E2; E3;) S;

Hình 1.4: CFG cho các lệnh while, for.

Trang 13

Hình 1.5: CFG của chương trình tính giai thừa.

1.4.2 Lý thuyết Dàn

Dàn

Trong kỹ thuật phân tích chương trình tĩnh của luận văn, các phân tích sửdụng cấu trúc toán học là Dàn (Lattices) [16], tập các thuộc tính (ví dụ: tập cácbiến, biểu thức, trong chương trình) cần thiết cho mỗi phân tích tĩnh trongchương trình

Định nghĩa Dàn

Một thứ tự bộ phận (partial order) là một cấu trúc toán học: L = (S , ⊑), với S

là một tập và ⊑ là quan hệ hai ngôi trên tập S , thỏa mãn các điều kiện sau:

Trang 14

• Phản xạ: ∀x ∈ S : x ⊑ s

• Phản xứng: ∀x, y ∈ S : x ⊑ y ∧ y ⊑ x ⇒ x = y

• Bắc cầu: ∀x, y, z ∈ S : x ⊑ y ∧ y ⊑ z ⇒ x ⊑ z

Biểu diễn Dàn thông qua biểu đồ Hasse

Cho Dàn L = (S , ⊑) là tập có thứ tự bộ phận, biểu đồ Hasse của Dàn L bao

gồm:

• Một tập hợp các điểm trong mặt phẳng tương ứng 1-1 với S, gọi là các đỉnh

• Một tập hợp các cung nối một số cặp đỉnh có quan hệ thứ tự bộ phận

Ví dụ, biểu diễn Dàn (2{x,y,x}, ⊆) (Hình 1.6 (a)) hoặc Dàn đảo ngược (Dànđược sắp bởi thứ tự ngược của các tập con và quan hệ hai ngôi ⊑ được định nghĩa

là ⊇) (2{x,y,x}, ⊇) (Hình 1.6 (b)) bằng biểu đồ Hasse:

Hình 1.6: Biểu đồ Hasse biểu diễn Dàn.

Cận trên, cận dưới

Cho X ⊆ S Ta nói rằng y ∈ S là một cận trên của X, ký hiệu X ⊑ y, nếu

∀x ∈ X : x ⊑ y Tương tự, y ∈ S là một cận dưới của X, ký hiệu y ⊑ X, nếu

∀x ∈ X : y ⊑ x Một cận trên nhỏ nhất, ký hiệu ⊔X, được định nghĩa bởi:

X ⊑ ⊔X ∧ ∀y ∈ X : X ⊑ y ⇒ ⊔X ⊑ y Bên cạnh đó, một cận dưới lớn nhất, ký hiệu ⊓X, được định nghĩa bởi:

Trang 15

⊓X ⊑ X ∧ ∀y ∈ X : y ⊑ X ⇒ y ⊑ ⊓X Một Dàn là một thứ tự bộ phận trong đó ⊔X và ⊓X tồn tại cho tất cả X ⊆ S

Phần tử lớn nhất, phân tử nhỏ nhất

Nếu x = ⊔X ∈ X thì x được gọi là phần tử lớn nhất của X, ký hiệu là ⊤ và

định nghĩa là ⊤ = ⊓S Từ tính phản xạ của quan hệ thứ tự ta suy ra rằng ⊤ nếutồn tại

là duy nhất

Tương tự ta có, nếu y = ⊓X ∈ X thì y được gọi là phần tử nhỏ nhất của X, ký

hiệu là ⊥ và định nghĩa là ⊥ = ⊔S Từ tính phản xạ của quan hệ thứ tự ta suy rarằng ⊥ nếu tồn tại là duy nhất

Do tính duy nhất của các phần tử trong ⊤ và ⊥ Ví dụ minh hoạ dưới đây chỉ ra

những thứ tự bộ phận nào là dàn và không phải là Dàn:

Độ cao của một Dàn được tính bằng chiều dài từ phần tử nhỏ nhất ⊥ tới phần

tử lớn nhất ⊤ Dàn L = (S , ⊑) là hữu hạn (chiều cao của Dàn là hữu hạn) nếu tập

S chứa hữu hạn số phần tử.

Trang 16

Với mỗi tập S hữu hạn định nghĩa một Dàn L = (2 S , ⊑), với:

• ⊤, ⊥ tồn tại và ⊥ = ∅, ⊤ = S

• Với mỗi cặp phần tử x, y trong S có x ⊔ y = x ∪ y, x ⊓ y = x ∩ y

Trong ví dụ về Dàn trong Hình 1.6 ở trên thì độ cao của Dàn là 3

Tổng quát, độ cao của Dàn hữu hạn L = (2 S , ⊑) có độ cao là |S |

Điểm cố định (Fixed-Point)

Hàm đơn điệu

Ánh xạ f : L → L được gọi là hàm đơn điệu khi ∀x, y ∈ S : x ⊑ y ⇒ f (x) ⊑

f (y) Chú ý rằng thuộc tính này không có nghĩa hàm f là hàm tăng ( ∀x ∈ S : x ⊑

f (x)); Ví dụ, tất cả các hàm hằng là hàm đơn điệu, những hàm ⊔ và ⊓ là nhữngđơn điệu trong cả hai trường hợp Lưu ý rằng những phép hợp của những hàm đơn điệu là hàm đơn điệu

Điểm cố định

Điều mà ta cần chính là việc tìm ra điểm cố định của một hàm Theo lý thuyết

điểm cố định [16], trong một Dàn L với độ cao hữu hạn, mọi hàm đơn điệu f có

duy nhất điểm cố định nhỏ nhất được định nghĩa:

để có f ( f ix( f )) = f ix( f ) Chứng minh của lý thuyết này khá đơn giản Chú

ý rằng ⊥⊑ f (⊥) từ đó ⊥ là phần tử nhỏ nhất Từ đó f là đơn điệu, nó cho phép rằng

f ( ⊥) ⊑ f 2(⊥) và bởi phương pháp quy nạp f i(⊥) ⊑ f i+1(⊥) Do đó, ta có chuỗi

tăng dần:

⊥⊑ f (⊥) ⊑ f 2(⊥) ⊑

Vì L được giả thiết là có chiều cao hữu hạn, ta thêm một số k để có f k (⊥) =

f k+1(⊥) Ta có định nghĩa f ix( f ) = f k (⊥) và vì rằng f ( f ix( f )) = f k+1(⊥) = f

k (⊥

) = f ix( f ), biết rằng f ix( f ) là một điểm cố định Bây giờ, giả sử rằng x là

một điểm cố định khác Khi đó ⊥⊑ x ta có f (⊥) ⊑ f (x) = x, vì f là đơn điệu và

bằng quy nạp ta có f ix( f ) = f k (⊥) ⊑ x Từ đây, f ix( f ) là điểm cố định nhỏnhất Theo

tính chất phản xứng, nó cũng là duy nhất

Trang 17

Thuộc tính đóng

Nếu L1, L2, , L n là những Dàn với độ cao hữu hạn, từ đó một phép toán tích

(product):

L1 × L2 × × L n = {(x1, x2, , x n)|xi ∈ L1}với ⊑ được định nghĩa là theo từng điểm (point-wise) Với chú ý rằng ⊔ và ⊓

có thể được tính toán theo từng điểm và như vậy height(L1 × L2 × × L n) =

height(L1) + + height(L n)

Tương tự ta có, một phép toán cộng (sum):

L1 + L2 + + L n = {(i, x i)|xi ∈ L i \ {⊥, ⊤}} ∪ {⊥, ⊤}

với ⊤ và ⊥ được cho trước và (i, x) ⊑ ( j, y) khi và chỉ khi i = j và x ⊑ y Chú

ý rằng height (L1 + + L n ) = max{height(L i)} Phép toán cộng được minh họa

nhưsau:

Trang 18

Nếu A là một tập hữu hạn (A không cần thiết phải là một Dàn), khi đó f lat(A)

được minh họa bởi:

Hình 1.11: Phép toán lift của các tập tạo thành dàn.

theo từng điểm thứ tự: f ⊑ g ⇔ ∀a i : f (a i) ⊑ g(ai) Nếu A là hữu hạn và L có độ

cao hữu hạn khi đó height(A 7→ L) = |A|.height(L).

với x i là những biến và F i : L n → L là một tập những hàm đơn điệu Mỗi hệ

chỉ có nghiệm nhỏ nhất duy nhất, nó được gọi là điểm cố định nhỏ nhất của hàm

F : L n → L được định nghĩa bởi:

F(x1, x2, , x n ) = (F1(x1, x2, , x n ), , F n (x1, x2, ,

x n))

Trang 19

Tương tự, có thể giải hệ các bất phương trình:

x1 ⊑ F1(x1, , x n)

x2 ⊑ F2(x1, , x n)

x n ⊑ F n (x1, , x n)

mặt khác, ta có quan hệ x ⊑ y tương đương với x = x ⊓ y Vì vậy, những các

nghiệm được duy trì bởi tái thể hiện lại hệ phương trình như sau:

1.4.3 Thuật toán điểm cố định

Giả thiết rằng ta có một đồ thị luồng dữ liệu-CFG (Mục 1.4.1 ở trên) và một

Dàn L có độ cao hữu hạn (Mục 1.4.2 ở trên) Mỗi nút v trong CFG ta gán một biến v thông qua các phần tử của Dàn L Trong mỗi bước xây dựng trong ngôn

J

ngữ lập trình, ta định nghĩa một ràng buộc luồng dữ liệu (data-flow constraint)

và được gọi là hàm chuyển (transfer function), nó liên quan đến giá trị của biến

và tương ứng với nút đến các nút khác (nút lân cận hay nút hàng xóm) trên CFG

Ta có thể tách một cách hệ thống một tập các ràng buộc thông qua các biến trênmột CFG đã cho Nếu tất cả các ràng buộc trong phương trình và bất phươngtrình là các hàm đơn điệu ở vế phải, thì ta có thể sử dụng thuật toán tìm điểm cốđịnh để giải nghiệm nhỏ nhất duy nhất

Các ràng buộc luồng dữ liệu là đúng (sound) nếu tất cả các nghiệm tương ứng

để thông tin đúng về chương trình Việc phân tích dựa trên tìm nghiệm nhỏ nhất

sẽ cho độ chính xác cao nhất

Nếu một CFG có tập những nút V = v1, v2, , v n, khi đó ta làm việc với Dàn

L n Giả thiết rằng nút vi đưa ra phương trình luồng dữ liệu

K

Trang 20

xây dựng các hàm F : L n → L n như mô tả trên một cách dễ dàng hơn:

F(x1, , x n ) = (F1(x1, , x n ), , F n (x1, , x n))Sau đây, luận văn giới thiệu một số thuật toán tìm điểm cố định:

Thuật toán naive

Thuật toán naive tìm điểm cố định x như sau:

Thuật toán lặp chaotic

Thuật toán lặp chaotic tìm điểm cố định (x1, x2, , x n) Thuật toán lặp chaoticnhư sau:

Thuật toán work-list

Một thuật toán tìm điểm cố định khác là thuật toán work-list, thuật toán nàytránh lãng phí khi các thông tin của tất cả các nút được tính toán lại trong mỗibước lặp, mặc dù ta có thể biết rằng nó không được thay đổi Trong trường hợp

tổng quát, tất cả các biến v iK đều phụ thuộc vào các biến khác Tuy nhiên, một

yêu cầu thực tế của F i sẽ chỉ đọc giá trị của một số các biến khác Thông tinđược biểu diễn như ánh xạ:

de p : V → 2V

v xuất hiện một cách bất

với mỗi nút v cho ta biết tập con của các nút khác mà J K

thường (nontrivial) trong vế phải của những phương trình Đó là, dep(v) là tập

hợp

J

Trang 21

các nút chứa thông tin có thể phụ thuộc vào thông tin của nút v Một thuật toán giải quyết cho vấn đề này được gọi là thuật toán work-list nhằm tính toán điểm

cố định (x1, , x n) Thuật toán work-list như sau:

Tập W thì đây gọi là work-list với các phép toán add và removeNext cho

thêm và loại bỏ một phần tử Độ phức tạp trường hợp xấu nhất không thay đổi,nhưng trong thực tế thuật toán này giúp tiết kiệm nhiều thời gian

Trang 22

Chương 2

Phân tích chương trình tĩnh

Trong chương này, luận văn giới thiệu kỹ thuật phân tích chương trình tĩnhdựa trên phân tích luồng dữ liệu trong hàm không có lời gọi hàm hay phân tíchnội thủ tục (intraprocedual) và trong chương trình có chứa lời gọi hàm hay phântích liên thủ tục (interprocedure)

2.1 Phân tích luồng dữ liệu nội thủ tục

Phân tích luồng dữ liệu hay còn gọi là khung đơn điệu (monotoneframework), là kỹ thuật phân tích chương trình tĩnh nhằm thu thập các hành vi

của chương trình và phát hiện lỗi thông qua đồ thị luồng dữ liệu (CFG) và Dàn L

có độ cao hữu hạn

Các bước để phân tích luồng dữ liệu bao gồm:

• Bước 1: Khởi tạo CFG của chương trình: Gọi V = v1, v2, là tập các nút trên

CFG

• Bước 2: Khởi tạo Dàn hữu hạn L thông qua các tập biến, biểu thức trong

chương trình

• Bước 3: Xây dựng các hệ phương trình là các ràng buộc luồng dữ liệu trong

v ∈ L, với mỗi khởi chương trình: với mỗi nút v trên CFG ta gán một biến J K

tạo trong ngôn ngữ lập trình, môt kết hợp ràng buộc luồng dữ liệu (dataflowconstraint) được xác định liên quan đến giá trị biến của các nút tương vớicác nút khác, đặc biệt là các nút láng giếng Hệ phương trình luồng dữ liệuđược xác định thông qua các ràng buộc là hàm đơn điệu:

J v1K v2 , , J )v nK

Trang 23

• Bước 4: Giải hệ phương trình các ràng buộc thông qua thuật toán tìm điểm

cố định (Mục 1.4.3):

F(x1, , x n ) = (F1(x1, , x n ), , F n (x1, , x n))

Nghiệm nhỏ nhất thu được sẽ được dùng để kết luận cho phân tích chương trình

2.1.1 Phân tích quay lại (backward)

Với mỗi điểm của chương trình (nút trên CFG), phân tích quay lại (backwardanalysis) là phân tích thông tin về hành vi trong tương lai Do đó, trong vế phảicủa các phương trình chỉ phụ thuộc vào các nút kế sau nó (successor) trên CFG

Phân tích lùi được bắt đầu từ nút exit của CFG và di chuyển quay lại trong CFG Một số phân tích điển hình: Phân tích tính sống của biến (Liveness), phân tích

biểu thức bận rộn (Busy Expression)

Phân tích tính sống của biến

Trong một chương trình việc xác định tính sống của biến tại mỗi điểm củachương trình là rất cần thiết, việc này giúp chương trình xác định và loại bỏ đượccác biến chết giúp tối ưu hóa bộ nhớ/ tối ưu hoá chương trình dịch và làm tăngtốc độ tính toán của chương trình Một biến được gọi là biến sống tại một điểm

của chương trình (liveness) nếu giá trị hiện tại của nó được đọc trong nút hiện tại

hoặc được đọc trong một số nút kế tiếp (không được ghi ở nút hiện tại) Thuộctính này có thể được xấp xỉ bởi phân tích tĩnh dựa trên CFG với một dàn là mộttập các biến trong chương trình

v , khi đó các ràng buộc

Gọi các biến ràng buộc cho mỗi nút v trên CFG là J K

cho tính sống của biến tại các nút với các cấu trúc lệnh trong chương trình đượcxác định như sau:

• Đối với nút exit, có ràng buộc là:

Trang 24

• Đối với những phép gán id = E, ràng buộc là:

Dàn ở đây là L = (2{f, uu_f, n}, ⊆), biểu diễn bằng biểu đồ Hasse:

{f,uu_f,n}

{f,uu_f} {f,n} {uu_f,n}

{}

Hình 2.1: Dàn cho chương trình phân tích tính sống của biến.

Các ràng buộc được tạo ra như sau:

J

Trang 25

int fKint uu_fK \ {f}

Hệ phương trình trên là để giải thông qua Dàn: L = (2{f, uu_f, n}, ⊆) Hơn

nữa, nó dễ dàng thấy rằng tất cả vế phải ràng buộc của phương trình định nghĩanhững hàm đơn điệu Theo kết quả, lý thuyết của điểm cố định nhỏ nhất có thểđược áp dụng [16] Nghiệm nhỏ nhất cho hệ phương trình (Được giải trong Phụlục A) là duy nhất là:

Từ những thông tin này một bộ dịch có thể suy ra rằng uu_f chưa bao giờ sống,

và giá trị được ghi trong phép gán uu_f = 0 chưa bao giờ được đọc Do đó,chương trình hàm lặp trong Mục 1.4.1 có thể chạy an toàn và tối ưu hóa như sau:int iterative(int n){

int f;// khai báo biến (f có kiểu là int)

Trang 26

Phân tích biểu thức bận rộn

Việc tính toán các biểu thức trong chương trình làm tăng bộ nhớ và làm chậmthời gian chạy kết quả của chương trình Do vậy, trong chương trình nếu hạn chếtính toán lại một biểu thức trong tương lai sẽ giúp chương trình chạy nhanh hơn

và giúp bộ nhớ tối ưu hơn

Một biểu thức E là bận rộn (busy) tại điểm p của chương trình nếu tất cả các đường (path) xuất phát từ một điểm chương trình p phải được đánh giá E trước khi một biến bất kỳ trong biểu thức E thay đổi Hoặc, ta cũng có thể hiểu là giá

trị của biểu thức được đánh giá tại thời điểm hiện tại hoặc sẽ được đánh giá trongtất cả các nút trong tương lai trừ khi một phép gán làm thay đổi giá trị của nó Đểxấp xỉ thuộc tính này, ta cần Dàn là tập các biểu thức trong chương trình Đối vớiphân tích này, ta xác định được các ràng buộc luồng dữ liệu cho các cấu trúclệnh như sau:

• Ràng buộc cho lệnh exit:

Trang 27

• Với tất cả các nút còn lại có ràng buộc là:

Hình 2.2: Dàn cho chương trình phân tích biểu thức bận rộn.

Đồ thị luồng dữ liệu tương ứng là:

Trang 29

2.1.2 Phân tích chuyển tiếp (forward)

Với mỗi điểm của chương trình (nút trên CFG), phân tích chuyển tiếp(forward analysis) là phân tích thông tin về hành vi trong quá khứ Do đó,trong vế phải của các phương trình chỉ phụ thuộc vào các nút kế trước nó(predecessor) trên CFG Phân tích tiến được bắt đầu từ nút entry của CFG và

di chuyển chuyển tiếp trong CFG Một số phân tích điển hình: Phân tích biểu

thức có sẵn(Available Expression), phân tích định nghĩa tới được(Reaching

Trang 30

Phân tích biểu thức có sẵn

Một biểu thức không bình thường (nontrivial) trong một chương trình là cósẵn (available) tại một điểm trong chương trình nếu giá trị của nó đã được tínhtoán sẵn trước đó trong khi thực thi Việc xác định các biểu thức đã có sẵntrước khi thực thi sẽ giúp cho việc tính toán nhanh và đơn giản hơn Do vậy,trong phân tích này chúng ta sử dụng các thông tin về hành vi trong quá khứ

Và, Dàn cho phân tích này là tập hợp các biểu thức xảy ra cho tất cả các điểm

chương trình và được sắp bởi các tập con đảo ngược (reverse) Đối với mỗi nút

v trên CFG tương ứng

v trên Dàn L chứa các tập con của các biểu thức mà nó

với một biến ràng buộc J K

được đảm bảo luôn luôn có sẵn tại điểm chương trình kế sau nút đó Ví dụ, biểu

thức a + b là có sẵn ở điều kiện trong vòng lặp, nhưng nó không phải là có sẵn

tại các phép gán trong vòng lặp Phân tích đưa ra sẽ bảo toàn kể từ khi thiết lậptính toán có thể là quá nhỏ Từ đó, có các ràng buộc luồng dữ liệu cho các cấutrúc lệnh trong phân tích như sau:

• Với mỗi nút entry ta có ràng buộc:

Trang 31

Với hàm ↓ id loại bỏ tất cả các biểu thức có chứa một tham chiếu đến biến id, và

exps(E1o pE2) = {E1o pE2} ∪ ex ps(E1) ∪ ex ps(E2)

Với o p là phép toán nhị phân bất kỳ Ta thấy rằng một biểu thức là có sẵn trong

v nếu nó có sẵn từ tất cả các cạnh hoặc được tính toán trong nút v, trừ khi giá trị

của nó đã được hủy bởi lệnh gán Một lần nữa, phía vế phải của những ràngbuộc là những hàm đơn điệu

Trang 32

{a+b}

{a+b,a-b}

{a+b,y<a+b} {a+b,a-1} {a-b,y<a+b} {a-b,a-1} {y<a+b,a-1}

{a+b,a-b,y<a+b} {a+b,a-b,a-1} {a+b,y<a+b,a-1} {a-b,y<a+b,a-1}

{a+b,a-b,y<a+b,a-1}

Hình 2.4: Dàn cho chương trình phân tích biểu thức có sẵn.

Phần tử lớn nhất của Dàn là ∅ và đồ thị luồng dữ liệu tương ứng với chương trình

Hình 2.5: CFG của chương trình phân tích biểu thức có sẵn.

Các ràng buộc sinh ra như sau:

Ngày đăng: 21/07/2014, 09:18

HÌNH ẢNH LIÊN QUAN

Hình 1.1: CFG cho các lệnh cơ bản. - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
Hình 1.1 CFG cho các lệnh cơ bản (Trang 11)
Hình 1.4: CFG cho các lệnh while, for. - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
Hình 1.4 CFG cho các lệnh while, for (Trang 12)
Hình 1.10: Phép toán nâng (lift) dàn. - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
Hình 1.10 Phép toán nâng (lift) dàn (Trang 18)
Hình 1.9: Phép toán cộng dàn. - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
Hình 1.9 Phép toán cộng dàn (Trang 18)
Hình 1.11: Phép toán lift của các tập tạo thành dàn. - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
Hình 1.11 Phép toán lift của các tập tạo thành dàn (Trang 19)
Hình 2.1:  Dàn cho chương trình phân tích tính sống của biến. - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
Hình 2.1 Dàn cho chương trình phân tích tính sống của biến (Trang 27)
Hình 2.2: Dàn cho chương trình phân tích biểu thức bận rộn. - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
Hình 2.2 Dàn cho chương trình phân tích biểu thức bận rộn (Trang 31)
Hình 2.3: CFG của chương trình phân tích biểu thức bận rộn. - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
Hình 2.3 CFG của chương trình phân tích biểu thức bận rộn (Trang 32)
Hình 2.4: Dàn cho chương trình phân tích biểu thức có sẵn. - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
Hình 2.4 Dàn cho chương trình phân tích biểu thức có sẵn (Trang 38)
Đồ thị luồng dữ liệu tương ứng với chương trình trên là: - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
th ị luồng dữ liệu tương ứng với chương trình trên là: (Trang 43)
Hình 2.9: CFG của chương trình phân tích liên thủ tục. - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
Hình 2.9 CFG của chương trình phân tích liên thủ tục (Trang 52)
Hình 2.10: CFG đơn biến. - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
Hình 2.10 CFG đơn biến (Trang 54)
Hình 3.1: Tổng quan về luồng làm việc của SOOT [10] - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
Hình 3.1 Tổng quan về luồng làm việc của SOOT [10] (Trang 59)
Hình 3.2: Phương thức copy() và merge() trong SOOT - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
Hình 3.2 Phương thức copy() và merge() trong SOOT (Trang 60)
Hình ảnh chạy phân tích tính sống của biến với SOOT: - NGHIÊN cứu kỹ THUẬT PHÂN TÍCH CHƯƠNG TRÌNH TĨNH TRONG VIỆC NÂNG CAO CHẤT LƯỢNG PHẦN mềm
nh ảnh chạy phân tích tính sống của biến với SOOT: (Trang 63)

TỪ KHÓA LIÊN QUAN

TRÍCH ĐOẠN

TÀI LIỆU CÙNG NGƯỜI DÙNG

TÀI LIỆU LIÊN QUAN

🧩 Sản phẩm bạn có thể quan tâm

w