PICASSO: a PI-CAlculus-based Static SOftware analyzer

Manual

Dependencies

To compile and run Picasso from the sources you will need sbt 0.12 (or a more recent version).

When proving termination of depth-bounded processes Picasso uses the additional tools: ARMC, Princess, Z3

Overview

Picasso is a tool for reachability analysis of mobile processes. From a technical perspective Picasso focuses on depth-bounded processes (a subset of the π-calculus) and the problem it tries to solve of the control-state reachability (or covering) problem. The core algorithm is a saturation algorithm based on forward exploration of the state-space with widening.

Picasso has currently one main frontend that works on graph rewriting systems. Additional frontends are for a simple actor language (with boolean variables and names as data) and for the Scala language (as a compiler plug-in).

How to run the graph rewriting system version

Picasso can directly reads graph rewriting systems. This version offers a greater degree of flexibility but can quite painful to write by hand. When using Picasso from the SVN repository the runGraph.sh script run Picasso with for graph rewriting systems. The script takes as argument one input file, and computes the cover of the graph rewriting system contained in the file. It is possible to get more details of the computation using the -v option (twice for debug output). The result can be saved in an html report using the -r option.

BNF grammar of the graph rewriting systems

A node consists of an identifier and a label. The identifier is unique to the node. On the other hand many nodes can have the same label. In case the label is not known, it is possible to have a special wildcard label (denoted _). Furthermore, it is possible to specify the multiplicity (nesting depth) of a node using *.

node ::= (ident, label)
| (ident, _ )
| node *

An edge is given by a pair of nodes. Optionally, the edge can be labelled.

edge ::= node -> node
| node -> node [ label ]

A graph is given by a sequence of edges. In case a node is not connected, it can be declared by prefixing it with "node".

graph ::= edge   graph
| node node   graph
|

A mapping is a dictionary between nodes identifiers. It is given by a sequence of pairs.

mapping ::= ident -> ident   mapping
|

A transition transform a subgraph (pre) into another one (post). The pre is matched within a larger graph (subgraph) and then replaced by the post. To preserve the connections with the rest of the graph during the replacement, the nodes in pre and post need to be connected by mappings. The forward mapping (==>) maps non-wildcard nodes from pre into non-wildcard nodes of post. The backward mapping (<==) maps wildcard nodes of post to wildcard nodes of pre. It is also possible to specify an inhibitory pattern that prevent the transition from firing.

transition ::= transition stringLit   pre graph   post graph   ==> mapping   <== mapping
| transition stringLit   pre graph   post graph   ==> mapping   <== mapping   no graph

A system is given by an initial state (a graph), a sequence of transitions, and, optionally, a target state.

initial ::= init graph
transitions ::= transition   transitions
|
target ::= target graph
system ::= init transitions target
| init transitions

Graph rewriting system example

Here is a simple client-server example. The process consists of one single server thread, an environment thread, and an unbounded number of client threads. In each loop iteration of a client, the client non-deterministically chooses to either wait for a response from the server, or to send a new request to the server. Requests are sent asynchronously and carry both the address of the server and the client. In each iteration of the server loop, the server waits for incoming requests and then asynchronously sends a response back to the client using the client's address received in the request. The environment thread models the fact that new clients can enter the system at anytime. In each iteration of the environment thread, it spawns a new client thread. The initial state of the system consists only of the server and the environment thread.

init
  (e, env) -> (s, server) [S]

transition "new client" 
pre
  (e, env) -> (s, server) [S]
post
  (e, env) -> (s, server) [S]
  (c, client) -> (s, server) [S]
==>
  e -> e
  s -> s
<==

transition "request" 
pre
  (c, client) -> (s, server) [S]
post
  (c, client) -> (s, server) [S]
  (m, msg) ->  (s, server) [S]
  (m, msg) ->  (c, client) [C]
==>
  s -> s
  c -> c
<==

transition "reply" 
pre
  (m, msg) -> (s, server) [S]
  (m, msg) -> (c, _) [C]
post
  (r, reply) -> (c, _) [C]
  node (s, server)
==>
  s -> s
<==
  c -> c

transition "receive reply" 
pre
  (r, reply) -> (c, _) [C]
post
  node (c, _)
==>
<==
  c -> c
Assuming that this file is named example.dbp, it is possible to run the example with the command:
# java -jar picasso-core.jar example.dbp

How to run the basic version

The so-called basic input language is a layer of syntactic sugar on top of the π-calculus. The goal is to have an human readable language that looks like some of the simple/early actor languages.

EBNF grammar of the basic frontend

TODO short intro

A literal is either true, false, or a string literal.

A pattern is either a wild card, a literal, an identifier, or a constructor (algebraic data type).

pattern ::= "_"
| literal
| ident   [ "(" pattern {"," pattern} ")"]

An expression is either a wild card (for undefined values), a literal, an identifier, or a constructor (algebraic data type).

expression ::= "_"
| literal
| ident   [ "(" expression {"," expression} ")"]

A process is a sequence of statements.

process ::= "begin" [ process {";" process } ] "end"
| "var" ident ":=" expression
| "val" ident ":=" expression
| ident ":=" expression
| expression "!" expression
| expression
| "new" ident "(" [ expression {"," expression} ] ")"
| "if" expression "then" process [ "else" process ]
| "while" expression "do" process
| "select" { "case" expression "?" pattern "=>" process }

An actor is just a syntactic unit that binds the free parameters occurring in a process.

actor ::= ident "(" [ ident { "," } ident ] ")" process

The initial configuration of the system is a list of actors. Identifiers in the arguments of the actors are assume to be top-level bounds names.

initial ::= "initial" ident "(" [ ident { "," } ident ] ")" {";" ident "(" [ ident { "," } ident ] ")" }

Type system of the basic frontend

TODO

Example

Here is a simple ping-pong example inspired from a scala example. There are two main difference compared to the scala version. First, we do not yet support integers. Therefore, we have replaced the counter by non-deterministic choice. Second, in scala each actor has its own channel. In out language the notions of channels (names) and processes are decoupled like in the π-calculus. Thus, the receive operations has to explicitly specify what it is receiving from.

Ping(self, pong)
  while true do
    select
      case self ? Start() =>
        pong ! Ping(self)
      case self ? SendPing() =>
        pong ! Ping(self)
      case self ? Pong() =>
        if random() then
          self ! SendPing()
        else
          begin
            pong ! Stop();
            exit()
          end

Pong(self)
  while true do
    select
      case self ? Ping(sender) => sender ! Pong()
      case self ? Stop() => exit()

Main()
  begin
    val ping := newChannel();
    val pong := newChannel();
    new Ping(ping, pong);
    new Pong(pong);
    ping ! Start()
  end

initial
  Main()
Assuming you are using Picasso from a copy of the SVN repository, you can possible to run the example with the command:
# ./runBasic.sh frontend/basic/src/test/resources/basic/scala-ping-pong.basic

How to run the scala compiler plug-in

TODO

IST Austria