1. Trang chủ
  2. » Giáo Dục - Đào Tạ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

70 32 0

Đ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

Định dạng
Số trang 70
Dung lượng 436,05 KB

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

Nội dung

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.. Trong mỗi bước xây dựng trong ngôn ngữ lập trình, ta định nghĩa một ràng b

Trang 2

LUẬN VĂN THẠC SĨ CÔNG NGHỆ THÔNG TIN

NGƯỜI HƯỚNG DẪN KHOA HỌC: TS Nguyễn Trường Thắng

Hà Nội – 2013 Ph

Trang 3

Mục lục

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

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

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

1.4 Nền tảng 12

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

1.4.2 Lý thuyết Dàn 15

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

2 Phân tích chương trình tĩnh 24 2.1 Phân tích luồng dữ liệu nội thủ tục 24

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

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

2.2 Phân tích luồng dữ liệu liên thủ tục 40

2.2.1 Xây dựng đồ thị luồng dữ liệu 41

2.2.2 Tính cảm ngữ cảnh (context sensitivity) 43

2.2.3 Ứng dụng phân tích luồng dữ liệu liên thủ tục 45

Trang 4

3 Thực nghiệm 47

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

Trang 5

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

CFG Control Flow GraphCNPM Công nghệ phần mềmCNTT Công nghệ thông tinDFA Data Flow Analysis

Trang 6

Danh sách hình vẽ

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

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

1.3 CFG cho các lệnh if, if-else 13

1.4 CFG cho các lệnh while, for 14

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

1.6 Biểu đồ Hasse biểu diễn Dàn 16.

1.7 Các biểu đồ Hasse là Dàn 17.

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

1.9 Phép toán cộng dàn 19

1.10 Phép toán nâng (lift) dàn 19.

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

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

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

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

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

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

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

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

2.8 Ví dụ CFG tổng quát cho chương trình có chứa lời gọi hàm 42

2.9 CFG của chương trình phân tích liên thủ tục 43

2.10 CFG đơn biến 44

2.11 CFG đa biến 45

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

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

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

Trang 7

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ình phầnmề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 40triệ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ấptrong 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ớihoàn cảnh đó, tỉ lệ giả định 1 lỗi trong 1000 dòng lệnh đối với những phần mềmnhư 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ântí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àyphát hiện tính chất/hành vi của một chương trình mà không yêu cầu chạy chươngtrì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ươngtrì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ăn tậptrung 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 8

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ápphâ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ọihà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ợpphá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 9

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ới việ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 10

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ương trì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ửa lỗ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ủachương trì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 11

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)được mộ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ễn giảitrừ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ầnquá chi tiết mà vẫn giữ lại những thông tin cần thiết về hệ thống đang được xemxét Trong lĩnh vực phần mềm, kiểm chứng mô hình là cách kiể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ỏa mã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 nguyhiể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ênquan bắt đầu từ trạng thái ban đầu tớ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 12

(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 (SatisfiabilityModulo Theories) 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 đơ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.1bên dưới:

Trang 13

id = E printf(E) return E int id

Hình 1.1: CFG cho các lệnh cơ bản.

Cho chuỗi lệnh tuần tự S 1 S 2, ta loại bỏ các nút exit của lệnh S 1 và nút entry

của lệnh S 2 và gắn các lệnh lại với nhau (Hình 1.2).

S1

S2

Hình 1.2: CFG cho các lệnh tuần tự.

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;

Trang 15

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 16

Đị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 17

• 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 địnhnghĩa là ⊇) (2{x,y,x}

; ⊇) (Hình 1.6 (b)) bằng biểu đồ Hasse:

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 18

⊓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 19

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:

f ix( f ) = f i(⊥)

i≥0

để 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ó

) = 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 20

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 )|x i ∈ L1}với ⊑ được định nghĩa là theo từng điểm (point-wise) Với

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

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ọanhư sau:

Trang 21

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.

Và f lat(A) là một Dàn có độ cao là 2.

Cuối cùng, nếu A và L được định nghĩa như trên, khi đó chúng ta thu được mộtDàn ánh xạ (map) với độ cao như sau:

A 7→L = {[a1 x1; :::; a n 7→x n ]|x i ∈ L}

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).

Phương trình và bất phương trình

Cho L là một Dàn với độ cao hữu hạn Một hệ phương trình được biểu diễn:

x1 = F1(x1; :::; x n)

x2 = F2(x1; :::; x n):::

x n = F n (x1; :::; x n)

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 22

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

x1 ⊑ F1(x1; :::; xn)

x2 ⊑ F2(x1; :::; xn):::

x n ⊑ F n (x1; :::; xn)

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:

x1 = x1 ⊓ F1(x1; :::; x n)

x2 = x2 ⊓ F2(x1; :::; x n):::

x n = x n ⊓ Fn (x1; :::; x n)

đây là một hệ phương trình với các hàm đơn điệu

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 JvK 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 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àngxó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ôngqua các biến trên một CFG đã cho Nếu tất cả các ràng buộc trong phương trình

và bất phương trình là các hàm đơn điệu ở vế phải, thì ta có thể sử dụng thuậttoá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 v i đưa ra phương trình luồng dữ liệu Jv i K = F i (Jv1K; :::; Jv nK),

Trang 23

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ặpchaotic như 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 Jv 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ạ:

dep : V → 2 V

với mỗi nút v cho ta biết tập con của các nút khác mà JvK xuất hiện một cách bất thường (nontrivial) trong vế phải của những phương trình Đó là, dep(v) là tập hợp

Trang 24

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ưngtrong thực tế thuật toán này giúp tiết kiệm nhiều thời gian

Trang 25

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 (monotone framework),

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

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

khởi 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(dataflow constraint) được xác định liên quan đến giá trị biến của các núttương với các nút khác, đặc biệt là các nút láng giếng Hệ phương trìnhluồng dữ liệu được xác định thông qua các ràng buộc là hàm đơn điệu:

Jv iK =F(Jv1K; Jv2K; :::; Jv nK)

Trang 26

• 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ỏđược cá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àmtăng tố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ộc tí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ột tập các biến trong chương trình

Gọi các biến ràng buộc cho mỗi nút v trên CFG là JvK, khi đó các ràng buộc

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 27

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

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:

Trang 28

JentryK = Jint fK

Jint fK = Jint uu_fK \ {f}

Jint uu_fK = Jf = 1K \ {uu_f}

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)

f = 1;

while (n > 0){

Trang 29

f = f*n;

n = n - 1;

}}

việc loại bỏ biến chết giúp tiết kiệm chi phí của phép gán và khai báo biến chohàm đơn giản Do đó, kỹ thuật phân tích này cho kết quả tốt hơn trong việc cấpphát bộ nhớ thanh ghi Ngoài ra, đối với những chương trình phức tạp hơn thì lợiích từ việc phân tích này có thể rất lớn trong tính toán của bộ nhớ và tối ưu hóatrong xử lý thông qua bộ chương trình dịch thông minh

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átrong tấ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ới phâ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ấutrúc lệnh như sau:

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

Trang 30

• 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 31

Jx > 0K = (Jreturn a*b-xK ∩ Jreturn a*bK)∪ {x>0}

Jreturn a*b-xK = Jx = x-1K ∪ {a*b-x} Jx = x-1K =

Trang 32

Các phân tích cho thấy một biểu thức a ∗ b là rất bận rộn bên trong vòng lặp.

Trình biên dịch có thể thực hiện cải tiến mã nguồn và chuyển các tính toán đếnđiểm chương trình sớm nhất mà nó là rất bận rộn Điều này sẽ làm thay đổichương trình trở nên hiệu quả hơn:

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 (forwardanalysis) là phân tích thông tin về hành vi trong quá khứ Do đó, trong vế phải củacá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 Denitions)

Trang 33

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ẵn trướckhi thực thi sẽ giúp cho việc tính toán nhanh và đơn giản hơn Do vậy, trongphâ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àncho 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ới một biến ràng buộc JvK trên Dàn L chứa các tập con của các

biểu thức mà nó đượ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ảotoàn kể từ khi thiết lập tính toán có thể là quá nhỏ Từ đó, có các ràng buộcluồng dữ liệu cho các cấu trúc lệnh trong phân tích như sau:

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

Trang 34

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à các hàm exps được định nghĩa là:

exps(intconst) =

exps(id) =

exps(input) =

exps(E1opE2) = {E1opE2} ∪ exps(E1) ∪ exps(E2)

Với op 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 35

{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

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: 11/11/2020, 22:04

TỪ KHÓA LIÊN QUAN

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