Esempio n. 1
0
 def flush(self):
     """Force any in-progress transactions to be completed. This should be
        called when you know the USB analyzer is finished outputting
        data, such as when a non-USBIO line appears in the log.
        """
     if self.current.dir:
         self.eventQueue.put(self.current)
         self.current = Types.Transaction()
Esempio n. 2
0
    def beginUrb(self, pipe):
        """Simulate a new URB being created on the supplied pipe. This
           begins a Down transaction and makes it pending and current.
           """
        t = Types.Transaction()
        t.dir = 'Down'
        t.dev, t.endpt = pipe
        t.timestamp = self.timestamp
        t.frame = parseInt(self._frameAttrs, 'frameNumber')

        t.status = 0

        self.pipes[pipe] = t
        self.pending[pipe] = t
        self.current = t
Esempio n. 3
0
    def parse(self, line):
        self.lineNumber += 1
        tokens = line.split()
        try:

            # Extract the time, convert to seconds
            nanotime = int(tokens[0])
            if not self.epoch:
                self.epoch = nanotime
            timestamp = (nanotime - self.epoch) / 1000000000.0

            # Detect the start- or end- prefix
            name = tokens[1]
            if name.startswith("begin-"):
                name = name.split('-', 1)[1]
                dirs = ('Down',)
            elif name.startswith("end-"):
                name = name.split('-', 1)[1]
                dirs = ('Up',)
            else:
                dirs = ('Down', 'Up')

            # Generate an 'endpoint' for the event name
            try:
                endpoint = self.nameEndpoints[name]
            except KeyError:
                endpoint = self.nextEp
                self.nameEndpoints[name] = endpoint
                self.nextEp = endpoint + 1

            for dir in dirs:
                trans = Types.Transaction()
                trans.dir = dir
                trans.timestamp = timestamp
                trans.lineNumber = self.lineNumber
                trans.endpt = endpoint
                trans.dev = 0
                trans.status = 0
                trans.datalen = 0x1000
                trans.appendDecoded(" ".join(tokens[1:]))
                self.eventQueue.put(trans)
        except:
            print "Error on line %d:" % self.lineNumber
            traceback.print_exc()
Esempio n. 4
0
    def flipUrb(self, pipe):
        """Begin the Up phase on a particular pipe. This
           completes the Down transaction, and makes an Up
           current (but not pending)
           """
        del self.pending[pipe]
        down = self.pipes[pipe]
        self.eventQueue.put(down)

        up = Types.Transaction()
        up.dir = 'Up'
        up.dev, up.endpt = pipe

        # Up and Down transactions share setup data, if applicable
        if down.hasSetupData():
            up.data = down.data[:8]

        self.pipes[pipe] = up
        self.current = up
Esempio n. 5
0
    def parse(self, line, timestamp=None):
        self.lineNumber += 1
        tokens = line.split()
        iso = 0
        try:
            # Do a small stupid sanity check if this is a correct usbmon log line
            try:
                if len(tokens) < 4:
                    return
                if not(int(tokens[0],16) and int(tokens[1]) and
                       (tokens[2] in ('S', 'C', 'E'))):
                    return
            except:
                print "Error on line %d:" % self.lineNumber
                return

            # Copied log file format description of the usbmon kernel
            # facility You can find the original manual including how
            # to use usbmon in your kernel sources: <linux
            # sources>/Documentation/usb/usbmon.txt
            #
            # Copied text starts here:
            # Any text format data consists of a stream of events,
            # such as URB submission, URB callback, submission
            # error. Every event is a text line, which consists of
            # whitespace separated words. The number or position of
            # words may depend on the event type, but there is a set
            # of words, common for all types.

            # Here is the list of words, from left to right:

            # - URB Tag. This is used to identify URBs, and is
            #   normally an in-kernel address of the URB structure in
            #   hexadecimal, but can be a sequence number or any other
            #   unique string, within reason.

            self.trans.lineNumber = self.lineNumber

            # TODO Usbmon's timestamps can wrap. Annoyingly, they can wrap
            # at either every 4096 seconds or (about) every 4296 seconds, see
            # bugzilla.redhat.com/show_bug.cgi?id=574024 Let's wait for some
            # feedback on that bugreport before adding (a possibly trivial)
            # way to handle that.

            # - Timestamp in microseconds, a decimal number. The
            #   timestamp's resolution depends on available clock, and
            #   so it can be much worse than a microsecond (if the
            #   implementation uses jiffies, for example).

            # Extract the time, convert to seconds
            microtime = int(tokens[1])
            if not self.epoch:
                self.epoch = microtime
            timestamp = (microtime - self.epoch) / 1000000.0

            self.trans.timestamp = timestamp

            # - Event Type. This type refers to the format of the
            #   event, not URB type.  Available types are: S -
            #   submission, C - callback, E - submission error.

            if tokens[2] == 'S':
                self.trans.dir = 'Down'
            else:
                self.trans.dir = 'Up'

            # - "Address" word (formerly a "pipe"). It consists of
            #   four fields, separated by colons: URB type and
            #   direction, Bus number, Device address, Endpoint
            #   number.  Type and direction are encoded with two bytes
            #   in the following manner:
            #
            #     Ci Co   Control input and output
            #     Zi Zo   Isochronous input and output
            #     Ii Io   Interrupt input and output
            #     Bi Bo   Bulk input and output
            #
            #   Bus number, Device address, and Endpoint are decimal
            #   numbers, but they may have leading zeros, for the sake
            #   of human readers.
            #
            #   Note that older kernels seem to omit the bus number field.
            #   We can parse either format.

            pipe = tokens[3].split(':')

            self.trans.dev = int(pipe[-2])
            self.trans.endpt = int(pipe[-1])

            if pipe[0][1] == 'i' and self.trans.endpt != 0:
                # Input endpoint
                self.trans.endpt |= 0x80

            if len(pipe) >= 4:
                self.trans.dev += int(pipe[-3]) * 1000

            # - URB Status word. This is either a letter, or several
            #   numbers separated by colons: URB status, interval,
            #   start frame, and error count. Unlike the "address"
            #   word, all fields save the status are
            #   optional. Interval is printed only for interrupt and
            #   isochronous URBs. Start frame is printed only for
            #   isochronous URBs. Error count is printed only for
            #   isochronous callback events.
            #
            #   The status field is a decimal number, sometimes
            #   negative, which represents a "status" field of the
            #   URB. This field makes no sense for submissions, but is
            #   present anyway to help scripts with parsing. When an
            #   error occurs, the field contains the error code.
            #
            #   In case of a submission of a Control packet, this
            #   field contains a Setup Tag instead of an group of
            #   numbers. It is easy to tell whether the Setup Tag is
            #   present because it is never a number. Thus if scripts
            #   find a set of numbers in this word, they proceed to
            #   read Data Length (except for isochronous URBs).  If
            #   they find something else, like a letter, they read the
            #   setup packet before reading the Data Length or
            #   isochronous descriptors.
            #
            # - Setup packet, if present, consists of 5 words: one of
            #   each for bmRequestType, bRequest, wValue, wIndex,
            #   wLength, as specified by the USB Specification 2.0.
            #   These words are safe to decode if Setup Tag was
            #   's'. Otherwise, the setup packet was present, but not
            #   captured, and the fields contain filler.
            #
            # - Number of isochronous frame descriptors and
            #   descriptors themselves.  If an Isochronous transfer
            #   event has a set of descriptors, a total number of them
            #   in an URB is printed first, then a word per descriptor,
            #   up to a total of 5. The word consists of 3
            #   colon-separated decimal numbers for status, offset, and
            #   length respectively. For submissions, initial length is
            #   reported. For callbacks, actual length is reported.

            if tokens[4] in ('s'):
                # This is a setup packet
                # Example data stage: 23 01 0010 0002 0040

                self.trans.status = 0

                data = ''.join(tokens[5:7])
                # use VMX's byte ordering for wValue, wIndex and wLength
                data = ''.join((data, tokens[7][2:4], tokens[7][0:2]))
                data = ''.join((data, tokens[8][2:4], tokens[8][0:2]))
                data = ''.join((data, tokens[9][2:4], tokens[9][0:2]))
                self.trans.appendHexData(data)

                # save the setup data to prepend it to the setup packet data stage
                self.setupData = data

            else:
                status_word = tokens[4].split(':')
                self.trans.status = int(status_word[0])

                # check if this is a Callback (the 'Up' part of a transaction)
                # on a CONTROL endpoint and prepend its Submission's (ie, its
                # 'Down' part) setup packet, just as is done in the VMX logs.
                if self.setupData and self.trans.endpt == 0 and \
                   self.trans.dir == 'Up':
                    self.trans.appendHexData(self.setupData)
                    self.setupData = None

                # - Data Length. For submissions, this is the requested
                #   length. For callbacks, this is the actual length.

                if len(tokens) >= 7:
                    if not pipe[0][0] == 'Z':
                        self.trans.datalen = int(tokens[5])
                    # The isochronous stuff is rather messy ... and probably
                    # fails with the legacy format. It also assumes input
                    # direction.
                    elif self.trans.dir == 'Down' and len(tokens) >= 8:
                        # skip two tokens:
                        # - number of isochronous frame descriptors
                        # - one descriptor
                        self.trans.datalen = int(tokens[7])
                        iso = 2
                    elif self.trans.dir == 'Up':
                        # The number of isochronous frame descriptors doesn't
                        # need to equal the number of following descriptors so
                        # we search for '=' and use the preceding token
                        try:
                           equal_sign = tokens.index('=')
                           if equal_sign > 6:
                               self.trans.datalen = int(tokens[equal_sign - 1])
                               iso = equal_sign - 6
                        except:
                            pass

                    # - Data tag. The usbmon may not always capture data, even
                    #   if length is nonzero.  The data words are present only
                    #   if this tag is '='.

                    # - Data words follow, in big endian hexadecimal
                    #   format. Notice that they are not machine words, but
                    #   really just a byte stream split into words to make it
                    #   easier to read. Thus, the last word may contain from
                    #   one to four bytes.  The length of collected data is
                    #   limited and can be less than the data length report in
                    #   Data Length word.

                    if tokens[6 + iso] in ('='):
                        self.trans.appendHexData(''.join(tokens[7 + iso:]))

            self.eventQueue.put(self.trans)
            self.trans = Types.Transaction()

        # End of copied usbmon description text

        # End of log file parsing

        except:
            print "Error on line %d:" % self.lineNumber
            traceback.print_exc()
Esempio n. 6
0
 def __init__(self, eventQueue):
     self.epoch = None
     self.trans = Types.Transaction()
     self.setupData = None
     self.eventQueue = eventQueue
Esempio n. 7
0
 def __init__(self, eventQueue):
     self.current = Types.Transaction()
     self.eventQueue = eventQueue
Esempio n. 8
0
 def __init__(self, completed):
     self.epoch = None
     self.trans = Types.Transaction()
     self.trans.frame = 0
     self.setupData = None
     self.completed = completed
Esempio n. 9
0
 def __init__(self, completed):
     self.current = Types.Transaction()
     self.completed = completed