Bộ công cụ SPIN là công cụ kiểm tra mô hình, là một hệ thống xác minh tính đúng đắn của các chương trình, nó hỗ trợ thiết kế hệ thống đa luồng, hệ thống tương tranh, v.v. SPIN như là một công cụ sử dụng phương pháp hình thức, nó các các đặc điểm:
Một công cụ trực quan, chương trình sử dụng các ký hiệu để thiết kế đặc tả rõ ràng mà không cần thực hiện cụ thể.
Là một công cụ mạnh với bộ kí tự rõ ràng dễ mô tả các yêu cầu cần kiểm tra sự đúng đắn của chúng.
Cung cấp phương pháp luận cho việc thể hiện tính thống nhất, hợp lý phù hợp với các điều kiện cần thỏa mãn [10].
Bộ công cụ SPIN không có các nguyên tắc chung cho kiểm chứng các hệ thống phần mềm khác nhau, vì vậy tùy vào yêu cầu kiểm chứng phần mềm cụ thể mà ta cố gắng đặc tả phần mềm theo một cách thức tương ứng. SPIN yêu cầu các quy tắc hữu hạn để diễn tả các hành vi khác nhau, điều này có nghĩa là tất cả các thuộc tính cần được thỏa mãn của hệ thống đều phải đặc tả hình thức với các ràng buộc về phạm vi, nguồn tài nguyên cần thiết để tính toán kiểm tra mô hình [10]. 3.2.2. Cấu trúc của SPIN
Cấu trúc cơ bản của mô hình SPIN được mô tả trong (Hình 2.2). Mô hình SPIN được bắt đầu bằng việc đặc tả hệ thống tương tranh đa luồng hoặc giải thuật phân tán dưới dạng mô hình bậc cao, sử dụng giao diện đồ họa SPIN. Sau khi được sửa lỗi cú pháp, SPIN sẽ thực hiện việc mô phỏng tương tác cho đến khi đạt độ tin cậy cơ bản để thiết kế các hành vi. SPIN sinh ra một chương trình xác thực theo giải thuật on-the-fly (duyệt nhanh) từ đặc tả mức cao. Bộ xác thực này được biên dịch với sự lựa chọn thời gian biên dịch hợp lý để thực hiện giải thuật giản lược. Nếu có lỗi được phát hiện qua các biến xác nhận lỗi của chương trình, nó sẽ thông báo lại cho bộ mô phỏng và kiểm tra chi tiết khi thực hiện và loại bỏ nguyên nhân gây lỗi [10].
SPIN cũng có thể xác thực trực tiếp phần mềm ở mức thực thi. Sử dụng SPIN như trình điều khiển và một máy logic để xác thực các thuộc tính thời gian ở mức cao. SPIN hoạt động dựa vào phương thức duyệt on-the-fly để xác thực các thuộc tính của hệ thống [10].
Hình 3.2. Cấu trúc mô hình kiểm chứng SPIN
SPIN là một công cụ hỗ trợ tự động gia tăng số lượng các tiến trình hoặc rút gọn số lượng các tiến trình. Nó hỗ trợ các quá trình mô phỏng dựa trên việc chứng minh cục bộ và tổng thể, dựa trên tìm kiếm theo chiều sâu để có thể kiểm soát được kích cỡ bài toán lớn có hiệu quả. SPIN khai thác kỹ thuật giản lược thứ tự từng phần và lưu trữ để tối ưu hóa khi xác thực. Để xác thực một mô hình phần mềm, mô hình đó phải là một mô hình hình thức được xây dựng bằng ngôn ngữ
Promela - ngôn ngữ mô hình hóa của bộ công cụ SPIN. Sử dụng công cụ SPIN
với ngôn ngữ Promela có thể: Mô phỏng tương tác phỏng ngẫu nhiên cho một hệ thống; Xác thực và chứng minh tính đúng đắn so với yêu cầu đặc tả của người sử dụng; Chứng minh sự xấp xỉ của các mô hình hệ thống lớn với việc bao phủ không gian trạng thái.
Cấu trúc và cách thức hoạt động của bộ công cụ SPIN dùng để kiểm tra mô hình có thỏa mãn hay không thỏa mãn tính chất có một số bước cụ thể. Tuy nhiên trong nội dung của luận văn này chỉ xét đến khía cạnh kiểm chứng sự tuân thủ hoặc không tuân thủ thể thức tương tác của chương trình nên ta thực hiện hai bước: mô hình hóa thể thức tương tác của chương trình bằng một tiến trình đồng thời mô hình hóa thể thức tương tác của một thành phần cũng bằng một tiến trình trong SPIN, hai tiến trình này chạy song song và sử dụng một kênh dữ liệu chung; thực hiện chạy SPIN để xác minh (verification). Kết quả sẽ được báo cáo trong
Thông báo lỗi cú pháp
Phân tích cú pháp Giao diện SPIN
Phân tích cú pháp LTL và dịch
Kiểm tra xác thực
Tối ưu bộ kiểm tra mô hình
Thực hiện xác thực on-the-fly Phản ví dụ
cửa sổ pan.out của ISPIN ngay sau khi chạy xác minh: nếu không có deadlock
(không báo Invalid EndState depth ( )) thì chương trình tuân thủ thể thức tương tác của thành phần, nếu có deadlock (báo Invalid endState depth ( )) thì thành phần không tuân thủ thể thức tương tác của chương trình. Cần phải yêu cầu bên thiết kế xây lại thể thức của chương trình.
2.2.3. Công cụ ISPIN
Bộ công cụ ISPIN có giao diện thân thiện, dễ sử dụng trong quá trình thực hiện kiểm chứng mô hình [19].
Hình 3.3. Giao diện ISPIN soạn thảo mã nguồn mô hình kiểm chứng
Công cụ ISPIN có cửa sổ để soạn thảo và chỉnh sửa chương trình (Hình 2.3)
khá đơn giản. Sau khi soạn thảo ta kiểm tra cú pháp với Tab Syntax Check. Kiểm tra
cú pháp phải được thực hiện nhằm chỉnh sửa cú pháp soạn thảo sai. Ngoài ra, thực
hiện Tab Redundancy Check nhằm xác định các thành phần không liên quan, sự dư
thừa của một số thành phần trong mô hình.
Với Tab Simulate/Replay là Tab quan trọng trong ISPIN, cửa sổ giao diện (Hình 3.4), trong cửa sổ của Tab này cần một số thiết lập:
Random, with seed: Khởi đầu ngẫu nhiên.
Interactive (for resolution of all nondeterminism): Tương tác
Guided, with trail: Đường dẫn dấu vết.
Initial steps skipped: Bỏ qua bước khởi tạo
Maximum number of steps: Số bước tối đa thiết lập.
Message Sequence Chart (MSC): Cung cấp quá trình giao tiếp của tiến trình
theo thời gian được thiết lập như thời gian trễ (delay) hoặc cách trình bày trên cửa sổ văn bản.
Trong Tab này quan trọng nhất là thực hiện (Re)Run và ta có thể quan sát
được MSC cùng sự thực hiện của dòng lệnh trên các cửa sổ msc.ps và simulation output cùng các bước thực hiện của mô hình.
Hình 3.4. Thiết lập thông số khi thực hiện Simulate/Replay.
Với Tab Verification là Tab ứng dụng cơ bản cần thiết trong nội dung này. Giao diện (Hình 3.5) với thiết lập quan trọng như sau:
Safety: Độ an toàn
+ Invalid endstates(deadlock): Trạng thái cuối không hợp lệ. + Assertion violation: Vi phạm khẳng định.
Ngoài ra có thể thiết lập: Depth first search (độ sâu tìm kiếm đầu tiên).
Report unreachable code: mã báo cáo không thể truy cập.
Sau khi Run chương trình thì kết quả xuất hiện trên cửa sổ pan.out, trong cửa
sổ này sẽ báo cáo có hoặc không deadlock (bế tắc) và báo cáo có hoặc không có violation (lỗi). Từ báo cáo này có thể kết luận được quá trình kiểm chứng.
3.3. Ngôn ngữ Promela
Để kiểm chứng thì cần xây dựng mô hình để mô phỏng chương trình trong
SPIN. Ngôn ngữ Promela giúp cho việc xây dựng mô hình trong SPIN một cách
rõ ràng, dễ hiểu và có được mô hình trực quan.
3.3.1. Tổng quan về Promela
Promela (Protocol meta language) là một ngôn ngữ mô hình hóa trong
SPIN, nó có thể trừu tượng hóa các giao thức trong các hệ thống tương tranh đa
luồng, hệ thống phân tán, v.v. Ngôn ngữ Promela trừu tượng hóa các giao thức
tương tác truyền dữ liệu, các thành phần trong phần mềm, hệ thống chuyển mạch, phần mềm điều khiển và có thể kiểm chứng sự tương tác của các giao thức này.
Quá trình xác thực bao gồm: Xây dựng mô hình trong SPIN bằng Promela,
mỗi mô hình được xác thực với SPIN dưới nhiều dạng khác nhau của môi trường giả định như: có sự tương tác giữa các giao thức với nhau hoặc không, có sự tuân thủ thể thức tương tác, hoặc như sự mất gói tin, các gói tin bị lặp v.v.
3.3.2. Chương trình Promela
Một chương trình Promela chứa các tiến trình (processes), biến (variable),
hằng…với các kiểu dữ liệu tùy ý, các tiến trình đặc tả hành vi của hệ thống.
Chương trình Promela có thể chứa các kênh thông điệp (message channel). Một chương trình Promela cơ bản:
Khai báo kiểu. Khai báo biến. Khai báo tiến trình. Tiến trình Init.
// Các khai báo
mtype = {MSG, ACK};/*khai bao kieu*/ chan toS = ... /*khai bao kenh*/
chan toR = ...
bool varbool; /*khai bao bien*/ // Một tiến trình
lenh_1; … lenh_n }
Init { /* Tien trinh khoi tao*/ ...
}
3.3.3. Tiến trình
Một tiến trình được khai báo bằng từ khóa proctype. Một chương trình
Promela có thể có một hoặc nhiều tiến trình, mỗi tiến trình chứa một hoặc nhiều các
câu lệnh và mỗi tiến trình có thể có tham số hoặc không có tham số. Tuy nhiên có
một chú ý quan trọng đó là tiến trình có thể được khai báo với từ khóa active thì được gán một giá trị pid (process id) duy nhất và có thể chạy chương trình.
Active proctype p( ){
Lenh_1;
……..
Lenh_n
}
Với một tiến trình mà khai báo không có từ khóa active thì sẽ không được cấp pid và nó chưa thể chạy.
proctype p( ){
Lenh_1;
……..
Lenh_n
}
Thân của tiến trình chứa một dãy các câu lệnh, để ngăn cách giữa các câu lệnh
tuần tự thì Promela sử dụng dấu ; hoặc dấu ->. Hai dấu hiệu phân cách này tương
đương nhau, trong đó dâu -> thể hiện mối quan hệ cho hai lệnh trước và sau dấu này,
nó như quan hệ nếu…thì. Câu lệnh cuối cùng trong một tiến trình không có dấu.
Các tiến trình có thể thực hiện đồng thời với các hành vi độc lập, nó có thể liên kết với nhau bởi các biến chung toàn cục hoặc sử dụng kênh chia sẻ biến chung. Mỗi tiến trình đều có trạng thái cục bộ và có thể có các biến cục bộ.
Ví dụ:
Proctype A{
byte tmp;
(state == 1) -> tmp = state; tmp = tmp + 1; state = tmp }
3.3.4. Tiến trình Init và Run
Một số tiến trình có thể được khai báo mà không có từ khóa active nó không được cấp pid và nó chưa thể chạy. Muốn chạy nó thì phải khai báo thêm
Run như sau:
proctype P( ){ … } Run P ( );
Tiến trình Init là một tiến trình khởi tạo, nó được thực thi ngay khi chạy chương trình trong SPIN. Các tiến trình khai báo sau định nghĩa Proctype chỉ là
khai báo tiến trình, do đó khi chạy tiến trình này chưa thực thi nếu đằng trước
Proctype không khai báo Active hoặc Run. Tuy nhiên, nếu không khai báo Active
và Run, để chạy các tiến trình này thì ta khai báo Run các tiến trình trong Init.
proctype P( ){ … } proctype Q( ){ … } … Init { Run P( ); Run Q( ); … }
Với câu lệnh Run, có thể áp dụng ở bất cứ tiến trình nào không nhất thiết
phải ở tiến trình khởi tạo. Các tiến trình có thể thực hiện xen kẽ nhau, không nhất thiết kết thúc tiến trình này mới đến tiến trình tiếp theo.
Hầu hết các ngôn ngữ lập trình đều yêu cầu khai báo biến trước khi sử dụng
chúng để tính toán trong chương trình. Với ngôn ngữ Promela là biến thể của ngôn
ngữ C thì điều này cũng hoàn toàn đúng, nó cũng yêu cầu khai báo biến trước khi sử
dụng chúng. Trong Promela, biến được khai báo tên biến ngay sau kiểu biến. Đây là
cách định nghĩa kiểu biến thường thấy trong ngôn ngữ C [1].
Ví dụ:
byte var1; short var2 = 1;
Tất cả các biến được khởi tạo mặc định là 0. Tuy nhiên, nên khởi tạo ngay khi khai báo biến.
Gán giá trị cho biến thì có thể sử dụng các lệnh như sau:
Lệnh gán: var1 = 1;
Khai báo kết hợp khởi tạo: Short var2 = 1;
Câu lệnh kiểm tra điều kiện: i * s + 5 ==27;
Biến trong Promela có thể là biến toàn cục hoặc biến cục bộ tùy thuộc vào
vị trí khai báo biến. Một biến cục bộ được khai báo trong một tiến trình, phạm vi hoạt động của biến đó chỉ nội trong tiến trình đó. Khi tiến trình đó kết thúc thì biến đó cũng không còn tồn tại.
3.3.6. Kiểu dữ liệu trong Promela
3.3.6.1. Kiểu dữ liệu cơ bản
Promela không tồn tại một số kiểu dữ liệu như: real, char, string, float như
một số ngôn ngữ khác. Kiểu dữ liệu cơ bản của Promela được thể hiện trong bảng
(Bảng 2.1).
Bảng 3.1 Kiểu dữ liệu cơ bản
Tên kiểu Giá trị Kích thước(bits)
bit, bool byte short int unsigned 0, 1, false, true 0..255 -32786..32767 -231..231-1 0..2n-1 18 16 32 ≤ 32
3.3.6.2. Kiểu dữ liệu có cấu trúc a) Kiểu mảng a) Kiểu mảng
Khai báo:
int arrtb[max];
Dữ liệu kiểu mảng được đánh chỉ số bắt đầu từ 0, như vậy phần tử của mảng theo khai báo trên sẽ là:
arrtb[0], arrtb[1], …, arrtb[max-1];
Ví dụ:
byte state[10]; //state[0],…,state[9];
b) Kiểu bản ghi
Kiểu bản ghi được khai báo như sau:
typedef Record {
byte var1; short var2; }
c) Kiểu liệt kê
Kiểu liệt kê là kiểu tập hợp các hằng số được khai báo như sau:
mtype = { LINE_CLEAR, TRAIN_ON_LINE, LINE_BLOCKED};
Chú ý: Một chương trình chỉ chứa một kiểu liệt kê và nó phải được sử dụng là toàn cầu (dùng chung).
Ngoài ra, Promela có một kiểu dữ liệu khác thường được sử dụng đó là dữ
liệu kiểu kênh. Kiểu dữ liệu này sẽ được đề cập riêng (phần 2.3.12). 3.3.7. Toán tử, dịnh danh, hằng và biểu thức
a) Toán tử
Chương trình Promela có các toán tử tương tự như các toán tử trong ngôn ngữ lập trình C. Liệt kê các toán tử (Bảng 2.2) trong Promela, nó có độ ưu tiên
thực hiện giảm dần từ trên xuống [2].
Bảng 3.2. Toán tử trong Promela
Thứ tự Toán tử Tên 1 2 3 4 5 ( ) [ ] . ! ~
Dấu ngoặc đơn Chỉ số mảng Lựa chọn trường Phủ định
6 7 8 9 10 11 12 13 14 15 16 17 18 ++, -- *, /, % +, - <<, >> <, <=, >, >= = =, != & ^ | && || (→ : ) = Tăng, giảm
Nhân, chia, chia lấy dư Cộng, trừ
Dịch bit trái, dịch bit phải Phép toán so sánh số học
Tương đương, không tương đương Và bit
Hoặc loại trừ bit Hoặc gộp bit Và (logic) Hoặc logic
Biểu thức có điều kiện Phép gán
b) Định danh
Định danh trong Promela có thể là một chữ cái, một ký tự, một dấu chấm
hay dấu gạch dưới, theo sau là một dấu hai chấm (:). Định danh giúp cho việc khi một tính toán của chương trình cần thiết phải chyển đến một vị trí cụ thể. Vị trí này được đặt định danh, nó giống như việc đánh dấu vị trí truy cập.
c) Hằng
Hằng số là một chuỗi ký tự đại diện cho số nguyên, số thập phân. Hằng số được xác định bởi mtype, hoặc thông qua một định nghĩa macro. Hằng số luôn được khai báo ở đầu chương trình. Ví dụ:
# define maxx 50
d) Biểu thức
Biểu thức trong Promela được xây dựng từ các biến, hằng, toán tử. Ví dụ các biểu thức như sau:
x = y + z; // phép gán
Hoặc:
z ++; // phép tăng
3.3.8. Câu lệnh trong Promela
Trong Promela, các câu lệnh được thi hành một cách bình đẳng, nó không
có sự phân biệt giữa các lệnh điều kiện và lệnh thông thường. Có các lệnh thi hành (executable) hoặc lệnh được trì hoãn tạm thời để chờ thực hiện tiếp theo một
điều kiện nào đó tùy thuộc vào trạng thái của hệ thống. Các câu lệnh điều kiện chỉ được thi hành khi nó thỏa mãn điều kiện, hay điều kiện của nó là True. Với lệnh thi hành thì nó thực hiện ngay lập tức khi gọi lệnh [2].
Một câu lệnh theo sau biểu thức logic được thi hành khi biểu thức có giá trị là true (giá trị biểu thức khác 0).
Một số câu lệnh trong Promela:
o Lệnh Printf: đây là câu lệnh thi hành. Nó không có tác dụng trong việc
đánh giá để xác thực điều gì đó của chương trình.
o Lệnh assert(<bt>): lệnh này luôn là lệnh thi hành. Khi <bt> có giá trị sai,
chương trình sẽ thoát và ghi nhận lỗi vi phạm. Câu lệnh này dùng để kiểm