overture icon indicating copy to clipboard operation
overture copied to clipboard

History counters and permission predicates in the debugger

Open ldcouto opened this issue 11 years ago • 3 comments

The ability to set break points at permission and inspect history counters (see #360) should already be present in the code base.

We need to investigate if this is possible and, if not, then it's a bug and we need to fix it.

ldcouto avatar Sep 01 '14 20:09 ldcouto

Confirmed. Breakpoints can be set for permission predicates but the interpreter does not stop at them.

If you force a deadlock (for example, by adding a conjunction with false), the debugger will stop at the permission predicate but it is still not possible no inspect the history counters. In fact, it's not possible to inspect any variables.

I'm marking this as a bug and closing the original task.

ldcouto avatar Oct 01 '14 13:10 ldcouto

I think I disabled the ​check for binary operators so that debugging was "sane" in some very complex pre/postconditions. These tend to have a long list of "and" clauses for the different aspects being verified. With the break on the operator (as opposed to the operands) stepping through the expression caused some very confusing "leaping about" because of the way a series of "and" clauses form the evaluation tree. So if you change this, create a specification with a long list of "and" clauses in a pre/post and try stepping through it to find out which clause "fails".

I'll post an example, to give you some idea...

nickbattle avatar Feb 23 '15 11:02 nickbattle

The following is an example postcondition (actually a function, called as a postcondition) showing what I mean about a long list of "and" clauses. Debugging these is difficult unless the "step" flow jumps from clause to clause in the "top to bottom" order you would expect:

    postRtsLineItemAdd: ARTS`RTSLineItemAdd * ARTS`RTSLineItemAddResponse * SessionState * SessionState +> bool
    postRtsLineItemAdd(request, response, new, old) ==
        -- req and resp are the request and response, respectively
        let req = request.arg0, resp = response.retval in

            -- If we have sent a retry repeat of the last response, then nothing
            -- has changed except the lastRequestId, which has the new retry count,
            -- and the lastResponseId, which is has an incrememnted logicalID.
            if old.lastRequestId <> nil
                and req.header.messageId.logicalID = old.lastRequestId.logicalID
                and old.lastResponse <> nil
            then
                new.basket = old.basket
                and new.basketState = old.basketState
                and new.basketId = old.basketId
                and new.lastSuccessId = old.lastSuccessId
                and new.lastResponseId.logicalID = old.lastResponseId.logicalID + 1 -- Updated
                and new.lastRequestId = req.header.messageId    -- Updated
                and resp = updatedResponse(old.lastResponse, new.lastResponseId, new.lastRequestId)
                and new.lastResponse = resp
            else

            -- It must be a valid response
            resp.header.actionCode = <ADD> and resp.header.messageType = <RESPONSE>

            -- If there is a sequence error, then lastRequestId is not changed, otherwise
            -- the lastRequestId is updated to that in the request header
            and (if getError(resp) = INVALID_SEQUENCE
                then new.lastRequestId = old.lastRequestId
                else new.lastRequestId = req.header.messageId)

            -- The last response (for retry calls) is the new response, even if
            -- it is an error response.
            and new.lastResponse = resp

            -- And the lastResponseId has been incremented
            and new.lastResponseId.logicalID = old.lastResponseId.logicalID + 1

            -- And the response's messageID will match the lastResponseId
            and resp.header.messageId = new.lastResponseId

            -- And the response request ID will be the same as the request's ID
            and resp.header.response.requestId = req.header.messageId

            -- The last successful response is set to this responseId (even business
            -- errors are considered successful - only system errors fail).
            and new.lastSuccessId = new.lastResponseId

            -- and the response carries the previous lastSuccessId value
            and resp.header.response.lastSuccessfulResponseID = old.lastSuccessId               

            -- if the response is any sort of error, the Horizon basket and basket state
            -- will not have changed, and there will be no POSLogs returned
            and if isErrorResponse(resp)
            then
                new.basket = old.basket
                and new.basketState = old.basketState
                and new.basketId = old.basketId
                and resp.posLog = nil

                -- And the error is one of the following
                and getError(resp) in set {
                    INVALID_REQUEST,
                    INVALID_SEQUENCE,
                    INVALID_BASKET_ID }

            -- otherwise this is a successful response and everything that follows can
            -- assume this:
            else
                -- If this was populated basket, the basketId hasn't changed since it was
                -- It is changed by the first add to a NO_BASKET - note that it is
                -- not a strict sequence
                (old.basketState = <POPULATED_BASKET> => new.basketId = old.basketId)

                -- The response carries the (possibly new) basketId back to the Terminal
                and resp.posLog.sessionSequenceNumber = new.basketId

                -- The BU/Till/Operator values have been set, if needed. The BU/till remain
                -- set for the HTTP session
                and areSetBUTillOperator(req, new.bu, new.till, new.operator)           

                -- The successful basket state is "POPULATED"
                and new.basketState = <POPULATED_BASKET>

                -- The POSLog returned consists of one POSLogRetailTransaction entry for the item
                -- added to the Horizon basket.
                and len resp.posLog.retailTransaction = 1
                and resp.posLog.retailTransaction = [basketItemToLog(new.basket(len new.basket))]

                -- If there was no basket before the call, then the new basket will have one
                -- item in it, otherwise the basket size will have increased by one.
                and (if old.basket = nil
                     then len new.basket = 1
                     else len new.basket = len old.basket + 1)

                -- In either case, the last item in the basket will correspond to
                -- the token lookup of the scan data passed in the request. We take
                -- the BID/TID from the last entry, since these are set by HBS.
                and let last = new.basket(len new.basket) in
                        last = logToBasketItem(req.posLog.retailTransaction(1), last.bid, last.tid);

nickbattle avatar Feb 23 '15 11:02 nickbattle