Example #1
0
    def test_generate_events_normal(self):
        """
        Test _generate_events under normal operation, using all net namespaces

        """
        tracer = object.__new__(ebpf.TCPTracer)
        tracer.options = None
        data = StringIO(
            "header1\n"
            "time type pid comm   ip  s_addr d_addr s_port d_port size netns\n"
            "1    A    2   comm1  4   127.   127.   3      4         1     5\n"
            "6    B    7   comm2  4   127.   127.   4      3         1     5\n"
            "1    A    2   comm3  4   x      x      3      4         1     5\n"
        )
        port_dict = {4: {('pid1', 'test1')}, 3: {('pid2', 'test2')}}
        result = list(tracer._generate_events(data, port_dict))
        expected = [
            data_io.EventDatum(time=1,
                               type='A',
                               connected=[('source_', 'dest_')],
                               specific_datum={
                                   "source_pid": 2,
                                   "source_comm": 'comm1',
                                   "source_port": 3,
                                   "dest_pid": 'pid1',
                                   "dest_comm": 'test1',
                                   "dest_port": 4,
                                   "size": 1,
                                   "net_ns": 5
                               }),
            data_io.EventDatum(time=6,
                               type='B',
                               connected=[('source_', 'dest_')],
                               specific_datum={
                                   "source_pid": 7,
                                   "source_comm": 'comm2',
                                   "source_port": 4,
                                   "dest_pid": 'pid2',
                                   "dest_comm": 'test2',
                                   "dest_port": 3,
                                   "size": 1,
                                   "net_ns": 5
                               })
        ]
        self.assertEqual(expected, result)
Example #2
0
    async def test_success(self, release_mock, re_mock):
        """ Test successful regex matching. """
        # Set up mocks
        release_mock.return_value = "100.0.0"  # so we ignore the kernel check
        self.strio_mock.return_value = StringIO('test_out2')
        match_mock = re_mock.match.return_value
        match_mock.group.side_effect = [
            "111.999", "test_pid", "test_name", "4", "test_event"
        ]

        collecter = perf.SchedulingEvents(self.time)
        data = await collecter.collect()
        sched_events = list(data.datum_generator)

        self.create_mock.assert_has_calls([
            asynctest.call("perf sched record -o " +
                           perf.SchedulingEvents._PERF_FILE_NAME + " sleep " +
                           str(self.time),
                           stderr=self.pipe_mock),
            asynctest.call().communicate(),
            asynctest.call("perf sched script -i " +
                           perf.SchedulingEvents._PERF_FILE_NAME +
                           " -F 'comm,pid,cpu,time,event'",
                           stdout=self.pipe_mock,
                           stderr=self.pipe_mock),
            asynctest.call().communicate()
        ])

        # self.log_mock.error.assert_has_calls([
        #     asynctest.call("test_err1"),
        #     asynctest.call("test_err2")
        # ])

        self.os_mock.remove.assert_called_once_with(
            self.os_mock.getcwd.return_value + "/" +
            perf.SchedulingEvents._PERF_FILE_NAME)

        re_mock.match.assert_called_once_with(
            r"\s*"
            r"(?P<name>\S+(\s+\S+)*)\s+"
            r"(?P<pid>\d+)\s+"
            r"\[(?P<cpu>\d+)\]\s+"
            r"(?P<time>\d+.\d+):\s+"
            r"(?P<event>\S+)", "test_out2")

        expected_event = data_io.EventDatum(
            specific_datum={
                'pid': 'test_pid',
                'cpu': '4',
                'comm': 'test_name'
            },
            #"test_name (pid: test_pid)", "cpu 4"),
            time=111000999,
            type="test_event",
            connected=None)

        self.assertEqual([expected_event], sched_events)
Example #3
0
 def test_with_newline_rn(self):
     """Ensure from_string copes with newlines"""
     expected = data_io.EventDatum(
         time=1, type="type",
         specific_datum={'pid': 'p1', 'comm': 'n1', 'cpu': 'c1'},
         connected=[('source_', 'dest_')])
     x = "1" + consts.field_separator + "type" + consts.field_separator + \
         "{'pid': 'p1', 'comm': 'n1', 'cpu': 'c1'}" + \
         consts.field_separator + "[('source_', 'dest_')]"
     self.check_from_str(x + '\r\n', expected)
Example #4
0
 def test_to_string(self):
     """Test datapoints are correctly converted to strings."""
     se = data_io.EventDatum(
         time=1, type="type",
         specific_datum={'pid': 'p1', 'comm': 'n1', 'cpu': 'c1'},
         connected=[('source_', 'dest_')])
     expected = "1" + consts.field_separator + "type" + consts.field_separator + \
         "{'pid': 'p1', 'comm': 'n1', 'cpu': 'c1'}" + \
         consts.field_separator + "[('source_', 'dest_')]"
     actual = str(se)
     self.assertEqual(expected, actual, msg='Expected {}, got {}'
                      .format(expected, actual))
Example #5
0
    def _normalise(self):
        """
        Normalises the times in the events so they start at 0

        Method that subtracts the minimum time from all the event times so that
        they start at 0. Since the EventDatum is imutable we can't just change
        the time, so we create new events with the new times.

        """
        for partition in self.event_partition.values():
            for idx, event in enumerate(partition):
                partition[idx] = data_io.EventDatum(event.time - self.min_time,
                                                    event.type,
                                                    event.specific_datum,
                                                    event.connected)
Example #6
0
    def test_generate_events_full_dict(self, output_mock):
        """
        Test _generate_events under normal operation, using all net namespaces

        """
        tracer = object.__new__(ebpf.TCPTracer)
        tracer.options = None
        data = StringIO(
            "header1\n"
            "time type pid comm   ip  s_addr d_addr s_port d_port size netns\n"
            "1    A    2   comm1  4   127.   127.   3      4         1     5\n"
            "6    B    7   comm2  4   127.   127.   4      3         1     5\n"
            "1    A    2   comm3  4   x      x      3      4         1     5\n"
        )
        port_dict = {
            4: {('pid1', 'test1')},
            3: {('pid2', 'test2'), ('pid3', 'test3')}
        }
        result = list(tracer._generate_events(data, port_dict))
        expected = [
            data_io.EventDatum(time=1,
                               type='A',
                               connected=[('source_', 'dest_')],
                               specific_datum={
                                   "source_pid": 2,
                                   "source_comm": 'comm1',
                                   "source_port": 3,
                                   "dest_pid": 'pid1',
                                   "dest_comm": 'test1',
                                   "dest_port": 4,
                                   "size": 1,
                                   "net_ns": 5
                               })
        ]
        self.assertEqual(expected, result)

        expected_errors = [
            asynctest.call(
                text="IPC: Too many destination port PIDs/comms found. "
                "Check log for details.",
                description="Too many destination port PIDs/comms found: "
                "Time: 6  Type: B  Source PID: 7  "
                "Source comm: comm2  Source port : 4  "
                "Dest (port, comm) pairs: "
                "[('pid2', 'test2'), ('pid3', 'test3')]  "
                "Net namespace: 5")
        ]
        output_mock.error_.assert_has_calls(expected_errors)
Example #7
0
    def _get_generator(self, raw_data):
        """ Convert raw data to standard datatypes and yield it """
        for event_data in raw_data:
            # e.g.   perf a  6997 [003] 363654.881950:       sched:sched_wakeup:

            event_data = event_data.strip()

            match = re.match(
                r"\s*"
                r"(?P<name>\S+(\s+\S+)*)\s+"
                r"(?P<pid>\d+)\s+"
                r"\[(?P<cpu>\d+)\]\s+"
                r"(?P<time>\d+.\d+):\s+"
                r"(?P<event>\S+)", event_data)

            # If it did not match, log it but continue
            if match is None:
                logger.error(
                    "Failed to parse event data: %s Expected "
                    "format: name pid cpu time event", event_data)
                continue

            # Convert time format to us. Perf output: [seconds].[us]
            time_str = match.group("time").split(".")
            time_int = int(time_str[0]) * 1000000 + int(time_str[1])

            specific_datum = {
                'pid': match.group("pid"),
                'comm': match.group('name'),
                'cpu': match.group('cpu')
            }

            # Connected is none to specify we have a standalone event with no
            # connections
            event = data_io.EventDatum(specific_datum=specific_datum,
                                       time=time_int,
                                       connected=None,
                                       type=match.group("event"))
            yield event
Example #8
0
    def _generate_events(self, data, port_lookup):
        """
        Generate EventDatum objects using tcptracer data and the port-mapping
        generated from that data by _generate_dict

        :param data:
            The raw tcptracer data.
        :param port_lookup:
            The port-mapping dictionary generated from the raw data by
            _generate_dict
        :return:
            A generator of :class:`EventDatum` objects.

        """
        # Skip header
        data.seek(0)
        data.readline()
        data.readline()

        for line in data:
            values = line.split()
            time = int(values[0])
            tcp_type = values[1]  # connect, accept, close, send or recv
            source_pid = int(values[2])
            source_comm = values[3]
            source_addr = values[5]
            dest_addr = values[6]
            source_port = int(values[7])
            dest_port = int(values[8])
            size = int(values[9])
            net_ns = int(values[10])

            # Discard external TCP
            if not source_addr.startswith("127.") or \
               not dest_addr.startswith("127.") or \
               self.options and self.options.net_ns != net_ns:
                continue

            # Get destination PIDs from port_lookup dictionary
            if dest_port not in port_lookup:
                output.error_(
                    text="IPC: Could not find destination port PID/comm. "
                    "Check log for details.",
                    description="Could not find destination port PID/comm: "
                    "Time: {}  Type: {}  Source PID: {}  "
                    "Source comm: {}  Source port : {}  "
                    "Dest port: {}  Net namespace: {}".format(
                        time, tcp_type, source_pid, source_comm, source_port,
                        dest_port, net_ns))
                continue

            dest_pids = port_lookup[dest_port]

            # Drop if there are multiple possible PIDs
            if len(dest_pids) != 1:
                output.error_(
                    text="IPC: Too many destination port PIDs/comms found. "
                    "Check log for details.",
                    description="Too many destination port PIDs/comms found: "
                    "Time: {}  Type: {}  Source PID: {}  "
                    "Source comm: {}  Source port : {}  "
                    "Dest (port, comm) pairs: {}  Net namespace: {}".format(
                        time, tcp_type, source_pid, source_comm, source_port,
                        str(sorted(dest_pids)), net_ns))
                continue

            dest_pid, dest_comm = dest_pids.pop()
            dest_pids.add((dest_pid, dest_comm))  # Ensure set isn't altered

            # Otherwise output event
            event = data_io.EventDatum(time=time,
                                       type=tcp_type,
                                       specific_datum={
                                           "source_pid": source_pid,
                                           "source_comm": source_comm,
                                           "source_port": source_port,
                                           "dest_pid": dest_pid,
                                           "dest_comm": dest_comm,
                                           "dest_port": dest_port,
                                           "size": size,
                                           "net_ns": net_ns
                                       },
                                       connected=[('source_', 'dest_')])

            yield event