在上一篇博文中,我们展示了如何用 KLEE 来为一个简单程序生成测试用例。在这篇博文中,我们将展示如何使用 KLEE 中的数组和断言。

0x1 测试无序数组

如下是二分查找的算法实现:

int binary_search(int arr[], int size, int target) {print_data(arr, size, target);int low = 0;int high = size - 1;int mid;while (low <= high) {mid = (low + high)/2;if (arr[mid] == target) {return mid;}if (arr[mid] < target) {low = mid + 1;}if (arr[mid] > target) {high = mid - 1;}}return -1;
}

为了测试这个函数,我们可以创建如下 C 文件(source_to_klee/examples/bin_search0/bin_search0.c):

#include <klee/klee.h>
#include <stdio.h>
#include <assert.h>void print_data(int arr[], int size, int target) {printf("searching for %d in:\n[", target);for (int i=0; i < size-1; i++) {printf("%d, ", arr[i]);}printf("%d]\n", arr[size-1]);
}int binary_search(int arr[], int size, int target) {print_data(arr, size, target);int low = 0;int high = size - 1;int mid;while (low <= high) {mid = (low + high)/2;if (arr[mid] == target) {return mid;}if (arr[mid] < target) {low = mid + 1;}if (arr[mid] > target) {high = mid - 1;}}return -1;
}int main() {int a[10];int x;klee_make_symbolic(&a, sizeof(a), "a");klee_make_symbolic(&x, sizeof(x), "x");int result = binary_search(a, 10, x);printf("result = %d\n", result);// check correctnessif (result != -1) {assert(a[result] == x);} else {// if result == -1, then we didn't find it. Therefore, it shouldn't be in the arrayfor (int i = 0; i < 10; i++) {assert(a[i] != x);}}return 1;
}

在上述代码中,我们声明一个包含 10 个元素的符号数组 ‘a’ ,一个代表查找目标值的符号化整数变量,然后调用二分查找函数。如果结果不等于 -1,则代表找到目标值,所以数组对应位置上的值应该等于我们查找的目标值,否则没有任何元素应该等于查找的目标值。

接下来,运行如下命令行命令来运行 KLEE 进行分析。
clang-9 -I source_to_klee/include -emit-llvm -c -g -O0 -Xclang -disable-O0-optnone bin_search0.c
klee --external-calls=all bin_search0.bc

获取到如下输出:

输出中有一行红色的信息表明是断言错误。

通过如下命令来找到导致断言错误的输入:

clang-9 -I source_to_klee/include -L source_to_klee/cmake-build-debug/lib bin_search0.c -lkleeRuntest
export LD_LIBRARY_PATH=source_to_klee/cmake-build-debug/lib/:$LD_LIBRARY_PATH
ls -1 klee-last/*.ktest | awk '{printf("echo \"----------------\"\n echo %s\n KTEST_FILE=%s ./a.out\n", $0, $0)}' | sh

可以看到在如下的输出中,第八个测试用例导致了断言错误。

在 test000008.ktest 中,我们试图在数组中寻找已存在的元素 1,但是却显示数组中不存在元素 1。这是为什么呢??原因就是这个数组是无序的!!!


0x2 测试有序数组

我们希望 KLEE 在测试前,数组已经是有序的。而幸运的是,KLEE 恰好给我们提供了一个 intrinsic 函数 klee_assume 来限定数组有序。以下是新版的二分查找代码(source_to_klee/examples/bin_search1/bin_search1.c):

#include <klee/klee.h>
#include <stdio.h>
#include <assert.h>void print_data(int arr[], int size, int target) {printf("searching for %d in:\n[", target);for (int i=0; i < size-1; i++) {printf("%d, ", arr[i]);}printf("%d]\n", arr[size-1]);
}int binary_search(int arr[], int size, int target) {print_data(arr, size, target);int low = 0;int high = size - 1;int mid;while (low <= high) {mid = (low + high)/2;if (arr[mid] == target) {return mid;}if (arr[mid] < target) {low = mid + 1;}if (arr[mid] > target) {high = mid - 1;}}return -1;
}int sorted(int arr[], int size) {for (int i = 0; i < size-1; i++) {if (arr[i] > arr[i+1]) {return 0;}}return 1;
}int main() {unsigned short n;klee_make_symbolic(&n, sizeof(n), "n");klee_assume(n > 0);klee_assume(n < 65535);int a[n];int x;klee_make_symbolic(&a, sizeof(a), "a");klee_assume(sorted(a, 10));klee_make_symbolic(&x, sizeof(x), "x");int result = binary_search(a, 10, x);printf("result = %d\n", result);if (result != -1) {assert(a[result] == x);} else {// if result == -1, then we didn't find it. Therefore, it shouldn't be in the arrayfor (int i = 0; i < 10; i++) {assert(a[i] != x);}}return 1;
}

第 50 行表示 KLEE 应该假设数组已经排序,第 33 到 40 行实现了检查。但是,我们得到以下输出:

如果我们像之前一样运行测试,我们会看到断言始终保持不变,并且测试输入数组确实是排序的。

然而,如果你想确保 KLEE 不输出这种错误,你可以将 sort 函数转换成总是在结束的时候退出;同时要避免布尔逻辑,这反过来可能导致不同的编译器使用简短逻辑和输出不同的结果。

如下是我们新版的代码文件(source_to_klee/examples/bin_search/bin_search.c):

#include <klee/klee.h>
#include <stdio.h>
#include <assert.h>void print_data(int arr[], int size, int target) {printf("searching for %d in:\n[", target);for (int i=0; i < size-1; i++) {printf("%d, ", arr[i]);}printf("%d]\n", arr[size-1]);
}int binary_search(int arr[], int size, int target) {print_data(arr, size, target);int low = 0;int high = size - 1;int mid;while (low <= high) {mid = (low + high)/2;if (arr[mid] == target) {return mid;}if (arr[mid] < target) {low = mid + 1;}if (arr[mid] > target) {high = mid - 1;}}return -1;
}int sorted(int arr[], int size) {int found_error = 0;for (int i = 0; i < size-1; i++) {found_error = found_error | (arr[i] > arr[i+1]);}return !found_error;
}int main() {int a[10];int x;klee_make_symbolic(&a, sizeof(a), "a");klee_assume(sorted(a, 10));klee_make_symbolic(&x, sizeof(x), "x");int result = binary_search(a, 10, x);printf("result = %d\n", result);if (result != -1) {assert(a[result] == x);} else {// if result == -1, then we didn't find it. Therefore, it shouldn't be in the arrayfor (int i = 0; i < 10; i++) {assert(a[i] != x);}}  return 1;
}


运行结果没有任何问题!

特别地,在每一个可能找到元素的位置都生成了测试用例:

ls -1 klee-last/*.ktest | awk '{printf("KTEST_FILE=%s ./a.out\n", $0)}' | sh  | grep result | sort


通过这种方法,我们成功地检查了二分查找(对于 10 个元素的数组)的正确性。

更有趣的事是,您可以尝试插入错误或更改代码。

例如,如果在 while 循环中我们用 < 更改 <= ,会发生什么?

while (low <= high)

KLEE 使用(三)------ 使用 KLEE 为二分查找生成测试相关推荐

  1. 三种方法实现二分查找

    最近打算研究一下基础的算法,就先从二分开始做吧,三种方法 #include <bits/stdc++.h> using namespace std;const int MAXN = 102 ...

  2. LeetCode Hot100 ---- 二分查找专题

    什么是二分查找 二分查找是计算机科学中最基本.最有用的算法之一. 它描述了在有序集合中搜索特定值的过程. 二分查找中使用的术语: 目标 Target -- 你要查找的值 索引 Index -- 你要查 ...

  3. 【数据结构与算法】二分查找

    一.什么是二分查找? 二分查找针对的是一个有序的数据集合,每次通过跟区间中间的元素对比,将待查找的区间缩小为之前的一半,直到找到要查找的元素,或者区间缩小为0. 二.时间复杂度分析? 1.时间复杂度 ...

  4. 开花(在b数组中二分查找a数组元素)

    注意: 代码一:二层循环暴力查找超时 代码二(最棒):借用STL set中的count()方法快速搞定,且没有超时so,集合查询应该很快注意:count()时间复杂度是线性变换的最坏的情况为O(n)s ...

  5. 关于二分查找及其上下界问题的一些思考

    个人认为在编程的时候,我的代码能力应该是到位的,但是昨天参加的某公司笔试彻底把这个想法给终结了,才意识到自己是多么的弱.其中印象最深刻的是一道关于二分查找上下界的问题.当时洋洋得意,STL 分分钟搞定 ...

  6. 对于递归算法和二分查找的理解

    前言 本人涉及web开发领域的学习以来,很少去学习算法相关知识,今日对于基础的算法进行了初步探究,包括递归.二分查找等,有所感发,发表这篇博客供大家学习参考,相互交流! 1.递归算法 例题1:递归求斐 ...

  7. 二分查找(Binary Search)需要注意的问题,以及在数据库内核中的实现

    问题背景 今年的实习生招聘考试,我出了一道二分查找(Binary Search)的题目.题目大意如下: 给定一个升序排列的自然数数组,数组中包含重复数字,例如:[1,2,2,3,4,4,4,5,6,7 ...

  8. 二分查找(折半查找)总结

    ** 二分查找(折半查找)总结 ** 文章目录 二分查找(折半查找)总结 一.基本概念 二.编写代码 1.二分查找 2.测试代码 三.输出结果 四.总结评价 一.基本概念 二分查找也叫折半查找,是一种 ...

  9. Python二分查找的三种思路

    二分查找的条件: 1.列表是有序的 2.掐头去尾去中间 第一种(最普通的方式): lst = [1, 4, 5, 7, 12, 15, 16, 23, 35, 56] n = 5 left = 0 r ...

最新文章

  1. History(历史)命令用法
  2. OpenCV使用神经网络检测颜色检查器
  3. 【Homework】说出 == 和 equals 的区别
  4. 修改octave的editor的背景颜色
  5. RT-Thread对GPIO操作两种方式的区别:1)通过设备操作接口2)直接通过通用GPIO设备驱动
  6. java unsafe park_Java魔法类——Unsafe应用解析
  7. .deploy 文件 php,关于php:Heroku deploy自动删除服务器文件?
  8. MVC教程第一篇:准备工作
  9. 让开发者 so easy 的一站式服务到底存不存在?
  10. logback 配置详解(一)configuration and logger
  11. 误删D盘数据怎么办?推荐使用数据恢复软件EasyRecovery
  12. 百度金融与农业银行战略合作,AI +金融的开放故事讲得好吗?
  13. PayPal WebHook事件异步通知
  14. AutoPatch java_Java Beta.canAutoPatch方法代碼示例
  15. 【paper-note7】Several Papers About Video Classification
  16. jQuery教程大纲
  17. 【postgresql】 获取月初月末日期
  18. flutter中android子工程报错,Flutter混合Android
  19. 大一上学期Python学习心得体会
  20. iOS ”打仙人掌“游戏一---游戏玩法实现

热门文章

  1. 网页版Android手机时钟网页特效
  2. 【python初级】 Ubuntu18.04上升级pip
  3. trie树的基本操作
  4. 微信是如何飞起来的(延伸阅读:性,微信下的机会?一个超级用户的自白:移动社交那点事类kik手机通信录互动应用前景探讨)
  5. rest API中后台接收json对象数组
  6. 利用Excel对数据进行标准化处理
  7. C语言char,char*赋值
  8. 使用 Fiddler工具模拟post四种请求数据
  9. 打造高效出差流程:SAP Press 《Concur - Travel and Expense Management with SAP》
  10. XXX.EXE已停止工作