Esempio n. 1
0
  /**
   * Augments the CFSM with a tracking channel and send synthetic messages on this channel to keep
   * track of the two events that are part of the binary invariant inv. Adds the synthetic events to
   * the CFSM alphabet.
   */
  private void augmentWithBinInvTracing(BinaryInvariant inv) {
    DistEventType e1 = inv.getFirst();
    DistEventType e2 = inv.getSecond();

    assert alphabet.contains(e1);
    assert alphabet.contains(e2);
    assert e1.getPid() < fsms.size();
    assert e2.getPid() < fsms.size();
    assert !invs.contains(invs);

    invs.add(inv);

    int scmId = this.channelIds.size();

    if (firstSyntheticChIndex > scmId) {
      firstSyntheticChIndex = scmId;
    }

    // Create and add a new invariant-specific channel.
    ChannelId invCid = new InvChannelId(inv, scmId);
    this.channelIds.add(invCid);

    // Update the FSM corresponding to e1.
    Set<FSMState> visited = Util.newSet();
    FSM f1 = this.fsms.get(e1.getPid());
    DistEventType e1Tracer1 = DistEventType.SynthSendEvent(e1, invCid, true);
    DistEventType e1Tracer2 = DistEventType.SynthSendEvent(e1, invCid, false);
    addSendToEventTx(f1, e1, e1Tracer1, e1Tracer2, visited);
    this.alphabet.add(e1Tracer1);
    this.alphabet.add(e1Tracer2);

    // Update the FSM corresponding to e2.
    visited.clear();
    FSM f2 = this.fsms.get(e2.getPid());
    DistEventType e2Tracer1 = DistEventType.SynthSendEvent(e2, invCid, true);
    DistEventType e2Tracer2 = DistEventType.SynthSendEvent(e2, invCid, false);
    addSendToEventTx(f2, e2, e2Tracer1, e2Tracer2, visited);
    this.alphabet.add(e2Tracer1);
    this.alphabet.add(e2Tracer2);

    inv.setFirstSynthTracers(e1Tracer1, e1Tracer2);
    inv.setSecondSynthTracers(e2Tracer1, e2Tracer2);
  }
Esempio n. 2
0
 /**
  * Augments the CFSM with synthetic events for model checking binv.
  *
  * <p>The basic strategy, regardless of invariant, is to create a separate FIFO queue that will be
  * used to record the sequence of executed events that are relevant to the invariant.
  *
  * <p>For instance, for a AFby b invariant, create a queue Q_ab. Modify any state p that has an
  * outgoing "a" transition, add a synthetic state p_synth, and redirect the "a" transition from p
  * to p_synth. Then, add just one outgoing transition on "Q_ab ! a" from p_synth to the original
  * state target of "a" in state p. That is, whenever "a" occurs, we will add "a" to Q_ab. Do the
  * same for event "b".
  *
  * <p>For a AFby b bad state pairs within the modified GFSM (per above procedure) are all initial
  * state and all states where all queues except Q_ab are empty, and where Q_ab = [*a], and where
  * the process states are terminal. In a sense, we've added Q_ab to track "a" and "b" executions,
  * and have not interfered with the normal execution of the FIFO system.
  *
  * <p>For a AP b, the procedure is identical, but the second bad state in every pair would have
  * Q_ab = [b*]. For a NFby b, Q_ab = [*a*b*]. In a sense, we've expressed LTL properties as
  * regular expressions of Q_ab queue contents.
  */
 public void augmentWithInvTracing(BinaryInvariant binv) throws Exception {
   if (binv instanceof EventuallyHappens) {
     augmentWithInvTracing((EventuallyHappens) binv);
   } else if (binv instanceof AlwaysPrecedes
       || binv instanceof AlwaysFollowedBy
       || binv instanceof NeverFollowedBy) {
     augmentWithBinInvTracing(binv);
   } else {
     throw new Exception("Unrecognized binary invarianr type: " + binv.toString());
   }
 }
Esempio n. 3
0
  /**
   * Generates a Promela representation of this CFSM, to be used with SPIN. The never claim is not
   * specified here and it is appended to the CFSM elsewhere.
   */
  public String toPromelaString(List<BinaryInvariant> invariants, int chanCapacity) {
    assert unSpecifiedPids == 0;

    String ret = "/* Spin-promela Multiple invariants */\n\n";

    // Message types:
    //
    // mtype is global and can only be declared once.
    // There is also limit of 255 for the size of mtype.

    // This outputs a set of event types for the CFSM.
    ret += "/* Message types: */\n";
    ret += "mtype = { ";

    Set<String> eventTypes = Util.newSet();
    for (DistEventType e : alphabet) {
      eventTypes.add(e.getPromelaEType());
    }

    ret += StringUtils.join(eventTypes, ", ");

    ret += " };\n"; // End mtype declaration.
    ret += "\n\n";

    // Define the channels:
    ret += "/* Channels: */\n\n";

    // Specifying channels as an array to work with inlines.
    ret +=
        String.format("chan channel[%d] = [%d] of { mtype };\n", channelIds.size(), chanCapacity);

    // The following block defines EMPTYCHANNELCHECK as a conditional that
    // checks if all the channels are empty. This is used in the never claim
    // to make sure our channels are empty before terminating.
    String emptyChannelCheck = "";
    for (int i = 0; i < channelIds.size(); i++) {
      ret += "/* Channel " + channelIds.get(i).toString() + " */\n";
      if (i != 0) {
        emptyChannelCheck += " && ";
      }
      emptyChannelCheck += "empty(channel[" + i + "])";
    }
    ret += String.format("#define EMPTYCHANNELCHECK (%s)\n", emptyChannelCheck);
    ret += "\n\n";

    // Tracks if the current states of each of the FSM are terminal.
    ret += "bit terminal[" + numProcesses + "];\n";

    // ENDSTATECHECK is the conditional used by the never claim to
    // check the terminal states in all CFSMs. The never claim has this to
    // ensure that the processes are in a proper terminal state when the
    // never claim is done.

    String endStateCheck = "";
    for (int pid = 0; pid < numProcesses; pid++) {
      // Set up the terminal check conditional.
      if (pid != 0) {
        endStateCheck += " && ";
      }
      endStateCheck += "terminal[" + pid + "]";
    }

    ret += String.format("#define ENDSTATECHECK (%s)\n", endStateCheck);

    // Event type definitions for type tracking

    // OTHEREVENTs are used for other transitions so we do not accidentally
    // trigger an "a NFby b". This can happen when a == b. This invariant
    // can be accepted if a transition happens that does not call
    // setRecentEvent. OTHEREVENT does not match any event we are interested
    // in tracking so it is safe to use during these transitions.

    ret += "#define OTHEREVENT (0)\n";
    // Event types we're actively tracking.
    ret += "#define LOCAL (1)\n";
    ret += "#define SEND (2)\n";
    ret += "#define RECV (3)\n";

    // Custom datatype to assist in tracking recent event.
    ret += "typedef myEvent {\n";
    // The type of event: LOCAL, SEND or RECV
    ret += "    byte type;\n";
    // id is the process id if the type is LOCAL and the channel id if the
    // type is SEND or RECV.
    ret += "    byte id;\n";
    // The event itself. These are the previously defined mtypes.
    ret += "    mtype event;\n";
    ret += "};\n";

    // Declaration of event tracker.
    ret += "myEvent recentEvent;\n";

    // Custom inline function to update most recent event.
    ret += "inline setRecentEvent(event_type, owner_id, event_message) {\n";
    ret += "  d_step{\n";
    ret += "    recentEvent.type = event_type;\n";
    ret += "    recentEvent.id = owner_id;\n";
    ret += "    recentEvent.event = event_message;\n";
    ret += "  };\n";
    ret += "}\n";

    // Each of the FSMs in the CFSM:
    for (int pid = 0; pid < numProcesses; pid++) {
      String labelPrefix = "state" + Integer.toString(pid);
      FSM f = fsms.get(pid);
      ret += "active proctype p" + Integer.toString(pid) + "(){\n";
      ret += f.toPromelaString(invariants, labelPrefix);
      ret += "}\n\n";
    }

    ret += "\n\n";

    for (BinaryInvariant inv : invariants) {
      ret += "/* " + inv.toString() + "*/\n";
      ret += inv.promelaNeverClaim();
      ret += "\n\n";
    }
    return ret;
  }
Esempio n. 4
0
  /**
   * Returns a set of bad states that correspond to a specific invariant that is augmenting this
   * CFSM. Each invariant corresponds to (possibly multiple) bad states. A bad states is a
   * combination of FSM states and a sequence of regular expressions that describe the contents of
   * each of the queues in the CFSM. For an invariant I, a bad state B has the property that if B is
   * reachable in the CFSM then I is falsified. That is, the path to reach B is the counter-example
   * for I.
   */
  public List<BadState> getBadStates(BinaryInvariant inv) {
    assert invs.contains(inv);

    // Without invariants there are no bad states.
    if (invs.isEmpty()) {
      return Collections.emptyList();
    }
    List<BadState> badStates = Util.newList();

    Set<CFSMState> accepts = this.getAcceptStates();
    if (accepts.isEmpty()) {
      assert !accepts.isEmpty();
    }

    List<String> qReList = Util.newList(channelIds.size());

    // Set non-synthetic queues reg-exps to accept the empty string.
    for (int i = 0; i < firstSyntheticChIndex; i++) {
      qReList.add("_");
    }

    int invIndex = invs.indexOf(inv);

    // Set the synthetic queue reg-exps.
    for (int i = firstSyntheticChIndex; i < channelIds.size(); i++) {
      if (i == firstSyntheticChIndex + invIndex) {

        // The invariant we care about checking.
        qReList.add(inv.scmBadStateQRe());

      } else if (i == localEventsChIndex) {

        // Add an RE for the local events queue.
        Set<String> localEvents = this.alphabet.getLocalEventScmStrings();
        if (!localEvents.isEmpty()) {
          String localEventsQueueRe = "(";
          for (String eLocal : localEvents) {
            localEventsQueueRe += eLocal + " | ";
          }
          // Remove the last occurrence of the "|" character.
          localEventsQueueRe = localEventsQueueRe.substring(0, localEventsQueueRe.length() - 3);
          localEventsQueueRe += ")^*";
          qReList.add(localEventsQueueRe);
        } else {
          // If there are no local events then the queue RE is the
          // empty string, since no corresponding local event messages
          // will be generated.
          qReList.add("_");
        }

      } else {
        // Initialize non-inv invariant synthetic queues to accept
        // everything that their alphabet permits.
        qReList.add(inv.someSynthEventsQRe());
      }
    }

    // For each accept, generate a bad state <accept, qReList>.
    for (CFSMState accept : accepts) {
      badStates.add(new BadState(accept, qReList));
    }

    return badStates;
  }