Biểu diễn trạng thái bộ nhớ Heap qua cấu trúc Logic

Một phần của tài liệu Nghiên cứu kỹ thuật giải thích trừu tượng (Trang 54)

Một cấu trúc 2-valued logic S là một bộ ( , ) trong đó là tập các phần tử (bao gồm các biến và hằng), các phần tử ở đây chính là các nút trên đồ thị Heap (tập các khối bộ nhớ được cấp phát), là tập các vị từ trên

(biểu diễn quan hệ tham chiếu trong Heap). Một vị từ p trên S được kí hiệu là . Với mỗi vị từ p có số chiều k, là hàm: : ( ) ⟶ {0, 1}.

Một heap cụ thể của chương trình được mô tả bằng cấu trúc 2-valued logic: là tập các nút trên đồ thị heap, tức là tập các khối bộ nhớ được cấp phát. Các vị từ biểu diễn quan hệ tham chiếu trong heap. [13]

Vị từ đơn nguyên có thể biểu diễn quan hệ tham chiếu giữa một biến

chương trình với một nút của heap: Nếu biến x tham chiếu tới nút n1, chúng ta tạo một vị từ đơn nguyên x mà x(n1) = 1. Vị từ nhị nguyên được dùng để

biểu diễn tham chiếu giữa các nút (định nghĩa các cạnh giữa các nút trong đồ thị heap). Do đó thông tin về heap được thể hiện thông qua việc đánh giá các vị từ liên quan.

TVLA đưa ra giả định rằng tập tất cả các vị từ là cố định, tuy nhiên tập các phần tử đại diện cho các nút có thể thay đổi trong suốt quá trình phân tích. Hiện tại TVLA chỉ hỗ trợ các vị từ có chiều nhỏ hơn hoặc bằng 2.

53

Các cấu trúc 2-valued logic như trên có thể được thể hiện thông qua

một đồ thị có hướng. Tồn tại một cạnh gán nhãn p hướng từ u1 tới u2 khi

( 1, 2) = 1, p cũng được vẽ trong nút u khi ( ) = 1.

Ví dụ: Hình 3.2 minh họa việc sử dụng một cấu trúc 2-valued logic để biểu diễn một heap gồm 3 nút. Thông tin về heap này được mã hóa bằng tệp TVS trong TVLA như sau:

// Các nút %n = {n1, n2, n3} //Các vị từ %p = { x = {n1} f = {n1 -> n2, n2 -> n3} }

Chúng ta thấy rằng vị từ đơn nguyên x thỏa mãn đối với 1, tức là

( 1) = 1, và n1 có trỏ tới 2 biểu diễn bởi vị từ nhị nguyên , tức là

( 1, 2) = 1. Tương tự ( 2, 3) = 1.

Hình 3.2: Biểu diễn heap cụ thể 3.2.3. Trừu tượng heap

Thay vì sử dụng cấu trúc 2-valued logic, cấu trúc 3-valued logic của Kleene được sử dụng để mô tả heap trừu tượng. Tương tự như cấu trúc 2- valued logic, 3-valued logic chỉ khác ở hàm vị từ: : ( ) ⟶ {0,1,1/2}. Cấu trúc 3-valued logic cũng được biểu diễn bằng các đồ thị có hướng như cấu trúc 2-valued logic nhưng cạnh ứng với giá trị 1/2 được vẽ bằng nét đứt.

TVLA sử dụng một thiết kế đặc biệt cho các vị từ đơn nguyên để duy trì thông tin nút tóm tắt. Một nút tóm tắt như có ( ) = 1/2, chỉ ra rằng có thể đại diện cho nhiều hơn một nút nhúng trong cấu trúc 2-

54

valued logic. Ngược lại, nếu ( ) 0 thì được biết đến là đại diện cho một nút duy nhất. Chỉ có các nút với ( ) = 1/2 có thể có nhiều hơn một nút ánh xạ tới chúng bằng các hàm nhúng [13].

Phép nhúng:

Cho và ’ là 2 cấu trúc, và hàm : → ’ là một toàn ánh, ta nói rằng phép nhúng trong ’ (ký hiệu là ⊑ ’) nếu với mỗi vị từ (bao gồm cả nút đại diện sm): ∀ , … , ∈ , ( , … , ) ⊑

’( ( ), … , ( )) và ∀ ’ ∈ ’: (|{ | ( ) = ’}| > 1) ⊑ ’( ’) Ta nói rằng được nhúng trong ’ nếu tồn tại một hàm được định nghĩa như trên. Một dạng đặc biệt của phép nhúng là phép nhúng hoàn toàn (tight embedding), trong đó lượng thông tin bị thất thoát là nhỏ nhất khi nhiều phần tử của được ánh xạ tới cùng một phần tử trong ’

Nhúng hoàn toàn:

Cấu trúc được nhúng hoàn toàn trong ’ khi tồn tại 1 hàm toàn ánh mờ (surjective function blur) : → ’ thỏa mãn điều kiện: với ∀ ( ≠ )

’( ’ , . . . , ’ ) = ⨆ ( ) ’ , ( , . . . , )

và ’ ∈ ’

’( ’) = (|{ | ( ) = ’}| > 1) ⊔ ⨆ ( ) ( )

Vì hàm blur là toàn ánh nên từ 2 công thức trên ta xác định duy nhất

′ = ( ) (adsbygoogle = window.adsbygoogle || []).push({});

Ví dụ: Hình 3.3 thể hiện cùng cấu trúc 2-valued logic trong ví dụ hình 3.2, nhưng sau khi đã được trừu tượng hóa (với là vị từ trừu tượng). Do

( 2) = ( 3) = 0 trong cấu trúc trước nên 2 và 3 được tóm tắt vào cùng một nút trong cấu trúc 3-valued logic.

%n = {n1, n2} %p = { x = {n1}

55

f = {n1 -> n2: 1/2, n2 -> n2: 1/2} sm = {n2: 1/2} // n2 là nút tóm tắt }

Hình 3.3: Trừu tượng hóa heap

Đặc tả heap trừu tượng đầu vào được biểu diễn dưới dạng cấu trúc 3 – valued logic và các thực thi được biểu diễn bằng công thức logic vị từ cấp một. Hệ phương trình ràng buộc được tạo ra từ các đặc tả này bằng thuật toán sinh ràng buộc Coerce [13]. Thuật toán lặp Focus[13] sẽ giải các ràng buộc và tìm điểm cố định nhỏ nhất của hệ phương trình. Nghiệm này chính là biểu diễn trừu tượng của các trạng thái heap có thể có trong quá trình thực thi.

Để xác định hệ phương trình các ràng buộc bằng thuật toán Coerce và giải hệ phương trình ràng buộc Focus sẽ được trình bày trong phần 3.3.

3.2.4. Biểu diễn ngữ nghĩa và chương trình

Để biểu diễn ngữ nghĩa cụ thể của các lệnh trong chương trình TVLA sử dụng các actions. Các actions có thể có tham số và sử dụng logic vị từ để mô tả quá trình chuyển từ một trạng thái tới một trạng thái khác khi lệnh được thực thi. Trong thực tế, tác động trong quá trình chuyển trạng thái được hoàn thành bằng việc sử dụng một tập các công thức vị từ cập nhật (Predicate- update formulae). Các vị từ, các công thức logic và các vị từ cập nhật, được cập nhật lại sau khi chuyển qua trạng thái mới.

Để biểu diễn ngữ nghĩa cụ thể của chương trình, TVLA sử dụng thêm đồ thị luồng điều khiển (Control Flow Graph - CFG), và các cạnh của CFG

được gắn thể hiện của các actions.

Ví dụ: Cho heap trừu tượng trong Hình 3.3 là trạng thái vào và giả sử

56

lập trình điều này có thể được biểu diễn dưới dạng x.f = null. Để biểu diễn quá trình này chúng ta đưa ra một action là setFieldNull(c, n), có thể được sử

dụng để thiết đặt một trường của một nút được trỏ tới bởi một biến trong

chương trình tới giá trị null. Sau đó chúng ta thể hiện action này với x (tương ứng nút n1) và trường f dưới dạng tham số. Cuối cùng là gắn thể hiện của action này với x và f lên cạnh của CFG. Chi tiết được cài đặt theo định dạng

TVP trong TVLA như sau: // Vị từ %p x(v_1) unique %p f(v_1,v_2) function %% // Các actions %action setFieldNull(c,n) { { n(v_1,v_2) = n(v_1,v_2) & !c(v_1) } } %% // CFG

start setNextNull(x,f) end

TVLA sẽ tự động sinh ngữ nghĩa trừu tượng từ ngữ nghĩa cụ thể của chương trình (biểu diễn qua các actions bằng logic vị từ) để thực thi. Kết quả tạo ra heap trừu tượng đầu ra được thể hiện trong Hình 3.4

Hình 3.4: Kết quả heap trừu tượng đầu ra

Tệp TVS đặc tả heap trừu tượng trên được TVLA xuất ra có nội dung như sau: %n = {n1, n2} %p = { x = {n1} f = {n2 -> n2: 1/2} sm = {n2: 1/2} }

57 3.2.5. Ví dụ về phân tích chương trình

Xét đoạn mã chương trình cần phân tích như sau:

/* list.h */

typedef struct node{ struct node *n; int data; } *L; /* reverse.c */ #include ‘‘list.h’’ L reverse(L x){ L y, t; y = NULL; while (x != NULL){ t = y; y = x; x = x->n; y->n = t; t = NULL; } return y; } (a) (b)

(a) là khai báo một kiểu dữ liệu danh sách liên kết trong ngôn ngữ C. (b) là một hàm của C đảo ngược danh sách x thành danh sách y

Trong ví dụ này, một cấu trúc 2 – valued logic đại diện cho một trạng thái bộ nhớ, với ý nghĩa của các vị từ chính và vị từ thiết lập được đưa ra trong Bảng 3.1 dưới đây:

Vị từ Ý nghĩa Định nghĩa công thức x(v) Con trỏ x trỏ đến vị trí của vùng nhớ v

n(v1,v2) n là số con trỏ để đi từ v1 đến v2 (adsbygoogle = window.adsbygoogle || []).push({});

r[n,x](v) Có thể truy cập đến vùng nhớ v của con

trỏ x, thông qua n con trỏ. ∃ : ( ( ) ∧ ∗( , ))

c[n](v) Vùng nhớ v xuất hiện trên chu trình có

độ dài n ( , )

t[n](v1,v2) Biểu diễn v1 tới được v2 theo phản xạ bắc cầu

is[n](v) Vùng nhớ v có thể được trỏ đến bởi n con trỏ khác

∃ , : ( , ) ∧

58

sm(v) v là đại diện cho nhiều cấu trúc con trỏ khác (nút đại diện).

Trong đó: ∗( 1, ) = 1 nếu trong tồn tại một đường đi từ 1 đến , đường đi này có số cạnh là 0 hoặc nhiều hơn n.

Trong quá trình phân tích thì các vị từ sẽ được cập nhật đề phù hợp với trạng thái hiện thời của đối tượng. Sau khi trừu tượng hóa các trạng thái hoạt động của chương trình, ta sẽ có cấu trúc CFG của chương trình.

Giả sử ta có CFG thể biểu diễn chương trình dưới dạng trạng thái bộ nhớ lưu trữ 4 cấu trúc của chương trình khi chạy:

Hình 3.5: Trạng thái của chương trình sử dụng cấu trúc 2-valued logic Tiếp theo là việc trừu tượng hóa bằng cách sử dụng các nút đại diện để mô tả các trạng thái có thể xảy ra.

Hình 3.6: Trạng thái của chương trình sử dụng 3-valued logic Cấu trúc 3-valued 4 thể hiện trong hình 3.6 đại diện cho cấu trúc 2- valued 3 cho ( 0) = à ( ) = ( ) = ( ) = . Trong thực tế,

59

cấu trúc thể hiện trong hình 3.6 đại diện cho tất cả các danh sách với hai hoặc nhiều hơn các phần tử.

Sau khi đã có đầy đủ việc mô tả trừu tượng của chương trình, việc sử dụng phân tích của giải thích trừu tượng sẽ thực hiện. Bắt đầu từ trạng thái ban đầu S0 của chương trình, xét các trạng thái tiếp theo thông qua CFG. Lặp lại thao tác trên cho tới khi kết thúc CFG.

Hình 3.7: Biểu diễn quá trình làm việc của phân tích chương trình 3.3. Thuật toán sinh hệ ràng buộc Coerce và thuật toán giải hệ ràng buộc tìm điểm cố định Focus

3.3.1. Công thức

Các tính chất của cấu trúc có thể được trích xuất bằng cách đánh giá công thức, ta sử dụng logic first-order với bao đóng bắc cầu và đẳng thức, nhưng không có các ký hiệu hàm và ký hiệu liên tục. Ở đây, ∗ biểu diễn bao đóng phản xạ bắc cầu của các vị từ . Do đó, trong mọi cấu trúc , ( 1)

nhận giá trị 1 nếu 1 là nút được trỏ đến bởi và ∗( 1, ) nhận giá trị là 1 trong nếu có tồn tại một đường đi bằng không hay nhiều hơn cạnh từ 1

60

đến . Cột thứ ba của bảng 3.1 biểu diễn các công thức định nghĩa của tất cả các các vị từ đo được sử dụng trong ví dụ ờ phần 3.2.5 [13].

∃ 1: ( ( 1) ∧ ∗ ( 1, ))

3.3.2. Các lớp con của công thức

Định nghĩa 3.2: Một mệnh đề Horn là một công thức có dạng

( ⋀ ) → , khi > 1, và là một công thức nguyên tử,

Cho một công thức , chúng ta định nghĩa ≡ à ≡ ¬ . Một mệnh đề Horn mở rộng là một công thức có dạng

⋀ ( ) ) → ( ) , khi > 1, là một công thức nguyên tử,

∈ {0,1} [13]. 3.3.3. Ngữ nghĩa (adsbygoogle = window.adsbygoogle || []).push({});

Định nghĩa 3.3: Một phép gán là một hàm từ một biến được giải phóng tới các vị từ đơn nguyên (ví dụ, một phép gán có các hàm

: { 1, 2 . . . } → ). Phép gán được định nghĩa trên tất cả các biến được giải phóng của một công thức được gọi là đầy đủ.

Ý nghĩa của một công thức , ký hiệu là ⟦ ⟧ ( ), xác định một giá trị chân lý trong {0,1,1/2} và được định nghĩa quy nạp như sau:

Đơn nguyên: Cho ký hiệu logic ∈ {0,1,1/2}, ⟦ ⟧ ( ) = (khi

∈ {0,1,1/2}). Đối với một công thức đơn nguyên ( 1, . . . , ),

⟦ ( 1, . . . , 2)⟧ ( ) = ( ( 1), . . . , ( ))

Cho mỗi một công thức đơn nguyên ( 1 = 2),

⟦ 1 = 2⟧ ( ) =

0 ( 1) ≠ ( 2) 1 ( 1) = ( 2) à ( ( 1)) = 0 1

2 ò ạ

Từ nối logic: Cho các công thức logic và

⟦ ∧ ⟧ ( ) = (⟦ ⟧ ( ), ⟦ ⟧ ( ))

61

⟦¬ ⟧ ( ) = 1 − ⟦ ⟧ ( )

Phép lượng hóa Nếu là một công thức logic,

⟦∀ 1: ⟧ ( ) = min

∈ ⟦ ⟧ ( [ 1 ⟼ ]) ⟦∃ 1: ⟧ ( ) = max

∈ ⟦ ⟧ ( [ 1 ⟼ ])

Bao đóng bắc cầu: cho ( 1, 2: )( 3, 4),

⟦ 1, 2: )( 3, 4)⟧ ( ) = max , ,..., ∈ ( ) , ( ) min , ⟦ ⟧ ( [ 1 ⟼ , 2 ⟼ ])

Ta nói rằng và có khả năng thỏa mãn (ký hiệu là , ⊨ ) nếu

⟦ ⟧ ( ) = 1/2 ℎ ặ ⟦ ⟧ ( ) = 1. Cuối cùng, chúng ta viết ⊨ nếu với mọi : , ⊨ .

Định lý nhúng chỉ ra rằng bất kỳ công thức nào đưa ra một giá trị xác định trong một cấu trúc 3-giá trị đều được dùng để xác định giá trị giống nhau trong cấu trúc 2-valued logic được nhúng vào trong cấu trúc 3-valued logic. Định lý nhúng là nền tảng cho việc sử dụng của 3-valued logic trong phân tích tĩnh: nó đảm bảo tính hợp lý để giải thích lại về cấu trúc 3-valued logic của các công thức khi công thức đó được biểu diễn trong cấu trúc 2-valued logic để định nghĩa các ngữ nghĩa hoạt động của chương trình [13].

3.3.4. Focus

Thứ nhất, thực hiện Focus chuyển đổi cấu trúc đầu vào thành một tập đã được tinh chỉnh của các cấu trúc đại diện cho cấu trúc 2-valued logic. Với một công thức, Focus đảm bảo rằng các công thức không bao giờ mang giá trị là 1/2 trong cấu trúc Focus. Focus (và Coerce) thực hiện việc đơn giản hóa ngữ nghĩa, tức là, chúng chuyển một cấu trúc 3-valued logic vào một tập cấu trúc 3-valued logic biểu diễn cho các trạng thái trên cùng một bộ nhớ. Một thuật toán cho Focus của một công thức tổng quát được đưa ra trong phần 3.3.7

62

Hình 3.8: Ứng dụng của giải thích trừu tượng cho phân tích câu lệnh

x= x-> n cho hàm đảo ngược danh sách liên kết trong phần 3.2.5

Trong ví dụ ở phần 3.2.5, công thức Focus được hiểu là ∃ 1: ( 1) ∧ ( 1, ), trong đó định nghĩa các giá trị của biến sau khi thực hiện action

Get_Next_L(x, x) (tương ứng với câu lệnh x=x->n). Focus trên công thức (adsbygoogle = window.adsbygoogle || []).push({});

này đảm bảo rằng ( ) là xác định tại mỗi nút trong mỗi cấu trúc sau mỗi action. Hình 3.8 biểu diễn cấu trúc là Focus cho action này. Ba trường

63

hợp được xem xét trong việc tinh chỉnh : (i) Các trường của không trỏ đến bất kỳ phân tử trong danh sách đại diện bởi ( ); (ii) Trường n của u0

trỏ đến tất cả các phần tử danh sách đại diện bởi ( ); và (iii) Các trường n

của u0 trỏ tới chỉ một số các phần tử danh sách đại diện bởi ( ): u được tách thành hai nút – một nút được trỏ đến bởi trường n của u0 được đại diện

bởi u.1, và một nút không trỏ đến bởi trường n của u0 được đại diện bởi u.0. Sau Focus, điều kiện được đánh giá. Nếu công thức điều kiện là có khả năng đáp ứng, sau đó các action sẽ được thực hiện; nếu không, các action bị bỏ qua. Cơ chế này có lợi cho (một phần) giải thích điều kiện chương trình.

Trong ví dụ phần 3.2.5, vòng lặp while(x!=NULL) có hai cạnh ra trong CFG: một với các điều kiện ¬(∃ : ( )), chỉ ra rằng nếu x=NULL câu lệnh sau vòng lặp được thực thi (đầu ra trong trường hợp của chúng ta). Các cạnh khác có điều kiện: ∃ : ( ), chỉ ra rằng nếu x!=NULL thân vòng lặp được thực thi [13].

3.3.5 Công thức cập nhật

Tác dụng của ngữ nghĩa hoạt động với câu lệnh được mô tả bởi một tập các bản cập nhật công thức xác định giá trị của mỗi vị từ sau hành động của câu lệnh đó. Định lý nhúng cho phép ta đánh giá lại các các công thức trong các cấu trúc trừu tượng và cho biết kết quả cung cấp là một ngữ nghĩa trừu tượng bảo toàn. Nếu không có công thức cập nhật được quy định cho một vị từ, thì nó được giữ nguyên bởi các hành động.

Trong hình 3.8, hiệu quả của các action Get_Next_L(x=x->n) được tính bằng cách sử dụng công thức cập nhật sau đây: (i) ( ) = ∃ 1: ( 1) ∧

( 1, ), (ii) [ , ]( ) = [ , ]( ) ∧ ( [ ]( ) ∨ ¬ ( )). Công thức cập nhật (i) biến là phần tử tiếp theo với gốc là . Công thức (ii) cập nhật thông tin về những nút có thể truy cập từ sau mỗi action.

Một nút là có thể đạt từ sau hành động nếu nó là có thể đạt từ trước hành động , trừ trường hợp các nút trực tiếp được trỏ đến bởi (trừ khi

64

xuất hiện trên một chu trình n, trong trường hợp các nút được trỏ đến bởi x là

vẫn có thể truy cập mặc dù chúng ta tiến đến phần tử kế tiếp của nó). Đối

Một phần của tài liệu Nghiên cứu kỹ thuật giải thích trừu tượng (Trang 54)