TLA+ 《Specifying Systems》翻译初稿——Section 14.1 Introduction to TLC(TLC介绍)

Chapter 14 The TLC Model Checker

TLC是一个用于查找TLA+ 规约(Specification)中错误的程序。它由袁宇设计和开发,并得到了莱斯利·兰波特、马克·海登和马克·塔特尔的帮助。它可以通过TLA官网获得。本章介绍TLC Version2。在我编写本文档时,Version 2仍在开发中,目前只有Version 1可用。请查阅软件附带的文档,了解它是什么版本,以及它与这里描述的版本的区别。

14.1 Introduction to TLC


TLC可以处理遵循如下标准格式的公式:

(14.1) I n i t [ N e x t ] v a r s T e m p o r a l Init \wedge \square[Next]_{vars}\wedge Temporal

其中,Init是初始谓词(initial predicate),Next是Next-state动作(action),vars是所有变量的元组,Temporal通常表示Liveness时态公式。Liveness公式在第八章中有描述。如果您的规约不包含时态公式,也就是它的形式为 I n i t [ N e x t ] v a r s Init \wedge \square[Next]_{vars} ,那么您可以忽略对时态逻辑检查的讨论。TLC不处理隐藏运算符 \boldsymbol{\color{Red}{\exists}} (时态存在量词),如果需要检查用 \boldsymbol{\exists} 隐藏变量的规约,可以检查它的子规约,在其中这些变量是可见的。

规约中查找错误最有效的方法是尝试验证它是否满足其属性(properties)。TLC可以检查规约是否满足(蕴含)的这一大类TLA+公式,这类公式的主要限制是公式中不能包含 \boldsymbol{\exists} 。您还可以只运行TLC而不检查任何属性,在这种情况下,它将只查找下列两种类型的错误:

  • "Silly"错误。如第6.2节所解释的,"silly"表达式如 3 + 1 , 2 3+\left \langle 1,2 \right \rangle ,其形式不符合TLA+的语义。如果某个特定的状态链是否符合规定,取决于"silly"表达式的含义,那么这个规约是不正确的。

  • 死锁。无死锁经常是我们希望一个规约需要满足的一个特殊性质;它是用不变性来表示的: ( ENABLED  N e x t ) \square(\text{ENABLED }Next) 。此属性的一个反例是一个导致死锁的行为状态链)序列, 即到达一个Next未使能的状态,因此不可能有进一步的重叠(stuttering)步骤。TLC通常默认检查死锁,但也可以禁用此检查,因为对于某些系统,死锁可能只是表示行为成功终止。

我们用一个简单的例子来展示TLC的使用:下面是一个Alternating Bit Protocol
规约,该协议通常用于在有损的FIFO传输线上发送数据。算法设计人员可能会将协议描述为如下所示的系统:

当1bit位上的值sBit和sAck相等时,发送方可以发送一个值。它将变量"sent"设置为要发送的值,并设置补码sBit。该值最终被投递到接收方,被赋给变量rcvd,同时接收方设置补码rBit,并给发送方回响应sAck。发送方收到sAck后,允许发送下一个值。该协议使用两条有损FIFO传输线:发送者在msgQ上发送数据和控制信息,接收者在ackQ上发送确认。完整的Alternating Bit Protocol 规约在图14.1上,可以在下文中找到。除了liveness条件外,其他表述都相当清晰。由于消息可能会从队列中经常性地丢失,所以需要对接收消息的action设置Strong Fairness属性,以确保重发的消息最终能够被收到。不过,不要担心规约的细节。现在,你所需要知道的,就是下面这些声明和变量:

CONSTANT Data
VARIABLES msgQ, ackQ, sBit, sAck, rBit, sent, rcvd



上图中,

  • msgQ是由 { 0 , 1 } × D a t a \left \{ 0,1 \right \}\times Data 集合中的元素组成的序列;
  • ackQ 是由 { 0 , 1 } \left \{ 0,1 \right \} 集合中的元素组成的序列;
  • sBit, sAck 和 rBit是 { 0 , 1 } \left \{ 0,1 \right \} 集合中的元素;
  • sent 和rcvd 是 D a t a Data 集合中的元素.

TLC的输入包括TLA+模块文件和配置文件。TLC假定规约具有公式(14.1)的形式。配置文件告诉TLC 规约的名称和要检查的属性。例如,AlternatingBit模块的配置文件包含声明

*规约* A B S p e c \textrm{*规约*}\: ABSpec

这个语句是告诉TLC,待检查的规约名称是ABSpec,如果规约的格式为 I n i t [ N e x t ] v a r s Init \wedge \square[Next]_{vars} (无Liveness条件),则无需使用规约语句,可以通过在配置文件中添加以下两个语句来声明初始状态谓词和Next-State Action:

INIT I n i t NEXT N e x t \\ \textrm{INIT} \: Init\\ \textrm{NEXT}\: Next

要检查的属性用PROPERTY语句指定。例如,为了检查ABTypeInv不变量,即 A B T y p e I n v *规约*\Rightarrow \square ABTypeInv ,可以在模块AlternatingBit的配置文件中添加如下定义:

I n v P r o p e r t y A B T y p e I n v InvProperty \triangleq \square ABTypeInv

并将语句 PROPERTY  I n v P r o p e r t y \textrm{PROPERTY }InvProperty 写入配置文件中。不变性检查非常常见,因此TLC允许您将以下语句放入配置文件中:

INVARIANT A B T y p e I n v \textrm{INVARIANT} ABTypeInv

INVARIANT语句必须指定一个状态谓词。若要检查PROPERTY语句的不变性,指定的属性必须为 P \square P 形式(因为 PROPERTY  P \textrm{PROPERTY } P 只是让TLC检查该规约是否蕴含P,也就是 P在满足规约的每个状态链的初始状态中为 TRUE)。

TLC通过生成并校验一系列满足规约状态链来工作。

为此,首先要给规约指定一个模型(model)。要定义模型,我们必须为规约的常量参数赋值。AlternatingBit协议规约的唯一常量参数是Data。通过在配置文件中放置以下声明,我们可以告诉TLC,Data为包含名为d1和d2两个任意元素的集合:

CONSTANT D a t a = { d 1 , d 2 } \textrm{CONSTANT} \: Data = \left \{d1, d2 \right \}

(我们可以使用包含至少一个字母的字母或数字串作为元素名称)。有两种使用TLC的方法。 默认方法是模型检查(model checking),这种方式将尝试查找所有可达的状态,即所有满足公式 I n i t [ N e x t ] v a r s Init \wedge \square[Next]_{vars} 状态链中可能出现的状态。

我们还可以在仿真模式下运行TLC,在该模式下,它会随机生成状态链,而无需尝试检查所有可达的状态。这里我们我们先考虑模型检查,模拟模式将在第243页的14.3.2节介绍。
对于AlternatingBit协议,不可能彻底检查所有可达状态,因为消息序列可以任意变长,因此存在无限多个可达状态。我们必须进一步约束模型使其有限,也就是说,它仅允许有限数量的可能状态。为此,我们定义了一个称为约束的状态谓词,该谓词声明了序列长度的界限。

例如,以下约束断言msgQ和ackQ的长度最多为2:

L e n ( m s g Q ) 2 L e n ( a c k Q ) 2 \\ \wedge Len(msgQ)\leqslant 2 \\ \wedge Len(ackQ)\leqslant 2

与其以这种方式指定序列长度的界限,不如让它们作为参数并在配置文件中赋值。我们不想在规约中加入仅为TLC方便考虑的声明和定义。因此,我们编写了一个名为MCAlternatingBit的新模块,该模块扩展了AlternatingBit模块,可以用作TLC的输入。该模块显示在下一页的图14.2中。下一页的图14.3中显示了该模块的可能配置文件。请注意,在这种情况下,配置文件必须为规约的所有常量参数指定值,即AlternatingBit模块中的参数Data和模块MCAlternatingBit本身中声明的两个参数。您可以使用第3.5节(第32页)中所述的TLA +注释语法在配置文件中添加注释。

当指定约束Constr时,TLC会检查满足 I n i t [ N e x t ] v a r s C o n s t r Init \wedge \square [Next]_{vars} \wedge \square Constr 规约状态链的每个状态。在本章的其余部分,这些状态将称为可达状态。

让TLC检查类型不变式会捕获许多简单的错误。当我们纠正了所有可以找到的错误后,我们便希望寻找不太明显的错误。一个常见的错误是某个操作在应启用时未启用,从而导致无法达到某些状态。您可以通过第252页上介绍的coverage选项来发现某个操作是否从未启用。要发现某个操作有时是否被错误地禁用,可以尝试检查Liveness。AlternatingBit协议中明显Liveness属性是,发送方发送的每个消息最终都将被传递给接收方。

当满足如下条件: s e n t = d a n d s B i t s A c k sent = d \: and \:sBit \neq sAck 时,一个消息d被发送。 因此,描述该属性的一种简单方法是:

S e n t L e a d s T o R c v d d D a t a : ( s e n t = d ) ( s B i t s A c k ) ( r c v d = d ) \\SentLeadsToRcvd \triangleq \\ \quad \quad \forall d \in Data : (sent = d ) \wedge (sBit \neq sAck ) \leadsto (rcvd = d )

公式SentLeadsToRcvd断言,对于任何数值d,如果在sBit不等于sAck时sent的值等于d,则rcvd最终必须等于d。这并不是说所有发送的消息都会最终传递到位,例如,对特定值d发送两次但仅接收一次的状态链也满足公式。但是,该公式足以满足我们的目的,因为该协议不依赖于实际发送的值。如果可能出现相同的值发送两次但仅接收一次,则也有可能发送两个不同的值而仅接收到一个,后者违反了SentLeadsToRcvd。因此,我们将SentLeadsToRcvd的定义添加到模块MCAlternatingBit中,并将以下语句添加到配置文件中:

检查liveness属性比其他类型的检查要慢得多,因此,只有在通过检查不变性发现尽可能多的错误之后,才执行此操作。检查类型正确性和属性SentLeadsToRcvd是开始查找错误的好方法。但最终,我们希望了解该协议是否符合其规约。但是,我们(可能)没有它的规范。实际上,在实践中通常需要我们检查系统设计的正确性,而无需对系统应该做什么做任何正式规约。在这种情况下,我们可以编写事后规范。下一页的图14.4中的ABCorrectness模块就是这种对alternating bit 协议的正确性的规约。它实际上是协议规约的简化版本,在该协议中,变量rcvd,rBit和sAck不是从消息中读取,而是直接从其他进程的变量中获取。我们要检查AlternatingBit模块的规范ABSpec是否蕴含ABCorrectness模块的公式ABCSpec。为此,我们通过添加以下语句来修改模块MCAlternatingBit:

INSTANCE    A B C o r r e c t n e s s \textrm{INSTANCE}\; ABCorrectness

然后将配置文件的PROPERTY语句修改为

PROPERTIES A B C S p e c S e n t L e a d s T o R c v d \textrm{PROPERTIES} \:ABCSpec \:SentLeadsToRcvd

此示例是非典型的,因为正确性规约 ABCSpec不涉及变量隐藏(时态存在量词)。现在让我们假设模块ABCorrectness确实声明了另一个变量h,该变量出现在ABCSpec中,并且alternating bit协议的正确性条件是隐藏了h的ABCSpec。 然后,在TLA+中正式表示正确性条件,如下所示:

A B ( h ) INSTANCE A B C o r r e c t n e s s THEOREM A B S p e c h : A B ( h ) ! A B C S p e c \\AB (h) \triangleq \textrm{INSTANCE} \:ABCorrectness \\\textrm{THEOREM}\: ABSpec \Rightarrow \: \exists h :AB (h)!ABCSpec

TLC无法直接检查该定理,因为TLC目前无法处理时间存在量词。我们将以与尝试证明该定理相同的方式通过TLC检验该定理,即通过使用细化映射。
如62页5.8节所述,我们将根据AlternatingBit模块的变量定义状态函数oh,然后证明

( 14.2 ) A B S p e c A B ( o h ) ! A B C S p e c (14.2)ABSpec \Rightarrow AB (oh)!ABCSpec

为了让TLC检查该定理,我们将添加定义

A B C S p e c B a r A B ( o h ) ! A B C S p e c ABCSpecBar \triangleq AB(oh)!ABCSpec ,

并让TLC检查属性ABCSpecBar.

TLC检查属性时,实际上并不会验证规约是否蕴含了该属性。相反,它检查

  1. 规约的safety部分隐含了property的safety部分,以及
  2. 规约是否蕴含了属性的liveness部分。

例如,假设规格Spec和属性Prop为

S p e c I n i t [ N e x t ] v a r s T e m p o r a l Spec \triangleq Init \wedge \square[Next]_{vars}\wedge Temporal
P r o p I m p l i e d I n i t [ I m p l i e d A c t i o n ] p v a r s I m p l i e d T e m p o r a l Prop \triangleq ImpliedInit \wedge \square[ImpliedAction]_{pvars} \wedge ImpliedTemporal

这里 Temporal 和ImpliedTemporal 是liveness 属性. 在这里,TLC校验如下两个公式

I n i t [ N e x t ] v a r s I m p l i e d I n i t [ I m p l i e d A c t i o n ] p v a r s Init \wedge \square[Next]_{vars} \Rightarrow ImpliedInit \wedge \square[ImpliedAction]_{pvars}
S p e c I m p l i e d T e m p o r a l Spec \Rightarrow ImpliedTemporal

这意味着不能使用TLC来检查non-machine-closed 规约是否满足safety要求。 (Machine closure在8.9.2节中讨论(请参阅第111页。)下面的14.3节更准确地描述了TLC如何检查属性。

发布了4 篇原创文章 · 获赞 1 · 访问量 5539

猜你喜欢

转载自blog.csdn.net/robinhzp/article/details/103512934