include common.mk
-SCFENCE_DIR := scfence
+SPEC_DIR := spec-analysis
OBJECTS := libthreads.o schedule.o model.o threads.o librace.o action.o \
nodestack.o clockvector.o main.o snapshot-interface.o cyclegraph.o \
snapshot.o malloc.o mymemory.o common.o mutex.o promise.o conditionvariable.o \
context.o scanalysis.o execution.o plugins.o libannotate.o
-CPPFLAGS += -Iinclude -I. -I$(SCFENCE_DIR)
+include $(SPEC_DIR)/Makefile
+
+CPPFLAGS += -Iinclude -I. -I$(SPEC_DIR)
LDFLAGS := -ldl -lrt -rdynamic
SHARED := -shared
README.html: README.md
$(MARKDOWN) $< > $@
+$(LIB_SO): $(OBJECTS)
+ $(CXX) $(SHARED) -o $(LIB_SO) $+ $(LDFLAGS)
malloc.o: malloc.c
- $(CC) -fPIC -c malloc.c -DMSPACES -DONLY_MSPACES -DHAVE_MMAP=0 $(CPPFLAGS) -Wno-unused-variable
-
-%.o : %.cc
- $(CXX) -MMD -MF .$@.d -fPIC -c $< $(CPPFLAGS)
-
-include $(SCFENCE_DIR)/Makefile
+ $(CC) -fPIC -c malloc.c -DMSPACES -DONLY_MSPACES -DHAVE_MMAP=0 $(CPPLAGS) -Wno-unused-variable
--include $(wildcard $(SCFENCE_DIR)/.*.d)
-
-$(LIB_SO): $(OBJECTS)
- $(CXX) $(SHARED) -o $(LIB_SO) $+ $(LDFLAGS)
+%.o: %.cc
+ $(CXX) -MMD -MF $(dir $@).$(notdir $@).d -fPIC -c $< -o $@ $(CPPFLAGS)
%.pdf: %.dot
dot -Tpdf $< -o $@
--include $(OBJECTS:%=.%.d)
+# Replace all the basename with
+ALL_DEPS := $(shell echo $(OBJECTS) | sed -E 's/([^ ]*\/)?([^\/ ]*)/\1\.\2.d/g')
+-include $(ALL_DEPS)
+#-include $(OBJECTS:%=.%.d)
+
PHONY += clean
clean:
- rm -f *.o *.so .*.d *.pdf *.dot $(SCFENCE_DIR)/.*.d $(SCFENCE_DIR)/*.o
+ rm -f *.o *.so $(ALL_DEPS) *.pdf *.dot $(OBJECTS)
$(MAKE) -C $(TESTS_DIR) clean
PHONY += mrclean
#include "common.h"
#include "threads-model.h"
#include "nodestack.h"
-#include "wildcard.h"
#define ACTION_INITIAL_CLOCK 0
uint64_t value, Thread *thread) :
type(type),
order(order),
- original_order(order),
location(loc),
value(value),
reads_from(NULL),
case ATOMIC_WAIT: return "wait";
case ATOMIC_NOTIFY_ONE: return "notify one";
case ATOMIC_NOTIFY_ALL: return "notify all";
- case ATOMIC_ANNOTATION: return "annotation";
+ case ATOMIC_ANNOTATION: return "atomic annotation";
default: return "unknown type";
};
}
thread_id_t get_tid() const { return tid; }
action_type get_type() const { return type; }
memory_order get_mo() const { return order; }
- memory_order get_original_mo() const { return original_order; }
- void set_mo(memory_order order) { this->order = order; }
void * get_location() const { return location; }
modelclock_t get_seq_number() const { return seq_number; }
uint64_t get_value() const { return value; }
/** @brief The memory order for this operation. */
memory_order order;
- /** @brief The original memory order parameter for this operation. */
- memory_order original_order;
-
/** @brief A pointer to the memory location for this action. */
void *location;
--- /dev/null
+<project name="SpecCheckerCompiler" default="compile" basedir=".">
+ <!-- Compile variables -->
+ <property name="src" location="src"/>
+ <property name="build" location="classes"/>
+ <property name="lib" location="lib/javacc.jar"/>
+
+ <target name="init">
+ <!-- Create the build directory -->
+ <mkdir dir="${build}"/>
+ </target>
+
+ <target name="compile" depends="init"
+ description="compile the source">
+ <!-- Compile the code from ${src} into ${build} -->
+ <javac srcdir="${src}" destdir="${build}" classpath="${lib}"
+ includeantruntime="false" />
+ </target>
+
+ <target name="clean"
+ description="clean up">
+ <!-- Delete the ${build} directory trees -->
+ <delete dir="${build}"/>
+ </target>
+</project>
--- /dev/null
+#!/bin/bash
+
+AutoGenDir=$(pwd)/src/edu/uci/eecs/utilParser
+echo "Deleting the old generated java files."
+rm -rf $AutoGenDir
+
+echo "Deleting the class files."
+rm -rf ./classes
--- /dev/null
+#!/bin/bash
+
+BENCH=(ms-queue linuxrwlocks mcs-lock \
+ chase-lev-deque-bugfix spsc-bugfix mpmc-queue ticket-lock \
+ concurrent-hashmap seqlock read-copy-update)
+
+ClassPath=$(dirname ${BASH_SOURCE[0]})/classes
+
+Class=edu/uci/eecs/codeGenerator/CodeGenerator
+
+# Use your own directory.
+# We recommend the original benchmarks and generated instrumented benchmarks to
+# be within the model checker's directory.
+BenchDir=../benchmarks
+GenerateDir=../test-cdsspec
+
+mkdir -p $GenerateDir
+
+java -cp $ClassPath $Class $BenchDir $GenerateDir ${BENCH[*]}
--- /dev/null
+/* util.jj Grammer definition for utility functions */
+
+options {
+ STATIC = false;
+ JAVA_UNICODE_ESCAPE = true;
+}
+
+PARSER_BEGIN(UtilParser)
+package edu.uci.eecs.utilParser;
+import edu.uci.eecs.specExtraction.FunctionHeader;
+import edu.uci.eecs.specExtraction.QualifiedName;
+import edu.uci.eecs.specExtraction.VariableDeclaration;
+//import edu.uci.eecs.specExtraction.WrongAnnotationException;
+
+import java.io.FileInputStream;
+import java.io.FileNotFoundException;
+import java.io.InputStream;
+import java.io.ByteArrayInputStream;
+import java.io.File;
+import java.util.ArrayList;
+
+
+public class UtilParser {
+ public static void main(String[] argvs)
+ throws ParseException, TokenMgrError {
+ try {
+ File f = new File("./grammer/spec1.txt");
+ FileInputStream fis = new FileInputStream(f);
+ UtilParser parser = new UtilParser(fis);
+
+ //parser.Test();
+ System.out.println("Parsing finished!");
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ }
+
+ public static ArrayList<VariableDeclaration> getTemplateArg(String line)
+ throws ParseException {
+ InputStream input = new ByteArrayInputStream(line.getBytes());
+ UtilParser parser = new UtilParser(input);
+ return parser.TemplateParamList();
+ }
+
+ public static FunctionHeader parseFuncHeader(String line)
+ throws ParseException {
+ InputStream input = new ByteArrayInputStream(line.getBytes());
+ UtilParser parser = new UtilParser(input);
+ return parser.FuncDecl();
+ }
+
+ public static VariableDeclaration parseDeclaration(String line)
+ throws ParseException {
+ InputStream input = new ByteArrayInputStream(line.getBytes());
+ UtilParser parser = new UtilParser(input);
+ return parser.Declaration();
+ }
+
+ public static String stringArray2String(ArrayList<String> content) {
+ StringBuilder sb = new StringBuilder();
+ if (content.size() == 1)
+ return content.get(0);
+ for (int i = 0; i < content.size(); i++) {
+ sb.append(content.get(i) + "\n");
+ }
+ return sb.toString();
+ }
+}
+PARSER_END(UtilParser)
+
+SKIP :
+{
+ " "
+|
+ "\n"
+|
+ "\r"
+|
+ "\r\n"
+|
+ "\t"
+}
+
+TOKEN :
+{
+/* Specification & C/C++ shared tokens */
+// Reserved keywords
+ <CONST: "const">
+|
+ <STRUCT: "struct">
+|
+ <CLASS: "class">
+|
+ <UNSIGNED: "unsigned">
+|
+ <TEMPLATE: "template">
+|
+ <INLINE: "inline">
+|
+ <STATIC: "static">
+|
+ <FOR: "for">
+|
+ <#DIGIT: ["0"-"9"]>
+|
+ <#LETTER: ["a"-"z", "A"-"Z"]>
+|
+ <IDENTIFIER: (<LETTER> | "_") (<LETTER> | <DIGIT> | "_")*>
+|
+ <POUND: "#">
+|
+ <OPEN_BRACKET: "[">
+|
+ <CLOSE_BRACKET: "]">
+|
+ <EQUALS: "=">
+|
+ <OPEN_PAREN: "(">
+|
+ <CLOSE_PAREN: ")">
+|
+ <OPEN_BRACE: "{">
+|
+ <CLOSE_BRACE: "}">
+|
+ <HB_SYMBOL: "->">
+|
+ <COMMA: ",">
+|
+/* C/C++ only token*/
+ <DOT: ".">
+|
+ <DOLLAR: "$">
+|
+ <STAR: "*">
+|
+ <NEGATE: "~">
+|
+ <EXCLAMATION: "!">
+|
+ <AND: "&">
+|
+ <OR: "|">
+|
+ <MOD: "%">
+|
+ <PLUS: "+">
+|
+ <PLUSPLUS: "++">
+|
+ <MINUS: "-">
+|
+ <MINUSMINUS: "--">
+|
+ <DIVIDE: "/">
+|
+ <BACKSLASH: "\\">
+|
+ <LESS_THAN: "<">
+|
+ <GREATER_THAN: ">">
+|
+ <GREATER_EQUALS: ">=">
+|
+ <LESS_EQUALS: "<=">
+|
+ <LOGICAL_EQUALS: "==">
+|
+ <NOT_EQUALS: "!=">
+|
+ <LOGICAL_AND: "&&">
+|
+ <LOGICAL_OR: "||">
+|
+ <XOR: "^">
+|
+ <QUESTION_MARK: "?">
+|
+ <COLON: ":">
+|
+ <DOUBLECOLON: "::">
+|
+ <DOUBLELESSTHAN: "<<">
+|
+ <DOUBLEGREATERTHAN: ">>">
+|
+ <TRIPLEGREATERTHAN: ">>>">
+|
+ <PLUS_EQUALS: "+=">
+|
+ <MINUS_EQUALS: "-=">
+|
+ <TIMES_EQUALS: "*=">
+|
+ <DIVIDE_EQUALS: "/=">
+|
+ <MOD_EQUALS: "%=">
+|
+ <XOR_EQUALS: "^=">
+|
+ <OR_EQUALS: "|=">
+|
+ <AND_EQUALS: "&=">
+|
+ <SEMI_COLON: ";">
+|
+ <STRING_LITERAL:
+ "\""
+ ((~["\"","\\","\n","\r"])
+ | ("\\"
+ ( ["n","t","b","r","f","\\","'","\""]
+ | ["0"-"7"] ( ["0"-"7"] )?
+ | ["0"-"3"] ["0"-"7"]
+ ["0"-"7"]
+ )
+ )
+ )*
+ "\"">
+|
+ <CHARACTER_LITERAL:
+ "'"
+ ((~["'","\\","\n","\r"])
+ | ("\\"
+ (["n","t","b","r","f","\\","'","\""]
+ | ["0"-"7"] ( ["0"-"7"] )?
+ | ["0"-"3"] ["0"-"7"]
+ ["0"-"7"]
+ )
+ )
+ )
+ "'">
+|
+ < INTEGER_LITERAL:
+ <DECIMAL_LITERAL> (["l","L"])?
+ | <HEX_LITERAL> (["l","L"])?
+ | <OCTAL_LITERAL> (["l","L"])?>
+|
+ < #DECIMAL_LITERAL: ["1"-"9"] (["0"-"9"])* >
+|
+ < #HEX_LITERAL: "0" ["x","X"] (["0"-"9","a"-"f","A"-"F"])+ >
+|
+ < #OCTAL_LITERAL: "0" (["0"-"7"])* >
+|
+ < FLOATING_POINT_LITERAL:
+ <DECIMAL_FLOATING_POINT_LITERAL>
+ | <HEXADECIMAL_FLOATING_POINT_LITERAL> >
+|
+ < #DECIMAL_FLOATING_POINT_LITERAL:
+ (["0"-"9"])+ "." (["0"-"9"])* (<DECIMAL_EXPONENT>)? (["f","F","d","D"])?
+ | "." (["0"-"9"])+ (<DECIMAL_EXPONENT>)? (["f","F","d","D"])?
+ | (["0"-"9"])+ <DECIMAL_EXPONENT> (["f","F","d","D"])?
+ | (["0"-"9"])+ (<DECIMAL_EXPONENT>)? ["f","F","d","D"]>
+|
+ < #DECIMAL_EXPONENT: ["e","E"] (["+","-"])? (["0"-"9"])+ >
+|
+ < #HEXADECIMAL_FLOATING_POINT_LITERAL:
+ "0" ["x", "X"] (["0"-"9","a"-"f","A"-"F"])+ (".")? <HEXADECIMAL_EXPONENT> (["f","F","d","D"])?
+ | "0" ["x", "X"] (["0"-"9","a"-"f","A"-"F"])* "." (["0"-"9","a"-"f","A"-"F"])+ <HEXADECIMAL_EXPONENT> (["f","F","d","D"])?>
+|
+ < #HEXADECIMAL_EXPONENT: ["p","P"] (["+","-"])? (["0"-"9"])+ >
+|
+ < #SPACE: (" " | "\t")+>
+|
+ < #TO_END_OF_LINE: (~["\n"])+>
+|
+ /* Macro token */
+ <INCLUDE: "#" (<SPACE>)? "include" <SPACE> (<STRING_LITERAL> | "<" (<LETTER> | <DOT>)+ ">")>
+|
+ <DEFINE: "#" (<SPACE>)? <TO_END_OF_LINE>>
+}
+
+String Type() :
+{
+ String type;
+ String str;
+ QualifiedName name;
+}
+{
+ { type = ""; }
+ (<CONST>
+ { type = "const"; }
+ )?
+ (
+ ((str = <STRUCT>.image | str = <CLASS>.image | str = <UNSIGNED>.image) {
+ type = type.equals("") ? type + str : type + " " + str;
+ })?
+ (
+ name = ParseQualifiedName() {
+ if (!type.equals(""))
+ type = type + " " + name.fullName;
+ else
+ type = name.fullName;
+ })
+ )
+ ((str = <CONST>.image {
+ if (!type.equals(""))
+ type = type + " " + str;
+ else
+ type = str;
+ }) |
+ (str = <STAR>.image {
+ if (!type.equals(""))
+ type = type + " " + str;
+ else
+ type = str;
+ }) |
+ (str = <AND>.image {
+ if (!type.equals(""))
+ type = type + " " + str;
+ else
+ type = str;
+ })
+ )*
+ {
+ return type;
+ }
+}
+
+void Test() :
+{
+ String str;
+ FunctionHeader func;
+}
+{
+ /*
+ str = Type()
+ {
+ System.out.println(str);
+ }
+ */
+ func = FuncDecl()
+ {
+ System.out.println(func);
+ }
+
+}
+
+String ParameterizedName() :
+{
+ String res = "";
+ String str;
+}
+{
+ (str = <IDENTIFIER>.image {res = str;})
+ (<LESS_THAN> str = Type() { res = res + "<" + str; }
+ (<COMMA> str = Type() { res = res + ", " + str; })* <GREATER_THAN>
+ { res = res + ">"; }
+ )?
+ {
+ return res;
+ }
+}
+
+FunctionHeader FuncDecl() :
+{
+ String ret;
+ QualifiedName funcName;
+ ArrayList<VariableDeclaration> args;
+}
+{
+ (<STATIC> | <INLINE>)*
+ ret = Type()
+ funcName = ParseQualifiedName()
+ args = FormalParamList()
+ {
+ FunctionHeader res = new FunctionHeader(ret, funcName, args);
+ //System.out.println(res);
+ return res;
+ }
+}
+
+QualifiedName ParseQualifiedName() :
+{
+ String qualifiedName, str;
+}
+{
+ { qualifiedName = ""; }
+ (str = ParameterizedName() { qualifiedName = qualifiedName + str; } )
+ ( <DOUBLECOLON> (str = ParameterizedName() { qualifiedName = qualifiedName +
+ "::" + str; } ))*
+ {
+ QualifiedName res = new QualifiedName(qualifiedName);
+ //System.out.println(res);
+ return res;
+ }
+}
+
+ArrayList<VariableDeclaration> TemplateParamList() :
+{
+ ArrayList<VariableDeclaration> params;
+ String type;
+ String name;
+}
+{
+ {
+ params = new ArrayList<VariableDeclaration>();
+ }
+ <TEMPLATE>
+ <LESS_THAN>
+ (type = <IDENTIFIER>.image
+ name = <IDENTIFIER>.image
+ {
+ params.add(new VariableDeclaration(type, name));
+ }
+ )
+
+ (<COMMA> type = <IDENTIFIER>.image
+ name = <IDENTIFIER>.image
+ {
+ params.add(new VariableDeclaration(type, name));
+ }
+ )*
+ <GREATER_THAN>
+ {
+ //System.out.println(params);
+ return params;
+ }
+}
+
+ArrayList<VariableDeclaration > FormalParamList() :
+{
+ ArrayList<VariableDeclaration > typeParams;
+ VariableDeclaration varDecl;
+}
+{
+ {
+ typeParams = new ArrayList<VariableDeclaration >();
+ }
+ <OPEN_PAREN>
+ ((varDecl = TypeParam() {typeParams.add(varDecl);})
+ ((<COMMA> varDecl = TypeParam() {typeParams.add(varDecl);}))*)?
+ <CLOSE_PAREN>
+ {
+ return typeParams;
+ }
+}
+
+VariableDeclaration Declaration() :
+{
+ String type, param;
+}
+{
+ (type = Type()) (param = <IDENTIFIER>.image) <SEMI_COLON>
+ {
+ return new VariableDeclaration(type, param);
+ }
+}
+
+VariableDeclaration TypeParam() :
+{
+ String type, param;
+}
+{
+ (type = Type()) (param = <IDENTIFIER>.image)
+ {
+ return new VariableDeclaration(type, param);
+ }
+}
--- /dev/null
+#include <iostream>
+#include <vector>
+#include <list>
+#include <string>
+#include <iterator>
+#include <algorithm>
+#include <set>
+
+#include <functional>
+
+#include <stdarg.h>
+
+using namespace std;
+
+struct MethodCall;
+
+typedef MethodCall *Method;
+typedef set<Method> *MethodSet;
+
+struct MethodCall {
+ string interfaceName; // The interface label name
+ void *value; // The pointer that points to the struct that have the return
+ // value and the arguments
+ void *state; // The pointer that points to the struct that represents
+ // the state
+ MethodSet prev; // Method calls that are hb right before me
+ MethodSet next; // Method calls that are hb right after me
+ MethodSet concurrent; // Method calls that are concurrent with me
+
+ MethodCall(string name) : MethodCall() { interfaceName = name; }
+
+ MethodCall() : interfaceName(""), prev(new set<Method>),
+ next(new set<Method>), concurrent(new set<Method>) { }
+
+ void addPrev(Method m) { prev->insert(m); }
+
+ void addNext(Method m) { next->insert(m); }
+
+ void addConcurrent(Method m) { concurrent->insert(m); }
+
+};
+
+
+typedef vector<int> IntVector;
+typedef list<int> IntList;
+typedef set<int> IntSet;
+
+typedef vector<double> DoubleVector;
+typedef list<double> DoubleList;
+typedef set<double> DoubleSet;
+
+/********** More general specification-related types and operations **********/
+
+#define NewMethodSet new set<Method>
+
+#define CAT(a, b) CAT_HELPER(a, b) /* Concatenate two symbols for macros! */
+#define CAT_HELPER(a, b) a ## b
+#define X(name) CAT(__##name, __LINE__) /* unique variable */
+
+/**
+ This is a generic ForEach primitive for all the containers that support
+ using iterator to iterate.
+*/
+#define ForEach(item, container) \
+ auto X(_container) = (container); \
+ auto X(iter) = X(_container)->begin(); \
+ for (auto item = *X(iter); X(iter) != X(_container)->end(); item = ((++X(iter)) != \
+ X(_container)->end()) ? *X(iter) : 0)
+
+/**
+ This is a common macro that is used as a constant for the name of specific
+ variables. We basically have two usage scenario:
+ 1. In Subset operation, we allow users to specify a condition to extract a
+ subset. In that condition expression, we provide NAME, RET(type), ARG(type,
+ field) and STATE(field) to access each item's (method call) information.
+ 2. In general specification (in pre- & post- conditions and side effects),
+ we would automatically generate an assignment that assign the current
+ MethodCall* pointer to a variable namedd _M. With this, when we access the
+ state of the current method, we use STATE(field) (this is a reference
+ for read/write).
+*/
+#define ITEM _M
+#define _M ME
+
+#define NAME Name(_M)
+
+#define STATE(field) State(_M, field)
+
+#define VALUE(type, field) Value(_M, type, field)
+#define RET(type) VALUE(type, RET)
+#define ARG(type, arg) VALUE(type, arg)
+
+/*
+#define Subset(s, subset, condition) \
+ MethodSet original = s; \
+ MethodSet subset = NewMethodSet; \
+ ForEach (_M, original) { \
+ if ((condition)) \
+ subset->insert(_M); \
+ } \
+*/
+
+
+/**
+ This operation is specifically for Method set. For example, when we want to
+ construct an integer set from the state field "x" (which is an
+ integer) of the previous set of method calls (PREV), and the new set goes to
+ "readSet", we would call "MakeSet(int, PREV, readSet, STATE(x));". Then users
+ can use the "readSet" as an integer set (set<int>)
+*/
+#define MakeSet(type, oldset, newset, field) \
+ auto newset = new set<type>; \
+ ForEach (_M, oldset) \
+ newset->insert(field); \
+
+/**
+ We provide a more general subset operation that takes a plain boolean
+ expression on each item (access by the name "ITEM") of the set, and put it
+ into a new set if the boolean expression (Guard) on that item holds. This
+ is used as the second arguments of the Subset operation. An example to
+ extract a subset of positive elements on an IntSet "s" would be "Subset(s,
+ GeneralGuard(int, ITEM > 0))"
+*/
+#define GeneralGuard(type, expression) std::function<bool(type)> ( \
+ [&](type ITEM) -> bool { return (expression);}) \
+
+/**
+ This is a more specific guard designed for the Method (MethodCall*). It
+ basically wrap around the GeneralGuard with the type Method. An example to
+ extract the subset of method calls in the PREV set whose state "x" is equal
+ to "val" would be "Subset(PREV, Guard(STATE(x) == val))"
+*/
+#define Guard(expression) GeneralGuard(Method, expression)
+
+/**
+ A general subset operation that takes a condition and returns all the item
+ for which the boolean guard holds.
+*/
+inline MethodSet Subset(MethodSet original, std::function<bool(Method)> condition) {
+ MethodSet res = new SnapSet<Method>;
+ ForEach (_M, original) {
+ if (condition(_M))
+ res->insert(_M);
+ }
+ return res;
+}
+
+/**
+ A general set operation that takes a condition and returns if there exists
+ any item for which the boolean guard holds.
+*/
+template <class T>
+inline bool HasItem(set<T> *original, std::function<bool(T)> condition) {
+ ForEach (_M, original) {
+ if (condition(_M))
+ return true;
+ }
+ return false;
+}
+
+
+
+/**
+ A general sublist operation that takes a condition and returns all the item
+ for which the boolean guard holds in the same order as in the old list.
+*/
+template <class T>
+inline list<T>* Sublist(list<T> *original, std::function<bool(T)> condition) {
+ list<T> *res = new list<T>;
+ ForEach (_M, original) {
+ if (condition(_M))
+ res->push_back(_M);
+ }
+ return res;
+}
+
+/**
+ A general subvector operation that takes a condition and returns all the item
+ for which the boolean guard holds in the same order as in the old vector.
+*/
+template <class T>
+inline vector<T>* Subvector(vector<T> *original, std::function<bool(T)> condition) {
+ vector<T> *res = new vector<T>;
+ ForEach (_M, original) {
+ if (condition(_M))
+ res->push_back(_M);
+ }
+ return res;
+}
+
+/** General for set, list & vector */
+#define Size(container) ((container)->size())
+
+#define _BelongHelper(type) \
+ template<class T> \
+ inline bool Belong(type<T> *container, T item) { \
+ return std::find(container->begin(), \
+ container->end(), item) != container->end(); \
+ }
+
+_BelongHelper(set)
+_BelongHelper(vector)
+_BelongHelper(list)
+
+/** General set operations */
+template<class T>
+inline set<T>* Intersect(set<T> *set1, set<T> *set2) {
+ set<T> *res = new set<T>;
+ ForEach (item, set1) {
+ if (Belong(set2, item))
+ res->insert(item);
+ }
+ return res;
+}
+
+template<class T>
+inline set<T>* Union(set<T> *s1, set<T> *s2) {
+ set<T> *res = new set<T>(*s1);
+ ForEach (item, s2)
+ res->insert(item);
+ return res;
+}
+
+template<class T>
+inline set<T>* Subtract(set<T> *set1, set<T> *set2) {
+ set<T> *res = new set<T>;
+ ForEach (item, set1) {
+ if (!Belong(set2, item))
+ res->insert(item);
+ }
+ return res;
+}
+
+template<class T>
+inline void Insert(set<T> *s, T item) { s->insert(item); }
+
+template<class T>
+inline void Insert(set<T> *s, set<T> *others) {
+ ForEach (item, others)
+ s->insert(item);
+}
+
+/*
+inline MethodSet MakeSet(int count, ...) {
+ va_list ap;
+ MethodSet res;
+
+ va_start (ap, count);
+ res = NewMethodSet;
+ for (int i = 0; i < count; i++) {
+ Method item = va_arg (ap, Method);
+ res->insert(item);
+ }
+ va_end (ap);
+ return res;
+}
+*/
+
+/********** Method call related operations **********/
+#define Name(method) method->interfaceName
+
+#define State(method, field) ((StateStruct*) method->state)->field
+
+#define Value(method, type, field) ((type*) method->value)->field
+#define Ret(method, type) Value(method, type, RET)
+#define Arg(method, type, arg) Value(method, type, arg)
+
+#define Prev(method) method->prev
+#define PREV _M->prev
+
+#define Next(method) method->next
+#define NEXT _M->next
+
+#define Concurrent(method) method->concurrent
+#define CONCURRENT _M->concurrent
+
+
+/***************************************************************************/
+/***************************************************************************/
+
+// This auto-generated struct can have different fields according to the read
+// state declaration. Here it's just a test example
+typedef struct StateStruct {
+ int x;
+} StateStruct;
+
+// These auto-generated struct can have different fields according to the return
+// value and arguments of the corresponding interface. The struct will have the
+// same name as the interface name. Here it's just a test example
+typedef struct Store {
+ int *loc;
+ int val;
+} Store;
+
+typedef struct Load {
+ int RET;
+ int *loc;
+} Load;
+
+int main() {
+ set<int> *is1 = new set<int>;
+ set<int> *is2 = new set<int>;
+
+ list<int> *il1 = new list<int>;
+ list<int> *il2 = new list<int>;
+
+ il1->push_back(2);
+ il1->push_back(3);
+
+ is1->insert(1);
+ is1->insert(3);
+
+ is2->insert(4);
+ is2->insert(5);
+
+
+ MethodSet ms = NewMethodSet;
+ Method m = new MethodCall;
+ m->interfaceName = "Store";
+ StateStruct *ss = new StateStruct;
+ ss->x = 1;
+ m->state = ss;
+ Store *st = new Store;
+ st->val = 2;
+ m->value = st;
+ ms->insert(m);
+
+ m = new MethodCall;
+ m->interfaceName= "Store";
+ ss = new StateStruct;
+ ss->x = 2;
+ m->state = ss;
+ st = new Store;
+ st->val = 0;
+ m->value = st;
+ ms->insert(m);
+
+ m = new MethodCall;
+ m->interfaceName= "Load";
+ ss = new StateStruct;
+ ss->x = 0;
+ m->state = ss;
+ Load *ld = new Load;
+ ld->RET = 2;
+ m->value = ld;
+ ms->insert(m);
+
+ //MakeSet(int, ms, newis, STATE(x));
+ //cout << "Size=" << Size(newis) << " | val= " << Belong(newis, 2) << endl;
+
+ int x = 2;
+ int y = 2;
+ cout << "HasItem=" << HasItem(ms, Guard(STATE(x) == x && y == 0)) << endl;
+
+ //ForEach (i, newis) {
+ //cout << "elem: " << i << endl;
+ //}
+
+
+ //Subset(ms, sub, NAME == "Store" && VALUE(Store, val) != 0);
+ //cout << "Size=" << Size(Subset(ms, Guard(NAME == "Store" && ARG(Store, val)
+ //>= 0 && STATE(x) >= 0 ))) << endl;
+ return 0;
+}
--- /dev/null
+****** Example1: ******
+Global Variable Declaration
+
+/* Include all the header files that contains the interface declaration */
+#inlcude <atomic>
+#include <memory>
+#include <assert.h>
+
+/* Other necessary header files */
+#include <stdint.h>
+#include <specannotation.h>
+#include <spec_lib.h>
+
+/* All other user-defined functions */
+ALL_USER_DEFINED_FUNCTIONS // Make them static
+
+/* Definition of interface info struct (per interface) */
+typedef struct Put_info {
+ shared_ptr<TypeV> __RET__;
+ TypeK & key;
+ TypeV & value;
+} Put_info;
+
+typedef struct Get_info {
+ shared_ptr<TypeV> __RET__;
+ TypeK & key;
+} Get_info;
+/* End of info struct definition */
+
+/* ID functions of interface */
+static id_t Put_id() {
+ id_t id = PUT_ID; // Default ID == 0;
+ return id;
+}
+
+static id_t Get_id() {
+ id_t id = GET_ID;
+ return id;
+}
+/* End of ID functions */
+
+
+
+/* Check_action function of interfaces */
+bool Put_check_action(void *info, id_t __ID__) {
+ bool check_passed;
+ Put_info *theInfo = (Put_info) info;
+ shared_ptr<TypeV> __RET__ = theInfo->__RET__;
+ TypeK & key = theInfo->key;
+ TypeV & value = theInfo->value;
+
+ // __COND_SAT__
+ bool __COND_SAT__ = PUT_CONDITION;
+
+ // Check
+ check_passed = PUT_CHECK_EXPRESSION;
+ if (!check_passed)
+ return false;
+
+ // Action
+ PUT_ACTION
+
+ // Post_check
+ check_passed = PUT_POST_CHECK_EXPRESSION;
+ if (!check_passed)
+ return false;
+
+ // Post_action
+ PUT_POST_ACTION
+}
+
+
+bool Get_check_action(void *info, id_t __ID__) {
+ //...
+}
+/* End of check_action function definitions */
+
+/* Initialization of interface<->function_ptr table */
+#define INTERFACE_SIZE 2
+void** func_ptr_table;
+
+/* Beginning of other user-defined variables */
+bool lock_acquired;
+int reader_lock_cnt;
+/* End of other user-defined variables */
+
+
+/* Define function for sequential code initialization */
+void __sequential_init() {
+ /* Init func_ptr_table */
+ func_ptr_table = (void**) malloc(sizeof(void*) * 2);
+ func_ptr_table[0] = (void*) &Put_id;
+ func_ptr_table[1] = (void*) &Put_check_action;
+ func_ptr_table[2] = (void*) &Get_id;
+ func_ptr_table[3] = (void*) &Get_check_action;
+
+ /* Init user-defined variables */
+ lock_acquired = false;
+ reader_lock_cnt = 0;
+
+ /* Pass function table info */
+ anno_func_table_init func_table;
+ func_table.size = INTERFACE_SIZE;
+ func_table.table = func_ptr_table;
+ spec_annotation func_init;
+ func_init.type = FUNC_TABLE_INIT;
+ func_init.annotation = &anno_func_table_init;
+ cdsannotate(SPEC_ANALYSIS, &func_init);
+
+ /* Pass the happens-before initialization here */
+ /* PutIfAbsent (putIfAbsent_Succ) -> Get (true) */
+ anno_hb_init hbConditionInit0;
+ hbConditionInit0.interface_num_before = 1;
+ hbConditionInit0.hb_condition_num_before = 1;
+ hbConditionInit0.interface_num_after = 2;
+ hbConditionInit0.hb_condition_num_after = 0;
+ spec_annotation hb_init0;
+ hb_init0.type = HB_INIT;
+ hb_init0.annotation = &hbConditionInit0;
+ cdsannotate(SPEC_ANALYSIS, &hb_init0);
+}
+/* End of Global construct generation in class */
+
+/* The following static field declaration is necessary for class */
+template <typename T>
+bool CLASS<T>::lock_acquired;
+
+template <typename T>
+int CLASS<T>::reader_lock_cnt;
+/* End of static field definition */
+
+
+****** Example2: ******
+Interface Wrapper
+
+/* Include the header files first */
+#include <threads.h>
+#include <cdsannotate.h>
+#include <specannotation.h>
+#include <spec_lib.h>
+
+TypeReturn interfaceName(ARGType1 arg1, ARGType2 arg2)
+{
+ // Interface begins
+ anno_interface_begin interface_begin;
+ interface_begin.interface_num = 0; // Interface number
+ spec_annotation annotation_interface_begin;
+ annotation_interface_begin.type = INTERFACE_BEGIN;
+ annotation_interface_begin.annotation = &interface_begin;
+ cdsannotate(SPEC_ANALYSIS, &annoation_interface_begin);
+
+ TypeReturn __RET__ = __wrapper_interfaceName(arg1, arg2);
+
+ // HB conditions (if any)
+ if (HB_CONDITION1) {
+ anno_hb_condition hb_condition1;
+ hb_condition1.interface_num = 0; // Interface number
+ hb_condition1.hb_condition_num = 1; // HB condition number
+ spec_annotation annotation_hb_condition;
+ annotation_hb_condition.type = HB_CONDITION;
+ annotation_hb_condition.annotation = &hb_condition;
+ cdsannotate(SPEC_ANALYSIS, &annotation_hb_condition);
+ }
+ // And more (if any)
+
+ // Interface ends
+ INTERFACE_LABEL_info info = (INTERFACE_LABEL_info*) malloc(sizeof(INTERFACE_LABEL_info));
+ info->__RET__ = __RET__;
+ info->arg1 = arg1;
+ info->arg2 = arg2;
+ anno_interface_end interface_end;
+ interface_end.interface_num = 0; // Interface number
+ interface_end.info = info; // Info
+ spec_annotation annotation_interface_end;
+ annotation_interface_end.type = INTERFACE_END;
+ annotation_interface_end.annotation = &interface_end;
+ cdsannotate(SPEC_ANALYSIS, &annoation_interface_end);
+}
+
+
+****** Example3: ******
+Potential Commit Point
+
+
+#include <stdint.h>
+#include <cdsannotate.h>
+
+/* Should add the __ATOMIC_RET__ if necessary */
+uint64_t __ATOMIC_RET = somevar.compare_exchange_explicit(...);
+
+if (POTENTIAL_CP_DEFINE_CONDITION) {
+ anno_potential_cp_define potential_cp_define;
+ potential_cp_define.label_num = 1; // Commit point label number
+ spec_annotation annotation_potential_cp_define;
+ annotation_potential_cp_define.type = POTENTIAL_CP_DEFINE;
+ annotation_potential_cp_define.annotation = &potential_cp_define;
+ cdsannotate(SPEC_ANALYSIS, &annotation_potential_cp_define);
+}
+
+****** Example4: ******
+Commit Point Define
+
+#include <threads.h>
+#include <cdsannotate.h>
+#include <stdint.h>
+
+/* Should add the __ATOMIC_RET__ if necessary */
+uint64_t __ATOMIC_RET = somevar.compare_exchange_explicit(...);
+
+/* For all the interface check at this commit point (if there is multiple
+ * checks here) */
+// Commit point 3 <=> potentialCP 4
+if (COMMIT_POINT3_CONDITION) {
+ anno_cp_define cp_define;
+ cp_define.label_num = 3;
+ cp_define.potential_cp_label_num = 1;
+ spec_annotation annotation_cp_define;
+ annotation_cp_define.type = CP_DEFINE;
+ annotation_cp_define.annotation = &cp_define;
+ cdsannotate(SPEC_ANALYSIS, &annotation_cp_define);
+}
+// More if there are any
+
+}
+
+
+***** Example5: ******
+Commit Point Define Check
+
+
+#include <threads.h>
+#include <cdsannotate.h>
+#include <stdint.h>
+
+/* Should add the __ATOMIC_RET__ if necessary */
+uint64_t __ATOMIC_RET = somevar.compare_exchange_explicit(...);
+
+
+/* For all the interface check at this commit point (if there is multiple
+ * checks here) */
+if (COMMIT_POINT3_CONDITION) {
+ anno_cp_define cp_define;
+ uint64_t call_sequence_num = get(&__sequential.interface_call_sequence, tid);
+ bool check_passed = false;
+ cp_define.check_passed = check_passed;
+ cp_define.interface_num = interface_num;
+ cp_define.label_num = 3;
+ cp_define.call_sequence_num = call_sequence_num;
+ spec_annotation annotation_cp_define;
+ annotation_cp_define.type = CP_DEFINE;
+ annotation_cp_define.annotation = &cp_define;
+ cdsannotate(SPEC_ANALYSIS, &annotation_cp_define);
+}
+// More if there are any
+
+}
--- /dev/null
+1. Template support
+ The global specification should be expanded right after the specification
+ definition (the golbal construct). The global variables and functions will be
+ the private members of the data structure. In this case, the template of the
+ data structure can be automatically passed to the variables and functions.
+ Besides, as the public member of the function, all the interface check can
+ access the specification variables and functions just as normal.
+
+2. On-the-fly spec check
+ For a check and action, they will be expanded right at the place where they
+ are declared and will get executed when the inserted annotation gets executed.
+ And the annotation is just a way to pass if the checks are satisfied. However,
+ it is wrong because the annotation is also a type of atomic operation. For
+ example, for a potential commit point and a later commit point check, they
+ might be interleaved by another commit point. Therefore, we need to check in
+ the specanalysis to see if the specific interleaving of annotation is "legal".
+
+3. Happens-before initialization
+ This concerns the place where we pass to the specanalysis the happens-before
+ rules????
+
+4. To make implementation easier and the spec cleaner, we make a difference C
+ and C++ programs. Since C does not support template and class, we require
+ users to provide an entry point where we can initialize the sequential
+ variables. By default, it's a C++ program, and everything is wrapped in an
+ inner class because it can have easy initialization and template support.
+
+5. We add @Interface_define construct in the specification. Basically,
+ @Interface construct can be used in both interface declaration and interface
+ definition, if they are not separated. However, if they are, then @Interface
+ is used for the interface declaration and @Interface_define is for the
+ interface definition. This is redundant information, but it makes the
+ implementation much easier because we don't need to parse the C/C++ program.
+
+6. Checking at runtime or check with complete trace analysis can have the
+ follwing concerns. Checking at runtime, HB might not be established yet (we
+ can leave it at trace analysis though). More importantly, we have potential
+ commit point and commit point check, which might be preempted by another
+ commit point. We can't decide whether to execute the commit point actions or
+ not since we can't decide if it's really a commit point at that time. Checking
+ with complete execution can be more clear and better designed, but it has a
+ tough challenge. Some data structure may check their predicate with pointers,
+ however, we can't guarantee those pointers are still valid (the object they
+ are pointing to may be changed or even deleted). To tackle this, we provide an
+ alternative which requires users to define the snapshot function for the
+ pointers if they are about to check with pointers that might change. In the
+ current data structure, it's not a problem because they only return reference
+ or pointers, which we can do simple equality check. We decided to take the
+ trace analysis approach because it's still more flexible and easier to
+ implement.
--- /dev/null
+Modification to the current specifications.
+
+I. Order
+-- Sequential order (SO): Some total order that is consistent with the union of
+happens-before and SC relation.
+
+II. State
+Previously, we keep a sequential state throughout the process of executing the
+sequential history. In our model, we keep a state local to each method call.
+Conceptually, this state is the accumulative side effects of the subset of
+method calls that happen before the current method call.
+
+To evaluate the state of each method call, an obvious approach is to
+execute the subset of methods that happen before the current method in the
+sequential order from the initial state. A optimization we can make is that we
+start to evaluate the state from the most recent deviding node which every other
+node in that subset is either hb before or after.
+
+III. Specifications
+Our specification language supports using the following primitives to access the
+state of method calls so that users can use those to write specifications with
+different level of tightness.
+
+To support tighter specifications, we introduce the concept of concurrent set of
+method calls, meaning that for a specific method call, it can basically see the
+effect of two different categories of method calls --- one that happens before
+it, and one that concurrently execute with it. It is worth noting that when two
+two method calls execute concurrently, in general there can be the following two
+effects: 1) those concurrent methods can happen in either order, and the final
+result remains the same. A concurrent FIFO is an example, in which concurrent
+enqueue and dequeue methods can happen in a random order; and 2) the order of
+those concurrent methods will affect the final result. The C/C++11 atomics is an
+example, in which when concurrent stores to the same location execute in
+different order, a later store will have different result.
+
+1. CONCURRENT: This primitive extracts all the methods that executes
+"concurrently" with the current method --- neither hb/SC before nor after the
+current method --- and returns as a set. It is worth noting that in order to
+preserve composability, when evaluating the state, the concurrent method calls'
+information cannot be used. That is to say, the concurrent set of mehtods are
+only used for assertions.
+
+2. PREV: This primitive extracts all the methods that execute right before the
+current method in the execution graph --- most recent method calls that are
+hb/SC before the current method call --- and returns as a set. For each method
+in this set, the current method's specification can access their state.
+
+3. NEXT: This primitive extracts all the methods that execute right after the
+current method in the execution graph, and returns as a set. For each method in
+this set, the current method's specification CANNOT access their state (for
+preserving composability).
+
+// FIXME: This might break composability!!??!!
+4. FINAL: This is the function that allows users to specify some final check
+on the state. This will enable to users to use the graph model (the relaxed
+atomics can be specified) although the complxity of using that can get quite
+complex.
+
+Our specifications allow two ways of evaluating the state of method calls. One
+way is to define "@Transition" on method calls, and then our checker executes
+the method calls that are hb/SC before me in the sequential order starting from
+the initial state. The other way is to define "@EvaluateState" after
+"@Transition", which can only access the states of method calls that are hb/SC
+before it. Usually users calculate the state by using the PREV primitive to
+access the state of previous method calls.
+
+IV. Specification Details
+
+/// Global specification
+@State: // Declare the state structure
+@Initial: // How do we initialize the state
+@Commutativity: Method1 <-> Method2 (Guard) // Guard can only access the return
+ // value and the arguments of the two method calls
+
+/// Interface specification
+@Interface: InterfaceName // Required; a label to represent the interface
+@Transition: // Optional; the transitional side effect from the initial state to
+ // the current method call by executing such effect on method calls
+ // that are hb/SC before me in sequential order. When this field is
+ // omitted, the current state seen before checking is the same as
+ // the initial state.
+@EvaluateState: // Optional; to calculate the state before this
+ // method call is checked in a customized fashion. This is
+ // evaluated after "@Transition". If omitted, the state we would
+ // see is the effect after the "@Transition".
+@PreCondition: // Optional; checking code
+@SideEffect: // Optional; to calculate the side effect this method call
+ // have on the state after the "@PreCondition".
+@PostCondition: // Optional; checking code
+
+
+/// Convienient operations
+We define a struct called MethodCall to represent the data we would collect and
+communicate between the real execution and the checking process.
+
+
+STATE(field) // A primitive to access the current state in the interface
+ // specification by the name of the field. We can use this as a
+ // lvalue & rvalue ==> "STATE(x) = 1" and "int i = STATE(x)" are
+ // both okay
+
+ // This can also be used to refer to the state of a method item
+ // in the Subset operation (see Subset)
+
+NAME // Used to refer to the name of a method item in the Subset
+ // operation (see Subset)
+
+RET(type) // Used to refer to the return value of a method item in the
+ // Subset operation (see Subset)
+
+ARG(type, arg) // Used to refer to the argument of a method item in the
+ // Subset operation (see Subset)
+
+
+PREV // Represent the set of previous method calls of the current method
+ // call
+
+CONCURRENT // Represent the set of concurrent method calls of the current
+ // method call
+
+NEXT // Represent the set of next method calls of the current method
+ // call
+
+Name(method) // The interface label name of the specific method
+
+State(method, field) // A primitive to access a specific state field of a
+ // specific method. Also used as lvalue and rvalue
+
+Ret(method, type) // The return value of the specific method; type should
+ // be the name of the interface label
+
+Arg(method, type, arg) // The arguement by name of the specific method; type should
+ // be the name of the interface label, and arg should be
+ // the name of the argument
+
+Prev(method) // Represent the set of previous method calls of the
+ // specific method call
+
+Next(method) // Represent the set of next method calls of the specific
+ // method call
+
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+ForEach(item, container) { ... } // Useful iteration primitive
+
+NewMethodSet // Create a new method set (set<MethodCall*>*)
+
+MakeSet(type, oldset, newset, field); // Construct a new set from an
+ // old set. We extract a specific field of that set item
+ // and form a new set. Specifically we expect users to
+ // use this on MethodSet. For example, if we want to
+ // construct an integer set from the state "x" of
+ // the previous methods, we use "MakeSet(int, PREV,
+ // newset, STATE(x))", and the newset contains the new
+ // set
+
+Subset(set, condition) // A generic subset operation that takes a condition and
+ // returns all the item for which the boolean guard
+ // holds. The condition can be specified with GUARD or
+ // GeneralGuard shown as follow.
+
+HasItem(set, condition) // A generic set operation that takes a condition and
+ // returns if there exists any item in "set" for which
+ // the condition holds. Its syntax is similar to that of
+ // Subset() operation
+
+Size(container) // Return the size of a container type
+
+Belong(container, item) // Return if item is in the container
+
+Union(set1, set2) // Union of two sets
+
+Intesection(set1, set2) // Intersection of two sets
+
+Subtract(set1, set2) // Returns the new set that represents the result of
+ // set1-set2
+Insert(set, item) // Insert item to set
+
+Insert(set, others) // Insert the whole set "others" to "set"
+
+ITEM // Used to refer to the set item in the GeneralGuard shown below
+
+GeneralGuard(type, expression) // Used as a condition for any set<T> type. We
+ // use "ITEM" to refer to a set item. For
+ // example, a subset of positive elements on an
+ // IntSet "s" would be
+ // "Subset(s, GeneralGuard(int, ITEM > 0))"
+
+Guard(expression) // Used specifically for MethodSet(set<MethodCall*>). An
+ // example to extract the subset of method calls in the PREV
+ // set that is a "Store" method and whose state "x" is equal
+ // to "val" and the return value is 5 would be
+ // "Subset(PREV, Guard(STATE(x) == val && NAME == "Store" &&
+ // RET(Store) == 5))"
+
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+
+To make things easier, we have the following helper macros.
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+/**
+ This is a common macro that is used as a constant for the name of specific
+ variables. We basically have two usage scenario:
+ 1. In Subset operation, we allow users to specify a condition to extract a
+ subset. In that condition expression, we provide NAME, RET(type), ARG(type,
+ field) and STATE(field) to access each item's (method call) information.
+ 2. In general specification (in pre- & post- conditions and side effects),
+ we would automatically generate an assignment that assign the current
+ MethodCall* pointer to a variable namedd _M. With this, when we access the
+ state of the current method, we use STATE(field) (this is a reference
+ for read/write).
+*/
+#define ITEM _M
+#define _M ME
+
+#define CAT(a, b) CAT_HELPER(a, b) /* Concatenate two symbols for macros! */
+#define CAT_HELPER(a, b) a ## b
+#define X(name) CAT(__##name, __LINE__) /* unique variable */
+
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
+
+*****************************************************************************
+typedef struct MethodCall {
+ string name; // The interface label name
+ void *value; // The pointer that points to the struct that have the return
+ // value and the arguments
+ void *state; // The pointer that points to the struct that represents
+ // the state
+ set<MethodCall*> *prev; // Method calls that are hb right before me
+ set<MethodCall*> *concurrent; // Method calls that are concurrent with me
+ set<MethodCall*> *next; // Method calls that are hb right after me
+} MethodCall;
+
+typedef MethodCall *Method;
+typedef set<Method> *MethodSet;
+
+typedef vector<int> IntVector;
+typedef list<int> IntList;
+typedef set<int> IntSet;
+*****************************************************************************
+
+We will automatically generate two types of struct. One is the state struct, and
+we will call it StateStruct. The other one is a per interface struct, and it
+wraps the return value (RET) and the arguments as its field. We will name those
+struct with the interface label name.
+
+-----------------------------------------------------------------------------
+For example, if we declare an ordered integer list in the specification state ,
+we will generate a struct as follow.
+
+@State: IntList *queue;
+@Initial: queue = new IntList;
+@...
+
+===>
+typedef struct StateStruct {
+ IntList *queue;
+} StateStruct;
+
+-----------------------------------------------------------------------------
+If we have two interface whose specifications are as follow, we would generate
+two struct accordingly.
+
+@Interface: Store
+@...
+void store(atomic_int *loc, int val) ...
+
+@Interface: Load
+@...
+int load(atomic_int *loc) ...
+
+===>
+typedef struct Store {
+ atomic_int *loc;
+ int val;
+} Store;
+
+typedef struct Load {
+ int RET;
+ atomic_int *loc;
+} Load;
+
+-----------------------------------------------------------------------------
+We will put all these generated struct in a automatically-generated header file
+called "generated.h". This file also includes header files that have commonly
+used data types that interface (return value and arguments) accesses. In order
+to make things work, users should include "generated.h" file in the end for
+using our specification checker.
+
+*****************************************************************************
+
+
+/// Ordering point specification
+@OPDefine: condition // If the specified condition satisfied, the atomic
+ // operation right before is an ordering point
+
+@PotentialOP(Label): condition // If the specified condition satisfied, the
+ // atomic operation right before is a potential
+ // ordering point, and we label it with a tag
+
+@OPCheck(Label): condition // If the specified condition satisfied, the
+ // potential ordering point defined earlier with the
+ // same tag becomes an ordering point
+
+@OPClear: condition // If the specified condition satisfied, all the
+ // ordering points and potential ordering points will be
+ // cleared
+
+@OPClearDefine: condition // If the specified condition satisfied, all the
+ // ordering points and potential ordering points will
+ // be cleared, and the atomic operation right before
+ // becomes an ordering point. This is a syntax sugar
+ // as the combination of an "OPClear" and "OPDefine"
+ // statement
+
+
+V. Examples
+
+!!!!!!! The register example should be extended to commute if we think of their
+transitional effects as set operations --- a set operation that will only mask
+out side the effects of its own previous behavior (things that are hb/SC before
+it) ---- VERY IMPORTANT note here!!
+
+
+1. The register examples: Basically, we can think of registers as the cache on a
+memory system. The effect of a load or store basically tells us what the current
+value in the cache line is, and a load can read from values that can be
+potentially in the cache --- either one of the concurrent store update the cache
+or it inherites one of the the previous state in the execution graph.
+
+---------- Interfae ----------
+void init(atomic_int &loc, int initial);
+int load(atomic_int &loc);
+void store(atomic_int &loc, int val);
+---------- Interfae ----------
+
+a. The SC atomics --- the classic linearizability approach
+
+b. The RA (release/acquire) C/C++ atomics
+// For RA atomics, a load must read its value from a store that happens before
+// it.
+---------- Specification ----------
+@State: int x;
+@Initial: x = 0;
+@Commutativity: Store <-> Store(true)
+@Commutativity: Load <-> Load(true)
+@Commutativity: Store <-> Load(true)
+
+/** No @Transition */
+@Interface: Store
+@SideEffect: STATE(x) = val;
+void store(int *loc, int val);
+
+@Interface: Load
+@PreCondition:
+ return HasItem(PREV, STATE(x) == RET);
+@SideEffect: STATE(x) = RET;
+int load(int *loc);
+
+c. The C/C++ atomics (a slightly loose specification)
+// Actually, any concurrent data structures that rely modification-order to be
+// correct would not have a precicely tight specification under our model, and
+// C/C++ relaxed atomics is an example. See the following read-read coherence
+// example.
+
+// T1 // T2
+x = 1; x = 2;
+
+// T3
+r1 = x; // r1 == 1
+r2 = x; // r2 == 2
+r3 = x; // r3 == 1
+
+Our model cannot prevent such a case from happening. However, we can still have
+a slightly loose specification which basically states that a load can read from
+any store that either immediately happens before it or concurrently executes.
+
+
+---------- Specification ----------
+@State: int x;
+@Initial: x = 0;
+
+@Interface: Store
+@SideEffect: STATE(x) = val;
+void store(int *loc, int val);
+
+
+@Interface: Load
+@PreCondition:
+ // Auto generated code
+ // MethodCall *ME = ((SomeTypeConversion) info)->method;
+
+ return HasItem(Prev, STATE(x) == RET) ||
+ + HasItem(CONCURRENT, NAME == "Store" && ARG(Store, val) == RET)
+@SideEffect: STATE(x) = RET;
+int load(int *loc);
+
+d. The C/C++ normal memory accesses
+- Use the admissibility requirement, then the classic linearizability approach
+on the admissible executions
+
+2. The FIFO queue example.
+---------- Specification ----------
+// A FIFO queue should have the following properties held:
+// 1. The enq() methods should conflict
+// 2. The deq() methods that succeed should conflict
+// 3. Corresponding enq() and deq() methods should conflict
+// 4. An enqueued item can be dequeued by at most once
+// 5. A dequeued item must have a corresponding enqueued item
+// 6. When a queue is NOT "empty" (users can tightly or loosely define
+// emptiness), and there comes a deq() method, the deq() method should succeed
+
+
+@DeclareVar: vector<int> *q;
+@InitVar: q = new voctor<int>;
+@Copy: New.q = new vector<int>(Old.q);
+// Fails to dequeue
+@Commutativity: Deq <-> Deq (!_M1.RET || !_M2.RET)
+@Commutativity: Enq <-> Deq (true)
+
+@Interface: Enq
+@Transition: q->push_back(val);
+void enq(queue_t *q, int val);
+
+@Interface: Deq
+@Transition: if (RET) q->pop_back();
+@PreCondition:
+ // Check whether the queue is really empty
+ // Either the state is an empty queue, or for all those remaining
+ // elements in the queue, there should be some concurrent dequeuers to
+ // dequeue them
+ if (!RET) {
+ // State is empty
+ if (STATE(q)->size() == 0) return true;
+ // Otherwise check there must be other concurrent dequeuers
+ ForEach (item, State(q)) {
+ // Check there's some concurrent dequeuer for this item
+ if (!HasItem(CONCURRENT, NAME == "Deq" && RET(Deq) &&
+ *ARG(Deq, res) == item)) return false;
+ }
+ return true;
+ } else { // Check the global queue state
+ return q->back() == *res;
+ }
+bool deq(queue_t *q, int *res);
+
+
+*******************************************************************************
+A good example to simulate a queue data structure is as follows.
+Suppose we have a special structure
+typedef struct Q {
+ atomic_int x;
+ atomic_int y;
+} Q;
+
+, and we have two interface on Q, read() and write(), where the write and read
+method calls are synchronized by themselves, and they have to read and write the
+x and y fields in turn.
--- /dev/null
+include ../benchmarks.mk
+
+TESTNAME = register
+all: $(TESTNAME)
+
+CFLAGS := $(CFLAGS) --std=c++11
+
+$(TESTNAME): $(TESTNAME).cc $(TESTNAME).h
+ $(CXX) -o $@ $< $(CFLAGS) $(LDFLAGS)
+
+clean:
+ rm -f $(TESTNAME) *.o
--- /dev/null
+#include <cdsannotate.h>
+#include <modeltypes.h>
+#include <atomic>
+#include <threads.h>
+#include <stdatomic.h>
+
+#include "mymemory.h"
+#include "specannotation.h"
+#include "cdsspec.h"
+#include "methodcall.h"
+
+using namespace std;
+
+typedef struct StateStruct {
+ int val;
+
+ SNAPSHOTALLOC
+} StateStruct;
+
+typedef struct LOAD {
+ int RET;
+ atomic_int *loc;
+} LOAD;
+
+typedef struct STORE {
+ atomic_int *loc;
+ int val;
+} STORE;
+
+inline void _initial(Method m) {
+ StateStruct *state = new StateStruct;
+ state->val = 0;
+ m->state = state;
+}
+
+inline void _copyState(Method dest, Method src) {
+ StateStruct *stateSrc = (StateStruct*) src;
+ StateStruct *stateDest = (StateStruct*) dest;
+ stateDest->val = stateSrc->val;
+}
+
+inline bool _checkCommutativity1(Method m1, Method m2) {
+ if (m1->interfaceName == "LOAD" && m2->interfaceName == "LOAD") {
+ if (true)
+ return true;
+ else
+ return false;
+ }
+ return false;
+}
+
+inline bool _checkCommutativity2(Method m1, Method m2) {
+ if (m1->interfaceName == "STORE" && m2->interfaceName == "LOAD") {
+ if (true)
+ return true;
+ else
+ return false;
+ }
+ return false;
+}
+
+inline bool _checkCommutativity3(Method m1, Method m2) {
+ if (m1->interfaceName == "STORE" && m2->interfaceName == "STORE") {
+ if (true)
+ return true;
+ else
+ return false;
+ }
+ return false;
+}
+
+inline void _LOAD_Transition(Method _M, Method _exec) {
+ return;
+}
+
+inline bool _LOAD_PreCondition(Method _M) {
+ int RET = ((LOAD*) _M->value)->RET;
+ atomic_int *loc = ((LOAD*) _M->value)->loc;
+ return HasItem(PREV, Guard(STATE(val) == RET && loc));
+}
+
+inline void _LOAD_SideEffect(Method _M) {
+ int RET = ((LOAD*) _M->value)->RET;
+ atomic_int *loc = ((LOAD*) _M->value)->loc;
+ STATE(val) = RET;
+}
+
+inline void _STORE_Transition(Method _M, Method _exec) {
+ return;
+}
+
+inline void _STORE_SideEffect(Method _M) {
+ atomic_int *loc = ((STORE*) _M->value)->loc;
+ int val = ((STORE*) _M->value)->val;
+ STATE(val) = val;
+}
+
+inline void _createInitAnnotation() {
+ int CommuteRuleSize = 3;
+ void *raw = CommutativityRule::operator new[](sizeof(CommutativityRule) * CommuteRuleSize);
+ CommutativityRule *commuteRules = static_cast<CommutativityRule*>(raw);
+
+ // Initialize commutativity rule 1
+ new( &commuteRules[0] )CommutativityRule("LOAD", "LOAD", "LOAD <-> LOAD (true)", _checkCommutativity1);
+ // Initialize commutativity rule 2
+ new( &commuteRules[1] )CommutativityRule("Store", "LOAD", "STORE <-> LOAD (true)", _checkCommutativity2);
+ // Initialize commutativity rule 3
+ new( &commuteRules[2] )CommutativityRule("STORE", "STORE", "STORE <-> STORE (true)", _checkCommutativity3);
+
+ // Initialize AnnoInit
+ AnnoInit *anno = new AnnoInit(_initial, _copyState, commuteRules, CommuteRuleSize);
+
+ // Initialize StateFunctions map
+ StateFunctions *stateFuncs;
+
+ // StateFunction for LOAD
+ stateFuncs = new StateFunctions("LOAD", _LOAD_Transition, NULL, _LOAD_PreCondition, _LOAD_SideEffect, NULL);
+ anno->addInterfaceFunctions("LOAD", stateFuncs);
+ // StateFunction for STORE
+ stateFuncs = new StateFunctions("STORE", _STORE_Transition, NULL, NULL, _STORE_SideEffect, NULL);
+ anno->addInterfaceFunctions("STORE", stateFuncs);
+
+ // Create and instrument with the INIT annotation
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(INIT, anno));
+}
+
+inline Method _createInterfaceBeginAnnotation(string name) {
+ Method cur = new MethodCall(name);
+ // Create and instrument with the INTERFACE_BEGIN annotation
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(INTERFACE_BEGIN, cur));
+ return cur;
+}
+
+inline void _createOPDefineAnnotation() {
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(OP_DEFINE, NULL));
+}
+
+inline void _createPotentialOPAnnotation(string label) {
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(POTENTIAL_OP, new AnnoPotentialOP(label)));
+}
+
+inline void _createOPCheckAnnotation(string label) {
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(OP_CHECK, new AnnoOPCheck(label)));
+}
+
+inline void _createOPClearAnnotation() {
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(OP_CLEAR, NULL));
+}
+
+inline void _createOPClearDefineAnnotation() {
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(OP_CLEAR_DEFINE, NULL));
+}
--- /dev/null
+#include <stdio.h>
+#include <threads.h>
+#include <stdatomic.h>
+
+#include "librace.h"
+#include "register.h"
+
+/** @DeclareState: int val;
+ @Initial: val = 0;
+ @Commutativity: LOAD <-> LOAD (true)
+ @Commutativity: STORE <-> LOAD (true)
+ @Commutativity: STORE <-> STORE (true)
+*/
+
+/** @Interface: LOAD
+ @PreCondition:
+ return HasItem(PREV, STATE(val) == RET);
+ @SideEffect: STATE(val) = RET;
+
+*/
+int Load_WRAPPER__(atomic_int *loc) {
+ return loc->load(memory_order_acquire);
+}
+
+int Load(atomic_int *loc) {
+ // Instrument with the INTERFACE_BEGIN annotation
+ Method cur = _createInterfaceBeginAnnotation("LOAD");
+ // Call the actual function
+ int RET = Load_WRAPPER__(loc);
+
+ // Initialize the value struct
+ LOAD *value = new LOAD;
+ value->RET = RET;
+ value->loc = loc;
+
+ // Store the value info into the current MethodCall
+ cur->value = value;
+
+ // Return if there exists a return value
+ return RET;
+}
+
+/** @Interface: STORE
+ @SideEffect: STATE(val) = val;
+
+*/
+void Store_WRAPPER__(atomic_int *loc, int val) {
+ loc->store(val, memory_order_release);
+}
+
+void Store(atomic_int *loc, int val) {
+ // Instrument with the INTERFACE_BEGIN annotation
+ Method cur = _createInterfaceBeginAnnotation("STORE");
+ // Call the actual function
+ Store_WRAPPER__(loc, val);
+
+ // Initialize the value struct
+ STORE *value = new STORE;
+ value->loc = loc;
+ value->val = val;
+
+ // Store the value info into the current MethodCall
+ cur->value = value;
+}
+
+static void a(void *obj)
+{
+ Store(&x, 1);
+ Store(&x, 2);
+}
+
+static void b(void *obj)
+{
+ Store(&x, 3);
+}
+
+static void c(void *obj)
+{
+ int r1 = Load(&x);
+ int r2 = Load(&x);
+}
+
+int user_main(int argc, char **argv)
+{
+ thrd_t t1, t2, t3;
+
+ /** @Entry */
+ _createInitAnnotation();
+
+ thrd_create(&t1, (thrd_start_t)&a, NULL);
+ thrd_create(&t2, (thrd_start_t)&b, NULL);
+ thrd_create(&t3, (thrd_start_t)&c, NULL);
+
+ thrd_join(t1);
+ thrd_join(t2);
+ thrd_join(t3);
+
+ return 0;
+}
--- /dev/null
+#include <stdio.h>
+#include <threads.h>
+#include <stdatomic.h>
+
+#include "librace.h"
+#include "register.h"
+
+/** @DeclareState: int val;
+ @Initial: val = 0;
+ @Commutativity: LOAD <-> LOAD (true)
+ @Commutativity: STORE <-> LOAD (true)
+ @Commutativity: STORE <-> STORE (true)
+*/
+
+/** @Interface: LOAD
+ @PreCondition:
+ return HasItem(PREV, STATE(val) == RET);
+ @SideEffect: STATE(val) = RET;
+
+*/
+int Load(atomic_int *loc) {
+ return loc->load(memory_order_acquire);
+}
+
+/** @Interface: STORE
+ @SideEffect: STATE(val) = val;
+
+*/
+void Store(atomic_int *loc, int val) {
+ loc->store(val, memory_order_release);
+}
+
+static void a(void *obj)
+{
+ Store(&x, 1);
+ Store(&x, 2);
+}
+
+static void b(void *obj)
+{
+ Store(&x, 3);
+}
+
+static void c(void *obj)
+{
+ int r1 = Load(&x);
+ int r2 = Load(&x);
+}
+
+int user_main(int argc, char **argv)
+{
+ thrd_t t1, t2, t3;
+
+ /** @Entry */
+
+ thrd_create(&t1, (thrd_start_t)&a, NULL);
+ thrd_create(&t2, (thrd_start_t)&b, NULL);
+ thrd_create(&t3, (thrd_start_t)&c, NULL);
+
+ thrd_join(t1);
+ thrd_join(t2);
+ thrd_join(t3);
+
+ return 0;
+}
--- /dev/null
+#include <stdio.h>
+#include <stdatomic.h>
+
+#include "librace.h"
+#include "cdsspec-generated.h"
+
+atomic_int x;
+
+int Load(atomic_int *loc);
+
+void Store(atomic_int *loc, int val);
--- /dev/null
+/**
+ This file contains the design thoughts and details about how we use set
+ theory and more formalized way to design a simple specification language to
+ abstractly describe a sequential data structure.
+*/
+
+1. Design goals:
+ We should design a specification language that users (developers of data
+ structures) can use to describe the specification of sequential data
+ structures. The language should be as mathematical and precise as possible,
+ while it provides enough expressiveness and usability for users. That's to
+ say, users can learn the language easily and take advantage of it to describe
+ as many data structures as possible, e.g, Lock, Queue, Dequeue, LinkedList,
+ Stack, Vector, Hashtable, and UnorderedPool(maybe).
+
+2. Common data structures:
+ Data structures, to some degree, can be viewed as a collector with specific
+ operation on it. Those operations are done according to the maintained state
+ and pre-defined logic. Set in mathematics has similar characteristics, which
+ contains a collection of elements. However, elements in a set are distint.
+ Order_list, which is a list of elements that have a total order and can
+ duplicate, can be used to complement set. For example, a queue is a container
+ that has a FIFO order on the elements while a stack with a LIFO order. For
+ hashtable, it is a set of pairs (tuples) that has restrictions on the pairs.
+
+3. Potential constructs:
+ a) Set
+ The set here is conceptually similar to the set in Set Theory. We define our
+ set as a collection of unique and unordered elements. Uniqueness here
+ implicates that the set element should provide an internal equality check.
+ The following is some basic manipulations on set:
+ 1) Tuple
+ Every element is an n-dimentional tuple, and each element of a tuple is a
+ tuple. This is basically used to represent the mapping between elements such
+ as a HashMap. Some examples:
+ a or (a) // 1-dimentional tuple, actually any variable is a 1-dimentional tuple
+ (key, value) // 2-dimentional tuple
+ (a, (a, b)) // 2-dimentional tuple, while the second column is a tuple too
+ 1-dimentional tuple is the most basic part, it should have a type, such as
+ integer, boolean, or other templated types. The type of a set can be derived
+ from the tuple too. For example, A, B and C are basic types or templated
+ types, we can have Set<(A)>, Set<(A, B)> or Set<(A, (B, C))> as different
+ types. Set<(A)> contains elements of type A, Set<(A, B)> contains tuples of
+ (A, B) and Set<(A, (B, C))> contains tuples of (A, (B, C)). We can get an
+ instance of tuple in a way like (key, value).
+ The tuple has its basic operation dimention(n), which returns the tuple of
+ its nth column.
+ 2) Union, intersection, and remove.
+ new_set = union(s1, s2); // returns a new set
+ or
+ s1.union(s2); // s1 becomes the union of s1 and s2, and it returns the new s1
+ It takes two sets and return the union of the two sets. Same for the
+ intersection and complement.
+ 3) Cartesian product
+ new_set = prod(s1, s2); // returns the cartisian product of s1 & s2
+ The result of this operation is that a new set of tuples of s1 and s2.
+ 4) Remove
+ set.remove(elem);
+ 5) Find
+ subset = set.find((key, *)); // use wildcard to find possible subset
+ b) Order_List
+ This is a list of tuples that has an order. It should have the following
+ operations:
+ 1) push_back
+ list.push_back(elem);
+ 2) push_front
+ list.push_front(elem);
+ 3) remove_back
+ elem = list.remove_back();
+ 4) remove_front
+ elem = list.remove_front();
+ 5) Find
+ elem = list.find(elem);
+ 6) IndexOf
+ index = list.indexOf(elem);
+ index = list.indexOf(elem, 1);
+ 7) PushAtIndex
+ list.pushAtIndex(1);
+ 8) PushAfterElem
+ list.pushAfterElem(target, elem, 10); // find the first matched target from index 10,
+ // insert elem after target
+ 9) RemoveIfExists
+ RemoveIfExists(elem)
+
+4. Examples:
+ 1) Hashtable
+ @Declare:
+ Set<(Key, Value)> table;
+ @Manipulation:
+ void Put((Key) key, (Value) value) {
+ table.remove(table.find((key, *))).union((key,value));
+ }
+
+ (Value) Get((Key) key) {
+ return table.find((key, *));
+ }
+
+ 2) Stack
+ @Declare:
+ Order_List<(Type)> stack;
+ @Manipulation:
+ void Push((Type) elem) {
+ stack.push_back(elem);
+ }
+
+ (Type) Pop() {
+ return stack.remove_back();
+ }
+
+ 3) LinkedList
+ // Suppose we store the pointer and they are unique??
+ @Declare:
+ Order_List<(Type)> list;
+ @Manipulation:
+ void add((Type) target, (Type) elem) {
+ assert(list.find(elem).size() == 1);
+ list.insertAfterElem(target, elem);
+ }
+
+ void remove((Type) target) {
+ list.remove(target);
+ }
+
+ 4) UnorderPool
+ // A possible data structure, basically returns an element only once
+ @Declare:
+ Order_List<(Type)> pool;
+ @Manipulation:
+ void insert((Type) elem) {
+ pool.push_back(elem);
+ }
+
+ // Check if elem is possible to be removed; if yes, remove it & return true,
+ // otherwise return false.
+ bool remove((Type) elem) {
+ return pool.removeIfExists(elem);
+ }
--- /dev/null
+#!/bin/bash
+
+SPEC_COMPILER_HOME=$(pwd)
+
+echo "CDSSpec Compiler home: " $SPEC_COMPILER_HOME
+
+JAVACC_PATH=$SPEC_COMPILER_HOME/lib
+
+UTIL_FILE=$SPEC_COMPILER_HOME/grammer/util.jj
+
+UTIL_OUTPUT_PATH=$SPEC_COMPILER_HOME/src/edu/uci/eecs/utilParser
+
+echo "Deleting the old generated java files."
+rm -rf $UTIL_OUTPUT_PATH/*
+
+mkdir -p $UTIL_OUTPUT_PATH
+
+java -cp $JAVACC_PATH/javacc.jar javacc -OUTPUT_DIRECTORY=$UTIL_OUTPUT_PATH $UTIL_FILE
--- /dev/null
+package edu.uci.eecs.codeGenerator;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.Comparator;
+
+import edu.uci.eecs.specExtraction.Code;
+
+/**
+ * <p>
+ * This class represents all the code additions that should be added to a
+ * specific file.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class CodeAdditions {
+
+ /**
+ * <p>
+ * This class represents the addition of code for a specific file. It
+ * records a list of lines to be inserted for a specific file, and the line
+ * after which the code should be inserted to the file.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+ public static class CodeAddition {
+ // The line after which the code should be inserted to the file
+ // E.g. insertingLine == 0 => insert the lines ine very beginning.
+ public final int insertingLine;
+
+ // The code to be added to the specified place
+ public final Code code;
+
+ public CodeAddition(int insertingLine, Code code) {
+ this.insertingLine = insertingLine;
+ this.code = code;
+ }
+
+ public static Comparator<CodeAddition> lineNumComparator = new Comparator<CodeAddition>() {
+ public int compare(CodeAddition addition1, CodeAddition addition2) {
+ return addition1.insertingLine - addition2.insertingLine;
+ }
+ };
+ }
+
+ // A list of code addition for the same file
+ public final ArrayList<CodeAddition> codeAdditions;
+
+ // The file that the list of additions belong to
+ public final File file;
+
+ public CodeAdditions(File file) {
+ this.file = file;
+ codeAdditions = new ArrayList<CodeAddition>();
+ }
+
+ public void addCodeAddition(CodeAddition a) {
+ this.codeAdditions.add(a);
+ }
+
+ /**
+ * <p>
+ * Whether the addition list is empty
+ * </p>
+ *
+ * @return
+ */
+ public boolean isEmpty() {
+ return this.codeAdditions.size() == 0;
+ }
+
+ /**
+ * <p>
+ * Sort the list of code additions to the same file in an increasing order
+ * by the inserting line number of the code additions. We will call this
+ * function so that we can process code addition more conveniently.
+ * </p>
+ */
+ public void sort() {
+ Collections.sort(codeAdditions, CodeAddition.lineNumComparator);
+ }
+}
--- /dev/null
+package edu.uci.eecs.codeGenerator;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+
+import edu.uci.eecs.codeGenerator.CodeAdditions.CodeAddition;
+import edu.uci.eecs.specExtraction.Code;
+import edu.uci.eecs.specExtraction.Construct;
+import edu.uci.eecs.specExtraction.DefineConstruct;
+import edu.uci.eecs.specExtraction.EntryConstruct;
+import edu.uci.eecs.specExtraction.InterfaceConstruct;
+import edu.uci.eecs.specExtraction.OPConstruct;
+import edu.uci.eecs.specExtraction.SpecExtractor;
+import edu.uci.eecs.specExtraction.SpecNaming;
+import edu.uci.eecs.specExtraction.WrongAnnotationException;
+import edu.uci.eecs.utilParser.ParseException;
+
+/**
+ * <p>
+ * This class represents the engine to generate instrumented code. To construct
+ * an object of this file, users need provide a string that represents the
+ * sub-directory under the benchmarks directory, then the engine will explore
+ * all the C/C++ files that ends with ".cc/.cpp/.c/.h" and extract specification
+ * annotations and generate instrumented code in the generated directory.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class CodeGenerator {
+ // Files that we need to process
+ private ArrayList<File> files;
+
+ // Code addition list --- per file
+ private ArrayList<CodeAdditions> allAdditions;
+ // Line change map list --- per file; Each map represents the
+ // line->InterfaceConstruct mapping that will rename the interface
+ // declaration line.
+ private ArrayList<HashMap<Integer, InterfaceConstruct>> renamedLinesMapList;
+
+ // The string that users provide as a sub-directory in the benchmarks
+ // directory: e.g. ms-queue
+ public final String dirName;
+
+ // The original directory --- the benchmarks directory: e.g.
+ // ~/model-checker/benchmarks/
+ public final String originalDir;
+ // The directory for generated files: e.g. ~/model-checker/test-cdsspec/
+ public final String generatedDir;
+
+ // The specification annotation extractor
+ private SpecExtractor extractor;
+
+ public CodeGenerator(String originalDir, String generatedDir, String dirName) {
+ this.dirName = dirName;
+ this.originalDir = originalDir + "/" + dirName + "/";
+ this.generatedDir = generatedDir + "/" + dirName + "/";
+
+ //this.originalDir = Environment.BenchmarksDir + dirName + "/";
+ //this.generatedDir = Environment.GeneratedFilesDir + dirName + "/";
+
+ try {
+ files = this.getSrcFiles(this.originalDir);
+ System.out.println("Original benchmarks directory: " + this.originalDir);
+ System.out.println("Generated benchmark directory: " + this.generatedDir);
+ System.out.println("The specific benchmark directory: " + this.dirName);
+ for (int i = 0; i < files.size(); i++) {
+ System.out.println(" Processing: " + files.get(i));
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ }
+ extractor = new SpecExtractor();
+ try {
+ extractor.extract(files);
+ } catch (WrongAnnotationException e) {
+ e.printStackTrace();
+ } catch (ParseException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * <p>
+ * This function initializes the list of code additions and line changes for
+ * all the files. For the code additions of a file, we sort them in an
+ * increasing order by the inserting line number.
+ * </p>
+ *
+ */
+ private void getAllCodeChanges() {
+ allAdditions = new ArrayList<CodeAdditions>();
+ renamedLinesMapList = new ArrayList<HashMap<Integer, InterfaceConstruct>>();
+ for (int i = 0; i < files.size(); i++) {
+ File file = files.get(i);
+ // One CodeAdditions per file
+ CodeAdditions additions = new CodeAdditions(file);
+ // Add the additions for this file to the list
+ allAdditions.add(additions);
+
+ // One CodeChange per file
+ HashMap<Integer, InterfaceConstruct> renamedLinesMap = new HashMap<Integer, InterfaceConstruct>();
+ // Add it the the list
+ renamedLinesMapList.add(renamedLinesMap);
+
+ // Extract all the additions
+ ArrayList<OPConstruct> OPList = extractor.OPListMap.get(file);
+ EntryConstruct entry = extractor.entryMap.get(file);
+ ArrayList<DefineConstruct> defineList = extractor.defineListMap
+ .get(file);
+ ArrayList<InterfaceConstruct> interfaceList = extractor.interfaceListMap
+ .get(file);
+ Code code = null;
+ CodeAddition addition = null;
+ // For ordering point constructs
+ if (OPList != null) {
+ for (OPConstruct con : OPList) {
+ code = CodeGeneratorUtils.Generate4OPConstruct(con);
+ addition = new CodeAddition(con.beginLineNum, code);
+ additions.addCodeAddition(addition);
+ }
+ }
+ // For entry constructs
+ if (entry != null) {
+ code = CodeGeneratorUtils.Generate4Entry(entry);
+ addition = new CodeAddition(entry.beginLineNum, code);
+ additions.addCodeAddition(addition);
+ }
+ // For define constructs
+ if (defineList != null) {
+ for (DefineConstruct con : defineList) {
+ code = CodeGeneratorUtils.Generate4Define(con);
+ addition = new CodeAddition(con.endLineNum, code);
+ additions.addCodeAddition(addition);
+ }
+ }
+ // For interface constructs
+ if (interfaceList != null) {
+ for (InterfaceConstruct con : interfaceList) {
+ code = CodeGeneratorUtils.GenerateInterfaceWrapper(con);
+ addition = new CodeAddition(con.getEndLineNumFunction(),
+ code);
+ additions.addCodeAddition(addition);
+ // Record the line to be changed
+ renamedLinesMap.put(con.endLineNum + 1, con);
+ }
+ }
+
+ // Sort additions by line number increasingly
+ additions.sort();
+ }
+ }
+
+ /**
+ * <p>
+ * For a specific file, given the code additions and line changes mapping
+ * for that file, this function will generate the new code for that file and
+ * write it back to the destination directory.
+ * </p>
+ *
+ * @param file
+ * The file to be processed
+ * @param additions
+ * The sorted code additions for the file
+ * @param renamedLinesMap
+ * The line change mapping for the file
+ */
+ private void writeCodeChange(File file, CodeAdditions additions,
+ HashMap<Integer, InterfaceConstruct> renamedLinesMap) {
+ Code newCode = new Code();
+ BufferedReader br = null;
+ LineNumberReader lineReader = null;
+ int lineNum = 0;
+ String curLine = null;
+
+ String dest = generatedDir + file.getName();
+ CodeAddition curAddition = null;
+ int additionIdx = -1;
+ if (!additions.isEmpty()) {
+ additionIdx = 0;
+ curAddition = additions.codeAdditions.get(0);
+ }
+
+ // Include the header for C/C++ files (.c/.cc/.cpp)
+ String name = file.getName();
+ if (name.endsWith(".c") || name.endsWith(".cc")
+ || name.endsWith(".cpp")) {
+ newCode.addLine(CodeGeneratorUtils.Comment("Add the"
+ + SpecNaming.CDSSpecGeneratedHeader + " header file"));
+ newCode.addLine(CodeGeneratorUtils
+ .IncludeHeader(SpecNaming.CDSSpecGeneratedHeader));
+ newCode.addLine("");
+ }
+
+ try {
+ br = new BufferedReader(new FileReader(file));
+ lineReader = new LineNumberReader(br);
+ while ((curLine = lineReader.readLine()) != null) {
+ lineNum = lineReader.getLineNumber();
+ InterfaceConstruct construct = null;
+ if ((construct = renamedLinesMap.get(lineNum)) != null) {
+ // Rename line
+ String newLine = construct.getFunctionHeader()
+ .getRenamedFuncLine();
+ newCode.addLine(newLine);
+ } else {
+ // First add the current line
+ newCode.addLine(curLine);
+ // Then check if we need to add code
+ if (curAddition != null
+ && lineNum == curAddition.insertingLine) {
+ // Need to insert code
+ newCode.addLines(curAddition.code);
+ // Increase to the next code addition
+ additionIdx++;
+ curAddition = additionIdx == additions.codeAdditions
+ .size() ? null : additions.codeAdditions
+ .get(additionIdx);
+ }
+ }
+ }
+ // Write new code change to destination
+ CodeGeneratorUtils.write2File(dest, newCode.lines);
+ // System.out.println("/*************** " + file.getName()
+ // + " *************/");
+ // System.out.println(newCode);
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+
+ /**
+ * <p>
+ * This function is the main interface for the CodeGenerator class. After
+ * constructing a CodeGenerator object, users can call this function to
+ * complete the code generation and file writing process.
+ * </p>
+ */
+ public void generateCode() {
+ // Extract all the code additions and line change
+ getAllCodeChanges();
+
+ // Generate the header file and CPP file
+ Code generatedHeader = CodeGeneratorUtils
+ .GenerateCDSSpecHeaderFile(extractor);
+ Code generatedCPP = CodeGeneratorUtils
+ .GenerateCDSSpecCPPFile(extractor);
+ CodeGeneratorUtils
+ .write2File(generatedDir + SpecNaming.CDSSpecGeneratedName
+ + ".h", generatedHeader.lines);
+ CodeGeneratorUtils.write2File(generatedDir
+ + SpecNaming.CDSSpecGeneratedCPP, generatedCPP.lines);
+
+ // Iterate over each file
+ for (int i = 0; i < files.size(); i++) {
+ File file = files.get(i);
+ CodeAdditions additions = allAdditions.get(i);
+ HashMap<Integer, InterfaceConstruct> renamedLinesMap = renamedLinesMapList
+ .get(i);
+ writeCodeChange(file, additions, renamedLinesMap);
+ }
+ }
+
+ /**
+ * <p>
+ * This is just a testing function that outputs the generated code, but not
+ * actually write them to the disk.
+ * </p>
+ */
+ private void testGenerator() {
+ // Test code generation
+ Code generatedHeader = CodeGeneratorUtils
+ .GenerateCDSSpecHeaderFile(extractor);
+ Code generatedCPP = CodeGeneratorUtils
+ .GenerateCDSSpecCPPFile(extractor);
+
+ System.out.println("/***** Generated header file *****/");
+ System.out.println(generatedHeader);
+ System.out.println("/***** Generated cpp file *****/");
+ System.out.println(generatedCPP);
+
+ for (File file : extractor.OPListMap.keySet()) {
+ ArrayList<OPConstruct> list = extractor.OPListMap.get(file);
+ for (OPConstruct con : list) {
+ Code code = CodeGeneratorUtils.Generate4OPConstruct(con);
+ System.out.println("/***** *****/");
+ System.out.println(con.annotation);
+ System.out.println(code);
+ }
+ }
+
+ for (File f : extractor.entryMap.keySet()) {
+ EntryConstruct con = extractor.entryMap.get(f);
+ System.out.println("/***** *****/");
+ System.out.println(con.annotation);
+ System.out.println(CodeGeneratorUtils.Generate4Entry(con));
+ }
+
+ for (File file : extractor.interfaceListMap.keySet()) {
+ ArrayList<InterfaceConstruct> list = extractor.interfaceListMap
+ .get(file);
+ for (InterfaceConstruct con : list) {
+ Code code = CodeGeneratorUtils.GenerateInterfaceWrapper(con);
+ System.out.println("/***** Interface wrapper *****/");
+ System.out.println(con.getFunctionHeader().getHeaderLine());
+ System.out
+ .println(con.getFunctionHeader().getRenamedFuncLine());
+ System.out.println(code);
+ }
+ }
+ }
+
+ public ArrayList<File> getSrcFiles(String dirName)
+ throws FileNotFoundException {
+ ArrayList<File> res = new ArrayList<File>();
+ File dir = new File(dirName);
+ if (dir.isDirectory() && dir.exists()) {
+ for (String file : dir.list()) {
+ if (file.endsWith(".h") || file.endsWith(".c")
+ || file.endsWith(".cc") || file.endsWith(".cpp")) {
+ res.add(new File(dir.getAbsolutePath() + "/" + file));
+ }
+ }
+ } else {
+ throw new FileNotFoundException(dirName
+ + " is not a valid directory!");
+ }
+ return res;
+ }
+
+ static public void main(String[] args) {
+ if (args.length < 3) {
+ System.out
+ .println("Usage: CodeGenerator <Benchmarks-directory> <Directory-for-generated-files> <specific-benchmark-lists...>");
+ return;
+ }
+
+ // String[] dirNames = args;
+
+ String[] dirNames = new String[args.length - 2];
+ for (int i = 0; i < args.length - 2; i++) {
+ dirNames[i] = args[i + 2];
+ }
+// String[] dirNames = Environment.Benchmarks;
+
+ for (int i = 0; i < dirNames.length; i++) {
+ String dirName = dirNames[i];
+ System.out.println("/********** Generating CDSSpec files for "
+ + dirName + " **********/");
+// CodeGenerator generator = new CodeGenerator(Environment.BenchmarksDir, Environment.GeneratedFilesDir, dirName);
+ CodeGenerator generator = new CodeGenerator(args[0], args[1], dirName);
+ generator.generateCode();
+ }
+ }
+}
--- /dev/null
+package edu.uci.eecs.codeGenerator;
+
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+import java.io.BufferedWriter;
+import java.io.File;
+import java.io.FileWriter;
+import java.io.IOException;
+
+import edu.uci.eecs.specExtraction.Code;
+import edu.uci.eecs.specExtraction.CommutativityRule;
+import edu.uci.eecs.specExtraction.DefineConstruct;
+import edu.uci.eecs.specExtraction.EntryConstruct;
+import edu.uci.eecs.specExtraction.FunctionHeader;
+import edu.uci.eecs.specExtraction.GlobalConstruct;
+import edu.uci.eecs.specExtraction.InterfaceConstruct;
+import edu.uci.eecs.specExtraction.OPConstruct;
+import edu.uci.eecs.specExtraction.SpecExtractor;
+import edu.uci.eecs.specExtraction.SpecNaming;
+import edu.uci.eecs.specExtraction.VariableDeclaration;
+
+/**
+ * <p>
+ * Some utility functions for generating specification checking code.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class CodeGeneratorUtils {
+
+ public static void PrintCode(ArrayList<String> code) {
+ for (int i = 0; i < code.size(); i++) {
+ System.out.println(code.get(i));
+ }
+ }
+
+ public static String Comment(String comment) {
+ return "/** " + comment + " */";
+ }
+
+ public static String ShortComment(String comment) {
+ return "// " + comment;
+ }
+
+ public static String IncludeHeader(String header) {
+ return "#include " + header;
+ }
+
+ public static String Brace(String val) {
+ return "(" + val + ")";
+ }
+
+ public static String Quote(String val) {
+ return "\"" + val + "\"";
+ }
+
+ public static String Assign(String varName, String val) {
+ return varName + " = " + val + ";";
+ }
+
+ public static String AssignToPtr(String structName, String field, String val) {
+ return structName + "->" + field + " = " + val + ";";
+ }
+
+ public static String Declare(String type, String name) {
+ return type + " " + name + ";";
+ }
+
+ public static String Declare(VariableDeclaration varDecl) {
+ return Declare(varDecl.type, varDecl.name);
+ }
+
+ public static String DeclareDefine(String type, String var, String val) {
+ return type + " " + var + " = " + val + ";";
+ }
+
+ /**
+ * <p>
+ * Insert a number of tabs at the beginning of the line.
+ * </p>
+ *
+ * @param line
+ * Input line
+ * @param tabCnt
+ * The number of tabs to be inserted
+ * @return A line that starts with the specific inserted tabs
+ */
+ public static String TabbedLine(String line, int tabCnt) {
+ String res = "";
+ for (int i = 0; i < tabCnt; i++)
+ res = "\t" + res;
+ res = res + line;
+ return res;
+ }
+
+ /**
+ * <p>
+ * Insert a tab at the beginning of the line.
+ * </p>
+ *
+ * @param line
+ * Input line
+ * @return A line that starts with one inserted tab
+ */
+ public static String TabbedLine(String line) {
+ return "\t" + line;
+ }
+
+ /**
+ * <p>
+ * This function generates the code for the header file that our
+ * specification generates automatically --- cdsspec-generated.h.
+ * </p>
+ *
+ * @param extractor
+ * The SpecExtractor that contains the extracted information
+ * @return The generated code
+ */
+ public static Code GenerateCDSSpecHeaderFile(SpecExtractor extractor) {
+ HashSet<String> headerFiles = extractor.headerFiles;
+ GlobalConstruct globalConstruct = extractor.getGlobalConstruct();
+ HashMap<File, ArrayList<InterfaceConstruct>> interfaceListMap = extractor.interfaceListMap;
+ HashSet<String> OPLabelSet = extractor.OPLabelSet;
+
+ Code code = new Code();
+
+ // Add auto-generated comments
+ code.addLine("/**");
+ code.addLine(TabbedLine("This is a header file auto-generated by CDSSpec compiler; together, CDSSpec"));
+ code.addLine(TabbedLine("compiler should have generated the accompanying implementation file that"));
+ code.addLine(TabbedLine("implements the some functions declared in this file. In order to instrument"));
+ code.addLine(TabbedLine("your benchmark for CDSSpec checker to check, you should include this header"));
+ code.addLine(TabbedLine("file in every file you use an CDSSpec annotation. Note that it should be"));
+ code.addLine(TabbedLine("placed in the end of all other header files. Currently we require a C++"));
+ code.addLine(TabbedLine("compiler that supports C++11."));
+ code.addLine("*/");
+ code.addLine("");
+
+ code.addLine("#ifndef _"
+ + SpecNaming.CDSSpecGeneratedName.toUpperCase().replace('-',
+ '_') + "_H");
+ code.addLine("#define _"
+ + SpecNaming.CDSSpecGeneratedName.toUpperCase().replace('-',
+ '_') + "_H");
+ code.addLine("");
+
+ // FIXME: We have included ad-hoc header files here
+ // System included headers
+ code.addLine(ShortComment("System included headers go here"));
+ code.addLine(IncludeHeader(SpecNaming.SPECANNOTATION_API));
+ code.addLine(IncludeHeader(SpecNaming.STDLIB));
+
+ code.addLine("");
+
+ // Users included headers
+ code.addLine(ShortComment("User included headers go here"));
+ for (String header : headerFiles) {
+ code.addLine(IncludeHeader(header));
+ }
+ code.addLine("");
+
+ // Decalre extern "C" --- begin
+ code.addLine("#ifdef __cplusplus");
+ code.addLine("extern \"C\" {");
+ code.addLine("#endif");
+ code.addLine("");
+
+ // Declare _createOPDefineUnattached
+ code.addLine(ShortComment("Declare _createOPDefineUnattached"));
+ // void _createOPDefineUnattached();
+ code.addLine("void _createOPDefineUnattached();");
+ code.addLine("");
+
+ // Declare _createOPClearDefineUnattached
+ code.addLine(ShortComment("Declare _createOPClearDefineUnattached"));
+ // void _createOPClearDefineUnattached();
+ code.addLine("void _createOPClearDefineUnattached();");
+ code.addLine("");
+
+ code.addLine(ShortComment("Declaration of some c-strings (CSTR)"));
+
+ // Interface name strings
+ code.addLine(ShortComment("Interface name strings"));
+ for (File file : interfaceListMap.keySet()) {
+ ArrayList<InterfaceConstruct> list = interfaceListMap.get(file);
+ for (InterfaceConstruct construct : list) {
+ String name = construct.getName();
+ code.addLine(Declare("extern " + SpecNaming.CString,
+ SpecNaming.AppendStr(name)));
+ }
+ }
+ code.addLine("");
+
+ // Ordering points label strings
+ code.addLine(ShortComment("Ordering points label strings"));
+ for (String label : OPLabelSet) {
+ code.addLine(Declare("extern " + SpecNaming.CString,
+ SpecNaming.AppendStr(label)));
+ }
+ code.addLine("");
+
+ // Declare customized value struct
+ for (File file : interfaceListMap.keySet()) {
+ ArrayList<InterfaceConstruct> list = interfaceListMap.get(file);
+ for (InterfaceConstruct construct : list) {
+ // Declare custom value struct for the interface
+ String name = construct.getName();
+ String structName = construct.getStructName();
+ code.addLine(ShortComment("Declare custom value struct for "
+ + name));
+ code.addLine("typedef struct " + structName + " {");
+ FunctionHeader funcHeader = construct.getFunctionHeader();
+ // C_RET & also the S_RET
+ // The idea is that we store the S_RET in the __value struct, and every time we access/update
+ // S_RET, we actually access "__value->S_RET" (good for read and write)...
+ if (!funcHeader.returnType.equals("void")) {
+ code.addLine(TabbedLine(Declare(funcHeader.returnType,
+ SpecNaming.C_RET)));
+ code.addLine(TabbedLine(Declare(funcHeader.returnType,
+ SpecNaming.S_RET)));
+ }
+ // Arguments
+ for (VariableDeclaration decl : funcHeader.args) {
+ code.addLine(TabbedLine(Declare(decl)));
+ }
+ code.addLine("} " + structName + ";");
+ code.addLine("");
+ }
+ }
+
+ // Declare INIT annotation instrumentation function
+ code.addLine(ShortComment("Declare INIT annotation instrumentation function"));
+ code.addLine("void _createInitAnnotation();");
+ code.addLine("");
+
+ // Decalre extern "C" --- begin
+ code.addLine("#ifdef __cplusplus");
+ code.addLine("};");
+ code.addLine("#endif");
+ code.addLine("");
+
+ // Declare #endif
+ code.addLine("#endif");
+
+ return code;
+ }
+
+ /**
+ * <p>
+ * This function generates the code for the CPP file that our specification
+ * generates automatically --- cdsspec-generated.cc.
+ * </p>
+ *
+ * @param extractor
+ * The SpecExtractor that contains the extracted information
+ * @return The generated code
+ */
+ public static Code GenerateCDSSpecCPPFile(SpecExtractor extractor) {
+ GlobalConstruct globalConstruct = extractor.getGlobalConstruct();
+ HashMap<File, ArrayList<InterfaceConstruct>> interfaceListMap = extractor.interfaceListMap;
+ HashSet<String> OPLabelSet = extractor.OPLabelSet;
+
+ Code code = new Code();
+ String line = null;
+ Code fieldsInit = null;
+
+ // Add auto-generated comments
+ code.addLine("/**");
+ code.addLine(TabbedLine("This is an implementation file auto-generated by CDSSpec compiler to"));
+ code.addLine(TabbedLine("instrument your benchmark for CDSSpec checker to check. Currently we require"));
+ code.addLine(TabbedLine("a C++ compiler that supports C++11."));
+ code.addLine("*/");
+ code.addLine("");
+
+ code.addLine("#include " + SpecNaming.CDSSpecGeneratedHeader);
+ code.addLine("#include " + SpecNaming.CDSANNOTATE);
+ code.addLine("#include " + SpecNaming.SPEC_COMMON);
+ code.addLine("#include " + SpecNaming.METHODCALL);
+ code.addLine("#include " + SpecNaming.CDSSPEC);
+ code.addLine("#include " + SpecNaming.SPECANNOTATION);
+ code.addLine("");
+ code.addLine("");
+
+ // Declare customized StateStruct
+ code.addLine(ShortComment("Declare customized StateStruct"));
+ code.addLine("typedef struct " + SpecNaming.StateStruct + " {");
+ for (VariableDeclaration decl : globalConstruct.declState) {
+ code.addLine(TabbedLine(Declare(decl)));
+ }
+ code.addLine("");
+ // Define state destructor
+ code.addLine(TabbedLine(ShortComment("Define state destructor")));
+ code.addLine(TabbedLine("~" + SpecNaming.StateStruct + "() {"));
+ if (!globalConstruct.autoGenClear) {
+ code.addLine(TabbedLine(
+ ShortComment("Execute user-defined state clear code"), 2));
+ } else {
+ code.addLine(TabbedLine(
+ ShortComment("Execute auto-generated state clear code"), 2));
+ }
+ globalConstruct.clearState.align(2);
+ code.addLines(globalConstruct.clearState);
+ code.addLine(TabbedLine("}"));
+ code.addLine("");
+
+ code.addLine(TabbedLine("SNAPSHOTALLOC"));
+ code.addLine("");
+ code.addLine("} " + SpecNaming.StateStruct + ";");
+ code.addLine("");
+ code.addLine("");
+
+ // Define the fake ordering point operation
+ code.addLine(ShortComment("Define the fake ordering point operation"));
+ // atomic_int _FAKE_OP_;
+ code.addLine("atomic_int " + SpecNaming.FakeOP + ";");
+ code.addLine("");
+
+ // Define _createOPDefineUnattached
+ code.addLine(ShortComment("Define _createOPDefineUnattached"));
+ code.addLine("void " + SpecNaming.CreateOPDefineUnattachedFunc + "() {");
+ code.addLine(TabbedLine(ShortComment("A load acting as the fake OP")));
+ code.addLine(TabbedLine(SpecNaming.FakeOP
+ + ".load(memory_order_relaxed);"));
+ code.addLine(TabbedLine(SpecNaming.CreateOPDefineAnnoFunc + "();"));
+ code.addLine("}");
+
+ // Define _createOPClearDefineUnattached
+ code.addLine(ShortComment("Define _createOPClearDefineUnattached"));
+ code.addLine("void " + SpecNaming.CreateOPClearDefineUnattachedFunc
+ + "() {");
+ code.addLine(TabbedLine(ShortComment("A load acting as the fake OP")));
+ code.addLine(TabbedLine(SpecNaming.FakeOP
+ + ".load(memory_order_relaxed);"));
+ code.addLine(TabbedLine(SpecNaming.CreateOPClearDefineAnnoFunc + "();"));
+ code.addLine("}");
+
+ code.addLine(ShortComment("Definition of some c-strings (CSTR)"));
+ code.addLine(ShortComment("A special empty string"));
+ code.addLine(DeclareDefine(SpecNaming.CString, SpecNaming.EmptyCString,
+ "\"\""));
+ code.addLine("");
+
+ // Interface name strings
+ code.addLine(ShortComment("Interface name strings"));
+ for (File file : interfaceListMap.keySet()) {
+ ArrayList<InterfaceConstruct> list = interfaceListMap.get(file);
+ for (InterfaceConstruct construct : list) {
+ String name = construct.getName();
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(name), Quote(name)));
+ }
+ }
+ code.addLine("");
+
+ // Commutativity rule strings
+ code.addLine(ShortComment("Commutativity rule strings"));
+ for (int i = 1; i <= globalConstruct.commutativityRules.size(); i++) {
+ CommutativityRule rule = globalConstruct.commutativityRules
+ .get(i - 1);
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(SpecNaming.Commutativity + i),
+ Quote(rule.toString())));
+ }
+ code.addLine("");
+
+ // Ordering points label strings
+ code.addLine(ShortComment("Ordering points label strings"));
+ for (String label : OPLabelSet) {
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(label), Quote(label)));
+ }
+ code.addLine("");
+
+ // Special function name strings
+ code.addLine(ShortComment("Special function name strings"));
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(SpecNaming.InitalState), Quote("_"
+ + SpecNaming.InitalState.toLowerCase())));
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(SpecNaming.CopyState), Quote("_"
+ + SpecNaming.CopyState.toLowerCase())));
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(SpecNaming.ClearState), Quote("_"
+ + SpecNaming.ClearState.toLowerCase())));
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(SpecNaming.FinalState), Quote("_"
+ + SpecNaming.FinalState.toLowerCase())));
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(SpecNaming.PrintState), Quote("_"
+ + SpecNaming.PrintState.toLowerCase())));
+ code.addLine("");
+
+ // Interface name strings
+ for (File file : interfaceListMap.keySet()) {
+ ArrayList<InterfaceConstruct> list = interfaceListMap.get(file);
+ for (InterfaceConstruct construct : list) {
+ String name = construct.getName();
+ code.addLine(ShortComment(name + " function strings"));
+ // Transition
+ String tmpFunc = name + "_" + SpecNaming.Transition;
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(tmpFunc), Quote("_" + tmpFunc)));
+ // PreCondition
+ tmpFunc = name + "_" + SpecNaming.PreCondition;
+ if (!construct.preCondition.isEmpty())
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(tmpFunc), Quote("_" + tmpFunc)));
+ else
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(tmpFunc),
+ SpecNaming.EmptyCString));
+ // JustifyingPrecondition
+ tmpFunc = name + "_" + SpecNaming.JustifyingPrecondition;
+ if (!construct.justifyingPrecondition.isEmpty())
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(tmpFunc), Quote("_" + tmpFunc)));
+ else
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(tmpFunc),
+ SpecNaming.EmptyCString));
+ // JustifyingPostcondition
+ tmpFunc = name + "_" + SpecNaming.JustifyingPostcondition;
+ if (!construct.justifyingPostcondition.isEmpty())
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(tmpFunc), Quote("_" + tmpFunc)));
+ else
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(tmpFunc),
+ SpecNaming.EmptyCString));
+ // PostCondition
+ tmpFunc = name + "_" + SpecNaming.PostCondition;
+ if (!construct.postCondition.isEmpty())
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(tmpFunc), Quote("_" + tmpFunc)));
+ else
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(tmpFunc),
+ SpecNaming.EmptyCString));
+ // Print
+ tmpFunc = name + "_" + SpecNaming.PrintValue;
+ if (!construct.print.isEmpty())
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(tmpFunc), Quote("_" + tmpFunc)));
+ else
+ code.addLine(DeclareDefine(SpecNaming.CString,
+ SpecNaming.AppendStr(tmpFunc),
+ SpecNaming.EmptyCString));
+ code.addLine("");
+ }
+ }
+
+ // Define @Initial
+ code.addLine(ShortComment("Define @" + SpecNaming.InitalState));
+ code.addLine("void _" + SpecNaming.InitalState.toLowerCase() + "("
+ + SpecNaming.Method + " " + SpecNaming.Method1 + ") {");
+ code.addLine(TabbedLine(DeclareDefine(SpecNaming.StateStruct, "*"
+ + SpecNaming.StateInst, "new " + SpecNaming.StateStruct)));
+ // Define macros
+ for (VariableDeclaration decl : globalConstruct.declState) {
+ code.addLine(TabbedLine("#define " + decl.name + " "
+ + SpecNaming.StateInst + "->" + decl.name));
+ }
+ if (!globalConstruct.autoGenInitial)
+ code.addLine(TabbedLine(ShortComment("User-defined state intialization code")));
+ else
+ // Auto-generated the initialization function
+ code.addLine(TabbedLine(ShortComment("Auto-generated state intialization code")));
+ // Align the code with one tab
+ globalConstruct.initState.align(1);
+ code.addLines(globalConstruct.initState);
+ // Undefine macros
+ for (VariableDeclaration decl : globalConstruct.declState) {
+ code.addLine(TabbedLine("#undef " + decl.name));
+ }
+ code.addLine("");
+ code.addLine(TabbedLine(AssignToPtr(SpecNaming.Method1,
+ SpecNaming.StateInst, SpecNaming.StateInst)));
+ code.addLine("}");
+ code.addLine("");
+
+ // Define @Copy
+ code.addLine(ShortComment("Define @" + SpecNaming.CopyState));
+ code.addLine("void _" + SpecNaming.CopyState.toLowerCase() + "("
+ + SpecNaming.Method + " " + "dest, " + SpecNaming.Method
+ + " src) {");
+ // StateStruct *OLD = (StateStruct*) src->state;
+ code.addLine(TabbedLine(DeclareDefine(SpecNaming.StateStruct, "*"
+ + SpecNaming.OldStateInst, Brace(SpecNaming.StateStruct + "*")
+ + " src->" + SpecNaming.StateInst)));
+ // StateStruct *NEW = new StateStruct;
+ code.addLine(TabbedLine(DeclareDefine(SpecNaming.StateStruct, "*"
+ + SpecNaming.NewStateInst, "new " + SpecNaming.StateStruct)));
+ if (!globalConstruct.autoGenCopy)
+ code.addLine(TabbedLine(ShortComment("User-defined state copy statements")));
+ else
+ // Auto-generated the copy function
+ code.addLine(TabbedLine(ShortComment("Auto-generated state copy statements")));
+ globalConstruct.copyState.align(1);
+ code.addLines(globalConstruct.copyState);
+ code.addLine("");
+ code.addLine(TabbedLine(AssignToPtr("dest", SpecNaming.StateInst,
+ SpecNaming.NewStateInst)));
+ code.addLine("}");
+ code.addLine("");
+
+ // Define @Clear
+ code.addLine(ShortComment("Define @" + SpecNaming.ClearState));
+ code.addLine("void _" + SpecNaming.ClearState.toLowerCase() + "("
+ + SpecNaming.Method + " " + SpecNaming.Method1 + ") {");
+ // Retrieve the state
+ code.addLine(TabbedLine(ShortComment("Retrieve the state")));
+ code.addLine(TabbedLine(DeclareDefine(SpecNaming.StateStruct, "*"
+ + SpecNaming.StateInst, "(" + SpecNaming.StateStruct + "*) "
+ + SpecNaming.Method1 + "->state")));
+ // Explicitly call the state destructor
+ code.addLine(TabbedLine(ShortComment("Explicitly call the state destructor")));
+ code.addLine(TabbedLine("delete " + SpecNaming.StateInst + ";"));
+ code.addLine("}");
+ code.addLine("");
+
+ // Define @Print
+ code.addLine(ShortComment("Define @" + SpecNaming.PrintState));
+ code.addLine("void _" + SpecNaming.PrintState.toLowerCase() + "("
+ + SpecNaming.Method + " " + SpecNaming.Method1 + ") {");
+
+ // Initialize state struct fields
+ fieldsInit = GenerateStateFieldsInitialization(SpecNaming.Method1,
+ SpecNaming.StateInst, globalConstruct);
+ fieldsInit.align(1);
+ code.addLines(fieldsInit);
+ code.addLine("");
+ if (!globalConstruct.autoGenPrint)
+ code.addLine(TabbedLine(ShortComment("Execute user-defined state printing code")));
+ else
+ // Auto-generated the copy function
+ code.addLine(TabbedLine(ShortComment("Execute auto-generated state printing code")));
+
+ // Align the code with one tab
+ globalConstruct.printState.align(1);
+ code.addLines(globalConstruct.printState);
+ code.addLine("}");
+ code.addLine("");
+
+ // Define @Commutativity
+ code.addLine(ShortComment("Define commutativity checking functions"));
+ for (int i = 1; i <= globalConstruct.commutativityRules.size(); i++) {
+ CommutativityRule rule = globalConstruct.commutativityRules
+ .get(i - 1);
+ code.addLine("bool _check" + SpecNaming.Commutativity + i + "("
+ + SpecNaming.Method + " m1, " + SpecNaming.Method
+ + " m2) {");
+ // if (m1->name == _ENQ_str && m2->name == _DEQ_str) {
+ code.addLine(TabbedLine("if (m1->name == "
+ + SpecNaming.AppendStr(rule.method1) + " && m2->name == "
+ + SpecNaming.AppendStr(rule.method2) + ") {"));
+ // Initialize M1 & M2 in commutativity rule
+ // e.g. ENQ *M1 = (ENQ*) m1->value;
+ String structName1 = InterfaceConstruct
+ .createStructName(rule.method1);
+ String structName2 = InterfaceConstruct
+ .createStructName(rule.method2);
+ code.addLine(TabbedLine(
+ DeclareDefine(structName1, "*M1", "(" + structName1
+ + "*) m1->value"), 2));
+ code.addLine(TabbedLine(
+ DeclareDefine(structName2, "*M2", "(" + structName2
+ + "*) m2->value"), 2));
+ code.addLine(TabbedLine("return !(" + rule.condition + ");", 2));
+ code.addLine(TabbedLine("}"));
+ code.addLine(TabbedLine("return true;"));
+
+ code.addLine("}");
+ code.addLine("");
+ }
+
+ // Define customized interface functions
+ for (File file : interfaceListMap.keySet()) {
+ ArrayList<InterfaceConstruct> list = interfaceListMap.get(file);
+ for (InterfaceConstruct construct : list) {
+ fieldsInit = null;
+
+ // Define interface functions
+ String name = construct.getName();
+ String structName = construct.getStructName();
+ code.addLine("/********** " + name
+ + " functions **********/");
+ // Define @Transition for INTERFACE
+ code.addLine(ShortComment("Define @" + SpecNaming.Transition
+ + " for " + name));
+ code.addLine("bool _" + name + "_" + SpecNaming.Transition
+ + "(" + SpecNaming.Method + " " + SpecNaming.Method1
+ + ", " + SpecNaming.Method + " " + SpecNaming.Method2
+ + ") {");
+
+ // Initialize value struct fields
+ fieldsInit = GenerateInterfaceFieldsInitialization(
+ SpecNaming.Method2, SpecNaming.InterfaceValueInst,
+ construct);
+ fieldsInit.align(1);
+ code.addLines(fieldsInit);
+
+ construct.transition.align(1);
+ code.addLine(TabbedLine(ShortComment("Execute Transition")));
+ code.addLines(construct.transition);
+
+ // By default, we will return true for state transition
+ code.addLine(TabbedLine(ShortComment("By default @Transition returns true")));
+ code.addLine(TabbedLine("return true;"));
+ code.addLine("}");
+ code.addLine("");
+
+ // Define @PreCondition
+ if (!construct.preCondition.isEmpty()) {
+ code.addLine(ShortComment("Define @"
+ + SpecNaming.PreCondition + " for " + name));
+ code.addLine("bool _" + name + "_" + SpecNaming.PreCondition
+ + "(" + SpecNaming.Method + " " + SpecNaming.Method1
+ + ", " + SpecNaming.Method + " " + SpecNaming.Method2
+ + ") {");
+
+ // Initialize value struct fields
+ fieldsInit = GenerateInterfaceFieldsInitialization(
+ SpecNaming.Method2, SpecNaming.InterfaceValueInst,
+ construct);
+ fieldsInit.align(1);
+ code.addLines(fieldsInit);
+
+ construct.preCondition.align(1);
+ code.addLine(TabbedLine(ShortComment("Execute PreCondition")));
+ code.addLines(construct.preCondition);
+
+ // By default, we will return true for @PreCondition
+ code.addLine(TabbedLine(ShortComment("By default @PreCondition returns true")));
+ code.addLine(TabbedLine("return true;"));
+
+ code.addLine("}");
+ code.addLine("");
+
+ }
+ // Define @JustifyingPrecondition
+ if (!construct.justifyingPrecondition.isEmpty()) {
+ code.addLine(ShortComment("Define @"
+ + SpecNaming.JustifyingPrecondition + " for " + name));
+ code.addLine("bool _" + name + "_" + SpecNaming.JustifyingPrecondition
+ + "(" + SpecNaming.Method + " " + SpecNaming.Method1
+ + ", " + SpecNaming.Method + " " + SpecNaming.Method2
+ + ") {");
+
+ // Initialize value struct fields
+ fieldsInit = GenerateInterfaceFieldsInitialization(
+ SpecNaming.Method2, SpecNaming.InterfaceValueInst,
+ construct);
+ fieldsInit.align(1);
+ code.addLines(fieldsInit);
+
+ construct.justifyingPrecondition.align(1);
+ code.addLine(TabbedLine(ShortComment("Execute JustifyingPrecondition")));
+ code.addLines(construct.justifyingPrecondition);
+
+ // By default, we will return true for @JustifyingPrecondition
+ code.addLine(TabbedLine(ShortComment("By default @JustifyingPrecondition returns true")));
+ code.addLine(TabbedLine("return true;"));
+
+ code.addLine("}");
+ code.addLine("");
+
+ }
+ // Define @JustifyingPostcondition
+ if (!construct.justifyingPostcondition.isEmpty()) {
+ code.addLine(ShortComment("Define @"
+ + SpecNaming.JustifyingPostcondition + " for " + name));
+ code.addLine("bool _" + name + "_" + SpecNaming.JustifyingPostcondition
+ + "(" + SpecNaming.Method + " " + SpecNaming.Method1
+ + ", " + SpecNaming.Method + " " + SpecNaming.Method2
+ + ") {");
+
+ // Initialize value struct fields
+ fieldsInit = GenerateInterfaceFieldsInitialization(
+ SpecNaming.Method2, SpecNaming.InterfaceValueInst,
+ construct);
+ fieldsInit.align(1);
+ code.addLines(fieldsInit);
+
+ construct.justifyingPostcondition.align(1);
+ code.addLine(TabbedLine(ShortComment("Execute JustifyingPostcondition")));
+ code.addLines(construct.justifyingPostcondition);
+
+ // By default, we will return true for @JustifyingPostcondition
+ code.addLine(TabbedLine(ShortComment("By default @JustifyingPostcondition returns true")));
+ code.addLine(TabbedLine("return true;"));
+
+ code.addLine("}");
+ code.addLine("");
+
+ }
+ // Define @PostCondition
+ if (!construct.postCondition.isEmpty()) {
+ code.addLine(ShortComment("Define @"
+ + SpecNaming.PostCondition + " for " + name));
+ code.addLine("bool _" + name + "_" + SpecNaming.PostCondition
+ + "(" + SpecNaming.Method + " " + SpecNaming.Method1
+ + ", " + SpecNaming.Method + " " + SpecNaming.Method2
+ + ") {");
+
+ // Initialize value struct fields
+ fieldsInit = GenerateInterfaceFieldsInitialization(
+ SpecNaming.Method2, SpecNaming.InterfaceValueInst,
+ construct);
+ fieldsInit.align(1);
+ code.addLines(fieldsInit);
+
+ construct.postCondition.align(1);
+ code.addLine(TabbedLine(ShortComment("Execute PostCondition")));
+ code.addLines(construct.postCondition);
+
+ // By default, we will return true for @PostCondition
+ code.addLine(TabbedLine(ShortComment("By default @PostCondition returns true")));
+ code.addLine(TabbedLine("return true;"));
+
+ code.addLine("}");
+ code.addLine("");
+ }
+ // Define @Print
+ if (!construct.print.isEmpty()) {
+ code.addLine(ShortComment("Define @"
+ + SpecNaming.PrintValue + " for " + name));
+ code.addLine("void _" + name + "_" + SpecNaming.PrintValue
+ + "(" + SpecNaming.Method + " "
+ + SpecNaming.Method1 + ") {");
+ // Initialize value struct fields
+ fieldsInit = GenerateInterfaceFieldsInitialization(
+ SpecNaming.Method1, SpecNaming.InterfaceValueInst,
+ construct);
+ fieldsInit.align(1);
+ code.addLines(fieldsInit);
+
+ construct.print.align(1);
+ if (!construct.autoGenPrint)
+ code.addLine(TabbedLine(ShortComment("Execute user-defined value printing code")));
+ else
+ // Auto-generated the value printing function
+ code.addLine(TabbedLine(ShortComment("Execute auto-generated value printing code")));
+ code.addLines(construct.print);
+
+ code.addLine("}");
+ code.addLine("");
+ }
+ }
+ }
+
+ // Define INIT annotation instrumentation function
+ code.addLine(ShortComment("Define INIT annotation instrumentation function"));
+ code.addLine("void _createInitAnnotation() {");
+
+ // Init the fake ordering point place holder
+ code.addLine(TabbedLine(ShortComment("Init the fake ordering point place holder")));
+ code.addLine(TabbedLine(SpecNaming.FakeOP
+ + ".store(1, memory_order_relaxed);"));
+
+ // Init commutativity rules
+ code.addLine(TabbedLine(ShortComment("Init commutativity rules")));
+ code.addLine(TabbedLine(DeclareDefine("int",
+ SpecNaming.CommutativityRuleSizeInst,
+ Integer.toString(globalConstruct.commutativityRules.size()))));
+ String tmp = SpecNaming.NewSize
+ + Brace(SpecNaming.CommutativityRule + ", sizeof"
+ + Brace(SpecNaming.CommutativityRule) + " * "
+ + SpecNaming.CommutativityRuleSizeInst);
+ code.addLine(TabbedLine(DeclareDefine(SpecNaming.CommutativityRule, "*"
+ + SpecNaming.CommutativityRuleInst, tmp)));
+ for (int i = 1; i <= globalConstruct.commutativityRules.size(); i++) {
+ CommutativityRule rule = globalConstruct.commutativityRules
+ .get(i - 1);
+ code.addLine(TabbedLine(ShortComment("Initialize commutativity rule ")
+ + i));
+ // new( &commuteRules[0] )CommutativityRule(_ENQ_str, _DEQ_str,
+ // _Commutativity1_str, _checkCommutativity1)
+ line = "new"
+ + Brace(" &" + SpecNaming.CommutativityRuleInst + "["
+ + (i - 1) + "] ") + SpecNaming.CommutativityRule
+ + "(" + SpecNaming.AppendStr(rule.method1) + ", "
+ + SpecNaming.AppendStr(rule.method2) + ", "
+ + SpecNaming.AppendStr(SpecNaming.Commutativity + i) + ", "
+ + "_check" + SpecNaming.Commutativity + i + ");";
+ code.addLine(TabbedLine(line));
+ }
+
+ // Initialize AnnoInit
+ code.addLine(TabbedLine(ShortComment("Initialize AnnoInit")));
+ // AnnoInit *init = new AnnoInit(
+ code.addLine(TabbedLine(SpecNaming.AnnoInit + " *"
+ + SpecNaming.AnnoInitInst + " = new " + SpecNaming.AnnoInit
+ + "("));
+ // new NamedFunction(_Initial_str, INITIAL, (void*) _initial),
+ code.addLine(TabbedLine("new " + SpecNaming.NamedFunction + "("
+ + SpecNaming.AppendStr(SpecNaming.InitalState) + ", "
+ + SpecNaming.InitalState.toUpperCase() + ", " + "(void*) _"
+ + SpecNaming.InitalState.toLowerCase() + "),", 2));
+ // new NamedFunction(_Final_str, FINAL, (void*) NULL_FUNC),
+ line = "new " + SpecNaming.NamedFunction + "("
+ + SpecNaming.AppendStr(SpecNaming.FinalState) + ", "
+ + SpecNaming.FinalState.toUpperCase() + ", " + "(void*) ";
+ if (globalConstruct.finalState.isEmpty()) {
+ line = line + SpecNaming.NullFunc + "),";
+ } else {
+ line = line + "_" + SpecNaming.FinalState.toUpperCase();
+ }
+ code.addLine(TabbedLine(line, 2));
+ // new NamedFunction(_Copy_str, COPY, (void*) _copy),
+ code.addLine(TabbedLine("new " + SpecNaming.NamedFunction + "("
+ + SpecNaming.AppendStr(SpecNaming.CopyState) + ", "
+ + SpecNaming.CopyState.toUpperCase() + ", " + "(void*) _"
+ + SpecNaming.CopyState.toLowerCase() + "),", 2));
+ // new NamedFunction(_Clear_str, CLEAR, (void*) _clear),
+ code.addLine(TabbedLine("new " + SpecNaming.NamedFunction + "("
+ + SpecNaming.AppendStr(SpecNaming.ClearState) + ", "
+ + SpecNaming.ClearState.toUpperCase() + ", " + "(void*) _"
+ + SpecNaming.ClearState.toLowerCase() + "),", 2));
+ // new NamedFunction(_Print_str, PRINT_STATE, (void*) _print),
+ code.addLine(TabbedLine("new " + SpecNaming.NamedFunction + "("
+ + SpecNaming.AppendStr(SpecNaming.PrintState) + ", "
+ + SpecNaming.PrintStateType + ", " + "(void*) _"
+ + SpecNaming.PrintState.toLowerCase() + "),", 2));
+ // commuteRules, CommuteRuleSize);
+ code.addLine(TabbedLine(SpecNaming.CommutativityRuleInst + ", "
+ + SpecNaming.CommutativityRuleSizeInst + ");", 2));
+ code.addLine("");
+
+ // Declare StateFunctions map
+ code.addLine(TabbedLine(ShortComment("Declare StateFunctions map")));
+ code.addLine(TabbedLine(Declare(SpecNaming.StateFunctions, "*"
+ + SpecNaming.StateFunctionsInst)));
+ code.addLine("");
+
+ // StateFunction for interface
+ for (File file : interfaceListMap.keySet()) {
+ ArrayList<InterfaceConstruct> list = interfaceListMap.get(file);
+ for (InterfaceConstruct construct : list) {
+ String name = construct.getName();
+ code.addLine(TabbedLine(ShortComment("StateFunction for "
+ + name)));
+ // stateFuncs = new StateFunctions(
+ code.addLine(TabbedLine(SpecNaming.StateFunctionsInst
+ + " = new " + SpecNaming.StateFunctions + "("));
+ // new NamedFunction(_ENQ_Transition_str, TRANSITION, (void*)
+ // _ENQ_Transition),
+ // Transition
+ code.addLine(TabbedLine(
+ "new "
+ + SpecNaming.NamedFunction
+ + "("
+ + SpecNaming.AppendStr(name + "_"
+ + SpecNaming.Transition) + ", "
+ + SpecNaming.TransitionType + ", (void*) _"
+ + name + "_" + SpecNaming.Transition + "),", 2));
+ // PreCondition
+ line = "new "
+ + SpecNaming.NamedFunction
+ + "("
+ + SpecNaming.AppendStr(name + "_"
+ + SpecNaming.PreCondition) + ", "
+ + SpecNaming.PreConditionType + ", (void*) ";
+ if (construct.preCondition.isEmpty()) {
+ line = line + SpecNaming.NullFunc + "),";
+ } else {
+ line = line + "_" + name + "_" + SpecNaming.PreCondition
+ + "),";
+ }
+ code.addLine(TabbedLine(line, 2));
+ // JustifyingPrecondition
+ line = "new "
+ + SpecNaming.NamedFunction
+ + "("
+ + SpecNaming.AppendStr(name + "_"
+ + SpecNaming.JustifyingPrecondition) + ", "
+ + SpecNaming.JustifyingPreconditionType + ", (void*) ";
+ if (construct.justifyingPrecondition.isEmpty()) {
+ line = line + SpecNaming.NullFunc + "),";
+ } else {
+ line = line + "_" + name + "_" + SpecNaming.JustifyingPrecondition
+ + "),";
+ }
+ code.addLine(TabbedLine(line, 2));
+ // JustifyingPostcondition
+ line = "new "
+ + SpecNaming.NamedFunction
+ + "("
+ + SpecNaming.AppendStr(name + "_"
+ + SpecNaming.JustifyingPostcondition) + ", "
+ + SpecNaming.JustifyingPostconditionType + ", (void*) ";
+ if (construct.justifyingPostcondition.isEmpty()) {
+ line = line + SpecNaming.NullFunc + "),";
+ } else {
+ line = line + "_" + name + "_" + SpecNaming.JustifyingPostcondition
+ + "),";
+ }
+ code.addLine(TabbedLine(line, 2));
+ // PostCondition
+ line = "new "
+ + SpecNaming.NamedFunction
+ + "("
+ + SpecNaming.AppendStr(name + "_"
+ + SpecNaming.PostCondition) + ", "
+ + SpecNaming.PostConditionType + ", (void*) ";
+ if (construct.postCondition.isEmpty()) {
+ line = line + SpecNaming.NullFunc + "),";
+ } else {
+ line = line + "_" + name + "_" + SpecNaming.PostCondition
+ + "),";
+ }
+ code.addLine(TabbedLine(line, 2));
+ // Print (PrintValue
+ line = "new "
+ + SpecNaming.NamedFunction
+ + "("
+ + SpecNaming.AppendStr(name + "_"
+ + SpecNaming.PrintValue) + ", "
+ + SpecNaming.PrintValueType + ", (void*) ";
+ if (construct.print.isEmpty()) {
+ line = line + SpecNaming.NullFunc + ")";
+ } else {
+ line = line + "_" + name + "_" + SpecNaming.PrintValue
+ + ")";
+ }
+ code.addLine(TabbedLine(line, 2));
+ code.addLine(TabbedLine(");"));
+
+ // init->addInterfaceFunctions(_ENQ_str, stateFuncs);
+ code.addLine(TabbedLine(SpecNaming.AnnoInitInst
+ + "->"
+ + SpecNaming.AddInterfaceFunctions
+ + Brace(SpecNaming.AppendStr(name) + ", "
+ + SpecNaming.StateFunctionsInst) + ";"));
+ code.addLine("");
+ }
+ }
+
+ // Create and instrument with the INIT annotation
+ code.addLine(TabbedLine(ShortComment("Create and instrument with the INIT annotation")));
+ // cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(INIT, init));
+ code.addLine(TabbedLine(SpecNaming.CDSAnnotateFunc
+ + Brace(SpecNaming.SPEC_ANALYSIS
+ + ", new "
+ + SpecNaming.SpecAnnotation
+ + Brace(SpecNaming.AnnoTypeInit + ", "
+ + SpecNaming.AnnoInitInst)) + ";"));
+
+ code.addLine("}");
+ code.addLine("");
+
+ return code;
+ }
+
+ /**
+ * <p>
+ * This function generates a list of lines that initialize the fields of the
+ * global state struct. See below.
+ * </p>
+ *
+ * <p>
+ * <code>
+ * StateStruct *state = (StateStruct*) _M->state;
+ * <br>
+ * IntList * q = state->q;
+ * </code>
+ * </p>
+ *
+ * <p>
+ * In this example, _M --> methodInst, state --> inst.
+ * </p>
+ *
+ * @param methodInst
+ * See description
+ * @param inst
+ * See description
+ * @param construct
+ * The global state construct
+ * @return The generated code
+ */
+ public static Code GenerateStateFieldsInitialization(String methodInst,
+ String inst, GlobalConstruct construct) {
+ Code res = new Code();
+ res.addLine(ShortComment("Initialize " + SpecNaming.StateStruct
+ + " fields"));
+ res.addLine(DeclareDefine(SpecNaming.StateStruct, "*" + inst, "("
+ + SpecNaming.StateStruct + "*) " + methodInst + "->state"));
+ for (VariableDeclaration decl : construct.declState) {
+ res.addLine(DeclareDefine(decl.type, decl.name, inst + "->"
+ + decl.name));
+ }
+ return res;
+ }
+
+ /**
+ * <p>
+ * This function generates a list of lines that initialize the fields of a
+ * specific interface struct. See below.
+ * </p>
+ *
+ * <p>
+ * <code>
+ * ENQ *info = (ENQ*) _M->value;
+ * <br>
+ * IntList * q = info->q;
+ * </code>
+ * </p>
+ *
+ * <p>
+ * In this example, ENQ --> structType, _M --> methodInst, info --> inst
+ * </p>
+ *
+ * @param methodInst
+ * See description
+ * @param inst
+ * See description
+ * @param construct
+ * The corresponding interface construct
+ * @return The generated code
+ */
+ public static Code GenerateInterfaceFieldsInitialization(String methodInst,
+ String inst, InterfaceConstruct construct) {
+ Code res = new Code();
+ String name = construct.getName();
+ String structName = construct.getStructName();
+ res.addLine(ShortComment("Initialize fields for " + name));
+ // The very first assignment "
+ res.addLine(DeclareDefine(structName, "*" + inst, "(" + structName
+ + "*) " + methodInst + "->" + SpecNaming.MethodValueField));
+ // Don't leave out the C_RET field
+ if (!construct.getFunctionHeader().isReturnVoid()) {
+ res.addLine(DeclareDefine(construct.getFunctionHeader().returnType,
+ SpecNaming.C_RET, inst + "->" + SpecNaming.C_RET));
+ }
+ // For arguments
+ for (VariableDeclaration decl : construct.getFunctionHeader().args) {
+ res.addLine(DeclareDefine(decl.type, decl.name, inst + "->"
+ + decl.name));
+ }
+ return res;
+ }
+
+ /**
+ * <p>
+ * This function generates the code to be inserted right after the ordering
+ * point construct (instrumentation code)
+ * </p>
+ *
+ * @param construct
+ * The corresponding ordering point construct
+ * @return The generated code
+ */
+ public static Code Generate4OPConstruct(OPConstruct construct) {
+ Code code = new Code();
+ String curLine = construct.annotation;
+ String label = construct.label;
+ String prefixTabs = curLine.substring(0, curLine.indexOf("/**"));
+ if (!construct.condition.equals("true")) {
+ code.addLine(prefixTabs + "if (" + construct.condition + ")");
+ prefixTabs = prefixTabs + "\t";
+ }
+
+ switch (construct.type) {
+ case OPDefine:
+ code.addLine(prefixTabs + SpecNaming.CreateOPDefineAnnoFunc + "();");
+ break;
+ case OPDefineUnattached:
+ code.addLine(prefixTabs + SpecNaming.CreateOPDefineUnattachedFunc
+ + "();");
+ break;
+ case PotentialOP:
+ code.addLine(prefixTabs + SpecNaming.CreatePotentialOPAnnoFunc
+ + "(" + SpecNaming.AppendStr(label) + ");");
+ break;
+ case OPCheck:
+ code.addLine(prefixTabs + SpecNaming.CreateOPCheckAnnoFunc + "("
+ + SpecNaming.AppendStr(label) + ");");
+ break;
+ case OPClear:
+ code.addLine(prefixTabs + SpecNaming.CreateOPClearAnnoFunc + "();");
+ break;
+ case OPClearDefine:
+ code.addLine(prefixTabs + SpecNaming.CreateOPClearDefineAnnoFunc
+ + "();");
+ break;
+ case OPClearDefineUnattached:
+ code.addLine(prefixTabs
+ + SpecNaming.CreateOPClearDefineUnattachedFunc + "();");
+ break;
+ default:
+ break;
+ }
+ return code;
+ }
+
+ /**
+ * <p>
+ * This function generates the code to be inserted right after the entry
+ * construct (instrumentation code)
+ * </p>
+ *
+ * @param construct
+ * The corresponding entry construct
+ * @return
+ */
+ public static Code Generate4Entry(EntryConstruct construct) {
+ Code res = new Code();
+ String curLine = construct.annotation;
+ String prefixTabs = curLine.substring(0, curLine.indexOf("/**"));
+ // _createInitAnnotation();
+ res.addLine(prefixTabs + SpecNaming.CreateInitAnnoFunc + "();");
+ return res;
+ }
+
+ /**
+ * <p>
+ * This function generates the code to be inserted right after the "@Define"
+ * construct (instrumentation code)
+ * </p>
+ *
+ * @param construct
+ * The corresponding entry construct
+ * @return
+ */
+ public static Code Generate4Define(DefineConstruct construct) {
+ Code code = new Code();
+ code.addLine("");
+ code.addLine("/********** User-defined code in annotation (BEGIN) **********/");
+ code.addLines(construct.code);
+ code.addLine("/********** User-defined code in annotation (END) **********/");
+ return code;
+ }
+
+ /**
+ * <p>
+ * This function generates the new interface wrapper code to be inserted
+ * right after the end of the interface definition
+ * </p>
+ *
+ * @param construct
+ * The corresponding interface construct
+ * @return The generated code
+ */
+ public static Code GenerateInterfaceWrapper(InterfaceConstruct construct) {
+ Code code = new Code();
+
+ String name = construct.getName();
+ String structName = construct.getStructName();
+ String beginLine = construct.getFunctionHeader().getHeaderLine();
+ Pattern regexpSpace = Pattern.compile("^(\\s*)\\S.*$");
+ Matcher matcherSpace = regexpSpace.matcher(beginLine);
+ String prefixTabs = "";
+ if (matcherSpace.find())
+ prefixTabs = matcherSpace.group(1);
+
+ // Add one line to separate
+ code.addLine("");
+ code.addLine(prefixTabs
+ + ShortComment("Generated wrapper interface for " + name));
+ if (beginLine.indexOf('{') == -1) { // We need to add the '{' to the end
+ // of the line
+ code.addLine(beginLine + " {");
+ } else {
+ code.addLine(beginLine);
+ }
+ // Instrument with the INTERFACE_BEGIN annotations
+ code.addLine(prefixTabs
+ + "\t"
+ + ShortComment("Instrument with the INTERFACE_BEGIN annotation"));
+ // CAnnoInterfaceInfo info = _createInterfaceBeginAnnotation(_DEQ_str);
+ code.addLine(prefixTabs
+ + "\t"
+ + DeclareDefine(SpecNaming.AnnoInterfaceInfo, "*"
+ + SpecNaming.AnnoInterfaceInfoInst,
+ SpecNaming.CreateInterfaceBeginAnnoFunc
+ + Brace(SpecNaming.AppendStr(name))));
+ // Call the actual function
+ code.addLine(prefixTabs + "\t"
+ + ShortComment("Call the actual function"));
+ // bool C_RET = dequeue_ORIGINAL__(q, retVal, reclaimNode);
+ code.addLine(prefixTabs + "\t"
+ + construct.getFunctionHeader().getRenamedCall() + ";");
+ code.addLine("");
+
+ // Initialize the value struct
+ code.addLine(prefixTabs + "\t"
+ + ShortComment("Initialize the value struct"));
+ // The very first assignment "
+ code.addLine(prefixTabs
+ + "\t"
+ + DeclareDefine(structName,
+ "*" + SpecNaming.InterfaceValueInst, SpecNaming.New
+ + Brace(structName)));
+ // Don't leave out the C_RET field
+ if (!construct.getFunctionHeader().isReturnVoid())
+ code.addLine(prefixTabs
+ + "\t"
+ + AssignToPtr(SpecNaming.InterfaceValueInst,
+ SpecNaming.C_RET, SpecNaming.C_RET));
+ // For arguments
+ for (VariableDeclaration decl : construct.getFunctionHeader().args)
+ code.addLine(prefixTabs
+ + "\t"
+ + AssignToPtr(SpecNaming.InterfaceValueInst, decl.name,
+ decl.name));
+ code.addLine("");
+
+ // Store the value info into the current MethodCall
+ // _setInterfaceBeginAnnotationValue(info, value);
+ code.addLine(prefixTabs
+ + "\t"
+ + ShortComment("Store the value info into the current MethodCall"));
+ code.addLine(prefixTabs
+ + "\t"
+ + SpecNaming.SetInterfaceBeginAnnoValueFunc
+ + Brace(SpecNaming.AnnoInterfaceInfoInst + ", "
+ + SpecNaming.InterfaceValueInst) + ";");
+ code.addLine("");
+
+ // Instrument with the INTERFACE_END annotations
+ code.addLine(prefixTabs + "\t"
+ + ShortComment("Instrument with the INTERFACE_END annotation"));
+ // _createInterfaceEndAnnotation(_DEQ_str);
+ code.addLine(prefixTabs + "\t"
+ + SpecNaming.CreateInterfaceEndAnnoFunc
+ + Brace(SpecNaming.AppendStr(name)) + ";");
+
+ // Return if necessary
+ if (!construct.getFunctionHeader().isReturnVoid())
+ code.addLine(prefixTabs + "\treturn " + SpecNaming.C_RET + ";");
+ code.addLine(prefixTabs + "}");
+
+ return code;
+ }
+
+ /**
+ * <p>
+ * Write a list of lines (as the whole of the file) to a file ---
+ * newFileName. If that file does not exist, we create that file and then
+ * write the lines.
+ * </p>
+ *
+ * @param newFileName
+ * The name of the file to be written
+ * @param content
+ * The list of lines that as a whole become the content of the
+ * file
+ */
+ public static void write2File(String newFileName, ArrayList<String> content) {
+ File newFile = new File(newFileName);
+ newFile.getParentFile().mkdirs();
+ if (!newFile.exists()) {
+ try {
+ newFile.createNewFile();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ BufferedWriter bw = null;
+ try {
+ bw = new BufferedWriter(new FileWriter(newFile));
+ for (int i = 0; i < content.size(); i++) {
+ bw.write(content.get(i) + "\n");
+ }
+ bw.flush();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ if (bw != null)
+ try {
+ bw.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+}
--- /dev/null
+package edu.uci.eecs.codeGenerator;
+
+import java.util.ArrayList;
+
+/**
+ * <p>
+ * This class contains some constant strings related to the code generation
+ * process.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class Environment {
+ public final static String HomeDir = System.getProperty("user.dir");
+ public final static String ModelCheckerHome = System
+ .getProperty("user.home")
+ + "/model-checker-priv/model-checker-priv/";
+ public final static String BenchmarksDir = ModelCheckerHome
+ + "/benchmarks/";
+ public final static String ModelCheckerTestDir = ModelCheckerHome
+ + "/test-cdsspec/";
+ public final static String GeneratedFilesDir = ModelCheckerTestDir;
+
+ public final static String REGISTER_ACQREL = "register-acqrel";
+ public final static String REGISTER_RELAXED = "register-relaxed";
+ public final static String MS_QUEUE = "ms-queue";
+ public final static String LINUXRWLOCKS = "linuxrwlocks";
+ public final static String MCS_LOCK = "mcs-lock";
+ public final static String DEQUE = "chase-lev-deque-bugfix";
+ public final static String DEQUE_BUGGY = "chase-lev-deque";
+ public final static String TREIBER_STACK = "treiber-stack";
+ public final static String TICKET_LOCK = "ticket-lock";
+ public final static String SEQLOCK = "seqlock";
+ public final static String READ_COPY_UPDATE = "read-copy-update";
+ public final static String CONCURRENT_MAP = "concurrent-hashmap";
+ public final static String SPSC = "spsc-bugfix";
+ public final static String MPMC = "mpmc-queue";
+
+ public final static String BLOCKING_QUEUE_EXAMPLE = "blocking-mpmc-example";
+
+ public final static String[] Benchmarks = {
+ MS_QUEUE,
+ LINUXRWLOCKS,
+ MCS_LOCK,
+ DEQUE,
+ DEQUE_BUGGY,
+ TICKET_LOCK,
+ SEQLOCK,
+ READ_COPY_UPDATE,
+ SPSC,
+ CONCURRENT_MAP,
+ MPMC,
+ BLOCKING_QUEUE_EXAMPLE,
+ };
+
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+import java.util.ArrayList;
+
+import edu.uci.eecs.specExtraction.SpecUtils.IntObj;
+
+/**
+ * <p>
+ * This class represents a piece of code --- a list of strings (lines). Such a
+ * wrapper makes code extraction and generation easier.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class Code {
+
+ // The list that represents all the lines of this code snippet
+ public final ArrayList<String> lines;
+
+ public Code() {
+ lines = new ArrayList<String>();
+ }
+
+ /**
+ * <p>
+ * It adds a line, which is very likely to be a simple C/C++ statement,
+ * which is a string lines of statement.
+ * </p>
+ *
+ * @param line
+ * A C/C++ line of statement
+ */
+ public void addLine(String line) {
+ lines.add(line);
+ }
+
+ /**
+ * <p>
+ * It adds a list of lines, which are very likely to be simple C/C++
+ * statements.
+ * </p>
+ *
+ * @param line
+ * A list of C/C++ lines of statement
+ */
+ public void addLines(ArrayList<String> code) {
+ for (int i = 0; i < code.size(); i++)
+ lines.add(code.get(i));
+ }
+
+ /**
+ * <p>
+ * It adds a list of lines, which are very likely to be simple C/C++
+ * statements.
+ * </p>
+ *
+ * @param line
+ * A Code object --- list of C/C++ lines of statement
+ */
+ public void addLines(Code code) {
+ addLines(code.lines);
+ }
+
+ /**
+ * @return Whether this code snippet is empty
+ */
+ public boolean isEmpty() {
+ return lines.size() == 0;
+ }
+
+ /**
+ * <p>
+ * Align the set of code with an initial number of tabs. This basically
+ * tries to make the generated code more readable.
+ *
+ * @param initialTabsCnt
+ * The number of tabs that we want to put before the code
+ * initially.
+ */
+ public void align(int initialTabsCnt) {
+ int tabLevel = initialTabsCnt;
+ IntObj idx = new IntObj(0);
+ alignHelper(idx, tabLevel, false);
+ }
+
+ /**
+ * <p>
+ * This is a helper function to align a list of code. Caller should
+ * initialize an IntObj with intial value "0", and pass with the initial tab
+ * level and pass the value of "false" to the noBraceKeyword.
+ *
+ * @param idx
+ * The IntObj that represents the current index of the code lines
+ * @param tabLevel
+ * The tab level we are currently at
+ * @param noBraceKeyword
+ * Whether we just encountered a "noBraceKeyword"
+ */
+ private void alignHelper(IntObj idx, int tabLevel, boolean noBraceKeyword) {
+ for (; idx.getVal() < lines.size(); idx.inc()) {
+ String curLine = lines.get(idx.getVal());
+ String newLine = null;
+ // Return to the previous recursive level
+ if (closingBrace(curLine)) {
+ return;
+ }
+ if ((noBraceKeyword && !keywordBrace(curLine) && !keywordNoBrace(curLine))) {
+ // Before returning, we just need to first add the line with the
+ // current tab
+ newLine = makeTabs(tabLevel) + curLine;
+ lines.set(idx.getVal(), newLine);
+ return;
+ }
+
+ newLine = makeTabs(tabLevel) + curLine;
+ lines.set(idx.getVal(), newLine);
+
+ if (keywordBrace(curLine)) {
+ idx.inc();
+ alignHelper(idx, tabLevel + 1, false);
+ // Add the closing line
+ curLine = lines.get(idx.getVal());
+ newLine = makeTabs(tabLevel) + curLine;
+ lines.set(idx.getVal(), newLine);
+ } else if (keywordNoBrace(curLine)) { // No brace
+ idx.inc();
+ alignHelper(idx, tabLevel + 1, true);
+ if (noBraceKeyword)
+ return;
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * Lines that starts with a key word and ends with ";".
+ * </p>
+ *
+ * @param curLine
+ * @return
+ */
+ private boolean closingBrace(String curLine) {
+ return curLine.endsWith("}");
+ }
+
+ /**
+ * <p>
+ * Lines that starts with a key word and ends with "{".
+ * </p>
+ *
+ * @param curLine
+ * @return
+ */
+ private boolean keywordBrace(String curLine) {
+ return (curLine.startsWith("for") || curLine.startsWith("ForEach")
+ || curLine.startsWith("if") || curLine.startsWith("else")
+ || curLine.startsWith("while") || curLine.startsWith("do"))
+ && curLine.endsWith("{");
+ }
+
+ /**
+ * <p>
+ * Lines that starts with a key word and ends with no "{" and no ";".
+ * </p>
+ *
+ * @param curLine
+ * @return
+ */
+ private boolean keywordNoBrace(String curLine) {
+ return (curLine.startsWith("for") || curLine.startsWith("ForEach")
+ || curLine.startsWith("if") || curLine.startsWith("else")
+ || curLine.startsWith("while") || curLine.startsWith("do"))
+ && !curLine.endsWith("{") && !curLine.endsWith(";");
+ }
+
+ /**
+ * @param tabCnt
+ * The number of tabs
+ * @return Generate a string whose content is a specific number (tabCnt) of
+ * tab symbols.
+ */
+ private String makeTabs(int tabCnt) {
+ String res = "";
+ for (int i = 0; i < tabCnt; i++)
+ res = res + "\t";
+ return res;
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ for (int i = 0; i < lines.size(); i++) {
+ sb.append(lines.get(i) + "\n");
+ }
+ return sb.toString();
+ }
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+/**
+ * <p> This class represents a commutativity rule in the specification.
+ * @author Peizhao Ou
+ *
+ */
+public class CommutativityRule {
+ public final String method1, method2;
+
+ public final String condition;
+
+ public CommutativityRule(String m1, String m2, String condition) {
+ this.method1 = m1;
+ this.method2 = m2;
+ this.condition = condition;
+ }
+
+ public String toString() {
+ return method1 + " <-> " + method2 + ": " + condition;
+ }
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+import java.io.File;
+
+/**
+ * <p>
+ * An abstract class for all different specification constructs, including
+ * global construct, interface construct and ordering point construct.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+abstract public class Construct {
+ // The file that this construct is in
+ public final File file;
+ // The beginning line number of this construct (the plain text line number)
+ public final int beginLineNum;
+
+ public Construct(File file, int beginLineNum) {
+ this.file = file;
+ this.beginLineNum = beginLineNum;
+ }
+
+ public String toString() {
+ return file.getName() + ": Line " + Integer.toString(beginLineNum);
+ }
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import edu.uci.eecs.specExtraction.SpecUtils.IntObj;
+import edu.uci.eecs.specExtraction.SpecUtils.Primitive;
+import edu.uci.eecs.utilParser.ParseException;
+import edu.uci.eecs.utilParser.UtilParser;
+
+/**
+ * <p>
+ * This class is a subclass of Construct. It represents user-defined code that
+ * we allow in the header file. Note that the code here basically are the same
+ * as writing code right in place. We require function declaration/definition to
+ * be inline. Users should be responsible for the correctness of their code.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class DefineConstruct extends Construct {
+ public final Code code;
+
+ // The ending line number of the specification annotation construct
+ public final int endLineNum;
+
+ public DefineConstruct(File file, int beginLineNum, int endLineNum,
+ ArrayList<String> annotations) throws WrongAnnotationException {
+ super(file, beginLineNum);
+ code = new Code();
+ this.endLineNum = endLineNum;
+ Primitive define = SpecUtils.extractPrimitive(file, beginLineNum,
+ annotations, new IntObj(0));
+ code.addLines(define.contents);
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString() + "\n");
+ sb.append("@Define:\n");
+ if (!code.isEmpty()) {
+ sb.append(code);
+ sb.append("\n");
+ }
+ return sb.toString();
+ }
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+import java.io.File;
+
+/**
+ * <p>
+ * This class is a subclass of Construct. It represents a complete entry
+ * annotation.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class EntryConstruct extends Construct {
+
+ // The original line of text of the entry annotation
+ public final String annotation;
+
+ public EntryConstruct(File file, int beginLineNum, String annotation) {
+ super(file, beginLineNum);
+ this.annotation = annotation;
+ }
+
+ public String toString() {
+ StringBuffer res = new StringBuffer();
+ res.append(super.toString() + "\n");
+ res.append("@Entry");
+ return res.toString();
+ }
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+import java.util.ArrayList;
+
+/**
+ * <p>
+ * This represents a function declaration header. For example,
+ * "void myFunction(int arg1, bool arg2)" is a function declaration header.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class FunctionHeader {
+ private ArrayList<VariableDeclaration> templateList;
+
+ // The return type of the function
+ public final String returnType;
+ // The function name
+ public final QualifiedName funcName;
+ // The argument list (with formal parameter names)
+ public final ArrayList<VariableDeclaration> args;
+
+ // The actually function definition line in plain text.
+ // E.g. "void myFunction(int arg1, bool arg2) {"
+ private String headerLine;
+
+ public FunctionHeader(String returnType, QualifiedName qualifiedName,
+ ArrayList<VariableDeclaration> args) {
+ this.templateList = null;
+ this.returnType = returnType;
+ this.funcName = qualifiedName;
+ this.args = args;
+ }
+
+ /**
+ *
+ * @return Whether the return type is void
+ */
+ public boolean isReturnVoid() {
+ return returnType.equals("void");
+ }
+
+ public void setTemplateList(ArrayList<VariableDeclaration> templateList) {
+ this.templateList = templateList;
+ }
+
+ public ArrayList<VariableDeclaration> getTemplateList() {
+ return this.templateList;
+ }
+
+ public String getTemplateFullStr() {
+ String templateStr = "";
+ if (templateList == null)
+ return templateStr;
+ VariableDeclaration decl;
+ decl = templateList.get(0);
+ templateStr = "<" + decl.type + " " + decl.name;
+ for (int i = 1; i < templateList.size(); i++) {
+ decl = templateList.get(i);
+ templateStr = templateStr + ", " + decl.type + " " + decl.name;
+ }
+ templateStr = templateStr + ">";
+ return templateStr;
+ }
+
+ public String getTemplateArgStr() {
+ String templateStr = null;
+ if (templateList.size() == 0)
+ return templateStr;
+ templateStr = "<" + templateList.get(0).name;
+ for (int i = 1; i < templateList.size(); i++) {
+ templateStr = templateStr + ", " + templateList.get(i);
+ }
+ templateStr = templateStr + ">";
+ return templateStr;
+ }
+
+ public String getFuncStr() {
+ String res = returnType + " " + funcName.fullName + "(";
+ if (args.size() >= 1) {
+ res = res + args.get(0);
+ }
+ for (int i = 1; i < args.size(); i++) {
+ res = res + ", " + args.get(i);
+ }
+ res = res + ")";
+ return res;
+ }
+
+ public String toString() {
+ String res = returnType + " " + funcName.fullName + "(";
+ if (args.size() >= 1) {
+ res = res + args.get(0);
+ }
+ for (int i = 1; i < args.size(); i++) {
+ res = res + ", " + args.get(i);
+ }
+ res = res + ")";
+ return res;
+ }
+
+ public String getRenamedFuncName() {
+ return funcName.qualifiedName + SpecNaming.WrapperPrefix + "_"
+ + funcName.bareName;
+ }
+
+ public FunctionHeader getRenamedHeader(String prefix) {
+ String newFullName = getRenamedFuncName();
+ FunctionHeader newHeader = new FunctionHeader(returnType,
+ new QualifiedName(newFullName), args);
+ return newHeader;
+ }
+
+ public FunctionHeader getRenamedHeader() {
+ return getRenamedHeader(SpecNaming.WrapperPrefix);
+ }
+
+ /**
+ *
+ * @return The string that represents the renamed function header line. For
+ * example, we would return
+ * <code>"bool Wrapper_myFunc(int x, int y)"</code> for the fucntion
+ * <code>"bool myFunc(int x, int y) {"</code>
+ */
+ public String getRenamedFuncLine() {
+ String bareName = this.funcName.bareName;
+ String newName = this.getRenamedFuncName();
+ return this.headerLine.replaceFirst(bareName, newName);
+ }
+
+ /**
+ *
+ * @return The string that represents the renamed function call. For
+ * example, we would return <code>"bool RET = myFunc(x, y)"</code>
+ * for the fucntion <code>"bool myFunc(int x, int y)"</code>
+ */
+ public String getRenamedCall() {
+ return getRenamedCall(SpecNaming.WrapperPrefix);
+ }
+
+ /**
+ *
+ * @return The original plain text line of the function header
+ */
+ public String getHeaderLine() {
+ return headerLine;
+ }
+
+
+ // No support for template right now
+ public String getDeclaration() {
+ String res = returnType + " " + funcName.fullName + "(";
+ if (args.size() >= 1) {
+ res = res + args.get(0).type + " " + args.get(0).name;
+ }
+ for (int i = 1; i < args.size(); i++) {
+ res = res + ", " + args.get(i).type + " " + args.get(i).name;
+ }
+ res = res + ")";
+ return res;
+ }
+
+ public String getRenamedCall(String prefix) {
+ String res = "";
+ if (!isReturnVoid()) {
+ res = res + returnType + " " + SpecNaming.C_RET + " = ";
+ }
+ res = res + prefix + "_" + funcName.fullName + "(";
+ if (args.size() >= 1) {
+ res = res + args.get(0).name;
+ }
+ for (int i = 1; i < args.size(); i++) {
+ res = res + ", " + args.get(i).name;
+ }
+ res = res + ")";
+ return res;
+ }
+
+ public void setHeaderLine(String headerLine) {
+ this.headerLine = headerLine;
+ }
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import edu.uci.eecs.specExtraction.SpecUtils.IntObj;
+import edu.uci.eecs.specExtraction.SpecUtils.Primitive;
+
+/**
+ * <p>
+ * This class is a subclass of Construct. It represents a complete global state
+ * annotation.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class GlobalConstruct extends Construct {
+ public final ArrayList<VariableDeclaration> declState;
+ public final Code initState;
+ public final Code copyState;
+ public final Code clearState;
+ public final Code finalState;
+ public final Code printState;
+ public final ArrayList<CommutativityRule> commutativityRules;
+
+ // Whether the state declaration is empty
+ public final boolean emptyState;
+ // Whether we have auto-gen the state initialization code
+ public final boolean autoGenInitial;
+ // Whether we have auto-gen the state copying code
+ public final boolean autoGenCopy;
+ // Whether we have auto-gen the state clearing code
+ public final boolean autoGenClear;
+ // Whether we have auto-gen the state printing code
+ public final boolean autoGenPrint;
+
+ public GlobalConstruct(File file, int beginLineNum,
+ ArrayList<String> annotations) throws WrongAnnotationException {
+ super(file, beginLineNum);
+ declState = new ArrayList<VariableDeclaration>();
+ initState = new Code();
+ copyState = new Code();
+ clearState = new Code();
+ finalState = new Code();
+ printState = new Code();
+ commutativityRules = new ArrayList<CommutativityRule>();
+
+ processAnnotations(annotations);
+
+ emptyState = declState.isEmpty();
+ if (emptyState) {
+ WrongAnnotationException.warning(file, beginLineNum,
+ "The state is empty. Make sure that's what you want!");
+ // Add a fake state declaration
+ declState.add(new VariableDeclaration("int", "FakeState"));
+ }
+
+ autoGenInitial = initState.isEmpty();
+ if (autoGenInitial) {
+ Code code = generateAutoInitalFunction();
+ initState.addLines(code);
+ }
+
+ autoGenCopy = copyState.isEmpty();
+ if (autoGenCopy) {
+ Code code = generateAutoCopyFunction();
+ copyState.addLines(code);
+ }
+
+ autoGenClear = clearState.isEmpty();
+ if (autoGenClear) {
+ Code code = generateAutoClearFunction();
+ clearState.addLines(code);
+ }
+
+ autoGenPrint = printState.isEmpty();
+ if (autoGenPrint) {
+ Code code = generateAutoPrintFunction();
+ printState.addLines(code);
+ }
+ }
+
+ /**
+ * <p>
+ * This function will automatically generate the initial statements for
+ * supported types if the user has not defined the "@Initial" primitive
+ * </p>
+ *
+ * @return The auto-generated state intialization statements
+ * @throws WrongAnnotationException
+ */
+ private Code generateAutoInitalFunction() throws WrongAnnotationException {
+ Code code = new Code();
+ if (emptyState) // Empty state should have empty initial function
+ return code;
+ for (VariableDeclaration decl : declState) {
+ String type = decl.type;
+ String name = decl.name;
+ // Primitive types
+ if (type.equals("int") || type.equals("unsigned")
+ || type.equals("unsigned int")
+ || type.equals("int unsigned") || type.equals("double")
+ || type.equals("double") || type.equals("bool")) {
+ // x = 0;
+ code.addLine(name + " = 0;");
+ } else if (type.equals("IntList") || type.equals("IntSet")
+ || type.equals("IntMap")) {
+ // Supported types
+ // q = IntList();
+ code.addLine(name + " = " + type + "();");
+ } else if (type.equals("IntList *") || type.equals("IntSet *")
+ || type.equals("IntMap *")) {
+ // Supported pointer types
+ // q = new IntList;
+ String originalType = SpecUtils.trimSpace(type
+ .replace('*', ' '));
+ code.addLine(name + " = new " + originalType + "();");
+ } else {
+ WrongAnnotationException
+ .err(file,
+ beginLineNum,
+ "You have types in the state declaration that we do not support auto-gen initial function.");
+ }
+ }
+
+ return code;
+ }
+
+ /**
+ * <p>
+ * This function will automatically generate the copy statements for
+ * supported types if the user has not defined the "@Copy" primitive
+ * </p>
+ *
+ * @return The auto-generated state copy statements
+ * @throws WrongAnnotationException
+ */
+ private Code generateAutoCopyFunction() throws WrongAnnotationException {
+ Code code = new Code();
+ if (emptyState) // Empty state should have empty copy function
+ return code;
+ for (VariableDeclaration decl : declState) {
+ String type = decl.type;
+ String name = decl.name;
+ // Primitive types
+ if (type.equals("int") || type.equals("unsigned")
+ || type.equals("unsigned int")
+ || type.equals("int unsigned") || type.equals("double")
+ || type.equals("double") || type.equals("bool")) {
+ // NEW->x = OLD->x;
+ code.addLine(SpecNaming.NewStateInst + "->" + name + " = "
+ + SpecNaming.OldStateInst + "->" + name + ";");
+ } else if (type.equals("IntList") || type.equals("IntSet")
+ || type.equals("IntMap")) {
+ // Supported types
+ // New->q = IntList(OLD->q);
+ code.addLine(SpecNaming.NewStateInst + "->" + name + " = "
+ + type + "(" + SpecNaming.OldStateInst + "->" + name
+ + ");");
+ } else if (type.equals("IntList *") || type.equals("IntSet *")
+ || type.equals("IntMap *")) {
+ // Supported pointer types
+ // New->q = new IntList(*OLD->q);
+ String originalType = SpecUtils.trimSpace(type
+ .replace('*', ' '));
+ code.addLine(SpecNaming.NewStateInst + "->" + name + " = new "
+ + originalType + "(*" + SpecNaming.OldStateInst + "->"
+ + name + ");");
+ } else {
+ WrongAnnotationException
+ .err(file,
+ beginLineNum,
+ "You have types in the state declaration that we do not support auto-gen copy function.");
+ }
+ }
+
+ return code;
+ }
+
+ /**
+ * <p>
+ * This function will automatically generate the clear statements for
+ * supported types if the user has not defined the "@Clear" primitive
+ * </p>
+ *
+ * @return The auto-generated state copy statements
+ * @throws WrongAnnotationException
+ */
+ private Code generateAutoClearFunction() throws WrongAnnotationException {
+ Code code = new Code();
+ if (emptyState) // Empty state should have empty copy function
+ return code;
+
+ // FIXME: Just try our best to generate recycling statements
+ for (VariableDeclaration decl : declState) {
+ String type = decl.type;
+ String name = decl.name;
+ if (type.equals("IntList *") || type.equals("IntSet *")
+ || type.equals("IntMap *")) {
+ // Supported pointer types
+ // if (stack) delete stack;
+ code.addLine("if (" + name + ") delete " + name + ";");
+ }
+ }
+
+ return code;
+ }
+
+ /**
+ * <p>
+ * This function will automatically generate the printing statements for
+ * supported types if the user has not defined the "@Print" primitive
+ * </p>
+ *
+ * @return The auto-generated state printing statements
+ * @throws WrongAnnotationException
+ */
+ private Code generateAutoPrintFunction() throws WrongAnnotationException {
+ Code code = new Code();
+ if (emptyState) // Empty state should have empty printing function
+ return code;
+ for (VariableDeclaration decl : declState) {
+ String type = decl.type;
+ String name = decl.name;
+ code.addLines(SpecUtils.generatePrintStatement(type, name));
+ }
+
+ return code;
+ }
+
+ /**
+ * <p>
+ * Assert that the global state primitive is valid; if not, throws an
+ * exception.
+ * </p>
+ *
+ * @param file
+ * Current file
+ * @param primitive
+ * The primitive that we have extracted earlier
+ * @throws WrongAnnotationException
+ */
+ private void assertValidPrimitive(File file, Primitive primitive)
+ throws WrongAnnotationException {
+ int lineNum = primitive.beginLineNum;
+ String name = primitive.name;
+ if (!name.equals(SpecNaming.DeclareState)
+ && !name.equals(SpecNaming.InitalState)
+ && !name.equals(SpecNaming.CopyState)
+ && !name.equals(SpecNaming.ClearState)
+ && !name.equals(SpecNaming.FinalState)
+ && !name.equals(SpecNaming.Commutativity)
+ && !name.equals(SpecNaming.PrintState)) {
+ WrongAnnotationException.err(file, lineNum, name
+ + " is NOT a valid CDSSpec global state primitive.");
+ }
+ }
+
+ /**
+ * <p>
+ * Given a "@DeclareState" primitive that has a list of strings of
+ * declarations, we initialize our local "declState" members.
+ *
+ * @param primitive
+ * @throws WrongAnnotationException
+ */
+ private void processDeclState(Primitive primitive)
+ throws WrongAnnotationException {
+ for (int i = 0; i < primitive.contents.size(); i++) {
+ int lineNum = i + primitive.beginLineNum;
+ String declLine = primitive.contents.get(i);
+ VariableDeclaration tmp = new VariableDeclaration(file, lineNum,
+ declLine);
+ declState.add(tmp);
+ }
+ }
+
+ /**
+ * <p>
+ * Given a "@DeclareState" primitive that has a list of strings of
+ * declarations, we initialize our local "declState" members.
+ *
+ * @param primitive
+ * @throws WrongAnnotationException
+ */
+ private void processCommutativity(Primitive primitive)
+ throws WrongAnnotationException {
+ // Mathch commutativity rule
+ Pattern regexpCommute = Pattern
+ .compile("\\s*(\\w+)\\s*<->\\s*(\\w+)\\s*\\((.*)\\)\\s*$");
+ Matcher matcherCommute = regexpCommute.matcher("");
+
+ for (int i = 0; i < primitive.contents.size(); i++) {
+ // FIXME: Currently we only allow a one-line commutativity rule
+ int curLineNum = primitive.beginLineNum + i;
+ String line = primitive.contents.get(i);
+ matcherCommute.reset(line);
+ if (matcherCommute.find()) {
+ String method1 = matcherCommute.group(1);
+ String method2 = matcherCommute.group(2);
+ String code = matcherCommute.group(3);
+ String rule = SpecUtils.trimSpace(SpecUtils
+ .trimTrailingCommentSymbol(code));
+ commutativityRules.add(new CommutativityRule(method1, method2,
+ rule));
+ } else {
+ WrongAnnotationException
+ .err(file,
+ curLineNum,
+ "The @Commutativity annotation should be: @Commutativity: Method1 <-> Method2 (condition)\n\t"
+ + "Problematic line: \"" + line + "\"");
+ }
+ // Done with processing the current commutativity
+ }
+ }
+
+ private void processAnnotations(ArrayList<String> annotations)
+ throws WrongAnnotationException {
+ IntObj curIdx = new IntObj(0);
+ Primitive primitive = null;
+
+ while ((primitive = SpecUtils.extractPrimitive(file, beginLineNum,
+ annotations, curIdx)) != null) {
+ String name = primitive.name;
+ assertValidPrimitive(file, primitive);
+ if (primitive.contents.size() == 0)
+ continue;
+ if (name.equals(SpecNaming.DeclareState)) {
+ processDeclState(primitive);
+ } else if (name.equals(SpecNaming.InitalState)) {
+ initState.addLines(primitive.contents);
+ } else if (name.equals(SpecNaming.CopyState)) {
+ copyState.addLines(primitive.contents);
+ } else if (name.equals(SpecNaming.ClearState)) {
+ clearState.addLines(primitive.contents);
+ } else if (name.equals(SpecNaming.FinalState)) {
+ finalState.addLines(primitive.contents);
+ } else if (name.equals(SpecNaming.PrintState)) {
+ printState.addLines(primitive.contents);
+ } else if (name.equals(SpecNaming.Commutativity)) {
+ processCommutativity(primitive);
+ }
+ }
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder("");
+ sb.append(super.toString() + "\n");
+ sb.append("@DeclareState:\n");
+ sb.append(declState);
+ sb.append("\n");
+ sb.append("@InitState:\n");
+ sb.append(initState);
+ if (!printState.isEmpty()) {
+ sb.append("@Print:\n");
+ sb.append(printState);
+ }
+
+ for (int i = 0; i < commutativityRules.size(); i++) {
+ sb.append("@Commutativity: " + commutativityRules + "\n");
+ }
+ return sb.toString();
+ }
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.HashMap;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import edu.uci.eecs.specExtraction.SpecUtils.IntObj;
+import edu.uci.eecs.specExtraction.SpecUtils.Primitive;
+import edu.uci.eecs.utilParser.ParseException;
+import edu.uci.eecs.utilParser.UtilParser;
+
+/**
+ * <p>
+ * This class is a subclass of Construct. It represents a complete interface
+ * annotation.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class InterfaceConstruct extends Construct {
+ // The interface label of the current interface; If not specified, we use
+ // the actual interface name to represent this interface
+ private String name;
+ // Each interface interface will have an auto-generated struct that
+ // represents the return value and the arguments of that interface method
+ // call. We will mangle the interface label name to get this name in case we
+ // have other name conflict
+ private String structName;
+ public final Code preCondition;
+ public final Code justifyingPrecondition;
+ public final Code transition;
+ public final Code justifyingPostcondition;
+ public final Code postCondition;
+ public final Code print;
+
+ // The ending line number of the specification annotation construct
+ public final int endLineNum;
+
+ // The ending line number of the function definition
+ private int endLineNumFunction;
+ // The function header of the corresponding interface --- The list of
+ // variable declarations that represent the RETURN value and
+ // arguments of the interface
+ private FunctionHeader funcHeader;
+
+ public final boolean autoGenPrint;
+
+ public InterfaceConstruct(File file, int beginLineNum, int endLineNum,
+ ArrayList<String> annotations) throws WrongAnnotationException {
+ super(file, beginLineNum);
+ this.endLineNum = endLineNum;
+ this.name = null;
+ this.structName = null;
+ this.preCondition = new Code();
+ this.justifyingPrecondition = new Code();
+ this.transition = new Code();
+ this.justifyingPostcondition = new Code();
+ this.postCondition = new Code();
+ this.print = new Code();
+
+ processAnnotations(annotations);
+
+ autoGenPrint = print.isEmpty();
+ }
+
+ public FunctionHeader getFunctionHeader() {
+ return this.funcHeader;
+ }
+
+ public boolean equals(Object other) {
+ if (!(other instanceof InterfaceConstruct)) {
+ return false;
+ }
+ InterfaceConstruct o = (InterfaceConstruct) other;
+ if (o.name.equals(this.name))
+ return true;
+ else
+ return false;
+ }
+
+ public String getName() {
+ return this.name;
+ }
+
+ /**
+ * <p>
+ * This function will automatically generate the printing statements for
+ * supported types if the user has not defined the "@Print" primitive
+ * </p>
+ *
+ * @return The auto-generated state printing statements
+ * @throws WrongAnnotationException
+ */
+ private Code generateAutoPrintFunction() {
+ Code code = new Code();
+ // For C_RET
+ code.addLines(SpecUtils.generatePrintStatement(funcHeader.returnType,
+ SpecNaming.C_RET));
+ // For arguments
+ for (VariableDeclaration decl : funcHeader.args) {
+ String type = decl.type;
+ String name = decl.name;
+ code.addLines(SpecUtils.generatePrintStatement(type, name));
+ }
+ return code;
+ }
+
+ /**
+ * <p>
+ * Assert that the interface primitive is valid; if not, throws an exception
+ * </p>
+ *
+ * @param file
+ * Current file
+ * @param primitive
+ * The primitive string that we have extracted earlier
+ * @throws WrongAnnotationException
+ */
+ private void assertValidPrimitive(File file, Primitive primitive)
+ throws WrongAnnotationException {
+ int lineNum = primitive.beginLineNum;
+ String name = primitive.name;
+ if (!name.equals(SpecNaming.Interface)
+ && !name.equals(SpecNaming.Transition)
+ && !name.equals(SpecNaming.PreCondition)
+ && !name.equals(SpecNaming.JustifyingPrecondition)
+ && !name.equals(SpecNaming.SideEffect)
+ && !name.equals(SpecNaming.JustifyingPostcondition)
+ && !name.equals(SpecNaming.PostCondition)
+ && !name.equals(SpecNaming.PrintValue)) {
+ WrongAnnotationException.err(file, lineNum, name
+ + " is NOT a valid CDSSpec interface primitive.");
+ }
+ }
+
+ /**
+ * <p>
+ * Assert that the "@Interface" primitive has correct syntax; if not, throws
+ * an exception. If so, it basically checks whether content of the primitive
+ * is a valid word and then return interface label name.
+ * </p>
+ *
+ * @param file
+ * Current file
+ * @param lineNum
+ * Current line number
+ * @param primitive
+ * The primitive string that we have extracted earlier
+ * @throws WrongAnnotationException
+ */
+ private String extractInterfaceName(File file, Primitive primitive)
+ throws WrongAnnotationException {
+ int lineNum = primitive.beginLineNum;
+ String name = primitive.name;
+ if (primitive.contents.size() != 1)
+ WrongAnnotationException.err(file, lineNum,
+ "The @Interface primitive: " + name + " has wrong syntax.");
+ String line = primitive.contents.get(0);
+ SpecUtils.matcherWord.reset(line);
+ if (!SpecUtils.matcherWord.find())
+ WrongAnnotationException.err(file, lineNum, name
+ + " is NOT a valid CDSSpec @Interface primitive.");
+ return line;
+ }
+
+ private void processAnnotations(ArrayList<String> annotations)
+ throws WrongAnnotationException {
+ IntObj curIdx = new IntObj(0);
+ Primitive primitive = null;
+
+ while ((primitive = SpecUtils.extractPrimitive(file, beginLineNum,
+ annotations, curIdx)) != null) {
+ String name = primitive.name;
+ assertValidPrimitive(file, primitive);
+ if (primitive.contents.size() == 0)
+ continue;
+ if (name.equals(SpecNaming.Interface)) {
+ String interName = extractInterfaceName(file, primitive);
+ setNames(interName);
+ } else if (name.equals(SpecNaming.Transition)) {
+ this.transition.addLines(primitive.contents);
+ } else if (name.equals(SpecNaming.PreCondition)) {
+ this.preCondition.addLines(primitive.contents);
+ } else if (name.equals(SpecNaming.JustifyingPrecondition)) {
+ this.justifyingPrecondition.addLines(primitive.contents);
+ } else if (name.equals(SpecNaming.JustifyingPostcondition)) {
+ this.justifyingPostcondition.addLines(primitive.contents);
+ } else if (name.equals(SpecNaming.PostCondition)) {
+ this.postCondition.addLines(primitive.contents);
+ } else if (name.equals(SpecNaming.PrintValue)) {
+ this.print.addLines(primitive.contents);
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * This function is called to extract all the declarations that should go to
+ * the corresponding value struct --- a C++ struct to be generated for this
+ * interface that contains the information of the return value and the
+ * arguments.
+ * </p>
+ *
+ * @param line
+ * The line that represents the interface declaration line
+ * @throws ParseException
+ */
+ public void processFunctionDeclaration(String line) throws ParseException {
+ // FIXME: Currently we only allow the declaration to be one-liner
+ funcHeader = UtilParser.parseFuncHeader(line);
+ // Record the original declaration line
+ funcHeader.setHeaderLine(line);
+
+ // If users have not defined @Interface, we should use the function name
+ // as the interface label
+ if (name == null) {
+ setNames(funcHeader.funcName.bareName);
+ }
+
+ // Once we have the compelte function declaration, we can auto-gen the
+ // print-out statements if it's not defined
+ if (autoGenPrint) {
+ print.addLines(generateAutoPrintFunction());
+ }
+ }
+
+ public String toString() {
+ StringBuilder sb = new StringBuilder();
+ sb.append(super.toString() + "\n");
+ sb.append("@Interface: " + name + "\n");
+ if (!transition.isEmpty())
+ sb.append("@Transition:\n" + transition);
+ if (!preCondition.isEmpty())
+ sb.append("@PreCondition:\n" + preCondition);
+ if (!postCondition.isEmpty())
+ sb.append("@PostCondition:\n" + postCondition);
+ if (!print.isEmpty())
+ sb.append("@Print:\n" + print + "\n");
+ sb.append(funcHeader);
+
+ return sb.toString();
+ }
+
+ public int getEndLineNumFunction() {
+ return endLineNumFunction;
+ }
+
+ public void setEndLineNumFunction(int endLineNumFunction) {
+ this.endLineNumFunction = endLineNumFunction;
+ }
+
+ public String getStructName() {
+ return structName;
+ }
+
+ private void setNames(String name) {
+ this.name = name;
+ if (name == null)
+ return;
+ structName = createStructName(name);
+ }
+
+ static public String createStructName(String labelName) {
+ return "__struct_" + labelName + "__";
+ }
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * <p>
+ * This class represents one complete ordering point annotation. We integrate
+ * all ordering point annotations into this class. We use the ordering point
+ * type (OPType) to differentiate each specific ordering point annotation.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class OPConstruct extends Construct {
+ // The ordering point type
+ public final OPType type;
+ // The ordering point label --- only for OPCheck and PotentialOP; otherwise
+ // null
+ public final String label;
+ // The condition under which the current annotation will be instrumented
+ public final String condition;
+
+ // The original line of text of the entry annotation
+ public final String annotation;
+
+ public OPConstruct(File file, int beginLineNum, OPType type, String label,
+ String condition, String annotation) {
+ super(file, beginLineNum);
+ this.type = type;
+ this.label = label;
+ // Trim the preceding and trailing spaces
+ this.condition = SpecUtils.trimSpace(condition);
+ this.annotation = annotation;
+ }
+
+ public String toString() {
+ StringBuffer res = new StringBuffer();
+ res.append(super.toString() + "\n");
+ res.append("@");
+ res.append(type);
+ if (type == OPType.PotentialOP || type == OPType.OPCheck) {
+ res.append("(" + label + ")");
+ }
+ res.append(": ");
+ res.append(condition + "\n");
+ return res.toString();
+ }
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+/**
+ * <p>
+ * The ordering point types.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public enum OPType {
+ OPDefine, PotentialOP, OPCheck, OPClear, OPClearDefine, OPDefineUnattached, OPClearDefineUnattached
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+/**
+ * <p>
+ * This class represents a qualified variable name in C++, e.g.
+ * Base::Mine::func. We use this class in the FunctionHeader class to represent
+ * the name of the function. However, for the sake of simplicity in usage, we
+ * only use it as a plain string with the bareName.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class QualifiedName {
+ // The full name --- Base::Mine::func
+ public final String fullName;
+ // The bare name --- func
+ public final String bareName;
+ // The qualified name --- Base::Mine
+ public final String qualifiedName;
+
+ public QualifiedName(String fullName) {
+ this.fullName = fullName;
+ this.bareName = getBareName();
+ this.qualifiedName = getQualifiedName();
+ }
+
+ private String getBareName() {
+ int beginIdx;
+ beginIdx = fullName.lastIndexOf(':');
+ if (beginIdx == -1)
+ return fullName;
+ else
+ return fullName.substring(beginIdx + 1);
+ }
+
+ private String getQualifiedName() {
+ int endIdx = fullName.lastIndexOf(bareName);
+ if (endIdx == 0)
+ return "";
+ return fullName.substring(0, endIdx);
+ }
+
+ public String toString() {
+ return fullName + "\n" + bareName;
+ }
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+import java.io.BufferedReader;
+import java.io.File;
+import java.io.FileNotFoundException;
+import java.io.FileReader;
+import java.io.IOException;
+import java.io.LineNumberReader;
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashMap;
+import java.util.HashSet;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import edu.uci.eecs.codeGenerator.CodeGeneratorUtils;
+import edu.uci.eecs.codeGenerator.Environment;
+import edu.uci.eecs.utilParser.ParseException;
+
+/**
+ * <p>
+ * This class represents the specification extractor of the specification. The
+ * main function of this class is to read C/C++11 source files and extract the
+ * corresponding specifications, and record corresponding information such as
+ * location, e.g., the file name and the line number, to help the code
+ * generation process.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class SpecExtractor {
+ public final HashMap<File, ArrayList<DefineConstruct>> defineListMap;
+ public final HashMap<File, ArrayList<InterfaceConstruct>> interfaceListMap;
+ public final HashMap<File, ArrayList<OPConstruct>> OPListMap;
+ public final HashSet<String> OPLabelSet;
+ // Note that we only allow one entry per file at most
+ public final HashMap<File, EntryConstruct> entryMap;
+
+ public final HashSet<String> headerFiles;
+
+ // In the generated header file, we need to forward the user-defined
+ public final HashSet<String> forwardClass;
+
+ private GlobalConstruct globalConstruct;
+
+ public SpecExtractor() {
+ defineListMap = new HashMap<File, ArrayList<DefineConstruct>>();
+ interfaceListMap = new HashMap<File, ArrayList<InterfaceConstruct>>();
+ OPListMap = new HashMap<File, ArrayList<OPConstruct>>();
+ OPLabelSet = new HashSet<String>();
+ entryMap = new HashMap<File, EntryConstruct>();
+ headerFiles = new HashSet<String>();
+ forwardClass = new HashSet<String>();
+ globalConstruct = null;
+ }
+
+ private void addDefineConstruct(DefineConstruct construct) {
+ ArrayList<DefineConstruct> list = defineListMap.get(construct.file);
+ if (list == null) {
+ list = new ArrayList<DefineConstruct>();
+ defineListMap.put(construct.file, list);
+ }
+ list.add(construct);
+ }
+
+ private void addInterfaceConstruct(InterfaceConstruct construct) {
+ ArrayList<InterfaceConstruct> list = interfaceListMap
+ .get(construct.file);
+ if (list == null) {
+ list = new ArrayList<InterfaceConstruct>();
+ interfaceListMap.put(construct.file, list);
+ }
+ list.add(construct);
+ }
+
+ private void addOPConstruct(OPConstruct construct) {
+ ArrayList<OPConstruct> list = OPListMap.get(construct.file);
+ if (list == null) {
+ list = new ArrayList<OPConstruct>();
+ OPListMap.put(construct.file, list);
+ }
+ list.add(construct);
+ }
+
+ private void addEntryConstruct(File file, EntryConstruct construct)
+ throws WrongAnnotationException {
+ EntryConstruct old = entryMap.get(file);
+ if (old == null)
+ entryMap.put(file, construct);
+ else { // Error processing
+ String errMsg = "Multiple @Entry annotations in the same file.\n\t Other @Entry at Line "
+ + old.beginLineNum + ".";
+ WrongAnnotationException.err(file, construct.beginLineNum, errMsg);
+ }
+ }
+
+ public GlobalConstruct getGlobalConstruct() {
+ return this.globalConstruct;
+ }
+
+ /**
+ * <p>
+ * A print out function for the purpose of debugging. Note that we better
+ * call this function after having called the checkSemantics() function to
+ * check annotation consistency.
+ * </p>
+ */
+ public void printAnnotations() {
+ System.out
+ .println("/********** Print out of specification extraction **********/");
+ System.out.println("// Extracted header files");
+ for (String header : headerFiles)
+ System.out.println(header);
+
+ System.out.println("// Global State Construct");
+ if (globalConstruct != null)
+ System.out.println(globalConstruct);
+
+ for (File file : interfaceListMap.keySet()) {
+ ArrayList<InterfaceConstruct> list = interfaceListMap.get(file);
+ System.out.println("// Interface in file: " + file.getName());
+ for (InterfaceConstruct construct : list) {
+ System.out.println(construct);
+ System.out.println("EndLineNumFunc: "
+ + construct.getEndLineNumFunction());
+ }
+ }
+
+ for (File file : OPListMap.keySet()) {
+ System.out.println("// Ordering points in file: " + file.getName());
+ ArrayList<OPConstruct> list = OPListMap.get(file);
+ for (OPConstruct construct : list)
+ System.out.println(construct);
+ }
+
+ for (File file : entryMap.keySet()) {
+ System.out.println("// Entry in file: " + file.getName());
+ System.out.println(entryMap.get(file));
+ }
+ }
+
+ /**
+ * <p>
+ * Perform basic semantics checking of the extracted specification.
+ * </p>
+ *
+ * @return
+ * @throws WrongAnnotationException
+ */
+ public void checkSemantics() throws WrongAnnotationException {
+ String errMsg = null;
+
+ // Assert that we have defined and only defined one global state
+ // annotation
+ if (globalConstruct == null) {
+ errMsg = "Spec error: There should be one global state annotation.\n";
+ throw new WrongAnnotationException(errMsg);
+ }
+
+ // Assert that the interface constructs have unique label name
+ HashMap<String, InterfaceConstruct> interfaceMap = new HashMap<String, InterfaceConstruct>();
+ for (File f : interfaceListMap.keySet()) {
+ ArrayList<InterfaceConstruct> list = interfaceListMap.get(f);
+ if (list != null) {
+ for (InterfaceConstruct construct : list) {
+ InterfaceConstruct existingConstruct = interfaceMap
+ .get(construct.getName());
+ if (existingConstruct != null) { // Error
+ errMsg = "Interface labels duplication with: \""
+ + construct.getName() + "\" in File \""
+ + existingConstruct.file.getName()
+ + "\", Line " + existingConstruct.beginLineNum
+ + ".";
+ WrongAnnotationException.err(construct.file,
+ construct.beginLineNum, errMsg);
+ } else {
+ interfaceMap.put(construct.getName(), construct);
+ }
+ }
+ }
+ }
+
+ // Process ordering point labels
+ for (File file : OPListMap.keySet()) {
+ ArrayList<OPConstruct> list = OPListMap.get(file);
+ for (OPConstruct construct : list) {
+ if (construct.type == OPType.OPCheck
+ || construct.type == OPType.PotentialOP) {
+ String label = construct.label;
+ OPLabelSet.add(label);
+ }
+ }
+ }
+
+ }
+
+ /**
+ * <p>
+ * This function applies on a String (a plain line of text) to check whether
+ * the current line is a C/C++ header include statement. If it is, it
+ * extracts the header file name and store it, and returns true; otherwise,
+ * it returns false.
+ * </p>
+ *
+ * @param line
+ * The line of text to be processed
+ * @return Returns true if the current line is a C/C++ header include
+ * statement
+ */
+ public boolean extractHeaders(String line) {
+ // "^( |\t)*#include( |\t)+("|<)([a-zA-Z_0-9\-\.])+("|>)"
+ Pattern regexp = Pattern
+ .compile("^( |\\t)*(#include)( |\\t)+(\"|<)([a-zA-Z_0-9\\-\\.]+)(\"|>)");
+ Matcher matcher = regexp.matcher(line);
+
+ // process the line.
+ if (matcher.find()) {
+ String header = null;
+ String braceSymbol = matcher.group(4);
+ if (braceSymbol.equals("<"))
+ header = "<" + matcher.group(5) + ">";
+ else
+ header = "\"" + matcher.group(5) + "\"";
+ if (!SpecNaming.isPreIncludedHeader(header)) {
+ headerFiles.add(header);
+ }
+ return true;
+ } else
+ return false;
+ }
+
+ /**
+ * <p>
+ * A sub-routine to extract the construct from beginning till end. When
+ * called, we have already match the beginning of the construct. We will
+ * call this sub-routine when we extract the interface construct and the
+ * global state construct.
+ * </p>
+ *
+ * <p>
+ * The side effect of this function is that the lineReader has just read the
+ * end of the construct, meaning that the caller can get the end line number
+ * by calling lineReader.getLineNumber().
+ * </p>
+ *
+ * @param file
+ * The file that we are processing
+ * @param lineReader
+ * The LineNumberReader that we are using when processing the
+ * current file.
+ * @param file
+ * The file that we are processing
+ * @param curLine
+ * The current line that we are processing. It should be the
+ * beginning line of the annotation construct.
+ * @param beginLineNum
+ * The beginning line number of the interface construct
+ * annotation
+ * @return Returns the annotation string list of the current construct
+ * @throws WrongAnnotationException
+ */
+ private ArrayList<String> extractTillConstructEnd(File file,
+ LineNumberReader lineReader, String curLine, int beginLineNum)
+ throws WrongAnnotationException {
+ ArrayList<String> annotations = new ArrayList<String>();
+ annotations.add(curLine);
+ // System.out.println(curLine);
+ // Initial settings for matching lines
+ // "\*/\s*$"
+ Pattern regexpEnd = Pattern.compile("\\*/\\s*$");
+ Matcher matcher = regexpEnd.matcher(curLine);
+ if (matcher.find()) {
+ // The beginning line is also the end line
+ // In this case, we have already add the curLine
+ return annotations;
+ } else {
+ try {
+ String line;
+ while ((line = lineReader.readLine()) != null) {
+ // process the line.
+ // System.out.println(line);
+
+ matcher.reset(line); // reset the input
+ annotations.add(line);
+ if (matcher.find())
+ return annotations;
+ }
+ WrongAnnotationException
+ .err(file,
+ beginLineNum,
+ "The interface annotation should have the matching closing symbol closing \"*/\"");
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ return null;
+ }
+
+ /**
+ * <p>
+ * A sub-routine to extract the global construct. When called, we have
+ * already match the beginning of the construct.
+ * </p>
+ *
+ * @param file
+ * The file that we are processing
+ * @param lineReader
+ * The LineNumberReader that we are using when processing the
+ * current file.
+ * @param curLine
+ * The current line that we are processing. It should be the
+ * beginning line of the annotation construct.
+ * @param beginLineNum
+ * The beginning line number of the interface construct
+ * annotation
+ * @throws WrongAnnotationException
+ */
+ private void extractGlobalConstruct(File file, LineNumberReader lineReader,
+ String curLine, int beginLineNum) throws WrongAnnotationException {
+ ArrayList<String> annotations = extractTillConstructEnd(file,
+ lineReader, curLine, beginLineNum);
+ GlobalConstruct construct = new GlobalConstruct(file, beginLineNum,
+ annotations);
+ if (globalConstruct != null) { // Check if we have seen a global state
+ // construct earlier
+ File otherDefinitionFile = globalConstruct.file;
+ int otherDefinitionLine = globalConstruct.beginLineNum;
+ String errMsg = "Multiple definition of global state.\n"
+ + "\tAnother definition is in File \""
+ + otherDefinitionFile.getName() + "\" (Line "
+ + otherDefinitionLine + ").";
+ WrongAnnotationException.err(file, beginLineNum, errMsg);
+ }
+ globalConstruct = construct;
+ }
+
+ /**
+ * @param file
+ * The current file we are processing
+ * @param lineReader
+ * Call this function when the lineReader will read the beginning
+ * of the definition right away
+ * @param startingLine
+ * The line that we should start processing
+ * @return The line number of the ending line of the interfae definition. If
+ * returning -1, it means the curl symbols in the interface do not
+ * match
+ * @throws WrongAnnotationException
+ */
+ private int findEndLineNumFunction(File file, LineNumberReader lineReader,
+ String startingLine) throws WrongAnnotationException {
+ String line = startingLine;
+ // FIXME: We assume that in the string of the code, there does not exist
+ // the symbol '{' & '{'
+ try {
+ boolean foundFirstCurl = false;
+ int unmatchedCnt = 0;
+ do {
+ // process the line.
+ // System.out.println(line);
+
+ // Extract the one-liner construct first
+ extractOneLineConstruct(file, lineReader.getLineNumber(), line);
+
+ for (int i = 0; i < line.length(); i++) {
+ char ch = line.charAt(i);
+ if (ch == '{') {
+ foundFirstCurl = true;
+ unmatchedCnt++;
+ } else if (ch == '}') {
+ unmatchedCnt--;
+ }
+ // The current line is the end of the function
+ if (foundFirstCurl && unmatchedCnt == 0) {
+ int endLineNumFunction = lineReader.getLineNumber();
+ return endLineNumFunction;
+ }
+ }
+ } while ((line = lineReader.readLine()) != null);
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ // -1 means the curl symbols in the interface do not match
+ return -1;
+ }
+
+ /**
+ * <p>
+ * A sub-routine to extract the define construct. When called, we have
+ * already match the beginning of the construct, and we also need to find
+ * the ending line number of the anntotation.
+ * </p>
+ *
+ * @param file
+ * The file that we are processing
+ * @param lineReader
+ * The LineNumberReader that we are using when processing the
+ * current file.
+ * @param curLine
+ * The current line that we are processing. It should be the
+ * beginning line of the annotation construct.
+ * @param beginLineNum
+ * The beginning line number of the interface construct
+ * annotation
+ * @throws WrongAnnotationException
+ * @throws IOException
+ * @throws ParseException
+ */
+ private void extractDefineConstruct(File file, LineNumberReader lineReader,
+ String curLine, int beginLineNum) throws WrongAnnotationException,
+ IOException, ParseException {
+ ArrayList<String> annotations = extractTillConstructEnd(file,
+ lineReader, curLine, beginLineNum);
+ int endLineNum = lineReader.getLineNumber();
+ DefineConstruct construct = new DefineConstruct(file, beginLineNum,
+ endLineNum, annotations);
+ addDefineConstruct(construct);
+ }
+
+ /**
+ * <p>
+ * A sub-routine to extract the interface construct. When called, we have
+ * already match the beginning of the construct, and we also need to find
+ * the ending line number of the closing brace of the corresponding
+ * function.
+ * </p>
+ *
+ * @param file
+ * The file that we are processing
+ * @param lineReader
+ * The LineNumberReader that we are using when processing the
+ * current file.
+ * @param curLine
+ * The current line that we are processing. It should be the
+ * beginning line of the annotation construct.
+ * @param beginLineNum
+ * The beginning line number of the interface construct
+ * annotation
+ * @throws WrongAnnotationException
+ * @throws IOException
+ * @throws ParseException
+ */
+ private void extractInterfaceConstruct(File file,
+ LineNumberReader lineReader, String curLine, int beginLineNum)
+ throws WrongAnnotationException, IOException, ParseException {
+ ArrayList<String> annotations = extractTillConstructEnd(file,
+ lineReader, curLine, beginLineNum);
+ int endLineNum = lineReader.getLineNumber();
+ InterfaceConstruct construct = new InterfaceConstruct(file,
+ beginLineNum, endLineNum, annotations);
+ addInterfaceConstruct(construct);
+
+ // Process the corresponding interface function declaration header
+ String line = null;
+ int lineNum = -1;
+ String errMsg;
+ try {
+ line = lineReader.readLine();
+ lineNum = lineReader.getLineNumber();
+ construct.processFunctionDeclaration(line);
+
+ // Record those user-defined struct
+ // RET
+ String returnType = construct.getFunctionHeader().returnType;
+ if (SpecUtils.isUserDefinedStruct(returnType))
+ forwardClass.add(SpecUtils.getPlainType(returnType));
+ // Arguments
+ for (VariableDeclaration decl : construct.getFunctionHeader().args) {
+ if (SpecUtils.isUserDefinedStruct(decl.type))
+ forwardClass.add(SpecUtils.getPlainType(decl.type));
+ }
+
+ } catch (IOException e) {
+ errMsg = "Spec error in file \""
+ + file.getName()
+ + "\", Line "
+ + lineNum
+ + " :\n\tThe function declaration should take only one line and have the correct syntax (follow the annotations immediately)\n";
+ System.out.println(errMsg);
+ throw e;
+ } catch (ParseException e) {
+ errMsg = "Spec error in file \""
+ + file.getName()
+ + "\", Line "
+ + lineNum
+ + " :\n\tThe function declaration should take only one line and have the correct syntax (follow the annotations immediately)\n";
+ System.out.println(errMsg);
+ throw e;
+ }
+
+ // Now we find the end of the interface definition
+ int endLineNumFunction = findEndLineNumFunction(file, lineReader, line);
+ construct.setEndLineNumFunction(endLineNumFunction);
+ if (endLineNumFunction == -1) {
+ WrongAnnotationException
+ .err(file, beginLineNum,
+ "The interface definition does NOT have matching curls '}'");
+ }
+ }
+
+ /**
+ * <p>
+ * A sub-routine to extract the ordering point construct. When called, we
+ * have already match the beginning of the construct.
+ * </p>
+ *
+ * @param file
+ * The file that we are processing
+ * @param beginLineNum
+ * The beginning line number of the interface construct
+ * annotation
+ * @param curLine
+ * The current line that we are processing. It should be the
+ * beginning line of the annotation construct.
+ * @param type
+ * The type of ordering point construct we are processing
+ * @throws WrongAnnotationException
+ */
+ private void extractOPConstruct(File file, int beginLineNum,
+ String curLine, OPType type) throws WrongAnnotationException {
+ String condition = null;
+ String label = null;
+
+ // "(\(\s?(\w+)\s?\))?\s:\s?(.+)\*/\s?$"
+ Pattern regexp = Pattern
+ .compile("(\\(\\s*(\\w+)\\s*\\))?\\s*:\\s*(.+)\\*/\\s*$");
+ Matcher matcher = regexp.matcher(curLine);
+ if (matcher.find()) {
+ label = matcher.group(2);
+ condition = matcher.group(3);
+ } else {
+ WrongAnnotationException
+ .err(file,
+ beginLineNum,
+ "Wrong syntax for the ordering point construct. You might need a colon before the condition.");
+ }
+ OPConstruct op = new OPConstruct(file, beginLineNum, type, label,
+ condition, curLine);
+ addOPConstruct(op);
+ }
+
+ /**
+ * <p>
+ * A sub-routine to extract the entry construct. When called, we have
+ * already match the beginning of the construct.
+ * </p>
+ *
+ * @param file
+ * The file that we are processing
+ * @param beginLineNum
+ * The beginning line number of the interface construct
+ * annotation
+ * @param curLine
+ * Current line being processed
+ * @throws WrongAnnotationException
+ */
+ public void extractEntryConstruct(File file, int beginLineNum,
+ String curLine) throws WrongAnnotationException {
+ addEntryConstruct(file, new EntryConstruct(file, beginLineNum, curLine));
+ }
+
+ /**
+ * <p>
+ * A sub-routine to extract those annotation constructs that take only one
+ * line --- Entry, OPDefine, PotentialOP, OPCheck, OPClear and OPClearDefin.
+ * </p>
+ *
+ * @param file
+ * The file that we are processing
+ * @param beginLineNum
+ * The beginning line number of the interface construct
+ * annotation
+ * @param curLine
+ * The current line that we are processing. It should be the
+ * beginning line of the annotation construct.
+ * @throws WrongAnnotationException
+ */
+ private void extractOneLineConstruct(File file, int beginLineNum,
+ String curLine) throws WrongAnnotationException {
+ // "/\*\*\s*@(Entry|OPDefine|PotentialOP|OPCheck|OPClear|OPClearDefine)"
+ Pattern regexpBegin = Pattern.compile("/\\*\\*\\s*@(\\w+)");
+ Matcher matcher = regexpBegin.matcher(curLine);
+ matcher.reset(curLine);
+ if (matcher.find()) {
+ String name = matcher.group(1);
+ if (name.equals("Entry"))
+ extractEntryConstruct(file, beginLineNum, curLine);
+ else if (name.equals("OPDefine") || name.equals("PotentialOP")
+ || name.equals("OPCheck") || name.equals("OPClear")
+ || name.equals("OPClearDefine")
+ || name.equals("OPDefineUnattached")
+ || name.equals("OPClearDefineUnattached"))
+ extractOPConstruct(file, beginLineNum, curLine,
+ OPType.valueOf(name));
+ }
+ }
+
+ /**
+ * <p>
+ * This function will process a given C/C++ file ( .h, .c or .cc). It will
+ * extract all the headers included in that file, and all the annotation
+ * constructs specified in that file. We then will store the information in
+ * the corresponding containers.
+ * </p>
+ *
+ * <p>
+ * The basic idea is to read the file line by line, and then use regular
+ * expression to match the specific annotations or the header files.
+ * </p>
+ *
+ * @param file
+ * The file object of the corresponding file to be processed
+ * @throws WrongAnnotationException
+ * @throws ParseException
+ */
+ public void extractConstruct(File file) throws WrongAnnotationException,
+ ParseException {
+ BufferedReader br = null;
+ LineNumberReader lineReader = null;
+ try {
+ // Initial settings for processing the lines
+ br = new BufferedReader(new FileReader(file));
+ lineReader = new LineNumberReader(br);
+ // "/\*\*\s*@(DeclareState|Interface)"
+ Pattern regexpBegin = Pattern
+ .compile("/\\*\\*\\s*@(DeclareState|Interface|PreCondition|JustifyingPrecondition|Transition|JustifyingPostcondition|PostCondition|Define)");
+ Matcher matcher = regexpBegin.matcher("");
+
+ String line;
+ while ((line = lineReader.readLine()) != null) {
+ // Start to process the line
+
+ // First try to process the line to see if it's a header file
+ // include
+ boolean succ = extractHeaders(line);
+ if (succ) // It's a header line and we successfully extract it
+ continue;
+
+ int beginLineNum = lineReader.getLineNumber();
+ // Extract the one-liner construct first
+ extractOneLineConstruct(file, beginLineNum, line);
+
+ // Now we process the line to see if it's an annotation (State
+ // or Interface)
+ matcher.reset(line); // reset the input
+ if (matcher.find()) { // Found the beginning line
+ // The matching annotation name
+ String constructName = matcher.group(1);
+
+ // Process each annotation accordingly
+ if (constructName.equals(SpecNaming.DeclareState)) {
+ extractGlobalConstruct(file, lineReader, line,
+ beginLineNum);
+ } else if (constructName.equals(SpecNaming.Interface)
+ || constructName.equals(SpecNaming.PreCondition)
+ || constructName.equals(SpecNaming.JustifyingPrecondition)
+ || constructName.equals(SpecNaming.Transition)
+ || constructName.equals(SpecNaming.JustifyingPostcondition)
+ || constructName.equals(SpecNaming.PostCondition)) {
+ extractInterfaceConstruct(file, lineReader, line,
+ beginLineNum);
+ } else if (constructName.equals(SpecNaming.Define)) {
+ extractDefineConstruct(file, lineReader, line,
+ beginLineNum);
+ } else {
+ WrongAnnotationException.err(file, beginLineNum,
+ constructName
+ + " is not a supported annotation.");
+ }
+
+ }
+ }
+ } catch (FileNotFoundException e) {
+ e.printStackTrace();
+ } catch (IOException e) {
+ e.printStackTrace();
+ } finally {
+ try {
+ lineReader.close();
+ } catch (IOException e) {
+ e.printStackTrace();
+ }
+ }
+ }
+
+ /**
+ * <p>
+ * Given a list of files, it scans each file and add found SpecConstrcut to
+ * the _constructs list.
+ * </p>
+ *
+ * @param files
+ * The list of files that needs to be processed. In general, this
+ * list only need to contain those that have specification
+ * annotations
+ * @throws WrongAnnotationException
+ * @throws ParseException
+ */
+ public void extract(File[] files) throws WrongAnnotationException,
+ ParseException {
+ for (int i = 0; i < files.length; i++)
+ extract(files[i]);
+
+ // Check basic specification semantics
+ checkSemantics();
+ }
+
+ public void extract(ArrayList<File> files) throws WrongAnnotationException,
+ ParseException {
+ for (int i = 0; i < files.size(); i++)
+ extract(files.get(i));
+
+ // Check basic specification semantics
+ checkSemantics();
+ }
+
+ /**
+ * <p>
+ * Extract the specification annotations and header files in the current
+ * file. This function should generally be called by extractFiles.
+ * </p>
+ *
+ * @param files
+ * The list of files that needs to be processed. In general, this
+ * list only need to contain those that have specification
+ * annotations
+ * @throws WrongAnnotationException
+ * @throws ParseException
+ */
+ public void extract(File file) throws WrongAnnotationException,
+ ParseException {
+ extractConstruct(file);
+ }
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+import java.util.ArrayList;
+import java.util.Collections;
+import java.util.HashSet;
+
+/**
+ * <p>
+ * This class contains most of the constant strings that we will be using
+ * throughout the whole annotation extraction and code generation process.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class SpecNaming {
+
+ // Specification annotation naming
+
+ // Global construct
+ public static final String DeclareState = "DeclareState";
+ public static final String InitalState = "Initial";
+ public static final String CopyState = "Copy";
+ public static final String ClearState = "Clear";
+ public static final String FinalState = "Final";
+ public static final String PrintState = "Print";
+ public static final String Commutativity = "Commutativity";
+
+ // Define construct
+ public static final String Define = "Define";
+
+ // Interface construct
+ public static final String Interface = "Interface";
+ public static final String Transition = "Transition";
+ public static final String PreCondition = "PreCondition";
+ public static final String JustifyingPrecondition = "JustifyingPrecondition";
+ public static final String SideEffect = "SideEffect";
+ public static final String JustifyingPostcondition = "JustifyingPostcondition";
+ public static final String PostCondition = "PostCondition";
+ public static final String PrintValue = "Print";
+
+ public static final String PrintStateType = "PRINT_STATE";
+ public static final String PrintValueType = "PRINT_VALUE";
+ public static final String TransitionType = "TRANSITION";
+ public static final String PreConditionType = "PRE_CONDITION";
+ public static final String JustifyingPreconditionType = "JUSTIFYING_PRECONDITION";
+ public static final String SideEffectType = "SIDE_EFFECT";
+ public static final String JustifyingPostconditionType = "JUSTIFYING_POSTCONDITION";
+ public static final String PostConditionType = "POST_CONDITION";
+
+ // Ordering point construct
+ public static final String OPDefine = "OPDefine";
+ public static final String PotentialOP = "PotentialOP";
+ public static final String OPCheck = "OPCheck";
+ public static final String OPClear = "OPClear";
+ public static final String OPClearDefine = "OPClearDefine";
+ // Entry construct
+ public static final String Entry = "Entry";
+
+ // Generated header file name
+ public static final String CDSSpecGeneratedName = "cdsspec-generated";
+ public static final String CDSSpecGeneratedHeader = "\""
+ + CDSSpecGeneratedName + ".h\"";
+ // Generated hedaer file comment
+ public static final String CDSSpecGeneratedHeaderComment = "This is a header file auto-generated by CDSSpec compiler; together, CDSSpec\n"
+ + "compiler should have generated the accompanying implementation file that\n"
+ + "implements the some functions declared in this file. In order to instrument\n"
+ + "your benchmark for CDSSpec checker to check, you should include this header\n"
+ + "file in every file you use an CDSSpec annotation. Note that it should be\n"
+ + "placed in the end of all other header files. Currently we require a C++\n"
+ + "compiler that supports C++11.";
+
+ // Generated CPP file name
+ public static final String CDSSpecGeneratedCPP = "cdsspec-generated.cc";
+ // Generated CPP file comment
+ public static final String CDSSpecGeneratedCPPComment = "This is an implementation file auto-generated by CDSSpec compiler to\n"
+ + "instrument your benchmark for CDSSpec checker to check. Currently we require\n"
+ + "a C++ compiler that supports C++11.";
+
+ // Pre-included header files
+ public static final HashSet<String> includedHeaders;
+ public static final ArrayList<String> includedHeadersList;
+
+ public static final String ATOMIC = "<atomic>";
+ public static final String THREADS = "<threads.h>";
+ public static final String STDATOMIC = "<stdatomic.h>";
+ public static final String STDLIB = "<stdlib.h>";
+ public static final String STDIO = "<stdio.h>";
+
+ public static final String MODELTYPES = "<modeltypes.h>";
+ public static final String CDSANNOTATE = "<cdsannotate.h>";
+ public static final String MYMEMORY = "\"mymemory.h\"";
+ public static final String MODELASSERT = "\"model-assert.h\"";
+ public static final String LIBRACE = "\"librace.h\"";
+ public static final String SPECANNOTATION = "\"specannotation.h\"";
+ public static final String SPEC_COMMON = "\"spec_common.h\"";
+ public static final String CDSSPEC = "\"cdsspec.h\"";
+ public static final String METHODCALL = "\"methodcall.h\"";
+
+ // Header files to include in the cdsspec-generated.h
+ public static final String SPECANNOTATION_API = "\"specannotation-api.h\"";
+
+ // Header files to include in the cdsspec-generated.cc
+ // <cdsannotate.h>
+ // "spec_common.h"
+ // "methodcall.h"
+ // "cdsspec.h"
+ // "specannotation.h"
+
+ static {
+ // Initialize the header set and list
+ includedHeaders = new HashSet<String>();
+ includedHeadersList = new ArrayList<String>();
+
+ // Add each header to the set
+ includedHeadersList.add(ATOMIC);
+ includedHeadersList.add(THREADS);
+ includedHeadersList.add(STDATOMIC);
+ includedHeadersList.add(STDLIB);
+ includedHeadersList.add(STDIO);
+
+ includedHeadersList.add(MODELTYPES);
+ includedHeadersList.add(CDSANNOTATE);
+ includedHeadersList.add(MYMEMORY);
+ includedHeadersList.add(MODELASSERT);
+ includedHeadersList.add(LIBRACE);
+ includedHeadersList.add(SPECANNOTATION);
+ includedHeadersList.add(SPEC_COMMON);
+ includedHeadersList.add(METHODCALL);
+ includedHeadersList.add(CDSSPEC);
+
+ // Add files to list
+ for (String header : includedHeadersList) {
+ includedHeaders.add(header);
+ }
+ }
+
+ public static boolean isPreIncludedHeader(String header) {
+ return includedHeaders.contains(header)
+ || header.equals(CDSSpecGeneratedHeader);
+ }
+
+ // Some CDSSpec keywords and function names
+ public static final String NewSize = "NEW_SIZE";
+ public static final String New = "NEW";
+ // Some CDSSpec types
+ public static final String CString = "CSTR";
+ public static final String EmptyCString = "_EMPTY";
+ public static final String NullFunc = "NULL_FUNC";
+
+ public static final String StateStruct = "StateStruct";
+ public static final String Method = "Method";
+ public static final String MethodValueField = "value";
+ public static final String CommutativityRule = "CommutativityRule";
+ public static final String StateFunctions = "StateFunctions";
+ public static final String NamedFunction = "NamedFunction";
+
+ public static final String SPEC_ANALYSIS = "SPEC_ANALYSIS";
+ // Spec annotations
+ public static final String AnnoInit = "AnnoInit";
+ public static final String AnnoTypeInit = "INIT";
+ public static final String AnnoInterfaceInfo = "AnnoInterfaceInfo";
+ public static final String CAnnoInterfaceInfo = "CAnnoInterfaceInfo";
+ public static final String SpecAnnotation = "SpecAnnotation";
+
+ // Some CDSSpec state functions
+ public static final String InitialFunc = "_Initial";
+ public static final String CopyFunc = "_Copy";
+ public static final String FinalFunc = "_Final";
+ public static final String PrintStateFunc = "_PrintState";
+
+ // Functions for instrumenting annotation
+ public static final String CreateInitAnnoFunc = "_createInitAnnotation";
+ public static final String CreateInterfaceBeginAnnoFunc = "_createInterfaceBeginAnnotation";
+ public static final String SetInterfaceBeginAnnoValueFunc = "_setInterfaceBeginAnnotationValue";
+ public static final String CreateInterfaceEndAnnoFunc = "_createInterfaceEndAnnotation";
+ public static final String CreateOPDefineAnnoFunc = "_createOPDefineAnnotation";
+ public static final String CreateOPDefineUnattachedFunc = "_createOPDefineUnattached";
+ public static final String CreateOPClearDefineUnattachedFunc = "_createOPClearDefineUnattached";
+ public static final String CreatePotentialOPAnnoFunc = "_createPotentialOPAnnotation";
+ public static final String CreateOPCheckAnnoFunc = "_createOPCheckAnnotation";
+ public static final String CreateOPClearAnnoFunc = "_createOPClearAnnotation";
+ public static final String CreateOPClearDefineAnnoFunc = "_createOPClearDefineAnnotation";
+
+ // Other CDSSpec functions
+ public static final String AddInterfaceFunctions = "addInterfaceFunctions";
+ public static final String CDSAnnotateFunc = "cdsannotate";
+ public static final String PRINT = "PRINT";
+ public static final String PrintContainer = "printContainer";
+ public static final String PrintMap = "printMap";
+
+ // Special instances
+ public static final String Method1 = "_M";
+ public static final String Method2 = "_exec";
+ public static final String StateInst = "state";
+ public static final String OldStateInst = "OLD";
+ public static final String NewStateInst = "NEW";
+ // Specification types and macros
+ public static final String C_RET = "C_RET";
+ public static final String S_RET = "S_RET";
+ public static final String InterfaceValueInst = "__value";
+
+ // The fake ordering point operation
+ public static final String FakeOP = "_FAKE_OP_";
+
+ // The wrapper prefix that we want to attach to the function name
+ public static final String WrapperPrefix = "Wrapper";
+
+ public static final String CommutativityRuleInst = "commuteRules";
+ public static final String CommutativityRuleSizeInst = "CommuteRuleSize";
+ public static final String StateFunctionsInst = "stateFuncs";
+ public static final String AnnoInitInst = "init";
+ public static final String AnnoInterfaceInfoInst = "info";
+
+ public static String AppendStr(String original) {
+ return "_" + original + "_str";
+ }
+
+ public static String CheckCommutativity(int ruleNum) {
+ return "_checkCommutativity" + ruleNum;
+ }
+
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+import java.io.File;
+import java.util.ArrayList;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+/**
+ * <p>
+ * This class contains a list of static utility functions for extracting
+ * specification annotations more conveniently.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class SpecUtils {
+
+ /**
+ * <p>
+ * This inner class defines a primitive --- a sub-annotation. For example,
+ * the "@DeclareState" in the global state is one primitive. We record the
+ * beginning line number of that primitive, the name of the primitive and a
+ * list of lines that belong to that primitive.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+ public static class Primitive {
+ public final int beginLineNum;
+ public final String name;
+ public final ArrayList<String> contents;
+
+ public Primitive(String name, int beginLineNum) {
+ this.name = name;
+ this.beginLineNum = beginLineNum;
+ contents = new ArrayList<String>();
+ }
+
+ public void addLine(String line) {
+ contents.add(line);
+ }
+
+ public String toString() {
+ return "@" + name + ":\n" + contents;
+ }
+ };
+
+ /**
+ * <p>
+ * This is a customized wrap of integer so that we can use integer type as
+ * if they were in a C/C++ pass-by-reference fashion. It basically wraps an
+ * int primitive to allow read, write and increment its internal int value.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+ public static class IntObj {
+ private int value;
+
+ public IntObj(int value) {
+ this.value = value;
+ }
+
+ public void setVal(int value) {
+ this.value = value;
+ }
+
+ public int getVal() {
+ return this.value;
+ }
+
+ public void dec() {
+ this.value--;
+ }
+
+ public void inc() {
+ this.value++;
+ }
+
+ public String toString() {
+ return Integer.toString(value);
+ }
+ }
+
+ // A regular expression pattern that matches "@WORD:"
+ public static final Pattern regexpPrimitive = Pattern.compile("@(\\w+):");
+ // The matcher for the primitive regular expression
+ public static final Matcher matcherPrimitive = regexpPrimitive.matcher("");
+
+ // Match code after colon: ":" + Code + "$"
+ public static final Pattern regexpCode = Pattern.compile(":(.*)$");
+ public static final Matcher matcherCode = regexpCode.matcher("");
+
+ // Match a valid word: "^\w+$"
+ public static final Pattern regexpWord = Pattern.compile("^\\w+$");
+ public static final Matcher matcherWord = regexpWord.matcher("");
+
+ /**
+ * <p>
+ * This function will look for the first line that contains a primitive from
+ * the "curIdx", and then extracts all the non-empty lines of that primitive
+ * until it gets to the end of the list or the beginning of the next
+ * primitive. It returns a list of the actual code for that primitive. When
+ * we are done with the function call, the curIdx either points to the next
+ * primitive or the end of the annotation list.
+ * </p>
+ *
+ * @param file
+ * The file begin processing
+ * @param beginLineNum
+ * The beginning line number of the first annotation lines
+ * @param annotations
+ * The list of annotations
+ * @param curIdx
+ * The current index (the index of the line that we are
+ * processing). We will update the current index after calling
+ * this function. Keep in mind that this is a customized wrap of
+ * integer so that we can use it as if it is a C/C++ reference
+ * @return The primitive starting from the curIdx till either the end of the
+ * annotations or the beginning line of the next primitive
+ * @throws WrongAnnotationException
+ */
+ public static Primitive extractPrimitive(File file, int beginLineNum,
+ ArrayList<String> annotations, IntObj curIdx)
+ throws WrongAnnotationException {
+ if (curIdx.getVal() == annotations.size()) // The current index points
+ // to the end
+ // of the list
+ return null;
+
+ String line = null;
+ int curLineNum = -1;
+ Primitive primitive = null;
+ // Find the first primitive
+ for (; curIdx.getVal() < annotations.size(); curIdx.inc()) {
+ line = annotations.get(curIdx.getVal());
+ curLineNum = curIdx.getVal() + beginLineNum;
+ matcherPrimitive.reset(line);
+ if (matcherPrimitive.find()) {
+ String name = matcherPrimitive.group(1);
+ primitive = new Primitive(name, curLineNum);
+ break;
+ }
+ }
+ // Assert that we must have found one primitive
+ if (primitive == null) {
+ WrongAnnotationException
+ .err(file, curLineNum,
+ "Something is wrong! We must have found one primitve here!\n");
+ }
+
+ // Process the current "primitive"
+ // Deal with the first special line. E.g. @DeclareState: int x;
+ String code = null;
+ matcherCode.reset(line);
+ if (matcherCode.find()) {
+ code = matcherCode.group(1);
+ String trimmedCode = trimSpace(trimTrailingCommentSymbol(code));
+ if (!trimmedCode.equals("")) {
+ primitive.addLine(trimmedCode);
+ }
+ } else {
+ WrongAnnotationException
+ .err(file, curLineNum,
+ "The state annotation should have correct primitive syntax (sub-annotations)");
+ }
+
+ // Deal with other normal line. E.g. y = 1;
+ curIdx.inc();
+ ;
+ for (; curIdx.getVal() < annotations.size(); curIdx.inc()) {
+ curLineNum = beginLineNum + curIdx.getVal();
+ line = annotations.get(curIdx.getVal());
+ matcherPrimitive.reset(line);
+ if (!matcherPrimitive.find()) {
+ // This is another line that we should add to the result
+ code = trimSpace(trimTrailingCommentSymbol(line));
+ if (!code.equals(""))
+ primitive.addLine(code);
+ } else
+ // We get to the end of the current primitive
+ break;
+ }
+
+ if (primitive.contents.size() == 0) { // The content of the primitive is
+ // empty
+ WrongAnnotationException.warning(file, curLineNum, "Primitive "
+ + primitive.name + " is empty.");
+ }
+ return primitive;
+ }
+
+ /**
+ *
+ * @param line
+ * The line to be processed
+ * @return The string whose beginning and ending space have been trimmed
+ */
+ public static String trimSpace(String line) {
+ // "^\s*(.*?)\s*$"
+ Pattern regexp = Pattern.compile("^\\s*(.*?)\\s*$");
+ Matcher matcher = regexp.matcher(line);
+ if (matcher.find()) {
+ return matcher.group(1);
+ } else {
+ return line;
+ }
+ }
+
+ /**
+ * <p>
+ * It processed the line in a way that it removes the trailing C/C++ comment
+ * symbols "STAR SLASH"
+ * </p>
+ *
+ * @param line
+ * @return
+ */
+ public static String trimTrailingCommentSymbol(String line) {
+ Pattern regexp = Pattern.compile("(.*?)\\s*(\\*/)?\\s*$");
+ Matcher matcher = regexp.matcher(line);
+ if (matcher.find()) {
+ return matcher.group(1);
+ } else {
+ return null;
+ }
+ }
+
+ public static boolean isUserDefinedStruct(String type) {
+ // FIXME: We only consider the type is either a one-level pointer or a
+ // struct
+ String bareType = trimSpace(type.replace('*', ' '));
+ return !bareType.equals("int") && !bareType.matches("unsigned\\s+int")
+ && !bareType.equals("unsigned") && !bareType.equals("bool")
+ && !bareType.equals("double") && !bareType.equals("float")
+ && !bareType.equals("void");
+ }
+
+ public static String getPlainType(String type) {
+ // FIXME: We only consider the type is either a one-level pointer or a
+ // struct
+ String bareType = trimSpace(type.replace('*', ' '));
+ return bareType;
+ }
+
+ /**
+ * <p>
+ * This function will automatically generate the printing statements for
+ * supported types when given a type and a name of the declaration.
+ * </p>
+ *
+ * @return The auto-generated state printing statements
+ */
+ public static Code generatePrintStatement(String type, String name) {
+ Code code = new Code();
+ // Primitive types
+ if (type.equals("int") || type.equals("unsigned")
+ || type.equals("unsigned int") || type.equals("int unsigned")
+ || type.equals("double") || type.equals("double")
+ || type.equals("bool")) {
+ // PRINT("\tx=%d\n", x);
+ code.addLine(SpecNaming.PRINT + "(\"\\t" + name + "=%d\\n\", "
+ + name + ");");
+ } else if (type.equals("int *") || type.equals("unsigned *")
+ || type.equals("unsigned int *")
+ || type.equals("int unsigned *") || type.equals("double *")
+ || type.equals("double *") || type.equals("bool *")) {
+ // Supported pointer types for primitive types
+ // PRINT("\t*x=%d\n", *x);
+ code.addLine(SpecNaming.PRINT + "(\"\\t*" + name + "=%d\\n\", *"
+ + name + ");");
+ } else if (type.equals("IntList") || type.equals("IntSet")
+ || type.equals("IntMap")) {
+ // Supported types
+ // PRINT("\tq: ");
+ // printContainer(&q);
+ // model_print("\n");
+ code.addLine(SpecNaming.PRINT + "(\"\\t" + name + ": \");");
+ if (type.equals("IntMap")) {
+ code.addLine(SpecNaming.PrintMap + "(&" + name + ");");
+ } else {
+ code.addLine(SpecNaming.PrintContainer + "(&" + name + ");");
+ }
+ code.addLine(SpecNaming.PRINT + "(\"\\n\");");
+ } else if (type.equals("IntList *") || type.equals("IntSet *")
+ || type.equals("IntMap *")) {
+ // Supported pointer types
+ // PRINT("\tq: ");
+ // printContainer(q);
+ // model_print("\n");
+ code.addLine(SpecNaming.PRINT + "(\"\\t" + name + ": \");");
+ if (type.equals("IntMap *")) {
+ code.addLine(SpecNaming.PrintMap + "(" + name + ");");
+ } else {
+ code.addLine(SpecNaming.PrintContainer + "(" + name + ");");
+ }
+ code.addLine(SpecNaming.PRINT + "(\"\\n\");");
+ } else if (type.equals("void")) {
+ // Just do nothing!
+ } else {
+ if (type.endsWith("*")) { // If it's an obvious pointer (with a STAR)
+ // Weak support pointer types (just print out the address)
+ // PRINT("\tmutex=%p\n", mutex);
+ code.addLine(SpecNaming.PRINT + "(\"\\t" + name + "=%p\\n\", "
+ + name + ");");
+ } else {
+ code.addLine("// We do not support auto-gen print-out for type: "
+ + type + ".");
+ }
+
+ }
+
+ return code;
+ }
+
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+import java.io.File;
+import java.util.regex.Matcher;
+import java.util.regex.Pattern;
+
+import edu.uci.eecs.utilParser.ParseException;
+import edu.uci.eecs.utilParser.UtilParser;
+
+/**
+ * <p>
+ * This class represents a variable declaration in C/C++, in which there exist a
+ * type and a name.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class VariableDeclaration {
+ public final String type;
+ public final String name;
+
+ public VariableDeclaration(String type, String name) {
+ this.type = SpecUtils.trimSpace(type);
+ this.name = SpecUtils.trimSpace(name);
+ }
+
+ public VariableDeclaration(File file, int lineNum, String line)
+ throws WrongAnnotationException {
+ VariableDeclaration decl = null;
+ try {
+ decl = UtilParser.parseDeclaration(line);
+ } catch (ParseException e) {
+ WrongAnnotationException.err(file, lineNum, "The declaration: \""
+ + line + "\" has wrong syntax.");
+ } finally {
+ type = decl == null ? null : decl.type;
+ name = decl == null ? null : decl.name;
+ }
+ }
+
+ public String toString() {
+ return type + ": " + name;
+ }
+}
--- /dev/null
+package edu.uci.eecs.specExtraction;
+
+import java.io.File;
+
+/**
+ * <p>
+ * This class is the main error processing exception in processing the
+ * annotations.
+ * </p>
+ *
+ * @author Peizhao Ou
+ *
+ */
+public class WrongAnnotationException extends Exception {
+ /**
+ *
+ */
+ private static final long serialVersionUID = 1L;
+
+ public WrongAnnotationException(String msg) {
+ super(msg);
+ }
+
+ /**
+ * <p>
+ * This is a utility function for more conveniently generating specification
+ * error exceptions. We help locate the error by the line number in the
+ * processing file, and an associated error message.
+ * </p>
+ *
+ * @param file
+ * The file that we see the error
+ * @param line
+ * The associated line number of the error
+ * @param msg
+ * The error message printout
+ * @throws WrongAnnotationException
+ */
+ public static void err(File file, int line, String msg)
+ throws WrongAnnotationException {
+ String errMsg = "Spec error in file \"" + file.getName() + "\", Line "
+ + line + " :\n\t" + msg + "\n";
+ throw new WrongAnnotationException(errMsg);
+ }
+
+ /**
+ * <p>
+ * This is a utility function for more conveniently generating specification
+ * warning. We help locate the warning by the line number in the processing
+ * file, and an associated warning message.
+ * </p>
+ *
+ * @param file
+ * The file that we see the warning
+ * @param line
+ * The associated line number of the warning
+ * @param msg
+ * The warning message printout
+ * @throws WrongAnnotationException
+ */
+ public static void warning(File file, int line, String msg) {
+ String errMsg = "Spec WARNING in file \"" + file.getName()
+ + "\", Line " + line + " :\n\t" + msg + "\n";
+ System.out.println(errMsg);
+ }
+}
#include "config.h"
extern int model_out;
-extern int switch_alloc;
-#define model_print(fmt, ...) do { switch_alloc = 1; dprintf(model_out, fmt, ##__VA_ARGS__); switch_alloc = 0; } while (0)
+#define model_print(fmt, ...) do { dprintf(model_out, fmt, ##__VA_ARGS__); } while (0)
#ifdef CONFIG_DEBUG
#define DEBUG(fmt, ...) do { model_print("*** %15s:%-4d %25s() *** " fmt, __FILE__, __LINE__, __func__, ##__VA_ARGS__); } while (0)
LIB_NAME := model
LIB_SO := lib$(LIB_NAME).so
-CPPFLAGS += -Wall -g -O3
+CPPFLAGS += -Wall -O3 -g
+
+CFLAGS := $(CPPFLAGS)
# Mac OSX options
ifeq ($(UNAME), Darwin)
too_many_reads(false),
no_valid_reads(false),
bad_synchronization(false),
- bad_sc_read(false),
asserted(false)
{ }
bool no_valid_reads;
/** @brief Incorrectly-ordered synchronization was made */
bool bad_synchronization;
- bool bad_sc_read;
bool asserted;
SNAPSHOTALLOC
priv->bad_synchronization = true;
}
-/** @brief Alert the model-checker that an incorrectly-ordered
- * synchronization was made */
-void ModelExecution::set_bad_sc_read()
-{
- priv->bad_sc_read = true;
-}
-
bool ModelExecution::assert_bug(const char *msg)
{
priv->bugs.push_back(new bug_message(msg));
if (rf) {
if (r_modification_order(act, rf))
updated = true;
- if (act->is_seqcst()) {
- ModelAction *last_sc_write = get_last_seq_cst_write(act);
- if (last_sc_write != NULL && rf->happens_before(last_sc_write)) {
- set_bad_sc_read();
- }
- }
} else if (promise) {
if (r_modification_order(act, promise))
updated = true;
ptr += sprintf(ptr, "[no valid reads-from]");
if (priv->bad_synchronization)
ptr += sprintf(ptr, "[bad sw ordering]");
- if (priv->bad_sc_read)
- ptr += sprintf(ptr, "[bad sc read]");
if (promises_expired())
ptr += sprintf(ptr, "[promise expired]");
if (promises.size() != 0)
priv->no_valid_reads ||
priv->too_many_reads ||
priv->bad_synchronization ||
- priv->bad_sc_read ||
priv->hard_failed_promise ||
promises_expired();
}
action_list_t::reverse_iterator rit;
for (rit = list->rbegin(); rit != list->rend(); rit++) {
ModelAction *act = *rit;
+ // FIXME: The read from promise might read from an annotation
+ if (act->get_type() == ATOMIC_ANNOTATION)
+ continue;
/* Skip curr */
if (act == curr)
}
}
+ /* C++, Section 29.3 statement 3 (second subpoint) */
+ if (curr->is_seqcst() && last_sc_write && act == last_sc_write) {
+ added = mo_graph->addEdge(act, rf) || added;
+ break;
+ }
+
/*
* Include at most one act per-thread that "happens
* before" curr
added = mo_graph->addEdge(act, curr) || added;
else if (act->is_read()) {
//if previous read accessed a null, just keep going
- if (act->get_reads_from() == NULL) {
- added = mo_graph->addEdge(act->get_reads_from_promise(), curr) || added;
- } else
- added = mo_graph->addEdge(act->get_reads_from(), curr) || added;
+ if (act->get_reads_from() == NULL)
+ continue;
+ added = mo_graph->addEdge(act->get_reads_from(), curr) || added;
}
break;
} else if (act->is_read() && !act->could_synchronize_with(curr) &&
action_list_t * get_action_trace() { return &action_trace; }
CycleGraph * const get_mo_graph() { return mo_graph; }
+
+ int get_execution_number() const;
SNAPSHOTALLOC
private:
- int get_execution_number() const;
-
ModelChecker *model;
const model_params * const params;
bool mo_may_allow(const ModelAction *writer, const ModelAction *reader);
bool promises_may_allow(const ModelAction *writer, const ModelAction *reader) const;
void set_bad_synchronization();
- void set_bad_sc_read();
bool promises_expired() const;
bool should_wake_up(const ModelAction *curr, const Thread *thread) const;
void wake_up_sleeping_actions(ModelAction *curr);
void atomic_flag_clear( volatile atomic_flag* __a__ )
{ atomic_flag_clear_explicit( __a__, memory_order_seq_cst ); }
-void __atomic_flag_wait__( volatile atomic_flag* __a__ ) {
- while ( atomic_flag_test_and_set( __a__ ) )
- ;
-}
+void __atomic_flag_wait__( volatile atomic_flag* __a__ )
+{ while ( atomic_flag_test_and_set( __a__ ) ); }
void __atomic_flag_wait_explicit__( volatile atomic_flag* __a__,
- memory_order __x__ ) {
- while ( atomic_flag_test_and_set_explicit( __a__, __x__ ))
- ;
-}
+ memory_order __x__ )
+{ while ( atomic_flag_test_and_set_explicit( __a__, __x__ ) ); }
}
#define CDS_ANNOTATE_H
#include <stdint.h>
+//#ifdef __cplusplus
+//extern "C" {
+//#endif
void cdsannotate(uint64_t analysistype, void *annotation);
+//#ifdef __cplusplus
+//}
+//#endif
+
#endif
inline void* atomic_fetch_add_explicit
( volatile atomic_address* __a__, ptrdiff_t __m__, memory_order __x__ )
{
- volatile __typeof__((__a__)->__f__)* __p__ = & ((__a__)->__f__);
- __typeof__((__a__)->__f__) __old__=(__typeof__((__a__)->__f__)) model_rmwr_action((void *)__p__, __x__);
- __typeof__((__a__)->__f__) __copy__= __old__;
- __copy__ = (void *) (((char *)__copy__) + __m__);
- model_rmw_action((void *)__p__, __x__, (uint64_t) __copy__);
- return __old__;
-}
+ void* volatile* __p__ = &((__a__)->__f__);
+ void* __r__ = (void *) model_rmwr_action((void *)__p__, __x__);
+ model_rmw_action((void *)__p__, __x__, (uint64_t) ((char*)(*__p__) + __m__));
+ return __r__; }
- inline void* atomic_fetch_add
+inline void* atomic_fetch_add
( volatile atomic_address* __a__, ptrdiff_t __m__ )
{ return atomic_fetch_add_explicit( __a__, __m__, memory_order_seq_cst ); }
inline void* atomic_fetch_sub_explicit
( volatile atomic_address* __a__, ptrdiff_t __m__, memory_order __x__ )
-{ volatile __typeof__((__a__)->__f__)* __p__ = & ((__a__)->__f__);
- __typeof__((__a__)->__f__) __old__=(__typeof__((__a__)->__f__)) model_rmwr_action((void *)__p__, __x__);
- __typeof__((__a__)->__f__) __copy__= __old__;
- __copy__ = (void *) (((char *)__copy__) - __m__);
- model_rmw_action((void *)__p__, __x__, (uint64_t) __copy__);
- return __old__;
-}
+{
+ void* volatile* __p__ = &((__a__)->__f__);
+ void* __r__ = (void *) model_rmwr_action((void *)__p__, __x__);
+ model_rmw_action((void *)__p__, __x__, (uint64_t)((char*)(*__p__) - __m__));
+ return __r__; }
inline void* atomic_fetch_sub
( volatile atomic_address* __a__, ptrdiff_t __m__ )
--- /dev/null
+#ifndef _MODEL_MEMORY_H
+#define _MODEL_MEMORY_H
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+void* snapshot_malloc(size_t size);
+void* snapshot_calloc(size_t count, size_t size);
+void snapshot_free(void *ptr);
+
+#define MODEL_MALLOC(x) snapshot_malloc((x))
+#define MODEL_CALLOC(x, y) snapshot_calloc((x), (y))
+#define MODEL_FREE(x) snapshot_free((x))
+
+#define CMODEL_MALLOC(x) snapshot_malloc((x))
+#define CMODEL_CALLOC(x, y) snapshot_calloc((x), (y))
+#define CMODEL_FREE(x) snapshot_free((x))
+
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+
+#endif
using std::atomic_wchar_t;
+/****** More atomic types *****/
+using std::atomic_int_least8_t;
+using std::atomic_uint_least8_t;
+using std::atomic_int_least16_t;
+using std::atomic_uint_least16_t;
+using std::atomic_int_least32_t;
+using std::atomic_uint_least32_t;
+using std::atomic_int_least64_t;
+using std::atomic_uint_least64_t;
+
+using std::atomic_int_fast8_t;
+using std::atomic_uint_fast8_t;
+using std::atomic_int_fast16_t;
+using std::atomic_uint_fast16_t;
+using std::atomic_int_fast32_t;
+using std::atomic_uint_fast32_t;
+using std::atomic_int_fast64_t;
+using std::atomic_uint_fast64_t;
+
+using std::atomic_intptr_t;
+using std::atomic_uintptr_t;
+
+using std::atomic_ssize_t;
+using std::atomic_size_t;
+
+using std::atomic_ptrdiff_t;
+
+using std::atomic_intmax_t;
+using std::atomic_uintmax_t;
+
+
+
using std::atomic;
using std::memory_order;
using std::memory_order_relaxed;
{"enabled", required_argument, NULL, 'e'},
{"bound", required_argument, NULL, 'b'},
{"verbose", optional_argument, NULL, 'v'},
- {"uninitialized", required_argument, NULL, 'u'},
- {"analysis", required_argument, NULL, 't'},
- {"options", required_argument, NULL, 'o'},
+ {"uninitialized", optional_argument, NULL, 'u'},
+ {"analysis", optional_argument, NULL, 't'},
+ {"options", optional_argument, NULL, 'o'},
{"maxexecutions", required_argument, NULL, 'x'},
{0, 0, 0, 0} /* Terminator */
};
TraceAnalysis * ta=(*installedanalysis)[i];
ta->setExecution(execution);
model->add_trace_analysis(ta);
- /** Call the installation event for each installed plugin */
- ta->actionAtInstallation();
}
}
main_argc = argc;
main_argv = argv;
- /*
- * If this printf statement is removed, CDSChecker will fail on an
- * assert on some versions of glibc. The first time printf is
- * called, it allocated internal buffers. We can't easily snapshot
- * libc since we also use it.
- */
-
- printf("CDSChecker\n"
- "Copyright (c) 2013 Regents of the University of California. All rights reserved.\n"
- "Distributed under the GPLv2\n"
- "Written by Brian Norris and Brian Demsky\n\n");
-
/* Configure output redirection for the model-checker */
redirect_output();
#include <algorithm>
#include <new>
#include <stdarg.h>
-#include <string.h>
#include "model.h"
#include "action.h"
ModelChecker::ModelChecker(struct model_params params) :
/* Initialize default scheduler */
params(params),
- restart_flag(false),
- exit_flag(false),
scheduler(new Scheduler()),
node_stack(new NodeStack()),
execution(new ModelExecution(this, &this->params, scheduler, node_stack)),
execution_number(1),
diverge(NULL),
earliest_diverge(NULL),
- trace_analyses(),
- inspect_plugin(NULL)
+ trace_analyses()
{
- memset(&stats,0,sizeof(struct execution_stats));
}
/** @brief Destructor */
checkDataRaces();
run_trace_analyses();
- } else if (inspect_plugin && !execution->is_complete_execution() &&
- (execution->too_many_steps())) {
- inspect_plugin->analyze(execution->get_action_trace());
}
record_stats();
if (complete)
earliest_diverge = NULL;
- if (restart_flag) {
- do_restart();
- return true;
- }
-
- if (exit_flag)
- return false;
-
if ((diverge = execution->get_next_backtrack()) == NULL)
return false;
/** @brief Run trace analyses on complete trace */
void ModelChecker::run_trace_analyses() {
+ IN_TRACE_ANALYSIS = true;
for (unsigned int i = 0; i < trace_analyses.size(); i++)
trace_analyses[i]->analyze(execution->get_action_trace());
+ IN_TRACE_ANALYSIS = false;
}
/**
Thread *old = thread_current();
scheduler->set_current_thread(NULL);
ASSERT(!old->get_pending());
- if (inspect_plugin != NULL) {
- inspect_plugin->inspectModelAction(act);
- }
old->set_pending(act);
if (Thread::swap(old, &system_context) < 0) {
perror("swap threads");
return false;
}
-/** @brief Exit ModelChecker upon returning to the run loop of the
- * model checker. */
-void ModelChecker::exit_model_checker()
-{
- exit_flag = true;
-}
-
-/** @brief Restart ModelChecker upon returning to the run loop of the
- * model checker. */
-void ModelChecker::restart()
-{
- restart_flag = true;
-}
-
-void ModelChecker::do_restart()
-{
- restart_flag = false;
- diverge = NULL;
- earliest_diverge = NULL;
- reset_to_initial_state();
- node_stack->full_reset();
- memset(&stats,0,sizeof(struct execution_stats));
- execution_number = 1;
-}
-
/** @brief Run ModelChecker for the user program */
void ModelChecker::run()
{
- bool has_next;
do {
thrd_t user_thread;
Thread *t = new Thread(execution->get_next_id(), &user_thread, &user_main_wrapper, NULL, NULL);
t = execution->take_step(curr);
} while (!should_terminate_execution());
- has_next = next_execution();
- if (inspect_plugin != NULL && !has_next) {
- inspect_plugin->actionAtModelCheckingFinish();
- // Check if the inpect plugin set the restart flag
- if (restart_flag) {
- model_print("******* Model-checking RESTART: *******\n");
- has_next = true;
- do_restart();
- }
- }
- } while (has_next);
+ } while (next_execution());
execution->fixup_release_sequences();
void run();
- /** Restart the model checker, intended for pluggins. */
- void restart();
-
- /** Exit the model checker, intended for pluggins. */
- void exit_model_checker();
-
- /** Check the exit_flag. */
- bool get_exit_flag() const { return exit_flag; }
-
/** @returns the context for the main model-checking system thread */
ucontext_t * get_system_context() { return &system_context; }
void assert_user_bug(const char *msg);
const model_params params;
- void add_trace_analysis(TraceAnalysis *a) { trace_analyses.push_back(a); }
- void set_inspect_plugin(TraceAnalysis *a) { inspect_plugin=a; }
+ void add_trace_analysis(TraceAnalysis *a) {
+ trace_analyses.push_back(a);
+ }
+
MEMALLOC
private:
- /** Flag indicates whether to restart the model checker. */
- bool restart_flag;
- /** Flag indicates whether to exit the model checker. */
- bool exit_flag;
-
/** The scheduler to use: tracks the running/ready Threads */
Scheduler * const scheduler;
NodeStack * const node_stack;
ModelVector<TraceAnalysis *> trace_analyses;
- /** @bref Implement restart. */
- void do_restart();
- /** @bref Plugin that can inspect new actions. */
- TraceAnalysis *inspect_plugin;
/** @brief The cumulative execution stats */
struct execution_stats stats;
void record_stats();
#define REQUESTS_BEFORE_ALLOC 1024
+bool IN_TRACE_ANALYSIS = false;
+
size_t allocatedReqs[REQUESTS_BEFORE_ALLOC] = { 0 };
int nextRequest = 0;
int howManyFreed = 0;
-int switch_alloc = 0;
#if !USE_MPROTECT_SNAPSHOT
static mspace sStaticSpace = NULL;
#endif
/** Bootstrap allocation. Problem is that the dynamic linker calls require
* calloc to work and calloc requires the dynamic linker to work. */
-#define BOOTSTRAPBYTES 131072
+#define BOOTSTRAPBYTES 4096
char bootstrapmemory[BOOTSTRAPBYTES];
size_t offset = 0;
sz = (sz + 7) & ~7;
if (sz > (BOOTSTRAPBYTES-offset)) {
- model_print("OUT OF BOOTSTRAP MEMORY. Increase the size of BOOTSTRAPBYTES in mymemory.cc\n");
+ model_print("OUT OF BOOTSTRAP MEMORY\n");
exit(EXIT_FAILURE);
}
void *malloc(size_t size)
{
if (user_snapshot_space) {
- if (switch_alloc) {
- return model_malloc(size);
- }
/* Only perform user allocations from user context */
- ASSERT(!model || thread_current());
+ ASSERT(!model || thread_current() || IN_TRACE_ANALYSIS);
return user_malloc(size);
} else
return HandleEarlyAllocationRequest(size);
/** @brief Snapshotting free implementation for user programs */
void free(void * ptr)
{
- if (!DontFree(ptr)) {
- if (switch_alloc) {
- return model_free(ptr);
- }
+ if (!DontFree(ptr))
mspace_free(user_snapshot_space, ptr);
- }
}
/** @brief Snapshotting realloc implementation for user programs */
return p; \
}
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+extern bool IN_TRACE_ANALYSIS;
+
void *model_malloc(size_t size);
void *model_calloc(size_t count, size_t size);
void model_free(void *ptr);
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
+
+#ifdef __cplusplus
+extern "C" {
+#endif
void * snapshot_malloc(size_t size);
void * snapshot_calloc(size_t count, size_t size);
void * snapshot_realloc(void *ptr, size_t size);
void snapshot_free(void *ptr);
+#ifdef __cplusplus
+} /* extern "C" */
+#endif
void * Thread_malloc(size_t size);
void Thread_free(void *ptr);
node_list.back()->clear_backtracking();
}
-/** Reset the node stack. */
-void NodeStack::full_reset()
-{
- for (unsigned int i = 0; i < node_list.size(); i++)
- delete node_list[i];
- node_list.clear();
- reset_execution();
- total_nodes = 1;
-}
-
Node * NodeStack::get_head() const
{
if (node_list.empty() || head_idx < 0)
Node * get_next() const;
void reset_execution();
void pop_restofstack(int numAhead);
- void full_reset();
int get_total_nodes() { return total_nodes; }
void print() const;
unsigned int enabledcount;
unsigned int bound;
unsigned int uninitvalue;
- int maxexecutions;
+ unsigned int maxexecutions;
/** @brief Maximum number of future values that can be sent to the same
* read */
#include "plugins.h"
#include "scanalysis.h"
-#include "scfence.h"
+#include "specanalysis.h"
ModelVector<TraceAnalysis *> * registered_analysis;
ModelVector<TraceAnalysis *> * installed_analysis;
registered_analysis=new ModelVector<TraceAnalysis *>();
installed_analysis=new ModelVector<TraceAnalysis *>();
registered_analysis->push_back(new SCAnalysis());
- registered_analysis->push_back(new SCFence());
+ registered_analysis->push_back(new SPECAnalysis());
}
ModelVector<TraceAnalysis *> * getRegisteredTraceAnalysis() {
}
const char * SCFence::name() {
- const char * name = "AUTOMO";
+ const char * name = "SCFENCE";
return name;
}
--- /dev/null
+# generic types
+*.o
+*.swp
+*.swo
+*.so
+*~
+*.dot
+.*.d
+*.pdf
+
+# files in this directory
+/tags
--- /dev/null
+# This make file is to be added by the Makefile at the main directory
+
+LOCAL_OBJS := specanalysis.o executiongraph.o specannotation.o methodcall.o
+
+SPEC_OBJS := $(LOCAL_OBJS:%=$(SPEC_DIR)/%)
+
+OBJECTS += $(SPEC_OBJS)
--- /dev/null
+#ifndef _CDSSPEC_H
+#define _CDSSPEC_H
+
+#include <vector>
+#include <list>
+#include <string>
+#include <iterator>
+#include <algorithm>
+#include <set>
+#include <unordered_map>
+
+#include <functional>
+
+#include <stdarg.h>
+
+#include "mymemory.h"
+//#include "common.h"
+#include "methodcall.h"
+
+using namespace std;
+
+/** Macro for output (stole from common.h) */
+extern int model_out;
+#define PRINT(fmt, ...) do { dprintf(model_out, fmt, ##__VA_ARGS__); } while (0)
+
+/**
+ A special kind of integer that has been embedded with a universal tag (ID)
+*/
+typedef struct TagInt {
+ unsigned int tag;
+ int val;
+
+ TagInt(unsigned int tag, int val) : tag(tag), val(val) { }
+
+ TagInt(int val) : tag(0), val(val) { }
+}TagInt;
+
+typedef SnapVector<int> IntVector;
+typedef SnapList<int> IntList;
+typedef SnapSet<int> IntSet;
+
+template<typename Key, typename Value>
+class Map: public unordered_map<Key, Value> {
+ public:
+ typedef unordered_map<Key, Value> map;
+
+ Map() : map() { }
+
+ Value get(Key key) {
+ return (*this)[key];
+ }
+
+ void put(Key key, Value value) {
+ (*this)[key] = value;
+ }
+
+ SNAPSHOTALLOC
+};
+
+class IntMap : public unordered_map<int, int> {
+ public:
+ typedef unordered_map<int, int> map;
+
+ IntMap() : map() { }
+
+ int get(int key) {
+ return (*this)[key];
+ }
+
+ void put(int key, int value) {
+ (*this)[key] = value;
+ }
+
+ SNAPSHOTALLOC
+};
+
+typedef SnapVector<double> DoubleVector;
+typedef SnapList<double> DoubleList;
+typedef SnapSet<double> DoubleSet;
+
+/********** Debugging functions **********/
+template<class Container>
+inline void printContainer(Container *container) {
+ if (!container || container->size() == 0)
+ PRINT("EMPTY");
+ for (auto it = container->begin(); it != container->end(); it++) {
+ int item = *it;
+ PRINT("%d ", item);
+ }
+}
+
+inline void printMap(IntMap *container) {
+ if (!container || container->size() == 0)
+ PRINT("EMPTY");
+ for (auto it = container->begin(); it != container->end(); it++) {
+ pair<int, int> item = *it;
+ PRINT("(%d, %d) ", item.first, item.second);
+ }
+}
+
+/********** More general specification-related types and operations **********/
+
+#define NewMethodSet new SnapSet<Method>
+
+#define CAT(a, b) CAT_HELPER(a, b) /* Concatenate two symbols for macros! */
+#define CAT_HELPER(a, b) a ## b
+#define X(name) CAT(__##name, __LINE__) /* unique variable */
+
+/**
+ This is a generic ForEach primitive for all the containers that support
+ using iterator to iterate.
+*/
+#define ForEach(item, container) \
+ auto X(_container) = (container); \
+ auto X(iter) = X(_container)->begin(); \
+ for (auto item = *X(iter); X(iter) != X(_container)->end(); item = ((++X(iter)) != \
+ X(_container)->end()) ? *X(iter) : 0)
+
+/**
+ This is a common macro that is used as a constant for the name of specific
+ variables. We basically have two usage scenario:
+ 1. In Subset operation, we allow users to specify a condition to extract a
+ subset. In that condition expression, we provide NAME, RET(type), ARG(type,
+ field) and STATE(field) to access each item's (method call) information.
+ 2. In general specification (in pre- & post- conditions and side effects),
+ we would automatically generate an assignment that assign the current
+ MethodCall* pointer to a variable namedd _M. With this, when we access the
+ state of the current method, we use STATE(field) (this is a reference
+ for read/write).
+*/
+#define ITEM _M
+#define _M ME
+
+#define S_RET __value->S_RET
+
+/**
+ This operation is specifically for Method set. For example, when we want to
+ construct an integer set from the state field "x" (which is an
+ integer) of the previous set of method calls (PREV), and the new set goes to
+ "readSet", we would call "MakeSet(int, PREV, readSet, STATE(x));". Then users
+ can use the "readSet" as an integer set (set<int>)
+*/
+#define MakeSet(type, oldset, newset, field) \
+ auto newset = new SnapSet<type>; \
+ ForEach (_M, oldset) \
+ newset->insert(field); \
+
+/**
+ We provide a more general subset operation that takes a plain boolean
+ expression on each item (access by the name "ITEM") of the set, and put it
+ into a new set if the boolean expression (Guard) on that item holds. This is
+ used as the second arguments of the Subset operation. An example to extract
+ a subset of positive elements on an IntSet "s" would be "Subset(s,
+ GeneralGuard(int, ITEM > 0))"
+*/
+#define GeneralGuard(type, expression) std::function<bool(type)> \
+ ([&](type ITEM) -> bool { return (expression);}) \
+
+/**
+ This is a more specific guard designed for the Method (MethodCall*). It
+ basically wrap around the GeneralGuard with the type Method. An example to
+ extract the subset of method calls in the PREV set whose state "x" is
+ equal to "val" would be "Subset(PREV, Guard(STATE(x) == val))"
+*/
+#define Guard(expression) GeneralGuard(Method, expression)
+
+#define MostRecent(guard) mostRecent(_M, guard)
+
+/**
+ A general subset operation that takes a condition and returns all the item
+ for which the boolean guard holds.
+*/
+inline SnapSet<Method>* mostRecent(Method method, std::function<bool(MethodCall*)> condition) {
+ SnapSet<Method> *res = new SnapSet<Method>;
+ // A list of potential nodes that are most recent
+ MethodList *toCheckList = new MethodList;
+ MethodSet prev = method->prev;
+ if (prev->empty()) // method is START node
+ return res;
+ else {
+ ForEach (_M, prev)
+ toCheckList->push_back(_M);
+ }
+ // Searching loop
+ while (!toCheckList->empty()) {
+ Method _M = toCheckList->front();
+ toCheckList->pop_front();
+
+ if (condition(_M)) {
+ bool recencyFlag = true;
+ ForEach (m, res) {
+ if (MethodCall::belong(_M->allNext, m)) {
+ recencyFlag = false;
+ break;
+ }
+ }
+ if (recencyFlag)
+ res->insert(_M);
+ }
+ else { // Not what we want, keep going up the graph
+ prev = _M->prev;
+ if (!prev->empty()) {
+ ForEach (_M, prev)
+ toCheckList->push_back(_M);
+ }
+ }
+ }
+ return res;
+}
+
+
+/**
+ A general subset operation that takes a condition and returns all the item
+ for which the boolean guard holds.
+*/
+template <class T>
+inline SnapSet<T>* Subset(SnapSet<T> *original, std::function<bool(T)> condition) {
+ SnapSet<T> *res = new SnapSet<T>;
+ ForEach (_M, original) {
+ if (condition(_M))
+ res->insert(_M);
+ }
+ return res;
+}
+
+/**
+ A general set operation that takes a condition and returns if there exists
+ any item for which the boolean guard holds.
+*/
+template <class T>
+inline bool HasItem(SnapSet<T> *original, std::function<bool(T)> condition) {
+ ForEach (_M, original) {
+ if (condition(_M))
+ return true;
+ }
+ return false;
+}
+
+
+
+/**
+ A general sublist operation that takes a condition and returns all the item
+ for which the boolean guard holds in the same order as in the old list.
+*/
+template <class T>
+inline list<T>* Sublist(list<T> *original, std::function<bool(T)> condition) {
+ list<T> *res = new list<T>;
+ ForEach (_M, original) {
+ if (condition(_M))
+ res->push_back(_M);
+ }
+ return res;
+}
+
+/**
+ A general subvector operation that takes a condition and returns all the item
+ for which the boolean guard holds in the same order as in the old vector.
+*/
+template <class T>
+inline vector<T>* Subvector(vector<T> *original, std::function<bool(T)> condition) {
+ vector<T> *res = new vector<T>;
+ ForEach (_M, original) {
+ if (condition(_M))
+ res->push_back(_M);
+ }
+ return res;
+}
+
+/** General for set, list & vector */
+#define Size(container) ((container)->size())
+
+#define _BelongHelper(type) \
+ template<class T> \
+ inline bool Belong(type<T> *container, T item) { \
+ return std::find(container->begin(), \
+ container->end(), item) != container->end(); \
+ }
+
+_BelongHelper(SnapSet)
+_BelongHelper(SnapVector)
+_BelongHelper(SnapList)
+
+/** General set operations */
+template<class T>
+inline SnapSet<T>* Intersect(SnapSet<T> *set1, SnapSet<T> *set2) {
+ SnapSet<T> *res = new SnapSet<T>;
+ ForEach (item, set1) {
+ if (Belong(set2, item))
+ res->insert(item);
+ }
+ return res;
+}
+
+template<class T>
+inline SnapSet<T>* Union(SnapSet<T> *s1, SnapSet<T> *s2) {
+ SnapSet<T> *res = new SnapSet<T>(*s1);
+ ForEach (item, s2)
+ res->insert(item);
+ return res;
+}
+
+template<class T>
+inline SnapSet<T>* Subtract(SnapSet<T> *set1, SnapSet<T> *set2) {
+ SnapSet<T> *res = new SnapSet<T>;
+ ForEach (item, set1) {
+ if (!Belong(set2, item))
+ res->insert(item);
+ }
+ return res;
+}
+
+template<class T>
+inline void Insert(SnapSet<T> *s, T item) { s->insert(item); }
+
+template<class T>
+inline void Insert(SnapSet<T> *s, SnapSet<T> *others) {
+ ForEach (item, others)
+ s->insert(item);
+}
+
+/*
+inline MethodSet MakeSet(int count, ...) {
+ va_list ap;
+ MethodSet res;
+
+ va_start (ap, count);
+ res = NewMethodSet;
+ for (int i = 0; i < count; i++) {
+ Method item = va_arg (ap, Method);
+ res->insert(item);
+ }
+ va_end (ap);
+ return res;
+}
+*/
+
+/********** Method call related operations **********/
+#define Id(method) method->id
+#define ID Id(_M)
+
+#define Name(method) method->name
+#define NAME Name(_M)
+
+#define StructName(type) __struct_ ## type ## __
+#define Value(method, type, field) ((StructName(type)*) method->value)->field
+#define Ret(method, type) Value(method, type, RET)
+#define CRet(method, type) Value(method, type, C_RET)
+#define Arg(method, type, arg) Value(method, type, arg)
+
+#define VALUE(type, field) Value(_M, type, field)
+#define RET(type) VALUE(type, RET)
+#define C_RET(type) VALUE(type, C_RET)
+#define ARG(type, arg) VALUE(type, arg)
+
+#define State(method, field) ((StateStruct*) method->state)->field
+#define STATE(field) State(_M, field)
+
+#define Prev(method) method->prev
+#define PREV _M->prev
+
+#define Next(method) method->next
+#define NEXT _M->next
+
+#define Concurrent(method) method->concurrent
+#define CONCURRENT _M->concurrent
+
+#define AllPrev(method) method->allPrev
+#define ALLPREV _M->allPrev
+
+#define AllNext(method) method->allNext
+#define ALLNEXT _M->allNext
+
+/***************************************************************************/
+/***************************************************************************/
+
+#endif
--- /dev/null
+#include <algorithm>
+#include "executiongraph.h"
+#include "action.h"
+#include "cyclegraph.h"
+#include "threads-model.h"
+#include "clockvector.h"
+#include "execution.h"
+#include <sys/time.h>
+#include <assert.h>
+#include <iterator>
+#include "modeltypes.h"
+#include "model-assert.h"
+#include "time.h"
+
+/******************** PotentialOP ********************/
+PotentialOP::PotentialOP(ModelAction *op, CSTR label) :
+ operation(op),
+ label(label)
+{
+
+}
+
+
+/******************** ExecutionGraph ********************/
+/********* ExecutionGraph (public functions) **********/
+/**
+ Initialze the necessary fields
+*/
+ExecutionGraph::ExecutionGraph(ModelExecution *e, bool allowCyclic) : execution(e), allowCyclic(allowCyclic) {
+ methodList = new MethodList;
+ randomHistory = NULL;
+
+ broken = false;
+ noOrderingPoint = false;
+ cyclic = false;
+ threadLists = new SnapVector<action_list_t*>;
+}
+
+void ExecutionGraph::buildGraph(action_list_t *actions) {
+ buildThreadLists(actions);
+
+ // First process the initialization annotation
+ bool hasInitAnno = false;
+ action_list_t::iterator iter = actions->begin();
+ for (; iter != actions->end(); iter++) {
+ ModelAction *act = *iter;
+ SpecAnnotation *anno = getAnnotation(act);
+ if (!anno)
+ continue;
+ // Deal with the Initialization annotation
+ if (anno->type == INIT) {
+ hasInitAnno = true;
+ AnnoInit *annoInit = (AnnoInit*) anno->annotation;
+ processInitAnnotation(annoInit);
+ break;
+ }
+ }
+ if (!hasInitAnno) {
+ model_print("There is no initialization annotation\n");
+ broken = true;
+ return;
+ }
+
+ // Process the nodes for method calls of each thread
+ buildNodesFromThreads();
+ // Establish the edges
+ buildEdges();
+}
+
+bool ExecutionGraph::isBroken() {
+ return broken;
+}
+
+bool ExecutionGraph::isNoOrderingPoint() {
+ return noOrderingPoint;
+}
+
+bool ExecutionGraph::hasCycle() {
+ if (cyclic)
+ return true;
+ if (randomHistory)
+ return false;
+ randomHistory = generateOneRandomHistory();
+ return cyclic;
+}
+
+void ExecutionGraph::resetBroken() {
+ broken = false;
+}
+
+bool ExecutionGraph::checkAdmissibility() {
+ MethodList::iterator iter1, iter2;
+ bool admissible = true;
+ for (iter1 = methodList->begin(); iter1 != methodList->end(); iter1++) {
+ Method m1 = *iter1;
+ iter1++;
+ iter2 = iter1;
+ iter1--;
+ for (; iter2 != methodList->end(); iter2++) {
+ Method m2 = *iter2;
+ if (isReachable(m1, m2) || isReachable(m2, m1))
+ continue;
+
+ /* Now we need to check whether we have a commutativity rule that
+ * says the two method calls should be ordered */
+ bool commute = true;
+ for (int i = 0; i < commuteRuleNum; i++) {
+ CommutativityRule rule = *(commuteRules + i);
+ /* Check whether condition is satisfied */
+ if (!rule.isRightRule(m1, m2)) // Not this rule
+ continue;
+ else
+ commute = rule.checkCondition(m1, m2);
+ if (!commute) // The rule requires them to be ordered
+ break;
+ }
+ // We have a rule that require m1 and m2 to be ordered
+ if (!commute) {
+ admissible = false;
+ model_print("These two nodes should not commute:\n");
+ model_print("\t");
+ ModelAction *begin1 = m1->begin;
+ ModelAction *begin2 = m2->begin;
+ int tid1 = id_to_int(begin1->get_tid());
+ int tid2 = id_to_int(begin2->get_tid());
+ model_print("%s_%d (T%d)", m1->name,
+ begin1->get_seq_number(), tid1);
+ model_print(" <-> ");
+ model_print("%s_%d (T%d)", m2->name,
+ begin2->get_seq_number(), tid2);
+ model_print("\n");
+ }
+ }
+ }
+
+ return admissible;
+}
+
+/** Checking cyclic graph specification */
+bool ExecutionGraph::checkCyclicGraphSpec(bool verbose) {
+ if (verbose) {
+ model_print("---- Start to check cyclic graph ----\n");
+ }
+
+ bool pass = false;
+ for (MethodList::iterator it = methodList->begin(); it != methodList->end();
+ it++) {
+ Method m = *it;
+ if (isFakeMethod(m))
+ continue;
+
+ StateFunctions *funcs = NULL;
+ if (verbose) {
+ m->print(false, false);
+ model_print("\n");
+
+ funcs = funcMap->get(m->name);
+ ASSERT (funcs);
+ UpdateState_t printValue = (UpdateState_t) funcs->print->function;
+ if (printValue) {
+ model_print("\t********** Value Info **********\n");
+ (*printValue)(m);
+ }
+ }
+
+ // Cyclic graph only supports @PreCondition
+ funcs = funcMap->get(m->name);
+ ASSERT (funcs);
+ CheckState_t preCondition = (CheckState_t)
+ funcs->preCondition->function;
+
+ // @PreCondition of Mehtod m
+ if (preCondition) {
+ pass = (*preCondition)(m, m);
+
+ if (!pass) {
+ model_print("PreCondition is not satisfied. Problematic method"
+ " is as follow: \n");
+ m->print(true, true);
+ if (verbose) {
+ model_print("---- Check cyclic graph END ----\n\n");
+ }
+ return false;
+ }
+ }
+ }
+
+ if (!pass) {
+ // Print out the graph
+ model_print("Problmatic execution graph: \n");
+ print(true);
+ } else if (verbose) {
+ // Print out the graph in verbose
+ model_print("Execution graph: \n");
+ print(true);
+ }
+
+ if (verbose) {
+ model_print("---- Check cyclic graph END ----\n\n");
+ }
+ return true;
+}
+
+
+
+
+/** To check "num" random histories */
+bool ExecutionGraph::checkRandomHistories(int num, bool stopOnFail, bool verbose) {
+ ASSERT (!cyclic);
+ bool pass = true;
+ int i;
+ for (i = 0; i < num; i++) {
+ MethodList *history = generateOneRandomHistory();
+ pass &= checkStateSpec(history, verbose, i + 1);
+ if (!pass) {
+ // Print out the graph
+ model_print("Problmatic execution graph: \n");
+ print();
+ if (stopOnFail) { // Just stop on this
+ // Recycle
+ delete history;
+ if (verbose)
+ model_print("We totally checked %d histories.\n", i + 1);
+ return false;
+ }
+ } else if (verbose) {
+ // Print out the graph in verbose
+ model_print("Execution graph: \n");
+ print();
+ }
+ // Recycle
+ delete history;
+ }
+
+ if (verbose)
+ model_print("We totally checked %d histories.\n", i);
+ return pass;
+}
+
+
+/**
+ To generate and check all possible histories.
+
+ If stopOnFailure is true, we stop generating any history and end the
+ checking process. Verbose flag controls how the checking process is exposed.
+*/
+bool ExecutionGraph::checkAllHistories(bool stopOnFailure, bool verbose) {
+ ASSERT (!cyclic);
+ MethodList *curList = new MethodList;
+ int numLiveNodes = methodList->size();
+ int historyIndex = 1;
+ if (verbose) {
+ // Print out the graph in verbose
+ print();
+ }
+
+ // FIXME: make stopOnFailure always true
+ stopOnFailure = true;
+ bool pass = checkAllHistoriesHelper(curList, numLiveNodes, historyIndex,
+ stopOnFailure, verbose);
+ if (pass) {
+ for (MethodList::iterator it = methodList->begin(); it !=
+ methodList->end(); it++) {
+ Method m = *it;
+ if (isFakeMethod(m))
+ continue;
+ if (!m->justified) {
+ model_print("\t");
+ m->print(false, false);
+ model_print(": unjustified\n");
+ pass = false;
+ break;
+ }
+ }
+ }
+
+ if (!pass && !verbose) {
+ // Print out the graph
+ print();
+ }
+ if (verbose)
+ model_print("We totally checked %d histories.\n", historyIndex - 1);
+ return pass;
+}
+
+
+/********** Several public printing functions (ExecutionGraph) **********/
+
+void ExecutionGraph::printOneHistory(MethodList *history, CSTR header) {
+ model_print("------------- %s (exec #%d) -------------\n", header,
+ execution->get_execution_number());
+ int idx = 1;
+ for (MethodList::iterator it = history->begin(); it != history->end(); it++) {
+ Method m = *it;
+ model_print("%d. ", idx++);
+ m->print(false);
+ }
+ model_print("------------- %s (exec #%d) (end) -------------\n",
+ header, execution->get_execution_number());
+}
+
+void ExecutionGraph::printAllHistories(MethodListVector *histories) {
+ model_print("------------- All histories (exec #%d) -------------\n",
+ execution->get_execution_number());
+ for (unsigned i = 0; i < histories->size(); i++) {
+ model_print("*********************** # %d ***********************\n", i + 1);
+ MethodList *history = (*histories)[i];
+ int idx = 1;
+ for (MethodList::iterator it = history->begin(); it != history->end();
+ it++) {
+ Method m = *it;
+ model_print("%d. ", idx++);
+ m->print(false);
+ }
+ if (i != histories->size() - 1)
+ model_print("\n");
+ }
+ model_print("------------- All histories (exec #%d) (end) "
+ "-------------\n\n", execution->get_execution_number());
+}
+
+/**
+ By default we print only all the edges that are directly from this mehtod
+ call node. If passed allEdges == true, we will print all the edges that are
+ reachable (SC/hb after) the current node.
+*/
+void ExecutionGraph::print(bool allEdges) {
+ model_print("\n");
+ const char *extraStr = allEdges ? "All Edges" : "";
+ model_print("------------------ Execution Graph -- %s (exec #%d)"
+ " ------------------\n", extraStr, execution->get_execution_number());
+ for (MethodList::iterator iter = methodList->begin(); iter !=
+ methodList->end(); iter++) {
+ Method m = *iter;
+ /* Print the info the this method */
+ m->print(false);
+ /* Print the info the edges directly from this node */
+ SnapSet<Method> *theSet = allEdges ? m->allNext : m->next;
+ for (SnapSet<Method>::iterator nextIter = theSet->begin(); nextIter !=
+ theSet->end(); nextIter++) {
+ Method next = *nextIter;
+ model_print("\t--> ");
+ next->print(false);
+ }
+ }
+ model_print("------------------ End Graph (exec #%d)"
+ " ------------------\n", execution->get_execution_number());
+ model_print("\n");
+}
+
+/**
+ By default we print only all the edges that are directly to this mehtod
+ call node. If passed allEdges == true, we will print all the edges that are
+ reachable (SC/hb before) from the current node.
+
+ Only for debugging!!
+*/
+void ExecutionGraph::PrintReverse(bool allEdges) {
+ model_print("\n");
+ const char *extraStr = allEdges ? "All Edges" : "";
+ model_print("------------------ Reverse Execution Graph -- %s (exec #%d)"
+ " ------------------\n", extraStr, execution->get_execution_number());
+ for (MethodList::iterator iter = methodList->begin(); iter !=
+ methodList->end(); iter++) {
+ Method m = *iter;
+ /* Print the info the this method */
+ m->print(false);
+ /* Print the info the edges directly from this node */
+ SnapSet<Method> *theSet = allEdges ? m->allPrev : m->prev;
+ for (SnapSet<Method>::iterator prevIter = theSet->begin(); prevIter !=
+ theSet->end(); prevIter++) {
+ Method prev = *prevIter;
+ model_print("\t--> ");
+ prev->print(false);
+ }
+ }
+ model_print("------------------ End Reverse Graph (exec #%d)"
+ " ------------------\n", execution->get_execution_number());
+ model_print("\n");
+}
+
+
+/********** Internal member functions (ExecutionGraph) **********/
+
+void ExecutionGraph::buildThreadLists(action_list_t *actions) {
+ int maxthreads = 0;
+ for (action_list_t::iterator it = actions->begin(); it != actions->end(); it++) {
+ ModelAction *act = *it;
+ int threadid = id_to_int(act->get_tid());
+ if (threadid == 0)
+ continue;
+ if (threadid > maxthreads) {
+ threadLists->resize(threadid + 1);
+ maxthreads = threadid;
+ }
+ action_list_t *list = (*threadLists)[threadid];
+ if (!list) {
+ list = new action_list_t;
+ (*threadLists)[threadid] = list;
+ }
+ list->push_back(act);
+ }
+}
+
+/**
+ Outside of this function, we have already processed the INIT type of
+ annotation, and we focus on extracting all the method call nodes in the
+ current thread list. This routine basically iterates the list, finds the
+ INTERFACE_BEGIN annotation, call the function extractMethod(), and advance
+ the iterator. That is to say, we would only see INIT or INTERFACE_BEGIN
+ annotations; if not, the specification annotations are wrong (we mark the
+ graph is broken)
+*/
+void ExecutionGraph::buildNodesFromThread(action_list_t *actions) {
+ action_list_t::iterator iter = actions->begin();
+
+ // FIXME: Just for the purpose of debugging
+ //printActions(actions, "BuildNodesFromThread");
+
+ // annoBegin == NULL means we are looking for the beginning annotation
+ while (iter != actions->end()) {
+ ModelAction *act = *iter;
+ SpecAnnotation *anno = getAnnotation(act);
+ if (!anno) { // Means this is not an annotation action
+ iter++;
+ continue;
+ }
+ if (anno->type == INTERFACE_BEGIN) { // Interface beginning anno
+ Method m = extractMethod(actions, iter);
+ if (m) {
+ // Get a complete method call node and store it
+ methodList->push_back(m);
+ } else {
+ broken = true;
+ model_print("Error with constructing a complete node.\n");
+ return;
+ }
+ } else if (anno->type != INIT) {
+ broken = true;
+ model_print("Missing beginning annotation.\n");
+ return;
+ } else { // This is an INIT annotation
+ iter++;
+ }
+ }
+}
+
+void ExecutionGraph::buildNodesFromThreads() {
+ /* We start from the 1st real thread */
+ for (unsigned i = 1; i < threadLists->size(); i++) {
+ buildNodesFromThread((*threadLists)[i]);
+ if (broken) // Early exit when detecting errors
+ return;
+ }
+}
+
+/**
+ Find the previous non-annotation model action (ordering point from the
+ current iterator
+*/
+ModelAction* ExecutionGraph::findPrevAction(action_list_t *actions, action_list_t::iterator
+ iter) {
+ while (iter != actions->begin()) {
+ iter--;
+ ModelAction *res = *iter;
+ if (res->get_type() != ATOMIC_ANNOTATION)
+ return res;
+ }
+ return NULL;
+}
+
+/**
+ When called, the current iter points to a beginning annotation; when done,
+ the iter points to either the end of the list or the next INTERFACE_BEGIN
+ annotation.
+*/
+Method ExecutionGraph::extractMethod(action_list_t *actions, action_list_t::iterator &iter) {
+ ModelAction *act = *iter;
+ SpecAnnotation *anno = getAnnotation(act);
+ ASSERT(anno && anno->type == INTERFACE_BEGIN);
+
+ // Partially initialize the commit point node with the already known fields
+ AnnoInterfaceInfo *info = (AnnoInterfaceInfo*) anno->annotation;
+ Method m = new MethodCall(info->name, info->value, act);
+
+ // Some declaration for potential ordering points and its check
+ CSTR label;
+ PotentialOP *potentialOP= NULL;
+ // A list of potential ordering points
+ PotentialOPList *popList = new PotentialOPList;
+ // Ordering point operation
+ ModelAction *op = NULL;
+ // Whether the potential ordering points were defined
+ bool hasAppeared = false;
+
+ bool methodComplete = false;
+ int nestedLevel = 0;
+ for (iter++; iter != actions->end(); iter++) {
+ act = *iter;
+ SpecAnnotation *anno = getAnnotation(act);
+ if (!anno)
+ continue;
+ // Now we are dealing with one annotation
+ switch (anno->type) {
+ case POTENTIAL_OP:
+ //model_print("POTENTIAL_OP\n");
+ label = (CSTR) anno->annotation;
+ op = findPrevAction(actions, iter);
+ if (!op) {
+ model_print("Potential ordering point annotation should"
+ "follow an atomic operation.\n");
+ model_print("%s_%d\n", label,
+ act->get_seq_number());
+ broken = true;
+ return NULL;
+ }
+ potentialOP = new PotentialOP(op, label);
+ popList->push_back(potentialOP);
+ break;
+ case OP_CHECK:
+ //model_print("OP_CHECK\n");
+ label = (CSTR) anno->annotation;
+ // Check if corresponding potential ordering point has appeared.
+ hasAppeared = false;
+ // However, in the current version of spec, we take the most
+ // recent one in the list as the commit point (so we use
+ // reverse iterator)
+ for (PotentialOPList::reverse_iterator popIter = popList->rbegin();
+ popIter != popList->rend(); popIter++) {
+ potentialOP = *popIter;
+ if (label == potentialOP->label) {
+ m->addOrderingPoint(potentialOP->operation);
+ hasAppeared = true;
+ break; // Done when find the "first" PCP
+ }
+ }
+ if (!hasAppeared) {
+ model_print("Ordering point check annotation should"
+ "have previous potential ordering point.\n");
+ model_print("%s_%d\n", label,
+ act->get_seq_number());
+ broken = true;
+ return NULL;
+ }
+ break;
+ case OP_DEFINE:
+ //model_print("CP_DEFINE_CHECK\n");
+ op = findPrevAction(actions, iter);
+ if (!op) {
+ model_print("Ordering point define should "
+ "follow an atomic operation.\n");
+ act->print();
+ broken = true;
+ return NULL;
+ }
+ m->addOrderingPoint(op);
+ break;
+ case OP_CLEAR:
+ //model_print("OP_CLEAR\n");
+ // Reset everything
+ // Clear the list of potential ordering points
+ popList->clear();
+ // Clear the previous list of commit points
+ m->orderingPoints->clear();
+ break;
+ case OP_CLEAR_DEFINE:
+ //model_print("OP_CLEAR_DEFINE\n");
+ // Reset everything
+ popList->clear();
+ m->orderingPoints->clear();
+ // Define the ordering point
+ op = findPrevAction(actions, iter);
+ if (!op) {
+ model_print("Ordering point clear define should "
+ "follow an atomic operation.\n");
+ act->print();
+ broken = true;
+ return NULL;
+ }
+ m->addOrderingPoint(op);
+ break;
+ case INTERFACE_BEGIN:
+ nestedLevel++;
+ break;
+ case INTERFACE_END:
+ if (nestedLevel == 0) {
+ methodComplete = true;
+ }
+ else
+ nestedLevel--;
+ break;
+ default:
+ model_print("Unknown type!! We should never get here.\n");
+ ASSERT(false);
+ return NULL;
+ }
+ if (methodComplete) // Get out of the loop when we have a complete node
+ break;
+ }
+
+ ASSERT (iter == actions->end() || (getAnnotation(*iter) &&
+ getAnnotation(*iter)->type == INTERFACE_END));
+ if (iter != actions->end())
+ iter++;
+
+ delete popList;
+ // XXX: We just allow methods to have no specified ordering points. In that
+ // case, the method is concurrent with every other method call
+ if (m->orderingPoints->size() == 0) {
+ noOrderingPoint = true;
+ return m;
+ } else {
+ // Get a complete method call
+ return m;
+ }
+}
+
+/**
+ A utility function to extract the actual annotation
+ pointer and return the actual annotation if this is an annotation action;
+ otherwise return NULL.
+*/
+SpecAnnotation* ExecutionGraph::getAnnotation(ModelAction *act) {
+ if (act->get_type() != ATOMIC_ANNOTATION)
+ return NULL;
+ if (act->get_value() != SPEC_ANALYSIS)
+ return NULL;
+ SpecAnnotation *anno = (SpecAnnotation*) act->get_location();
+ ASSERT (anno);
+ return anno;
+}
+
+void ExecutionGraph::processInitAnnotation(AnnoInit *annoInit) {
+ // Assign state initial (and copy) function
+ NamedFunction *func = annoInit->initial;
+ ASSERT (func && func->type == INITIAL);
+ initial= annoInit->initial;
+
+ func = annoInit->final;
+ ASSERT (func && func->type == FINAL);
+ final= annoInit->final;
+
+ func = annoInit->copy;
+ ASSERT (func && func->type == COPY);
+ copy= annoInit->copy;
+
+ func = annoInit->clear;
+ ASSERT (func && func->type == CLEAR);
+ clear= annoInit->clear;
+
+ func = annoInit->printState;
+ ASSERT (func && func->type == PRINT_STATE);
+ printState = annoInit->printState;
+
+ // Assign the function map (from interface name to state functions)
+ funcMap = annoInit->funcMap;
+
+ // Initialize the commutativity rules array and size
+ commuteRules = annoInit->commuteRules;
+ commuteRuleNum = annoInit->commuteRuleNum;
+}
+
+/**
+ After building up the graph (both the nodes and egdes are correctly built),
+ we also call this function to initialize the most recent justified node of
+ each method node.
+
+ A justified method node of a method m is a method that is in the allPrev set
+ of m, and all other nodes in the allPrev set of m are either before or after
+ it. The most recent justified node is the most recent one in the hb/SC
+ order.
+*/
+void ExecutionGraph::initializeJustifiedNode() {
+ MethodList::iterator it = methodList->begin();
+ // Start from the second methods in the list --- skipping the START node
+ for (it++; it != methodList->end(); it++) {
+ Method m = *it;
+ // Walk all the way up, when we have multiple immediately previous
+ // choices, pick one and check if that node is a justified node --- its
+ // concurrent set should be disjoint with the whole set m->allPrev. If
+ // not, keep going up; otherwise, that node is the most recent justified
+ // node
+
+ MethodSet prev = NULL;
+ Method justified = m;
+ do {
+ prev = justified->prev;
+ // At the very least we should have the START nodes
+ ASSERT (!prev->empty());
+
+ // setIt points to the very beginning of allPrev set
+ SnapSet<Method>::iterator setIt = prev->begin();
+ justified = *setIt;
+ // Check whether justified is really the justified node
+ if (MethodCall::disjoint(justified->concurrent, m->allPrev))
+ break;
+ } while (true);
+
+ ASSERT (justified != m);
+ // Don't forget to set the method's field
+ m->justifiedMethod = justified;
+
+ // Ensure we reset the justified field to be false in the beginning
+ m->justified = false;
+ }
+}
+
+
+/**
+ This is a very important interal function to build the graph. When called,
+ we assume that we have a list of method nodes built (extracted from the raw
+ execution), and this routine is responsible for building the connection
+ edges between them to yield an execution graph for checking
+*/
+void ExecutionGraph::buildEdges() {
+
+ MethodList::iterator iter1, iter2;
+ // First build the allPrev and allNext set (don't care if they are right
+ // previous or right next first)
+ for (iter1 = methodList->begin(); iter1 != methodList->end(); iter1++) {
+ Method m1 = *iter1;
+ iter1++;
+ iter2 = iter1;
+ iter1--;
+ for (; iter2 != methodList->end(); iter2++) {
+ Method m2 = *iter2;
+ int val = conflict(m1, m2);
+ if (val == 1) {
+ m1->allNext->insert(m2);
+ m2->allPrev->insert(m1);
+ } else if (val == -1) {
+ m2->allNext->insert(m1);
+ m1->allPrev->insert(m2);
+ } else if (val == SELF_CYCLE) {
+ if (allowCyclic) {
+ // m1 -> m2
+ m1->allNext->insert(m2);
+ m2->allPrev->insert(m1);
+ // m2 -> m1
+ m2->allNext->insert(m1);
+ m1->allPrev->insert(m2);
+ }
+ }
+ }
+ }
+
+ // Initialize two special nodes (START & FINISH)
+ Method startMethod = new MethodCall(GRAPH_START);
+ Method finishMethod = new MethodCall(GRAPH_FINISH);
+ // Initialize startMethod and finishMethd
+ startMethod->allNext->insert(finishMethod);
+ finishMethod->allPrev->insert(startMethod);
+ for (MethodList::iterator iter = methodList->begin(); iter !=
+ methodList->end(); iter++) {
+ Method m = *iter;
+ startMethod->allNext->insert(m);
+ m->allPrev->insert(startMethod);
+ m->allNext->insert(finishMethod);
+ finishMethod->allPrev->insert(m);
+ }
+ // Push these two special nodes to the beginning & end of methodList
+ methodList->push_front(startMethod);
+ methodList->push_back(finishMethod);
+
+ // Now build the prev, next and concurrent sets
+ for (MethodList::iterator iter = methodList->begin(); iter != methodList->end();
+ iter++) {
+ Method m = *iter;
+ // prev -- nodes in allPrev that are before none in allPrev
+ // next -- nodes in allNext that are after none in allNext (but we
+ // actually build this set together with building prev
+ SnapSet<Method>::iterator setIt;
+ for (setIt = m->allPrev->begin(); setIt != m->allPrev->end(); setIt++) {
+ Method prevMethod = *setIt;
+ if (MethodCall::disjoint(m->allPrev, prevMethod->allNext)) {
+ m->prev->insert(prevMethod);
+ prevMethod->next->insert(m);
+ }
+ }
+
+ // concurrent -- all other nodes besides MYSELF, allPrev and allNext
+ for (MethodList::iterator concurIter = methodList->begin(); concurIter !=
+ methodList->end(); concurIter++) {
+ Method concur = *concurIter;
+ if (concur != m && !MethodCall::belong(m->allPrev, concur)
+ && !MethodCall::belong(m->allNext, concur))
+ m->concurrent->insert(concur);
+ }
+ }
+
+ if (!cyclic)
+ AssertEdges();
+ // Initialize the justified method of each method
+ if (!cyclic)
+ initializeJustifiedNode();
+}
+
+/**
+ This method call is used to check whether the edge sets of the nodes are
+ built correctly --- consistently. We should only use this routine after the
+ builiding of edges when debugging
+*/
+void ExecutionGraph::AssertEdges() {
+ // Assume there is no self-cycle in execution (ordering points are fine)
+ ASSERT (!cyclic);
+
+ MethodList::iterator it;
+ for (it = methodList->begin(); it != methodList->end(); it++) {
+ Method m = *it;
+ SnapSet<Method>::iterator setIt, setIt1;
+ int val = 0;
+
+ // Soundness of sets
+ // 1. allPrev is sound
+ for (setIt = m->allPrev->begin(); setIt != m->allPrev->end(); setIt++) {
+ Method prevMethod = *setIt;
+ val = conflict(prevMethod, m);
+ ASSERT (val == 1);
+ }
+ // 2. allNext is sound
+ for (setIt = m->allNext->begin(); setIt != m->allNext->end(); setIt++) {
+ Method nextMethod = *setIt;
+ val = conflict(m, nextMethod);
+ ASSERT (val == 1);
+ }
+ // 3. concurrent is sound
+ for (setIt = m->concurrent->begin(); setIt != m->concurrent->end(); setIt++) {
+ Method concur = *setIt;
+ val = conflict(m, concur);
+ ASSERT (val == 0);
+ }
+ // 4. allPrev & allNext are complete
+ ASSERT (1 + m->allPrev->size() + m->allNext->size() + m->concurrent->size()
+ == methodList->size());
+ // 5. prev is sound
+ for (setIt = m->prev->begin(); setIt != m->prev->end(); setIt++) {
+ Method prev = *setIt;
+ ASSERT (MethodCall::belong(m->allPrev, prev));
+ for (setIt1 = m->allPrev->begin(); setIt1 != m->allPrev->end();
+ setIt1++) {
+ Method middle = *setIt1;
+ if (middle == prev)
+ continue;
+ val = conflict(prev, middle);
+ // prev is before none
+ ASSERT (val != 1);
+ }
+ }
+ // 6. prev is complete
+ for (setIt = m->allPrev->begin(); setIt != m->allPrev->end(); setIt++) {
+ Method prev = *setIt;
+ if (MethodCall::belong(m->prev, prev))
+ continue;
+ // Ensure none of other previous nodes should be in the prev set
+ bool hasMiddle = false;
+ for (setIt1 = m->allPrev->begin(); setIt1 != m->allPrev->end();
+ setIt1++) {
+ Method middle = *setIt1;
+ if (middle == prev)
+ continue;
+ val = conflict(prev, middle);
+ if (val == 1)
+ hasMiddle = true;
+ }
+ ASSERT (hasMiddle);
+ }
+
+ // 7. next is sound
+ for (setIt = m->next->begin(); setIt != m->next->end(); setIt++) {
+ Method next = *setIt;
+ ASSERT (MethodCall::belong(m->allNext, next));
+ for (setIt1 = m->allNext->begin(); setIt1 != m->allNext->end();
+ setIt1++) {
+ Method middle = *setIt1;
+ if (middle == next)
+ continue;
+ val = conflict(middle, next);
+ // next is after none
+ ASSERT (val != 1);
+ }
+ }
+ // 8. next is complete
+ for (setIt = m->allNext->begin(); setIt != m->allNext->end(); setIt++) {
+ Method next = *setIt;
+ if (MethodCall::belong(m->next, next))
+ continue;
+ // Ensure none of other next nodes should be in the next set
+ bool hasMiddle = false;
+ for (setIt1 = m->allNext->begin(); setIt1 != m->allNext->end();
+ setIt1++) {
+ Method middle = *setIt1;
+ if (middle == next)
+ continue;
+ val = conflict(middle, next);
+ if (val == 1)
+ hasMiddle = true;
+ }
+ ASSERT (hasMiddle);
+ }
+ }
+}
+
+/**
+ The conflicting relation between two model actions by hb/SC. If act1 and
+ act2 commutes, it returns 0; Otherwise, if act1->act2, it returns 1; and if
+ act2->act1, it returns -1
+*/
+int ExecutionGraph::conflict(ModelAction *act1, ModelAction *act2) {
+ if (act1->happens_before(act2))
+ return 1;
+ else if (act2->happens_before(act1))
+ return -1;
+
+ if (act1->is_seqcst() && act2->is_seqcst()) {
+ if (act1->get_seq_number() < act2->get_seq_number())
+ return 1;
+ else
+ return -1;
+ } else
+ return 0;
+}
+
+/**
+ If there is no conflict between the ordering points of m1 and m2, then it
+ returns 0; Otherwise, if m1->m2, it returns 1; and if m2->m1, it returns -1.
+ If some ordering points have inconsistent conflicting relation, we print out
+ an error message (Self-cycle) and set the broken flag and return
+*/
+int ExecutionGraph::conflict(Method m1, Method m2) {
+ ASSERT (m1 != m2);
+
+ if (isStartMethod(m1))
+ return 1;
+ if (isFinishMethod(m2))
+ return 1;
+
+ action_list_t *OPs1= m1->orderingPoints;
+ action_list_t *OPs2= m2->orderingPoints;
+ // Method calls without ordering points are concurrent with any others
+ if (OPs1->empty() || OPs2->empty())
+ return 0;
+ int val = 0;
+ action_list_t::iterator iter1, iter2;
+ for (iter1 = OPs1->begin(); iter1 != OPs1->end(); iter1++) {
+ ModelAction *act1 = *iter1;
+ for (iter2 = OPs2->begin(); iter2 != OPs2->end(); iter2++) {
+ ModelAction *act2 = *iter2;
+ int res = conflict(act1, act2);
+ if (res == 0) // Commuting actions
+ continue;
+ if (val == 0)
+ val = res;
+ else if (val != res) { // Self cycle
+ cyclic = true;
+ if (!allowCyclic) {
+ model_print("There is a self cycle between the following two "
+ "methods\n");
+ m1->print();
+ m2->print();
+ broken = true;
+ }
+ return SELF_CYCLE;
+ }
+ }
+ }
+ return val;
+}
+
+/**
+ Whether m2 is before m2 in the execution graph
+*/
+bool ExecutionGraph::isReachable(Method m1, Method m2) {
+ return MethodCall::belong(m1->allNext, m2);
+}
+
+MethodVector* ExecutionGraph::getRootNodes() {
+ MethodVector *vec = new MethodVector;
+ for (MethodList::iterator it = methodList->begin(); it != methodList->end();
+ it++) {
+ Method m = *it;
+ if (!m->exist)
+ continue;
+ MethodSet prevMethods = m->allPrev;
+ if (prevMethods->size() == 0) { // Fast check (naturally a root node)
+ vec->push_back(m);
+ continue;
+ }
+
+ // Also a root when all previous nodes no longer exist
+ bool isRoot = true;
+ for (SnapSet<Method>::iterator setIt = prevMethods->begin(); setIt !=
+ prevMethods->end(); setIt++) {
+ Method prev = *setIt;
+ if (prev->exist) { // It does have an incoming edge
+ isRoot = false;
+ break;
+ }
+ }
+ // It does not have an incoming edge now
+ if (isRoot)
+ vec->push_back(m);
+ }
+ return vec;
+}
+
+/**
+ Collects the set of method call nodes that do NOT have any following nodes
+ (the tail of the graph)
+*/
+MethodVector* ExecutionGraph::getEndNodes() {
+ MethodVector *vec = new MethodVector;
+ for (MethodList::iterator it = methodList->begin(); it != methodList->end();
+ it++) {
+ Method m = *it;
+ if (m->next->size() == 0)
+ vec->push_back(m);
+ }
+ return vec;
+}
+
+/**
+ This is a helper function for generating all the topological sortings of the
+ execution graph. The idea of this function is recursively do the following
+ process: logically mark whether a method call node is alive or not
+ (initially all are alive), find a list of nodes that are root nodes (that
+ have no incoming edges), continuously pick one node in that list, add it to the curList
+ (which stores one topological sorting), and then recursively call itself to
+ resolve the rest.
+
+ Arguments:
+ curList -> It represents a temporal result of a specific topological
+ sorting; keep in mind that before calling this function, pass an empty
+ list.
+ numLiveNodes -> The number of nodes that are logically alive (that have not
+ been selected and added in a specific topological sorting yet). We keep
+ such a number as an optimization since when numLinveNodes equals to 0,
+ we can backtrack. Initially it is the size of the method call list.
+ historyIndex -> The current history index. We should start with 1.
+ stopOnFailure -> Stop the checking once we see a failed history
+ verbose -> Whether the verbose mode is on
+*/
+bool ExecutionGraph::checkAllHistoriesHelper(MethodList *curList, int
+ &numLiveNodes, int &historyIndex, bool stopOnFailure, bool verbose) {
+ if (cyclic)
+ return false;
+
+ bool satisfied = true;
+ if (numLiveNodes == 0) { // Found one sorting, and we can backtrack
+ // Don't forget to increase the history number
+ satisfied = checkStateSpec(curList, verbose, historyIndex++);
+ // Don't forget to recycle
+ delete curList;
+ return satisfied;
+ }
+
+ MethodVector *roots = getRootNodes();
+ // Cycle exists (no root nodes but still have live nodes
+ if (roots->size() == 0) {
+ model_print("There is a cycle in this graph so we cannot generate"
+ " sequential histories\n");
+ cyclic = true;
+ return false;
+ }
+
+ for (unsigned i = 0; i < roots->size(); i++) {
+ Method m = (*roots)[i];
+ m->exist = false;
+ numLiveNodes--;
+ // Copy the whole current list and use that copy for the next recursive
+ // call (because we will need the same copy for other iterations at the
+ // same level of recursive calls)
+ MethodList *newList = new MethodList(*curList);
+ newList->push_back(m);
+
+ bool oneSatisfied = checkAllHistoriesHelper(newList, numLiveNodes,
+ historyIndex, stopOnFailure, verbose);
+ // Stop the checking once failure or cycle detected
+ if (!oneSatisfied && (cyclic || stopOnFailure)) {
+ delete curList;
+ delete roots;
+ return false;
+ }
+ satisfied &= oneSatisfied;
+ // Recover
+ m->exist = true;
+ numLiveNodes++;
+ }
+ delete curList;
+ delete roots;
+ return satisfied;
+}
+
+/** To check one generated history */
+bool ExecutionGraph::checkHistory(MethodList *history, int historyIndex, bool
+ verbose) {
+ bool pass = checkStateSpec(history, verbose, historyIndex);
+ if (!pass) {
+ // Print out the graph
+ model_print("Problmatic execution graph: \n");
+ print();
+ } else if (verbose) {
+ // Print out the graph in verbose
+ model_print("Execution graph: \n");
+ print();
+ }
+ return pass;
+}
+
+/** Generate one random topological sorting */
+MethodList* ExecutionGraph::generateOneRandomHistory() {
+ MethodList *res = new MethodList;
+ int liveNodeNum = methodList->size();
+ generateOneRandomHistoryHelper(res, liveNodeNum);
+ if (cyclic) {
+ delete res;
+ return NULL;
+ }
+ // Reset the liveness of each method
+ for (MethodList::iterator it = methodList->begin(); it != methodList->end();
+ it++) {
+ Method m = *it;
+ m->exist = true;
+ }
+ return res;
+}
+
+/**
+ The helper function to generate one random topological sorting
+*/
+void ExecutionGraph::generateOneRandomHistoryHelper(MethodList
+ *curList, int &numLiveNodes) {
+ if (cyclic)
+ return;
+
+ if (numLiveNodes == 0) { // Found one sorting, and we can return
+ // Don't forget to recycle
+ return;
+ }
+
+ MethodVector *roots = getRootNodes();
+ // Cycle exists (no root nodes but still have live nodes
+ if (roots->size() == 0) {
+ model_print("There is a cycle in this graph so we cannot generate"
+ " sequential histories\n");
+ cyclic = true;
+ return;
+ }
+
+ srand (time(NULL));
+ int pick = rand() % roots->size();
+ Method m = (*roots)[pick];
+
+ m->exist = false;
+ numLiveNodes--;
+ curList->push_back(m);
+
+ delete roots;
+ generateOneRandomHistoryHelper(curList, numLiveNodes);
+}
+
+Method ExecutionGraph::getStartMethod() {
+ return methodList->front();
+}
+
+Method ExecutionGraph::getFinishMethod() {
+ return methodList->back();
+}
+
+/**
+ Print out the ordering points and dynamic calling info (return value &
+ arguments) of all the methods in the methodList
+*/
+void ExecutionGraph::printAllMethodInfo(bool verbose) {
+ model_print("------------------ Method Info (exec #%d)"
+ " ------------------\n", execution->get_execution_number());
+ for (MethodList::iterator iter = methodList->begin(); iter !=
+ methodList->end(); iter++) {
+ Method m = *iter;
+ printMethodInfo(m, verbose);
+ }
+ model_print("------------------ End Info (exec #%d)"
+ " ------------------\n\n", execution->get_execution_number());
+}
+
+/**
+ Print out the ordering points and dynamic calling info (return value &
+ arguments).
+*/
+void ExecutionGraph::printMethodInfo(Method m, bool verbose) {
+ StateFunctions *funcs = NULL;
+ m->print(verbose, true);
+
+ if (isFakeMethod(m))
+ return;
+
+ funcs = funcMap->get(m->name);
+ ASSERT (funcs);
+ UpdateState_t printValue = (UpdateState_t) funcs->print->function;
+
+ model_print("\t********** Value Info **********\n");
+ if (printValue) {
+ (*printValue)(m);
+ } else {
+ model_print("\tNothing printed..\n");
+ }
+}
+
+
+/** Clear the states of the method call */
+void ExecutionGraph::clearStates() {
+ UpdateState_t clearFunc = (UpdateState_t) clear->function;
+ for (MethodList::iterator it = methodList->begin(); it != methodList->end();
+ it++) {
+ Method m = *it;
+ if (m->state) {
+ (*clearFunc)(m);
+ }
+ }
+}
+
+
+/**
+ Checking the state specification (in sequential order)
+*/
+bool ExecutionGraph::checkStateSpec(MethodList *history, bool verbose, int
+ historyIndex) {
+ if (verbose) {
+ if (historyIndex > 0)
+ model_print("---- Start to check history #%d ----\n", historyIndex);
+ else
+ model_print("---- Start to check history ----\n");
+ }
+
+ // @Transition can also return a boolean. For example when a steal() and
+ // take() in the deque both gets the last element, then we can see a bug in
+ // the evaluating the list of @Transitions for a following operation.
+ bool satisfied = true;
+
+ // @Initial state (with a fake starting node)
+ Method startMethod = getStartMethod();
+ UpdateState_t initialFunc = (UpdateState_t) initial->function;
+ UpdateState_t printStateFunc = (UpdateState_t) printState->function;
+ UpdateState_t clearFunc = (UpdateState_t) clear->function;
+
+ // We execute the equivalent sequential data structure with the state of the
+ // startMethod node
+ (*initialFunc)(startMethod);
+ if (verbose) {
+ startMethod->print(false, true);
+ model_print("\t@Initial on START\n");
+ if (printStateFunc) { // If user has defined the print-out function
+ model_print("\t********** State Info **********\n");
+ (*printStateFunc)(startMethod);
+ }
+ }
+
+ StateFunctions *funcs = NULL;
+ /** Execute each method call in the history */
+ for (MethodList::iterator it = history->begin(); it != history->end();
+ it++) {
+ Method m = *it;
+ if (isFakeMethod(m))
+ continue;
+
+ StateTransition_t transition = NULL;
+
+ if (verbose) {
+ m->print(false, true);
+ funcs = funcMap->get(m->name);
+ ASSERT (funcs);
+ UpdateState_t printValue = (UpdateState_t) funcs->print->function;
+ if (printValue) {
+ model_print("\t********** Value Info **********\n");
+ (*printValue)(m);
+ }
+ }
+
+ funcs = funcMap->get(m->name);
+ ASSERT (funcs);
+
+ CheckState_t preCondition = (CheckState_t)
+ funcs->preCondition->function;
+ // @PreCondition of Mehtod m
+ if (preCondition) {
+ satisfied = (*preCondition)(startMethod, m);
+
+ if (!satisfied) {
+ model_print("PreCondition is not satisfied. Problematic method"
+ " is as follow: \n");
+ m->print(true, true);
+ printOneHistory(history, "Failed History");
+ if (verbose) {
+ if (historyIndex > 0)
+ model_print("---- Check history #%d END ----\n\n",
+ historyIndex);
+ else
+ model_print("---- Check history END ----\n\n");
+ }
+ break;
+ }
+ }
+
+ // After checking the PreCondition, we run the transition on the
+ // startMethod node to update its state
+ transition = (StateTransition_t) funcs->transition->function;
+ // @Transition on the state of startMethod
+ satisfied = (*transition)(startMethod, m);
+ if (!satisfied) { // Error in evaluating @Transition
+ model_print("Transition returns false. Problematic method"
+ " is as follow: \n");
+ m->print(true, true);
+ printOneHistory(history, "Failed History");
+ if (verbose) {
+ if (historyIndex > 0)
+ model_print("---- Check history #%d END ----\n\n",
+ historyIndex);
+ else
+ model_print("---- Check history END ----\n\n");
+ }
+ break;
+ }
+
+ if (verbose) {
+ model_print("\t@Transition on itself\n");
+ if (printStateFunc) {
+ model_print("\t********** State Info **********\n");
+ (*printStateFunc)(startMethod);
+ }
+ }
+
+ // @PostCondition of Mehtod m
+ CheckState_t postCondition = (CheckState_t)
+ funcs->postCondition->function;
+ if (postCondition) {
+ satisfied = (*postCondition)(startMethod, m);
+
+ if (!satisfied) {
+ model_print("PostCondition is not satisfied. Problematic method"
+ " is as follow: \n");
+ m->print(true, true);
+ printOneHistory(history, "Failed History");
+ if (verbose) {
+ if (historyIndex > 0)
+ model_print("---- Check history #%d END ----\n\n",
+ historyIndex);
+ else
+ model_print("---- Check history END ----\n\n");
+ }
+ break;
+ }
+ }
+ }
+
+ // Clear out the states created when checking
+ (*clearFunc)(startMethod);
+
+ if (satisfied && verbose) {
+ printOneHistory(history, "Passed History");
+ // Print the history in verbose mode
+ if (historyIndex > 0)
+ model_print("---- Check history #%d END ----\n\n", historyIndex);
+ else
+ model_print("---- Check history END ----\n\n");
+ }
+
+ if (verbose) {
+ if (historyIndex > 0)
+ model_print("---- Start to check justifying subhistory #%d ----\n", historyIndex);
+ else
+ model_print("---- Start to check justifying subhistory ----\n");
+
+ }
+ for (MethodList::iterator it = history->begin(); it != history->end();
+ it++) {
+ Method m = *it;
+ if (isFakeMethod(m))
+ continue;
+ // Check justifying subhistory
+ if (!m->justified) {
+ funcs = funcMap->get(m->name);
+ CheckState_t justifyingPrecondition = (CheckState_t)
+ funcs->justifyingPrecondition->function;
+ CheckState_t justifyingPostcondition = (CheckState_t)
+ funcs->justifyingPostcondition->function;
+ if (!justifyingPrecondition && !justifyingPostcondition ) {
+ // No need to check justifying conditions
+ m->justified = true;
+ if (verbose) {
+ model_print("\tMethod call ");
+ m->print(false, false);
+ model_print(": automatically justified.\n");
+ }
+ } else {
+ bool justified = checkJustifyingSubhistory(history, m, verbose, historyIndex);
+ if (justified) {
+ // Set the "justified" flag --- no need to check again for cur
+ m->justified = true;
+ if (verbose) {
+ model_print("\tMethod call ");
+ m->print(false, false);
+ model_print(": is justified\n");
+ }
+ } else {
+ if (verbose) {
+ model_print("\tMethod call ");
+ m->print(false, false);
+ model_print(": has NOT been justified yet\n");
+ }
+ }
+ }
+ } else {
+ if (verbose) {
+ model_print("\tMethod call ");
+ m->print(false, false);
+ model_print(": is justified\n");
+ }
+ }
+ }
+ if (verbose) {
+ if (historyIndex > 0)
+ model_print("---- Start to check justifying subhistory #%d END ----\n\n", historyIndex);
+ else
+ model_print("---- Start to check justifying subhistory # END ----\n\n");
+
+ }
+
+ return satisfied;
+}
+
+bool ExecutionGraph::checkJustifyingSubhistory(MethodList *history, Method
+ cur, bool verbose, int historyIndex) {
+ if (verbose) {
+ model_print("\tMethod call ");
+ cur->print(false, false);
+ model_print(": is being justified\n");
+ }
+
+ // @Transition can also return a boolean. For example when a steal() and
+ // take() in the deque both gets the last element, then we can see a bug in
+ // the evaluating the list of @Transitions for a following operation.
+ bool satisfied = true;
+
+ // @Initial state (with a fake starting node)
+ Method startMethod = getStartMethod();
+ UpdateState_t initialFunc = (UpdateState_t) initial->function;
+ UpdateState_t printStateFunc = (UpdateState_t) printState->function;
+ UpdateState_t printVauleFunc = NULL;
+ UpdateState_t clearFunc = (UpdateState_t) clear->function;
+
+ // We execute the equivalent sequential data structure with the state of the
+ // current method call
+ (*initialFunc)(cur);
+ if (verbose) {
+ startMethod->print(false, true);
+ model_print("\t@Initial on ");
+ cur->print(false, true);
+ if (printStateFunc) { // If user has defined the print-out function
+ model_print("\t********** State Info **********\n");
+ (*printStateFunc)(cur);
+ }
+ }
+
+ StateFunctions *funcs = NULL;
+ StateTransition_t transition = NULL;
+ /** Execute each method call in the justifying subhistory till cur */
+ for (MethodList::iterator it = history->begin(); it != history->end(); it++) {
+ Method m = *it;
+ if (m == cur)
+ break;
+ if (isFakeMethod(m))
+ continue;
+
+ // Ignore method calls that are not in my justifying subhistory
+ if (!MethodCall::belong(cur->allPrev, m))
+ continue;
+
+
+ if (verbose) {
+ m->print(false, true);
+ funcs = funcMap->get(m->name);
+ ASSERT (funcs);
+ UpdateState_t printValue = (UpdateState_t) funcs->print->function;
+ if (printValue) {
+ model_print("\t********** Value Info **********\n");
+ (*printValue)(m);
+ }
+ }
+
+ funcs = funcMap->get(m->name);
+ ASSERT (funcs);
+ transition = (StateTransition_t)
+ funcs->transition->function;
+
+ // In checking justifying behaviors, we don't check precondition &
+ // postcondition for other method calls
+
+ // @Transition on the state of the "cur" method call
+ satisfied = (*transition)(cur, m);
+ if (!satisfied) { // Error in evaluating @Transition
+ if (verbose) {
+ model_print("\tFailed @Transition before\n");
+ }
+ // Clear out the states created when checking
+ (*clearFunc)(startMethod);
+ return false;
+ } else {
+ if (verbose) {
+ model_print("\t@Transition on itself\n");
+ if (printStateFunc) {
+ model_print("\t********** State Info **********\n");
+ (*printStateFunc)(startMethod);
+ }
+ }
+ }
+ }
+
+ // For justifying subhistory, we only check the @JustifyingPrecondition &
+ // @JustifyingPostcondition for the last method call (the one we are
+ // checking)
+
+ // First check the @JustifyingPrecondition
+ funcs = funcMap->get(cur->name);
+ if (satisfied) {
+ // Check the justifying preondition on cur
+ CheckState_t justifyingPrecondition = (CheckState_t)
+ funcs->justifyingPrecondition->function;
+ if (justifyingPrecondition) {
+ // @JustifyingPrecondition of Mehtod cur
+ satisfied = (*justifyingPrecondition)(cur, cur);
+ }
+ if (!satisfied) {
+ if (verbose) {
+ model_print("\tFailed @JustifyingPrecondition\n");
+ }
+ // Clear out the states created when checking
+ (*clearFunc)(startMethod);
+ return false;
+ }
+ }
+
+ // Then execute the @Transition
+ transition = (CheckState_t) funcs->transition->function;
+ // @Transition on the state of the "cur" method call
+ satisfied = (*transition)(cur, cur);
+ if (!satisfied) { // Error in evaluating @Transition
+ if (verbose) {
+ model_print("\tFailed @Transition on itself\n");
+ }
+ // Clear out the states created when checking
+ (*clearFunc)(startMethod);
+ return false;
+ }
+ if (verbose) {
+ model_print("\t@Transition on itself\n");
+ if (printStateFunc) {
+ model_print("\t********** State Info **********\n");
+ (*printStateFunc)(startMethod);
+ }
+ }
+
+ // Finally check the @JustifyingPostcondition
+ if (satisfied) {
+ // Check the justifying preondition on cur
+ funcs = funcMap->get(cur->name);
+ CheckState_t justifyingPostcondition = (CheckState_t)
+ funcs->justifyingPostcondition->function;
+ if (justifyingPostcondition) {
+ // @JustifyingPostcondition of Mehtod cur
+ satisfied = (*justifyingPostcondition)(cur, cur);
+ }
+ }
+
+ // Clear out the states created when checking
+ (*clearFunc)(startMethod);
+ return satisfied;
+}
+
+
+/**
+ To take a list of actions and print it out in a nicer way
+*/
+void ExecutionGraph::printActions(action_list_t *actions, const char *header) {
+ model_print("%s\n", header);
+ model_print("---------- Thread List (Begin) ---------\n");
+ for (action_list_t::iterator it = actions->begin(); it != actions->end();
+ it++) {
+ ModelAction *act = *it;
+ SpecAnnotation *anno = getAnnotation(act);
+ if (anno) {
+ model_print("%s -> ", specAnnoType2Str(anno->type));
+ }
+ act->print();
+ }
+ model_print("---------- Thread List (End) ---------\n");
+}
+
+void ExecutionGraph::printOneSubhistory(MethodList *history, Method cur, CSTR header) {
+ model_print("------------- %s (exec #%d) -------------\n", header,
+ execution->get_execution_number());
+ int idx = 1;
+ for (MethodList::iterator it = history->begin(); it != history->end(); it++) {
+ Method m = *it;
+ if (!MethodCall::belong(cur->allPrev, m))
+ continue;
+ model_print("%d. ", idx++);
+ m->print(false);
+ }
+ model_print("------------- %s (exec #%d) (end) -------------\n",
+ header, execution->get_execution_number());
+}
+
--- /dev/null
+#ifndef _EXECUTIONGRAPH_H
+#define _EXECUTIONGRAPH_H
+
+#include <stack>
+
+#include "hashtable.h"
+#include "specannotation.h"
+#include "mymemory.h"
+#include "modeltypes.h"
+#include "action.h"
+#include "common.h"
+#include "execution.h"
+
+#include "methodcall.h"
+#include "specannotation.h"
+
+
+const int SELF_CYCLE = 0xfffe;
+
+/**
+ Record the a potential commit point information, including the potential
+ commit point label number and the corresponding operation
+*/
+typedef struct PotentialOP {
+ ModelAction *operation;
+ CSTR label;
+
+ PotentialOP(ModelAction *op, CSTR name);
+
+ SNAPSHOTALLOC
+} PotentialOP;
+
+class Graph;
+
+
+typedef SnapList<PotentialOP*> PotentialOPList;
+
+/**
+ This represents the execution graph at the method call level. Each node is a
+ MethodCall type that has the runtime info (return value & arguments) and the
+ state info the method call. The edges in this graph are the hb/SC edges
+ dereived from the ordering points of the method call. Those edges
+ information are stoed in the PREV and NEXT set of MethodCall struct.
+*/
+class ExecutionGraph {
+ public:
+ ExecutionGraph(ModelExecution *e, bool allowCyclic);
+
+ /********** Public class interface for the core checking engine **********/
+ /**
+ Build the graph for an execution. It should returns true when the graph
+ is correctly built
+ */
+ void buildGraph(action_list_t *actions);
+
+ /** Check whether this is a broken execution */
+ bool isBroken();
+
+ /** Check whether this is an execution that has method calls without
+ * ordering points */
+ bool isNoOrderingPoint();
+
+ /**
+ Check whether this is a cyclic execution graph, meaning that the graph
+ has an cycle derived from the ordering points' hb/SC relation
+ */
+ bool hasCycle();
+
+ /** Reset the internal graph "broken" state to okay again */
+ void resetBroken();
+
+ /** Check whether the execution is admmisible */
+ bool checkAdmissibility();
+
+ /** Checking cyclic graph specification */
+ bool checkCyclicGraphSpec(bool verbose);
+
+ /** Check whether a number of random history is correct */
+ bool checkRandomHistories(int num = 1, bool stopOnFail = true, bool verbose = false);
+
+ /** Check whether all histories are correct */
+ bool checkAllHistories(bool stopOnFailure = true, bool verbose = false);
+
+ /********** A few public printing functions for DEBUGGING **********/
+
+ /**
+ Print out the ordering points and dynamic calling info (return value &
+ arguments) of all the methods in the methodList
+ */
+ void printAllMethodInfo(bool verbose);
+
+ /** Print a random sorting */
+ void printOneHistory(MethodList *list, CSTR header = "A random history");
+
+ /** Print all the possible sortings */
+ void printAllHistories(MethodListVector *sortings);
+
+ /** Print out the graph for the purpose of debugging */
+ void print(bool allEdges = false);
+
+ /**
+ Print out the graph in reverse order (with previous edges) for the
+ purpose of debugging
+ */
+ void PrintReverse(bool allEdges);
+
+ SNAPSHOTALLOC
+
+ private:
+ /** The corresponding execution */
+ ModelExecution *execution;
+
+ /** All the threads each in a separate list of actions */
+ SnapVector<action_list_t*> *threadLists;
+
+ /** A plain list of all method calls (Method) in this execution */
+ MethodList *methodList;
+
+ /**
+ A list that represents some random history. The reason we have this here
+ is that before checking anything, we have to check graph acyclicity, and
+ that requires us to check whether we can generate a topological sorting.
+ Therefore, when we check it, we store that random result here.
+ */
+ MethodList *randomHistory;
+
+ /** Whether this is a broken graph */
+ bool broken;
+
+ /** Whether this graph has method calls that have no ordering points */
+ bool noOrderingPoint;
+
+ /** Whether there is a cycle in the graph */
+ bool cyclic;
+
+ /** Whether users expect us to check cyclic graph */
+ bool allowCyclic;
+
+
+ /** The state initialization function */
+ NamedFunction *initial;
+
+ /** FIXME: The final check function; we might delete this */
+ NamedFunction *final;
+
+ /** The state copy function */
+ NamedFunction *copy;
+
+ /** The state clear function */
+ NamedFunction *clear;
+
+ /** The state print-out function */
+ NamedFunction *printState;
+
+ /** The map from interface label name to the set of spec functions */
+ StateFuncMap *funcMap;
+
+ /** The commutativity rule array and its size */
+ CommutativityRule *commuteRules;
+ int commuteRuleNum;
+
+ /********** Internal member functions **********/
+
+ /**
+ Simply build a vector of lists of thread actions. Such info will be very
+ useful for later constructing the execution graph
+ */
+ void buildThreadLists(action_list_t *actions);
+
+ /** Build the nodes from a thread list */
+ void buildNodesFromThread(action_list_t *actions);
+
+ /** Build the nodes from all the thread lists */
+ void buildNodesFromThreads();
+
+
+ /**
+ Find the previous non-annotation model action (ordering point from the
+ current iterator
+ */
+ ModelAction* findPrevAction(action_list_t *actions, action_list_t::iterator
+ iter);
+
+ /**
+ Extract a MethodCall node starting from the current iterator, and the
+ iter iterator will be advanced to the next INTERFACE_BEGIN annotation.
+ When called, the iter must point to an INTERFACE_BEGIN annotation
+ */
+ Method extractMethod(action_list_t *actions, action_list_t::iterator &iter);
+
+ /**
+ Process the initialization annotation block to initialize the
+ commutativity rule info and the checking function info
+ */
+ void processInitAnnotation(AnnoInit *annoInit);
+
+ /**
+ A utility function to extract the actual annotation
+ pointer and return the actual annotation if this is an annotation action;
+ otherwise return NULL.
+ */
+ SpecAnnotation* getAnnotation(ModelAction *act);
+
+ /**
+ After building up the graph (both the nodes and egdes are correctly
+ built), we also call this function to initialize the most recent
+ justified node of each method node.
+
+ A justified method node of a method m is a method that is in the allPrev
+ set of m, and all other nodes in the allPrev set of m are either before
+ or after it. The most recent justified node is the most recent one in
+ the hb/SC order.
+ */
+ void initializeJustifiedNode();
+
+ /**
+ After extracting the MethodCall info for each method call, we use this
+ function to build the edges (a few different sets of edges that
+ represent the edge of the execution graph (PREV, NEXT & CONCURRENT...)
+ */
+ void buildEdges();
+
+ /**
+ This method call is used to check whether the edge sets of the nodes are
+ built correctly --- consistently. We should only use this routine after the
+ builiding of edges when debugging
+ */
+ void AssertEdges();
+
+ /**
+ The conflicting relation between two model actions by hb/SC. If act1 and
+ act2 commutes, it returns 0; Otherwise, if act1->act2, it returns 1; and
+ if act2->act1, it returns -1
+ */
+ int conflict(ModelAction *act1, ModelAction *act2);
+
+ /**
+ If there is no conflict between the ordering points of m1 and m2, then
+ it returns 0; Otherwise, if m1->m2, it returns 1; and if m2->m1, it
+ returns -1. If some ordering points have inconsistent conflicting
+ relation, we print out an error message (Self-cycle) and set the broken
+ flag and return
+ */
+ int conflict(Method m1, Method m2);
+
+ /**
+ Check whether m1 is before m2 in hb/SC; Reachability <=> m1 -hb/SC-> m2.
+ We need to call this method when checking admissibility properties
+ */
+ bool isReachable(Method m1, Method m2);
+
+ /**
+ Find the list of nodes that do not have any incoming edges (root nodes).
+ Be careful that when generating histories, this function will also
+ consider whether a method node logicall exists.
+ */
+ MethodVector* getRootNodes();
+
+ /**
+ Find the list of nodes that do not have any outgoing edges (end nodes)
+ */
+ MethodVector* getEndNodes();
+
+ /**
+ A helper function for generating and check all sequential histories. Before calling this function, initialize an empty
+ MethodList , and pass it as curList. Also pass the size of the method
+ list as the numLiveNodes.
+
+ If you only want to stop the checking process when finding one failed
+ history, pass true to stopOnFailure.
+ */
+ bool checkAllHistoriesHelper(MethodList *curList, int &numLiveNodes, int
+ &historyNum, bool stopOnFailure, bool verbose);
+
+
+ /** Check whether a specific history is correct */
+ bool checkHistory(MethodList *history, int historyIndex, bool verbose = false);
+
+ /** Generate one random topological sorting */
+ MethodList* generateOneRandomHistory();
+
+ /**
+ The helper function to generate one random topological sorting
+ */
+ void generateOneRandomHistoryHelper(MethodList
+ *curList, int &numLiveNodes);
+
+ /** Return the fake nodes -- START & END */
+ Method getStartMethod();
+ Method getFinishMethod();
+
+ /** Whether method call node m is a fake node */
+ inline bool isFakeMethod(Method m) {
+ return isStartMethod(m) || isFinishMethod(m);
+ }
+
+ /** Whether method call node m is a starting node */
+ inline bool isStartMethod(Method m) {
+ return m->name == GRAPH_START;
+ }
+
+ /** Whether method call node m is a finish node */
+ inline bool isFinishMethod(Method m) {
+ return m->name == GRAPH_FINISH;
+ }
+
+ /**
+ Print out the ordering points and dynamic calling info (return value &
+ arguments).
+ */
+ void printMethodInfo(Method m, bool verbose);
+
+ /** Clear the states of the method call */
+ void clearStates();
+
+ /**
+ Check the state specifications (PreCondition & PostCondition & state
+ transition and evaluation) based on the graph (called after
+ admissibility check). The verbose option controls whether we print a
+ detailed list of checking functions that we have called
+ */
+ bool checkStateSpec(MethodList *history, bool verbose, int historyNum);
+
+ /**
+ Check the justifying subhistory with respect to history of m.
+ */
+ bool checkJustifyingSubhistory(MethodList *subhistory, Method cur,
+ bool verbose, int historyIndex);
+
+ /** Print a problematic thread list */
+ void printActions(action_list_t *actions, const char *header = "The problematic thread list:");
+
+ /** Print one jusifying subhistory of method call cur */
+ void printOneSubhistory(MethodList *history, Method cur,
+ CSTR header);
+};
+
+#endif
--- /dev/null
+#ifndef _SPECANNOTATION_API_H
+#define _SPECANNOTATION_API_H
+
+#define NEW_SIZE(type, size) (type*) malloc(size)
+#define NEW(type) NEW_SIZE(type, sizeof(type))
+
+struct MethodCall;
+typedef struct MethodCall *Method;
+typedef const char *CSTR;
+
+struct AnnoInterfaceInfo;
+typedef struct AnnoInterfaceInfo AnnoInterfaceInfo;
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+struct AnnoInterfaceInfo* _createInterfaceBeginAnnotation(CSTR name);
+
+void _setInterfaceBeginAnnotationValue(struct AnnoInterfaceInfo *info, void *value);
+
+void _createInterfaceEndAnnotation(CSTR name);
+
+void _createOPDefineAnnotation();
+
+void _createPotentialOPAnnotation(CSTR label);
+
+void _createOPCheckAnnotation(CSTR label);
+
+void _createOPClearAnnotation();
+
+void _createOPClearDefineAnnotation();
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
--- /dev/null
+#include <algorithm>
+#include "common.h"
+#include "threads-model.h"
+#include "methodcall.h"
+#include "spec_common.h"
+
+CSTR GRAPH_START = "START_NODE";
+CSTR GRAPH_FINISH = "FINISH_NODE";
+
+const unsigned int METHOD_ID_MAX = 0xffffffff;
+const unsigned int METHOD_ID_MIN = 0;
+
+MethodCall::MethodCall(CSTR name, void *value, ModelAction *begin) :
+ name(name), value(value), prev(new SnapSet<Method>), next(new
+ SnapSet<Method>), concurrent(new SnapSet<Method>), justifiedMethod(NULL),
+ justified(false), end(NULL), orderingPoints(new action_list_t), allPrev(new
+ SnapSet<Method>), allNext(new SnapSet<Method>), exist(true) {
+ if (name == GRAPH_START) {
+ this->begin = NULL;
+ id = METHOD_ID_MIN;
+ tid = 1; // Considered to be the main thread
+ } else if (name == GRAPH_FINISH) {
+ this->begin = NULL;
+ id = METHOD_ID_MAX;
+ tid = 1; // Considered to be the main thread
+ } else {
+ this->begin = begin;
+ ASSERT (begin);
+ id = begin->get_seq_number();
+ tid = id_to_int(begin->get_tid());
+ }
+}
+
+void MethodCall::addPrev(Method m) { prev->insert(m); }
+
+void MethodCall::addNext(Method m) { next->insert(m); }
+
+void MethodCall::addConcurrent(Method m) { concurrent->insert(m); }
+
+void MethodCall::addOrderingPoint(ModelAction *act) {
+ bool hasIt = orderingPoints->end() != std::find(orderingPoints->begin(),
+ orderingPoints->end(), act);
+ if (!hasIt)
+ orderingPoints->push_back(act);
+}
+
+
+bool MethodCall::before(Method another) {
+ return belong(allNext, another);
+}
+
+bool MethodCall::belong(MethodSet s, Method m) {
+ return s->end() != std::find(s->begin(), s->end(), m);
+}
+
+bool MethodCall::identical(MethodSet s1, MethodSet s2) {
+ if (s1->size() != s2->size())
+ return false;
+ SnapSet<Method>::iterator it;
+ for (it = s1->begin(); it != s1->end(); it++) {
+ Method m1 = *it;
+ if (belong(s2, m1))
+ return false;
+ }
+ return true;
+}
+
+/**
+ Put the union of src and dest to dest.
+*/
+void MethodCall::Union(MethodSet dest, MethodSet src) {
+ SnapSet<Method>::iterator it;
+ for (it = src->begin(); it != src->end(); it++) {
+ Method m = *it;
+ dest->insert(m);
+ }
+}
+
+MethodSet MethodCall::intersection(MethodSet s1, MethodSet s2) {
+ MethodSet res = new SnapSet<Method>;
+ SnapSet<Method>::iterator it;
+ for (it = s1->begin(); it != s1->end(); it++) {
+ Method m = *it;
+ if (belong(s2, m))
+ res->insert(m);
+ }
+ return res;
+}
+
+bool MethodCall::disjoint(MethodSet s1, MethodSet s2) {
+ SnapSet<Method>::iterator it;
+ for (it = s1->begin(); it != s1->end(); it++) {
+ Method m = *it;
+ if (belong(s2, m))
+ return false;
+ }
+ return true;
+}
+
+void MethodCall::print(bool printOP, bool breakAtEnd) {
+ if (name == GRAPH_START) {
+ model_print("%s", GRAPH_START);
+ if (breakAtEnd)
+ model_print("\n");
+ return;
+ }
+ if (name == GRAPH_FINISH) {
+ model_print("%s", GRAPH_FINISH);
+ if (breakAtEnd)
+ model_print("\n");
+ return;
+ }
+ model_print("%u.%s(T%d)", id, name, id_to_int(begin->get_tid()));
+ if (breakAtEnd)
+ model_print("\n");
+ if (!printOP)
+ return;
+ int i = 1;
+ for (action_list_t::iterator it = orderingPoints->begin(); it !=
+ orderingPoints->end(); it++) {
+ ModelAction *op = *it;
+ model_print("\t-> %d. ", i++);
+ op->print();
+ }
+}
--- /dev/null
+#ifndef _METHODCALL_H
+#define _METHODCALL_H
+
+#include "stl-model.h"
+#include "action.h"
+#include "spec_common.h"
+
+class MethodCall;
+
+typedef MethodCall *Method;
+typedef SnapSet<Method> *MethodSet;
+typedef SnapList<ModelAction *> action_list_t;
+
+typedef SnapList<Method> MethodList;
+typedef SnapVector<Method> MethodVector;
+typedef SnapVector<MethodList*> MethodListVector;
+
+/**
+ This is the core class on which the whole checking process will be
+ executing. With the original execution (with the raw annotation
+ information), we construct an execution graph whose nodes represent method
+ calls, and whose edges represent the hb/SC relation between the ordering
+ points of method calls. In that graph, the MethodCall class acts as the node
+ that contains the core information for checking --- the name of the
+ interface, the value (return value and arguments), the state of the current
+ method call, and a set of previous, next and concurrent method calls.
+
+ Plus, this class contains extra info about the ordering points, a set of all
+ method calls that are hb/SC before me (for evaluating the state), and also
+ some other helping internal member variable for generating checking schemes.
+*/
+class MethodCall {
+ public:
+ unsigned int id; // The method call id (the seq_num of the begin action)
+ int tid; // The thread id
+ CSTR name; // The interface label name
+ void *value; // The pointer that points to the struct that have the return
+ // value and the arguments
+ void *state; // The pointer that points to the struct that represents
+ // the state
+ MethodSet prev; // Method calls that are hb right before me
+ MethodSet next; // Method calls that are hb right after me
+ MethodSet concurrent; // Method calls that are concurrent with me
+
+ Method justifiedMethod; // The method before me and is not concurrent with
+ // any other mehtods in my allPrev set
+
+ /**
+ This indicates if the non-deterministic behaviors of this method call
+ has been justified by any of its justifying subhistory. If so, we do not
+ need to check on its justifying subhistory. Initially it is false.
+ */
+ bool justified;
+
+ MethodCall(CSTR name, void *value = NULL, ModelAction *begin = NULL);
+
+ void addPrev(Method m);
+
+ void addNext(Method m);
+
+ void addConcurrent(Method m);
+
+ void addOrderingPoint(ModelAction *act);
+
+ bool before(Method another);
+
+ static bool belong(MethodSet s, Method m);
+
+ static bool identical(MethodSet s1, MethodSet s2);
+
+ /** Put the union of src and dest to dest */
+ static void Union(MethodSet dest, MethodSet src);
+
+ static MethodSet intersection(MethodSet s1, MethodSet s2);
+
+ static bool disjoint(MethodSet s1, MethodSet s2);
+
+ /**
+ Print the method all name with the seq_num of the begin annotation and
+ its thread id.
+
+ printOP == true -> Add each line with the ordering point operation's
+ print()
+
+ breakAtEnd == true -> Add a line break at the end of the print;
+ otherwise, the print will be a string without line breaker when printOP
+ is false.
+ */
+ void print(bool printOP = true, bool breakAtEnd = true);
+
+ /**
+ FIXME: The end action is not really used or necessary here, maybe we
+ should clean this
+ */
+ ModelAction *begin;
+ ModelAction *end;
+ action_list_t *orderingPoints;
+
+ /**
+ Keep a set of all methods that are hb/SC before&after me to calculate my
+ state
+ */
+ MethodSet allPrev;
+ MethodSet allNext;
+
+ /** Logically exist (for generating all possible topological sortings) */
+ bool exist;
+
+ SNAPSHOTALLOC
+};
+
+
+
+#endif
--- /dev/null
+#ifndef _SPEC_COMMON_H
+#define _SPEC_COMMON_H
+
+#include <set>
+#include <stdlib.h>
+#include "mymemory.h"
+
+/** Comment this out if you don't want unnecessary debugging printing out */
+#define CDSSPEC_DEBUG
+
+#ifdef CDSSPEC_DEBUG
+#define debug_print(fmt, ...) do { dprintf(model_out, fmt, ##__VA_ARGS__); } while (0)
+#else
+#define debug_print(fmt, ...) do { } while (0)
+#endif
+
+#define SPEC_ANALYSIS 1
+
+/** Null function pointer */
+#define NULL_FUNC NULL
+
+#define NEW_SIZE(type, size) (type*) malloc(size)
+#define NEW(type) NEW_SIZE(type, sizeof(type))
+
+#define EQ(str1, str2) (strcmp(str1, str2) == 0)
+
+extern const unsigned int METHOD_ID_MAX;
+extern const unsigned int METHOD_ID_MIN;
+
+typedef const char *CSTR;
+
+extern CSTR GRAPH_START;
+extern CSTR GRAPH_FINISH;
+
+/** Define a snapshotting set for CDSChecker backend analysis */
+template<typename _Tp>
+class SnapSet : public std::set<_Tp, std::less<_Tp>, SnapshotAlloc<_Tp> >
+{
+ public:
+ typedef std::set<_Tp, std::less<_Tp>, SnapshotAlloc<_Tp> > set;
+
+ SnapSet() : set() { }
+
+ SNAPSHOTALLOC
+};
+
+#endif
--- /dev/null
+#include "specanalysis.h"
+#include "action.h"
+#include "cyclegraph.h"
+#include "threads-model.h"
+#include "clockvector.h"
+#include "execution.h"
+#include <sys/time.h>
+#include <assert.h>
+#include "modeltypes.h"
+#include "executiongraph.h"
+
+
+SPECAnalysis::SPECAnalysis()
+{
+ execution = NULL;
+ stats = (struct spec_stats*) model_calloc(1, sizeof(struct spec_stats));
+ print_always = false;
+ print_inadmissible = true;
+ quiet = false;
+ checkCyclic = false;
+ stopOnFail = false;
+ checkRandomNum = 0;
+}
+
+SPECAnalysis::~SPECAnalysis() {
+}
+
+void SPECAnalysis::setExecution(ModelExecution * execution) {
+ this->execution = execution;
+}
+
+const char * SPECAnalysis::name() {
+ const char * name = "SPEC";
+ return name;
+}
+
+void SPECAnalysis::finish() {
+ model_print("\n");
+ model_print(">>>>>>>> SPECAnalysis finished <<<<<<<<\n");
+
+ model_print("Total execution checked: %d\n", stats->bugfreeCnt);
+ model_print("Detected by CDSChecker: %d\n", stats->buggyCnt);
+ model_print("Broken graph: %d\n", stats->brokenCnt);
+ model_print("Cyclic graph: %d\n", stats->cyclicCnt);
+ model_print("Inadmissible executions: %d\n", stats->inadmissibilityCnt);
+ model_print("Failed executions: %d\n", stats->failedCnt);
+
+ if (stats->cyclicCnt > 0 && checkCyclic) {
+ model_print("Warning: You have cycle in your execution graphs.\n");
+ }
+
+ if (stats->failedCnt == 0) {
+ model_print("Yay! All executions have passed the specification.\n");
+ if (stats->brokenCnt > 0)
+ model_print("However! You have executions with a broken graph.\n");
+ if (stats->cyclicCnt > 0) {
+ model_print("Warning: You have cyclic execution graphs!\n");
+ if (checkCyclic) {
+ model_print("Make sure that's what you expect.\n");
+ }
+ }
+ if (stats->inadmissibilityCnt > 0)
+ model_print("However! You have inadmissible executions.\n");
+ if (stats->noOrderingPointCnt > 0 && print_always)
+ model_print("You have execution graphs that have no ordering points.\n");
+
+ }
+}
+
+bool SPECAnalysis::isCheckRandomHistories(char *opt, int &num) {
+ char *p = opt;
+ bool res = false;
+ if (*p == 'c') {
+ p++;
+ if (*p == 'h') {
+ p++;
+ if (*p == 'e') {
+ p++;
+ if (*p == 'c') {
+ p++;
+ if (*p == 'k') {
+ res = true;
+ p++;
+ }
+ }
+ }
+ }
+ }
+ if (res) {
+ // p now should point to '-'
+ if (*p == '-') {
+ p++;
+ num = atoi(p);
+ if (num > 0)
+ return true;
+ }
+ return false;
+ }
+ return res;
+}
+
+bool SPECAnalysis::option(char * opt) {
+ if (strcmp(opt, "verbose")==0) {
+ print_always=true;
+ return false;
+ } else if (strcmp(opt, "no-admissible")==0) {
+ print_inadmissible = false;
+ return false;
+ } else if (strcmp(opt, "quiet")==0) {
+ quiet = true;
+ return false;
+ } else if (strcmp(opt, "check-cyclic") == 0) {
+ checkCyclic = true;
+ return false;
+ } else if (strcmp(opt, "stop-on-fail")==0) {
+ stopOnFail = true;
+ return false;
+ } else if (isCheckRandomHistories(opt, checkRandomNum)) {
+ return false;
+ } else if (strcmp(opt, "help") != 0) {
+ model_print("Unrecognized option: %s\n", opt);
+ }
+
+ model_print("SPEC Analysis options\n"
+ "By default SPEC outputs the graphs of those failed execution\n"
+ "verbose -- print graphs of any traces\n"
+ "quiet -- do not print out graphs\n"
+ "inadmissible-quiet -- print the inadmissible\n"
+ "check-one -- only check one random possible topological"
+ "sortings (check all possible by default)\n"
+ );
+ model_print("\n");
+
+ return true;
+}
+
+void SPECAnalysis::analyze(action_list_t *actions) {
+ /* Count the number traces */
+ stats->traceCnt++;
+ if (execution->have_bug_reports()) {
+ /* Count the number traces */
+ stats->buggyCnt++;
+ return;
+ }
+
+ /* Count the number bug-free traces */
+ stats->bugfreeCnt++;
+
+ // FIXME: Make checkCyclic false by default
+ ExecutionGraph *graph = new ExecutionGraph(execution, checkCyclic);
+ graph->buildGraph(actions);
+ if (graph->isBroken()) {
+ stats->brokenCnt++;
+ if (print_always && !quiet) { // By default not printing
+ model_print("Execution #%d has a broken graph.\n\n",
+ execution->get_execution_number());
+ }
+ return;
+ }
+
+ if (print_always) {
+ model_print("------------------ Checking execution #%d"
+ " ------------------\n", execution->get_execution_number());
+ }
+
+ // Count how many executions that have no-ordering-point method calls
+ if (graph->isNoOrderingPoint()) {
+ stats->noOrderingPointCnt++;
+ }
+
+ if (!graph->checkAdmissibility()) {
+ /* One more inadmissible trace */
+ stats->inadmissibilityCnt++;
+ if (print_inadmissible && !quiet) {
+ model_print("Execution #%d is NOT admissible\n",
+ execution->get_execution_number());
+ graph->print();
+ if (print_always)
+ graph->printAllMethodInfo(true);
+ }
+ return;
+ }
+
+ bool pass = false;
+ if (graph->hasCycle()) {
+ /* One more trace with a cycle */
+ stats->cyclicCnt++;
+ if (!checkCyclic) {
+ if (!quiet)
+ graph->print();
+ if (print_always && !quiet) { // By default not printing
+ model_print("Execution #%d has a cyclic graph.\n\n",
+ execution->get_execution_number());
+ }
+ } else {
+ if (print_always && !quiet) {
+ model_print("Checking cyclic execution #%d...\n",
+ execution->get_execution_number());
+ }
+ pass = graph->checkCyclicGraphSpec(print_always && !quiet);
+ }
+ } else if (checkRandomNum > 0) { // Only a few random histories
+ if (print_always && !quiet)
+ model_print("Check %d random histories...\n", checkRandomNum);
+ pass = graph->checkRandomHistories(checkRandomNum, true, print_always && !quiet);
+ } else { // Check all histories
+ if (print_always && !quiet)
+ model_print("Check all histories...\n");
+ pass = graph->checkAllHistories(true, print_always && !quiet);
+ }
+
+ if (!pass) {
+ /* One more failed trace */
+ stats->failedCnt++;
+ model_print("Execution #%d failed.\n\n",
+ execution->get_execution_number());
+ } else {
+ /* One more passing trace */
+ stats->passCnt++;
+
+ if (print_always && !quiet) { // By default not printing
+ model_print("Execution #%d passed.\n\n",
+ execution->get_execution_number());
+ }
+ }
+}
--- /dev/null
+#ifndef _SPECANALYSIS_H
+#define _SPECANALYSIS_H
+
+#include <stack>
+
+#include "traceanalysis.h"
+#include "mymemory.h"
+#include "modeltypes.h"
+#include "action.h"
+
+struct spec_stats {
+ /** The number of traces that have passed the checking */
+ unsigned passCnt;
+
+ /** The number of inadmissible traces */
+ unsigned inadmissibilityCnt;
+
+ /** The number of all checked traces */
+ unsigned traceCnt;
+
+ /** The number of traces with a cyclic graph */
+ unsigned cyclicCnt;
+
+ /** The number of traces with broken graphs */
+ unsigned brokenCnt;
+
+ /** The number of traces with graphs that has no ordering points */
+ unsigned noOrderingPointCnt;
+
+ /** The number of traces that failed */
+ unsigned failedCnt;
+
+ /** The number of buggy and bug-free traces (by CDSChecker) */
+ unsigned buggyCnt;
+ unsigned bugfreeCnt;
+};
+
+class SPECAnalysis : public TraceAnalysis {
+ public:
+ SPECAnalysis();
+ ~SPECAnalysis();
+
+ virtual void setExecution(ModelExecution * execution);
+ virtual void analyze(action_list_t *actions);
+ virtual const char * name();
+ virtual bool option(char *);
+ virtual void finish();
+
+ /** Some stats */
+ spec_stats *stats;
+
+ SNAPSHOTALLOC
+ private:
+ /** The execution */
+ ModelExecution *execution;
+
+ /** A few useful options */
+ /* Print out the graphs of all executions */
+ bool print_always;
+ /* Print out the graphs of the inadmissible traces */
+ bool print_inadmissible;
+ /* Never print out the graphs of any traces */
+ bool quiet;
+ /* Whether we still want to check cyclic executions */
+ bool checkCyclic;
+ /* Stop checking when seeing one failed history */
+ bool stopOnFail;
+ /* The number of random histories to be checked; If 0, we check all possible
+ * histories */
+ int checkRandomNum;
+
+ /** Whether this is a "check-12" like option */
+ bool isCheckRandomHistories(char *opt, int &num);
+};
+
+
+#endif
--- /dev/null
+#include <algorithm>
+#include "specannotation.h"
+#include "cdsannotate.h"
+
+/********** Annotations **********/
+
+SpecAnnotation::SpecAnnotation(SpecAnnoType type, const void *anno) : type(type),
+ annotation(anno) { }
+
+
+CommutativityRule::CommutativityRule(CSTR method1, CSTR method2, CSTR rule,
+ CheckCommutativity_t condition) : method1(method1),
+ method2(method2), rule(rule), condition(condition) {}
+
+bool CommutativityRule::isRightRule(Method m1, Method m2) {
+ return (m1->name == method1 && m2->name == method2) ||
+ (m1->name == method2 && m2->name == method1);
+}
+
+bool CommutativityRule::checkCondition(Method m1, Method m2) {
+ if (m1->name == method1 && m2->name == method2)
+ return (*condition)(m1, m2);
+ else if (m1->name == method2 && m2->name == method1)
+ return (*condition)(m2, m1);
+ else // The checking should only be called on the right rule
+ ASSERT(false);
+ return false;
+}
+
+NamedFunction::NamedFunction(CSTR name, CheckFunctionType type, void *function) : name(name),
+ type(type), function(function) { }
+
+StateFunctions::StateFunctions(NamedFunction *transition, NamedFunction
+ *preCondition, NamedFunction * justifyingPrecondition,
+ NamedFunction *justifyingPostcondition,
+ NamedFunction *postCondition, NamedFunction *print) : transition(transition),
+ preCondition(preCondition), justifyingPrecondition(justifyingPrecondition),
+ justifyingPostcondition(justifyingPostcondition),
+ postCondition(postCondition), print(print) { }
+
+
+AnnoInit::AnnoInit(NamedFunction *initial, NamedFunction *final, NamedFunction
+ *copy, NamedFunction *clear, NamedFunction *printState, CommutativityRule
+ *commuteRules, int ruleNum) : initial(initial), final(final), copy(copy),
+ clear(clear), printState(printState), commuteRules(commuteRules),
+ commuteRuleNum(ruleNum)
+{
+ funcMap = new StateFuncMap;
+}
+
+
+void AnnoInit::addInterfaceFunctions(CSTR name, StateFunctions *funcs) {
+ funcMap->put(name, funcs);
+}
+
+AnnoInterfaceInfo::AnnoInterfaceInfo(CSTR name) : name(name), value(NULL) { }
+
+
+/********** Universal functions for rewriting the program **********/
+
+AnnoInterfaceInfo* _createInterfaceBeginAnnotation(CSTR name) {
+ AnnoInterfaceInfo *info = new AnnoInterfaceInfo(name);
+ // Create and instrument with the INTERFACE_BEGIN annotation
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(INTERFACE_BEGIN, info));
+ return info;
+}
+
+void _createInterfaceEndAnnotation(CSTR name) {
+ // Create and instrument with the INTERFACE_END annotation
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(INTERFACE_END, (void*) name));
+}
+
+
+void _setInterfaceBeginAnnotationValue(AnnoInterfaceInfo *info, void *value) {
+ info->value = value;
+}
+
+void _createOPDefineAnnotation() {
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(OP_DEFINE, NULL));
+}
+
+void _createPotentialOPAnnotation(CSTR label) {
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(POTENTIAL_OP, label));
+}
+
+void _createOPCheckAnnotation(CSTR label) {
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(OP_CHECK, label));
+}
+
+void _createOPClearAnnotation() {
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(OP_CLEAR, NULL));
+}
+
+void _createOPClearDefineAnnotation() {
+ cdsannotate(SPEC_ANALYSIS, new SpecAnnotation(OP_CLEAR_DEFINE, NULL));
+}
--- /dev/null
+#ifndef _SPECANNOTATION_H
+#define _SPECANNOTATION_H
+
+#include "modeltypes.h"
+#include "model-assert.h"
+#include "methodcall.h"
+#include "action.h"
+#include "spec_common.h"
+#include "hashtable.h"
+
+using namespace std;
+
+/**
+ We can only pass a void* pointer from the real program execution to the
+ checking engine, so we need to wrap the key information into that pointer.
+ We use the SpecAnntotation struct to represent that info. Different types
+ here mean different annotations.
+
+ FIXME: Currently we actually do not need to have the INTERFACE_END types. We
+ basically wrap the MethodCall* pointer of the method call in the
+ INTERFACE_BEGIN type of annotation.
+*/
+typedef enum SpecAnnoType {
+ INIT, POTENTIAL_OP, OP_DEFINE, OP_CHECK, OP_CLEAR, OP_CLEAR_DEFINE,
+ INTERFACE_BEGIN, INTERFACE_END
+} SpecAnnoType;
+
+inline CSTR specAnnoType2Str(SpecAnnoType type) {
+ switch (type) {
+ case INIT:
+ return "INIT";
+ case POTENTIAL_OP:
+ return "POTENTIAL_OP";
+ case OP_DEFINE:
+ return "OP_DEFINE";
+ case OP_CHECK:
+ return "OP_CHECK";
+ case OP_CLEAR:
+ return "OP_CLEAR";
+ case OP_CLEAR_DEFINE:
+ return "OP_CLEAR_DEFINE";
+ case INTERFACE_BEGIN:
+ return "INTERFACE_BEGIN";
+ case INTERFACE_END:
+ return "INTERFACE_END";
+ default:
+ return "UNKNOWN_TYPE";
+ }
+}
+
+typedef
+struct SpecAnnotation {
+ SpecAnnoType type;
+ const void *annotation;
+
+ SpecAnnotation(SpecAnnoType type, const void *anno);
+
+} SpecAnnotation;
+
+typedef bool (*CheckCommutativity_t)(Method, Method);
+/**
+ The first method is the target (to update its state), and the second method
+ is the method that should be executed (to access its method call info (ret
+ & args)
+*/
+typedef bool (*StateTransition_t)(Method, Method);
+typedef bool (*CheckState_t)(Method, Method);
+typedef void (*UpdateState_t)(Method);
+// Copy the second state to the first state
+typedef void (*CopyState_t)(Method, Method);
+
+/**
+ This struct contains a commutativity rule: two method calls represented by
+ two unique integers and a function that takes two "info" pointers (with the
+ return value and the arguments) and returns a boolean to represent whether
+ the two method calls are commutable.
+*/
+typedef
+struct CommutativityRule {
+ CSTR method1;
+ CSTR method2;
+
+ /** The plain text of the rule (debugging purpose) */
+ CSTR rule;
+ CheckCommutativity_t condition;
+
+ CommutativityRule(CSTR method1, CSTR method2, CSTR rule,
+ CheckCommutativity_t condition);
+
+ bool isRightRule(Method m1, Method m2);
+
+ bool checkCondition(Method m1, Method m2);
+
+} CommutativityRule;
+
+typedef enum CheckFunctionType {
+ INITIAL, COPY, CLEAR, FINAL, PRINT_STATE, TRANSITION, PRE_CONDITION,
+ JUSTIFYING_PRECONDITION, SIDE_EFFECT, JUSTIFYING_POSTCONDITION,
+ POST_CONDITION, PRINT_VALUE
+} CheckFunctionType;
+
+typedef struct NamedFunction {
+ CSTR name;
+ CheckFunctionType type;
+ void *function;
+
+ /**
+ StateTransition_t transition;
+ CheckState_t preCondition;
+ CheckState_t postCondition;
+ */
+ NamedFunction(CSTR name, CheckFunctionType type, void *function);
+} NamedFunction;
+
+typedef
+struct StateFunctions {
+ NamedFunction *transition;
+ NamedFunction *preCondition;
+ NamedFunction *justifyingPrecondition;
+ NamedFunction *justifyingPostcondition;
+ NamedFunction *postCondition;
+ NamedFunction *print;
+
+ StateFunctions(NamedFunction *transition, NamedFunction *preCondition,
+ NamedFunction *justifyingPrecondition,
+ NamedFunction *justifyingPostcondition,
+ NamedFunction *postCondition, NamedFunction *print);
+
+} StateFunctions;
+
+/** A map from a constant c-string to its set of checking functions */
+typedef HashTable<CSTR, StateFunctions*, uintptr_t, 4, malloc, calloc, free > StateFuncMap;
+
+typedef
+struct AnnoInit {
+ /**
+ For the initialization of a state; We actually assume there are two
+ special nodes --- INITIAL & FINAL. They actually have a special
+ MethodCall class representing these two nodes, and their interface name
+ would be INITIAL and FINAL. For the initial function, we actually also
+ use it as the state copy function when evaluating the state of other
+ method calls
+
+ UpdateState_t --> initial
+ */
+ NamedFunction *initial;
+
+ /**
+ TODO: Currently we just have this "final" field, which was supposed to
+ be a final checking function after all method call nodes have been
+ executed on the graph. However, before we think it through, this might
+ potentially be a violation of composability
+
+ CheckState_t --> final;
+ */
+ NamedFunction *final;
+
+ /**
+ For copying out an existing state from a previous method call
+
+ CopyState_t --> copy
+ */
+ NamedFunction *copy;
+
+ /**
+ For clearing out an existing state object
+
+ UpdateState_t --> delete
+ */
+ NamedFunction *clear;
+
+
+ /**
+ For printing out the state
+
+ UpdateState_t --> printState
+ */
+ NamedFunction *printState;
+
+ /** For the state functions. We can conveniently access to the set of state
+ * functions with a hashmap */
+ StateFuncMap *funcMap;
+
+ /** For commutativity rules */
+ CommutativityRule *commuteRules;
+ int commuteRuleNum;
+
+ AnnoInit(NamedFunction *initial, NamedFunction *final, NamedFunction *copy,
+ NamedFunction *clear, NamedFunction *printState, CommutativityRule
+ *commuteRules, int ruleNum);
+
+ void addInterfaceFunctions(CSTR name, StateFunctions *funcs);
+
+} AnnoInit;
+
+typedef
+struct AnnoInterfaceInfo {
+ CSTR name;
+ void *value;
+
+ AnnoInterfaceInfo(CSTR name);
+} AnnoInterfaceInfo;
+
+/********** Universal functions for rewriting the program **********/
+
+#ifdef __cplusplus
+extern "C" {
+#endif
+
+AnnoInterfaceInfo* _createInterfaceBeginAnnotation(CSTR name);
+
+void _createInterfaceEndAnnotation(CSTR name);
+
+void _setInterfaceBeginAnnotationValue(AnnoInterfaceInfo *info, void *value);
+
+void _createOPDefineAnnotation();
+
+void _createPotentialOPAnnotation(CSTR label);
+
+void _createOPCheckAnnotation(CSTR label);
+
+void _createOPClearAnnotation();
+
+void _createOPClearDefineAnnotation();
+
+#ifdef __cplusplus
+};
+#endif
+
+#endif
--- /dev/null
+DIRS := ms-queue linuxrwlocks mcs-lock \
+ chase-lev-deque-bugfix ticket-lock seqlock read-copy-update \
+ concurrent-hashmap spsc-bugfix mpmc-queue
+
+.PHONY: $(DIRS)
+
+all: $(DIRS)
+
+clean: $(DIRS:%=clean-%)
+cleanse: $(DIRS:%=cleanse-%)
+
+$(DIRS):
+ $(MAKE) -C $@
+
+clean-%:
+ -$(MAKE) -C $* clean
+
+cleanse-%:
+ -$(MAKE) -C $* cleanse
--- /dev/null
+# A few common Makefile items
+
+CC = gcc
+CXX = g++
+
+UNAME = $(shell uname)
+
+LIB_NAME = model
+LIB_SO = lib$(LIB_NAME).so
+
+BASE = ../..
+INCLUDE = -I$(BASE) -I$(BASE)/include -I../include -I$(BASE)/spec-analysis/include
+
+# C preprocessor flags
+CPPFLAGS += $(INCLUDE) -O3 -g
+
+# C++ compiler flags
+CXXFLAGS += $(CPPFLAGS)
+
+# C compiler flags
+CFLAGS += $(CPPFLAGS)
+
+# Linker flags
+LDFLAGS += -L$(BASE) -l$(LIB_NAME) -rdynamic
+
+# Mac OSX options
+ifeq ($(UNAME), Darwin)
+MACFLAGS = -D_XOPEN_SOURCE -DMAC
+CPPFLAGS += $(MACFLAGS)
+CXXFLAGS += $(MACFLAGS)
+CFLAGS += $(MACFLAGS)
+LDFLAGS += $(MACFLAGS)
+endif
+
+
+CDSSPEC_NAME = cdsspec-generated
+CDSSPEC = $(CDSSPEC_NAME).o
+
+$(CDSSPEC): $(CDSSPEC_NAME).cc
+ $(CXX) -MMD -MF .$@.d -o $@ -fPIC -c $< $(CXXFLAGS) -I$(BASE)/spec-analysis -std=c++11 $(LDFLAGS)
+
--- /dev/null
+BENCH := deque
+
+TESTS := main testcase1 testcase2 testcase3
+
+all: $(TESTS)
+
+# Should include it after "all"; otherwise it will only make cdsspec-generated.o
+include ../benchmarks.mk
+
+%.o : %.c
+ $(CC) -fPIC -c -MMD -MF .$@.d -o $@ $< $(CPPFLAGS) $(LDFLAGS)
+
+$(TESTS): % : %.o $(BENCH).o $(CDSSPEC)
+ $(CXX) -o $@ $^ $(CPPFLAGS) $(LDFLAGS)
+
+-include .*.d
+
+clean:
+ rm -rf $(TESTS) *.o .*.d *.dSYM
+
+cleanse: clean
+ rm -rf *.c *.cc *.h
+
+.PHONY: clean all cleanse
--- /dev/null
+#!/bin/bash
+#
+
+BENCHS=(ms-queue linuxrwlocks mcs-lock chase-lev-deque-bugfix \
+ ticket-lock seqlock read-copy-update concurrent-hashmap spsc-bugfix \
+ mpmc-queue)
+
+for i in ${BENCHS[*]}; do
+ echo "Cleansing $i..."
+ echo "rm -rf $i/*.c $i/*.cc $i/*.h $i/*.o $i/main $i/testcase* $i/.*.d"
+ rm -rf $i/*.c $i/*.cc $i/*.h $i/*.o $i/main $i/testcase* $i/.*.d
+done
--- /dev/null
+BENCH := hashmap
+
+TESTS := main testcase1 testcase2
+
+all: $(TESTS)
+
+# Should include it after "all"; otherwise it will only make cdsspec-generated.o
+include ../benchmarks.mk
+
+%.o : %.cc
+ $(CXX) -fPIC -c -MMD -MF .$@.d -o $@ $< $(CXXFLAGS) $(LDFLAGS)
+
+$(TESTS) : % : %.o $(BENCH).o $(CDSSPEC)
+ $(CXX) -MMD -MF .$@.d -o $@ $^ $(CXXFLAGS) $(LDFLAGS)
+
+-include .*.d
+
+clean:
+ rm -rf $(TESTS) *.o .*.d *.dSYM
+
+cleanse: clean
+ rm -rf *.c *.cc *.h
+
+.PHONY: clean all cleanse
--- /dev/null
+#ifndef __UNRELACY_H__
+#define __UNRELACY_H__
+
+#include <stdatomic.h>
+#include <stdlib.h>
+#include <stdio.h>
+#include <mutex>
+#include <condition_variable>
+
+#include <model-assert.h>
+#include <librace.h>
+
+#define $
+#ifndef ASSRT
+#define ASSERT(expr) MODEL_ASSERT(expr)
+#endif
+#define RL_ASSERT(expr) MODEL_ASSERT(expr)
+
+#define RL_NEW new
+#define RL_DELETE(expr) delete expr
+
+#define mo_seqcst memory_order_seq_cst
+#define mo_release memory_order_release
+#define mo_acquire memory_order_acquire
+#define mo_acq_rel memory_order_acq_rel
+#define mo_relaxed memory_order_relaxed
+
+#define seq_cst memory_order_seq_cst
+#define release memory_order_release
+#define acquire memory_order_acquire
+#define acq_rel memory_order_acq_rel
+#define relaxed memory_order_relaxed
+
+namespace rl {
+
+ /* This 'useless' struct is declared just so we can use partial template
+ * specialization in our store and load functions. */
+ template <typename T, size_t n>
+ struct useless {
+ static void store(void *addr, T val);
+ static T load(const void *addr);
+ };
+
+ template <typename T>
+ struct useless<T, 1> {
+ static void store(void *addr, T val) { store_8(addr, (uint8_t)val); }
+ static T load(const void *addr) { return (T)load_8(addr); }
+ };
+
+ template <typename T>
+ struct useless<T, 2> {
+ static void store(void *addr, T val) { store_16(addr, (uint16_t)val); }
+ static T load(const void *addr) { return (T)load_16(addr); }
+ };
+
+ template <typename T>
+ struct useless<T, 4> {
+ static void store(void *addr, T val) { store_32(addr, (uint32_t)val); }
+ static T load(const void *addr) { return (T)load_32(addr); }
+ };
+
+ template <typename T>
+ struct useless<T, 8> {
+ static void store(void *addr, T val) { store_64(addr, (uint64_t)val); }
+ static T load(const void *addr) { return (T)load_64(addr); }
+ };
+
+ template <typename T>
+ struct var {
+ var() { useless<T, sizeof(T)>::store(&value, 0); }
+ var(T v) { useless<T, sizeof(T)>::store(&value, v); }
+ var(var const& r) {
+ value = r.value;
+ }
+ ~var() { }
+
+ void operator = (T v) { useless<T, sizeof(T)>::store(&value, v); }
+ T operator () () { return useless<T, sizeof(T)>::load(&value); }
+ void operator += (T v) {
+ useless<T, sizeof(T)>::store(&value,
+ useless<T, sizeof(T)>::load(&value) + v);
+ }
+ bool operator == (const struct var<T> v) const { return useless<T, sizeof(T)>::load(&value) == useless<T, sizeof(T)>::load(&v.value); }
+
+ T value;
+ };
+
+ class backoff_t
+ {
+ public:
+ typedef int debug_info_param;
+ void yield(debug_info_param info) { }
+ void yield() { }
+ };
+
+
+ typedef backoff_t backoff;
+ typedef backoff_t linear_backoff;
+ typedef backoff_t exp_backoff;
+
+}
+
+#endif /* __UNRELACY_H__ */
--- /dev/null
+BENCH := linuxrwlocks
+
+TESTS := main testcase1 testcase2 testcase3
+
+all: $(TESTS)
+
+# Should include it after "all"; otherwise it will only make cdsspec-generated.o
+include ../benchmarks.mk
+
+%.o : %.c
+ $(CC) -fPIC -c -MMD -MF .$@.d -o $@ $< $(CPPFLAGS) $(LDFLAGS)
+
+$(TESTS): % : %.o $(BENCH).o $(CDSSPEC)
+ $(CXX) -o $@ $^ $(CPPFLAGS) $(LDFLAGS)
+
+-include .*.d
+
+clean:
+ rm -rf $(TESTS) *.o .*.d *.dSYM
+
+cleanse: clean
+ rm -rf *.c *.cc *.h
+
+.PHONY: clean all cleanse
--- /dev/null
+BENCH := mcs-lock
+
+TESTS := main testcase
+
+all: $(TESTS)
+
+# Should include it after "all"; otherwise it will only make cdsspec-generated.o
+include ../benchmarks.mk
+
+%.o : %.cc
+ $(CXX) -fPIC -c -MMD -MF .$@.d -o $@ $< $(CXXFLAGS) $(LDFLAGS)
+
+$(TESTS) : % : %.o $(BENCH).o $(CDSSPEC)
+ $(CXX) -MMD -MF .$@.d -o $@ $^ $(CXXFLAGS) $(LDFLAGS)
+
+-include .*.d
+
+clean:
+ rm -rf $(TESTS) *.o .*.d *.dSYM
+
+cleanse: clean
+ rm -rf *.c *.cc *.h
+
+.PHONY: clean all cleanse
--- /dev/null
+BENCH := mpmc-queue
+
+TESTS := main testcase1 testcase2
+
+all: $(TESTS)
+
+# Should include it after "all"; otherwise it will only make cdsspec-generated.o
+include ../benchmarks.mk
+
+%.o : %.cc
+ $(CXX) -fPIC -c -MMD -MF .$@.d -o $@ $< $(CXXFLAGS) $(LDFLAGS)
+
+$(TESTS) : % : %.o $(BENCH).o $(CDSSPEC)
+ $(CXX) -MMD -MF .$@.d -o $@ $^ $(CXXFLAGS) $(LDFLAGS)
+
+-include .*.d
+
+clean:
+ rm -rf $(TESTS) *.o .*.d *.dSYM
+
+cleanse: clean
+ rm -rf *.c *.cc *.h
+
+.PHONY: clean all cleanse
--- /dev/null
+main
+testcase1
+testcase2
+testcase3
+testcase4
+*.dSYM/
+*.o
+.*.d
--- /dev/null
+BENCH := queue
+
+TESTS := main testcase1 testcase2 testcase3 testcase4
+
+all: $(TESTS)
+
+# Should include it after "all"; otherwise it will only make cdsspec-generated.o
+include ../benchmarks.mk
+
+%.o : %.c
+ $(CC) -fPIC -c -MMD -MF .$@.d -o $@ $< $(CPPFLAGS) $(LDFLAGS)
+
+$(TESTS): % : %.o $(BENCH).o $(CDSSPEC)
+ $(CXX) -o $@ $^ $(CPPFLAGS) $(LDFLAGS)
+
+-include .*.d
+
+clean:
+ rm -rf $(TESTS) *.o .*.d *.dSYM
+
+cleanse: clean
+ rm -rf *.c *.cc *.h
+
+.PHONY: clean all cleanse
--- /dev/null
+#!/bin/bash
+#
+
+if [[ $# -gt 0 ]]; then
+ BENCHS=($1)
+else
+ BENCHS=(ms-queue linuxrwlocks mcs-lock chase-lev-deque-bugfix \
+ ticket-lock seqlock read-copy-update concurrent-hashmap spsc-bugfix \
+ mpmc-queue)
+fi
+
+echo ${BENCHS[*]}
+for i in ${BENCHS[*]}; do
+ if [ -e "$i/main" ]; then
+ echo "---------- Start to run the main for $i ----------"
+ time ./run.sh $i/main -m2 -y -u3 -tSPEC
+ else
+ echo "No main for $i."
+ fi
+done
--- /dev/null
+BENCH := rcu
+
+TESTS := main testcase
+
+all: $(TESTS)
+
+# Should include it after "all"; otherwise it will only make cdsspec-generated.o
+include ../benchmarks.mk
+
+%.o : %.cc
+ $(CXX) -fPIC -c -MMD -MF .$@.d -o $@ $< $(CXXFLAGS) $(LDFLAGS)
+
+$(TESTS) : % : %.o $(BENCH).o $(CDSSPEC)
+ $(CXX) -MMD -MF .$@.d -o $@ $^ $(CXXFLAGS) $(LDFLAGS)
+
+-include .*.d
+
+clean:
+ rm -rf $(TESTS) *.o .*.d *.dSYM
+
+cleanse: clean
+ rm -rf *.c *.cc *.h
+
+.PHONY: clean all cleanse
--- /dev/null
+#!/bin/bash
+#
+
+BENCHS=(ms-queue linuxrwlocks mcs-lock chase-lev-deque-bugfix treiber-stack
+ ticket-lock seqlock read-copy-update concurrent-hashmap spsc-bugfix mpmc-queue)
+
+echo ${BENCHS[*]}
+for i in ${BENCHS[*]}; do
+ echo "---------- Start to run $i ----------"
+ tests=$(ls $i/testcase[1-9])
+ if [ -e "$i/main" ]; then
+ tests=($tests "$i/main")
+ fi
+ if [ -e "$i/testcase" ]; then
+ tests=($tests "$i/testcase")
+ fi
+
+ for j in ${tests[*]}; do
+ ./run.sh $j -m2 -y -u3 -tSPEC
+ done
+done
--- /dev/null
+#!/bin/sh
+#
+# Runs a simple test (default: ./barrier/barrier)
+# Syntax:
+# ./run.sh [test program] [OPTIONS]
+# ./run.sh [OPTIONS]
+# ./run.sh [gdb [test program]]
+#
+# If you include a 'gdb' argument, the your program will be launched with gdb.
+# You can also supply a test program argument to run something besides the
+# default program.
+#
+
+# Get the directory in which this script is located
+BINDIR="${0%/*}"
+
+BIN=${BINDIR}/barrier/barrier
+PREFIX=
+
+export LD_LIBRARY_PATH=${BINDIR}/..
+# For Mac OSX
+export DYLD_LIBRARY_PATH=${BINDIR}/..
+
+[ $# -gt 0 ] && [ "$1" = "gdb" ] && PREFIX=gdb && shift
+[ $# -gt 0 ] && [ -e "$1" ] && BIN="$1" && shift
+
+set -xe
+$PREFIX $BIN $@
--- /dev/null
+BENCH := seqlock
+
+TESTS := main testcase1 testcase2
+
+all: $(TESTS)
+
+# Should include it after "all"; otherwise it will only make cdsspec-generated.o
+include ../benchmarks.mk
+
+%.o : %.cc
+ $(CXX) -fPIC -c -MMD -MF .$@.d -o $@ $< $(CXXFLAGS) $(LDFLAGS)
+
+$(TESTS) : % : %.o $(BENCH).o $(CDSSPEC)
+ $(CXX) -MMD -MF .$@.d -o $@ $^ $(CXXFLAGS) $(LDFLAGS)
+
+-include .*.d
+
+clean:
+ rm -rf $(TESTS) *.o .*.d *.dSYM
+
+cleanse: clean
+ rm -rf *.c *.cc *.h
+
+.PHONY: clean all cleanse
--- /dev/null
+BENCH := queue
+
+TESTS := main testcase1
+
+all: $(TESTS)
+
+# Should include it after "all"; otherwise it will only make cdsspec-generated.o
+include ../benchmarks.mk
+
+%.o : %.cc
+ $(CXX) -fPIC -c -MMD -MF .$@.d -o $@ $< $(CXXFLAGS) $(LDFLAGS)
+
+$(TESTS) : % : %.o $(BENCH).o $(CDSSPEC)
+ $(CXX) -MMD -MF .$@.d -o $@ $^ $(CXXFLAGS) $(LDFLAGS)
+
+-include .*.d
+
+clean:
+ rm -rf $(TESTS) *.o .*.d *.dSYM
+
+cleanse: clean
+ rm -rf *.c *.cc *.h
+
+.PHONY: clean all cleanse
--- /dev/null
+BENCH := lock
+
+TESTS := main testcase1
+
+all: $(TESTS)
+
+# Should include it after "all"; otherwise it will only make cdsspec-generated.o
+include ../benchmarks.mk
+
+%.o : %.c
+ $(CC) -fPIC -c -MMD -MF .$@.d -o $@ $< $(CPPFLAGS) $(LDFLAGS)
+
+$(TESTS): % : %.o $(BENCH).o $(CDSSPEC)
+ $(CXX) -o $@ $^ $(CPPFLAGS) $(LDFLAGS)
+
+-include .*.d
+
+clean:
+ rm -rf $(TESTS) *.o .*.d *.dSYM
+
+cleanse: clean
+ rm -rf *.c *.cc *.h
+
+.PHONY: clean all cleanse
DEPS := $(join $(addsuffix ., $(dir $(OBJECTS))), $(addsuffix .d, $(notdir $(OBJECTS))))
CPPFLAGS += -I$(BASE) -I$(BASE)/include
+CFLAGS += -I$(BASE) -I$(BASE)/include
all: $(OBJECTS)
-include $(DEPS)
%.o: %.c
- $(CC) -MMD -MF $(@D)/.$(@F).d -o $@ $< $(CPPFLAGS) -L$(BASE) -l$(LIB_NAME)
+ $(CC) -MMD -MF $(@D)/.$(@F).d -o $@ $< $(CFLAGS) -L$(BASE) -l$(LIB_NAME)
%.o: %.cc
$(CXX) -MMD -MF $(@D)/.$(@F).d -o $@ $< $(CPPFLAGS) -L$(BASE) -l$(LIB_NAME)
+++ /dev/null
-/**
- * @file iriw.cc
- * @brief Independent read and independent write test
- */
-
-#include <atomic>
-#include <threads.h>
-#include <stdio.h>
-
-#include "wildcard.h"
-#include "model-assert.h"
-
-using namespace std;
-
-atomic_int x, y;
-int r1, r2, r3, r4; /* "local" variables */
-
-static void a(void *obj)
-{
- x.store(1, memory_order_seq_cst);
-}
-
-static void b(void *obj)
-{
- y.store(1, memory_order_seq_cst);
-}
-
-static void c(void *obj)
-{
- r1 = x.load(memory_order_acquire);
- r2 = y.load(memory_order_seq_cst);
-}
-
-static void d(void *obj)
-{
- r3 = y.load(memory_order_acquire);
- r4 = x.load(memory_order_seq_cst);
-}
-
-
-int user_main(int argc, char **argv)
-{
- thrd_t t1, t2, t3, t4;
-
- atomic_init(&x, 0);
- atomic_init(&y, 0);
-
- printf("Main thread: creating 4 threads\n");
- thrd_create(&t1, (thrd_start_t)&a, NULL);
- thrd_create(&t2, (thrd_start_t)&b, NULL);
- thrd_create(&t3, (thrd_start_t)&c, NULL);
- thrd_create(&t4, (thrd_start_t)&d, NULL);
-
- thrd_join(t1);
- thrd_join(t2);
- thrd_join(t3);
- thrd_join(t4);
- printf("Main thread is finished\n");
-
- /*
- * This condition should not be hit if the execution is SC */
- bool sc = (r1 == 1 && r2 == 0 && r3 == 1 && r4 == 0);
- printf("r1 = %d, r2 = %d, r3 = %d and r4 = %d\n", r1, r2, r3, r4);
- MODEL_ASSERT(!sc);
- return 0;
-}
+++ /dev/null
-/**
- * @file iriw.cc
- * @brief Independent read and independent write test
- */
-
-#include <atomic>
-#include <threads.h>
-#include <stdio.h>
-
-#include "wildcard.h"
-#include "model-assert.h"
-
-using namespace std;
-
-atomic_int x, y;
-int r1, r2, r3, r4; /* "local" variables */
-
-static void a(void *obj)
-{
- x.store(1, wildcard(1));
-}
-
-static void b(void *obj)
-{
- y.store(1, wildcard(2));
-}
-
-static void c(void *obj)
-{
- r1 = x.load(wildcard(3));
- r2 = y.load(wildcard(4));
-}
-
-static void d(void *obj)
-{
- r3 = y.load(wildcard(5));
- r4 = x.load(wildcard(6));
-}
-
-
-int user_main(int argc, char **argv)
-{
- thrd_t t1, t2, t3, t4;
-
- atomic_init(&x, 0);
- atomic_init(&y, 0);
-
- printf("Main thread: creating 4 threads\n");
- thrd_create(&t1, (thrd_start_t)&a, NULL);
- thrd_create(&t2, (thrd_start_t)&b, NULL);
- thrd_create(&t3, (thrd_start_t)&c, NULL);
- thrd_create(&t4, (thrd_start_t)&d, NULL);
-
- thrd_join(t1);
- thrd_join(t2);
- thrd_join(t3);
- thrd_join(t4);
- printf("Main thread is finished\n");
-
- /*
- * This condition should not be hit if the execution is SC */
- bool sc = (r1 == 1 && r2 == 0 && r3 == 1 && r4 == 0);
- //MODEL_ASSERT(!sc);
-
- return 0;
-}
/** Allocate a stack for a new thread. */
static void * stack_allocate(size_t size)
{
- return Thread_malloc(size);
+ return snapshot_malloc(size);
}
/** Free a stack for a terminated thread. */
static void stack_free(void *stack)
{
- Thread_free(stack);
+ snapshot_free(stack);
}
/**
#define TRACE_ANALYSIS_H
#include "model.h"
-
class TraceAnalysis {
public:
/** setExecution is called once after installation with a reference to
virtual void finish() = 0;
- /** This method is used to inspect the normal/abnormal model
- * action. */
- virtual void inspectModelAction(ModelAction *act) {}
-
- /** This method will be called by when a plugin is installed by the
- * model checker. */
- virtual void actionAtInstallation() {}
-
- /** This method will be called when the model checker finishes the
- * executions; With this method, the model checker can alter the
- * state of the plugin and then the plugin can choose whether or not
- * restart the model checker. */
- virtual void actionAtModelCheckingFinish() {}
-
SNAPSHOTALLOC
};
#endif