Page 299 - 《软件学报》2026年第5期
P. 299

2178                                                       软件学报  2026  年第  37  卷第  5  期


                 的执行顺序, 通过确定性模拟执行技术来执行调度的事件. 为应对状态爆炸问题, 研究者们提出了多种优化技术,
                 包括状态空间约减技术和探索效率优化. 接下来, 我们将分别讨论这些技术的实现和优化.
                  3.2.1    状态空间搜索方法
                    状态空间搜索方法可分为有状态搜索              (stateful search) [91] 和无状态搜索  (stateless search) [77] . 根据定义, 有状态
                 搜索的下一次搜索依赖于历史状态, 即之前探索的路径; 而无状态搜索不记录历史状态, 每次搜索的决策独立于之
                 前的探索过程.
                    第  3.1  节中提到确定性模拟执行技术分为基于状态和基于事件两种方法. 在状态空间搜索上, 基于事件的方
                 法由于缺乏状态保存能力, 只能采用无状态搜索; 而基于状态的方法具备完整的状态保存能力, 通常采用有状态搜
                 索, 也可采用无状态搜索.
                    有状态搜索     (stateful search): 有状态搜索的原理与传统模型检验搜索算法类似, 算法从一个初始状态开始, 搜
                 索并探索整个状态空间, 算法伪代码如图             3 所示  [77] . 算法维护两个集合: 已探索状态集       (Explored) 和未探索状态
                 集  (Unexplored). 初始状态  s 0 加入未探索集合中   (第  2  行). 然后, 算法循环从未探索状态集取出一个状态. 如果该
                 状态不在已探索集合中, 则对其进行探索并将其加入已探索集合                      (第 3–13 行). 对被探索的状态的所有可执行/使
                 能  (enabled) 转移进行执行并产生新的后继状态, 并将这些后继状态加入未探索集合中                      (第 7–11 行). 循环此过程
                 直到未探索状态集为空.

                                           1   Initialize:  Unexplored is empty;  Explored is empty;
                                           2         add s 0  to Unexplored;
                                           3   Loop: while  Unexplored ≠  do {
                                           4        take  s out of Unexplored;
                                           5        if s is NOT already in  Explored then {
                                           6          enter  s in Explored;
                                           7          T = enabled(s);
                                           8          for all  t in T do {
                                           9           s′ = succ(s) after  t;
                                           10          add  s′ to Unexplored;
                                           11         }
                                           12       }
                                           13      }
                                             图 3 有状态搜索算法       (引自 VeriSoft [77] )

                    下面解释    DMCK  中有状态搜索的具体实现细节, 不同实现方式可能影响探索效率和缺陷发现速度.
                    ● 状态取出顺序影响缺陷发现的路径和速度               (第 4 行). 深度优先搜索    (未探索状态集为 LIFO 栈) 优先探索较
                 深状态, 可更快触及长路径缺陷. 广度优先搜索             (FIFO  队列) 发现的缺陷触发路径最短, 便于分析, 但当缺陷位于较
                 深层时, 探索时间可能更长. 随机策略可提高状态多样性, 加快代码覆盖率. 启发式策略需结合领域知识, 优先探索
                 高价值状态, 可提升特定缺陷发现速度, 并可与其他搜索方式结合                   [42] .
                    ● 状态比对用于状态去重         (第  5  行). 最基本的方法是计算状态哈希进行比对, 更高级的方法是在计算哈希前
                 进行状态标准化, 例如利用节点角色对称性将等价状态归一化                    [74] , 以进一步减少冗余状态.
                    ● 状态表示方式影响状态空间规模. 最直接的方法是记录系统内存信息                       [42] , 更精简的方法是仅存储协议关键
                 变量状态   [44,69] , 可减少状态数量和内存占用, 但可能误判不同状态为相同, 导致遗漏.
                    ● 可执行/使能转移      (enabled transitions) 由确定性模拟执行器报告   (第  7  行). 例如, 可触发的超时、可送达的
                 消息和可注入的错误等. 由于不确定性, 多个动作可能同时使能, 确定性模拟执行器通常使用 Toss/Choose                         [42,45,77] 函
                 数, 在指定范围内依次返回不同值          (类似  fork  效果), 确保探索所有路径.
                    无状态搜索     (stateless search): 无状态搜索按定义不记录历史状态, 每次探索的决策独立于此前的执行路径. 这
                 种策略虽实现简单, 但会导致等价状态无法区分而被多次重复遍历, 使状态/路径空间急剧增长. 此外, 无状态搜索
                 回溯时需从初始状态重新执行事件序列来生成状态, 增加了                    CPU  开销. 因此, 纯粹的无状态搜索算法在实践中效
   294   295   296   297   298   299   300   301   302   303   304