2017-06-09

这是KLEE的第三篇tutorial,感觉还是挺有意思的,虽然简单还是记录一下,原文在此

问题描述

问题也比较简单,给出一个路径,在下面的迷宫中从’X’走到’#’,a表示左,d表示右,w表示上,s表示下。

"+-+---+---+"
"|X|     |#|"
"| | --+ | |"
"| |   | | |"
"| +-- | | |"
"|     |   |"
"+-----+---+" 

传统方法

// http://feliam.wordpress.com/2010/10/07/the-symbolic-maze/ ‎
// twitter.com/feliam
/*
* It's a maze!
* Use a,s,d,w to move "through" it.
*/

#include<string.h>
#include<stdio.h>
#include<stdlib.h>


/**
* Maze hardcoded dimensions
*/
#define H 7
#define W 11
/**
* Tha maze map
*/
char maze[H][W] = { "+-+---+---+",
					"| |     |#|",
					"| | --+ | |",
					"| |   | | |",
					"| +-- | | |",
					"|     |   |",
					"+-----+---+" };

/**
* Draw the maze state in the screen!
*/
void draw ()
{
		int i, j;
		for (i = 0; i < H; i++)
		{
				for (j = 0; j < W; j++)
								printf ("%c", maze[i][j]);
				printf ("\n");
		}
		printf ("\n");
}


/**
* The main function
*/
int
main (int argc, char *argv[])
{
		int x, y;     //Player position
		int ox, oy;   //Old player position
		int i = 0;    //Iteration number
	#define ITERS 28
	char program[ITERS];

//Initial position
		x = 1;
		y = 1;
	maze[y][x]='X';

//Print some info
	printf ("Maze dimensions: %dx%d\n", W, H);
	printf ("Player pos: %dx%d\n", x, y);
	printf ("Iteration no. %d\n",i);
	printf ("Program the player moves with a sequence of 'w', 's', 'a' and 'd'\n");
	printf ("Try to reach the price(#)!\n");

//Draw the maze
	draw ();    
//Read the directions 'program' to execute...
	read(0,program,ITERS);

//Iterate and run 'program'
		while(i < ITERS)
		{
		//Save old player position
				ox = x;
				oy = y;
		//Move polayer position depending on the actual command
				switch (program[i])
					{
					case 'w':
							y--;
							break;
					case 's':
							y++;
							break;
					case 'a':
							x--;
							break;
					case 'd':
							x++;
							break;
					default:
						printf("Wrong command!(only w,s,a,d accepted!)\n");
						printf("You loose!\n");
						exit(-1);
					}

		//If hit the price, You Win!!            
				if (maze[y][x] == '#')
					{
							printf ("You win!\n");
							printf ("Your solution <%42s>\n",program);
							exit (1);
					}
		//If something is wrong do not advance
				if (maze[y][x] != ' '
					&&
					!((y == 2 && maze[y][x] == '|' && x > 0 && x < W)))
					{
							x = ox;
							y = oy;
					}
		
		//Print new maze state and info...
		printf ("Player pos: %dx%d\n", x, y);
		printf ("Iteration no. %d. Action: %c. %s\n",i,program[i], ((ox==x && oy==y)?"Blocked!":""));
		
		//If crashed to a wall! Exit, you loose
		if (ox==x && oy==y){
					printf("You loose\n");
				exit(-2);
		}
		//put the player on the maze...
		maze[y][x]='X';
		//draw it
				draw ();
		//increment iteration
				i++;
				//me wait to human
				sleep(1);
		}
//You couldn't make it! You loose!       
printf("You loose\n");
}

程序是很简单的,就是给一串输入,然后依次判断,最终给出win或者loose的输出。 可以通过观察看到一个解ssssddddwwaawwddddssssddwwww,当然也可以用回溯算法让程序找解。 这里我们主要看看用KLEE如何找解。

KLEE求解

KLEE的作用主要是将输入符号化,所以,首先首先将read调用改成klee_make_symbolic,这样就符号化 了program变量,当然需要包含头文件#include <klee\/klee.h>。

//read(0,program,ITERS);
klee_make_symbolic(program,ITERS,"program");

这样之后KLEE就会找出所有的路径,但是这样是不够的,因为我们只对win的路径感兴趣, 所以需要由一个flag来表示。我们可以在

printf ("You win!\n");

这个语句之后增加一个

klee_assert(0);

这样只要找到一个成功的路径,就会触发一个assert。开始执行。

$ clang -I ../klee/include -emit-llvm -c maze.c
$ klee maze.bc 
...
KLEE: done: total instructions = 127519
KLEE: done: completed paths = 309
KLEE: done: generated tests = 306
test@ubuntu:~/kleestudy$ ls klee-last/*.err
klee-last/test000135.assert.err
test@ubuntu:~/kleestudy$ ktest-tool klee-last/test000135.ktest
ktest file : 'klee-last/test000135.ktest'
args       : ['maze.bc']
num objects: 1
object    0: name: 'program'
object    0: size: 28
object    0: data: 'sddwddddsddwssssssssssssssss'

我们看到输出一个解,

sddwddddsddwssssssssssssssss

直接用这个待入之前的第二节的程序,可以看到是正确的。这里我们注意到KLEE这里输出的解跟我们肉眼 的解是不一样的。确实是这样,大多数情况下,KLEE只会输出一个错误状态的路径。要输出所有这个 路径的所有输入,需要使用-emit-all-errors:

$ klee -emit-all-errors maze.bc
test@ubuntu:~/kleestudy$ ls klee-last/*.err
klee-last/test000139.assert.err  klee-last/test000238.assert.err
klee-last/test000220.assert.err  klee-last/test000301.assert.err

我们看到输出了四个解。其实从运行的时候可以看出,y==2的时候穿墙了,代码里面也看得出来。



blog comments powered by Disqus