Refactor utils from linuxautomaton/common into the common package
authorAntoine Busque <abusque@efficios.com>
Thu, 10 Mar 2016 19:24:14 +0000 (14:24 -0500)
committerAntoine Busque <abusque@efficios.com>
Thu, 10 Mar 2016 22:57:41 +0000 (17:57 -0500)
This refactor/clean-up targets the utility functions previously found
at linuxautomaton/common.py, despite them being used across all
packages. Unused methods have been culled, while others have been
merged, split, or simplified.

All methods are extensively documented. One of the main goals of this
refactor was to improve testability. Unit tests will therefore be
implemented for these methods shortly.

The methods have been pythonified quite a bit as well. Most now make
use of exceptions, in concordance with the python philosophy of EAFP
over LBYL.

Signed-off-by: Antoine Busque <abusque@efficios.com>
14 files changed:
lttnganalyses/cli/command.py
lttnganalyses/cli/io.py
lttnganalyses/cli/irq.py
lttnganalyses/cli/sched.py
lttnganalyses/common/format_utils.py
lttnganalyses/common/parse_utils.py [new file with mode: 0644]
lttnganalyses/common/time_utils.py [new file with mode: 0644]
lttnganalyses/common/trace_utils.py [new file with mode: 0644]
lttnganalyses/core/io.py
lttnganalyses/linuxautomaton/common.py [deleted file]
lttnganalyses/linuxautomaton/io.py
lttnganalyses/linuxautomaton/statedump.py
lttnganalyses/linuxautomaton/sv.py
tests/expected/iolatencytop.txt

index c1fb08363e308909010854d910f261b2c4d93ffb..bdf371eea6f0c0f838954d0a79883c9bde3a4ff4 100644 (file)
@@ -29,13 +29,12 @@ import re
 import sys
 import subprocess
 from babeltrace import TraceCollection
-from . import mi
-from .. import _version
-from . import progressbar
-from .. import __version__
-from ..common import version_utils
+from . import mi, progressbar
+from .. import _version, __version__
 from ..core import analysis
-from ..linuxautomaton import common
+from ..common import (
+    format_utils, parse_utils, time_utils, trace_utils, version_utils
+)
 from ..linuxautomaton import automaton
 
 
@@ -252,13 +251,18 @@ class Command:
         self._post_analysis()
 
     def _print_date(self, begin_ns, end_ns):
-        date = 'Timerange: [%s, %s]' % (
-            common.ns_to_hour_nsec(begin_ns, gmt=self._args.gmt,
-                                   multi_day=True),
-            common.ns_to_hour_nsec(end_ns, gmt=self._args.gmt,
-                                   multi_day=True))
+        time_range_str = format_utils.format_time_range(
+            begin_ns, end_ns, print_date=True, gmt=self._args.gmt
+        )
+        date = 'Timerange: {}'.format(time_range_str)
+
         self._print(date)
 
+    def _format_timestamp(self, timestamp):
+        return format_utils.format_timestamp(
+            timestamp, print_date=self._args.multi_day, gmt=self._args.gmt
+        )
+
     def _get_uniform_freq_values(self, durations):
         if self._args.uniform_step is not None:
             return (self._args.uniform_min, self._args.uniform_max,
@@ -288,7 +292,7 @@ class Command:
         refresh_period_ns = None
         if args.refresh is not None:
             try:
-                refresh_period_ns = common.duration_str_to_ns(args.refresh)
+                refresh_period_ns = parse_utils.parse_duration(args.refresh)
             except ValueError as e:
                 self._cmdline_error(str(e))
 
@@ -477,30 +481,36 @@ class Command:
         pass
 
     def _process_date_args(self):
-        def date_to_epoch_nsec(date):
-            ts = common.date_to_epoch_nsec(self._handles, date, self._args.gmt)
-            if ts is None:
-                self._cmdline_error('Invalid date format: "{}"'.format(date))
+        def parse_date(date):
+            try:
+                ts = parse_utils.parse_trace_collection_date(
+                    self._traces, date, self._args.gmt
+                )
+            except ValueError as e:
+                self._cmdline_error(str(e))
 
             return ts
 
-        self._args.multi_day = common.is_multi_day_trace_collection(
-            self._handles)
+        self._args.multi_day = trace_utils.is_multi_day_trace_collection(
+            self._traces
+        )
         begin_ts = None
         end_ts = None
 
         if self._args.timerange:
-            begin_ts, end_ts = common.extract_timerange(self._handles,
-                                                        self._args.timerange,
-                                                        self._args.gmt)
-            if None in [begin_ts, end_ts]:
-                self._cmdline_error(
-                    'Invalid time format: "{}"'.format(self._args.timerange))
+            try:
+                begin_ts, end_ts = (
+                    parse_utils.parse_trace_collection_time_range(
+                        self._traces, self._args.timerange, self._args.gmt
+                    )
+                )
+            except ValueError as e:
+                self._cmdline_error(str(e))
         else:
             if self._args.begin:
-                begin_ts = date_to_epoch_nsec(self._args.begin)
+                begin_ts = parse_date(self._args.begin)
             if self._args.end:
-                end_ts = date_to_epoch_nsec(self._args.end)
+                end_ts = parse_date(self._args.end)
 
                 # We have to check if timestamp_begin is None, which
                 # it always is in older versions of babeltrace. In
index e3da95a85d20055e379f7286e4b8ae997c82aa47..38c3cfa1d9f961f47bda0a029d6ae1d446809fde 100644 (file)
@@ -31,7 +31,6 @@ from . import termgraph
 from ..core import io
 from ..common import format_utils
 from .command import Command
-from ..linuxautomaton import common
 
 
 _UsageTables = collections.namedtuple('_UsageTables', [
@@ -668,7 +667,7 @@ class IoAnalysisCommand(Command):
             title='Disk Request Average Latency',
             label_header='Disk',
             unit='ms',
-            get_value=lambda row: row.rtps.value / common.NSEC_PER_MSEC,
+            get_value=lambda row: row.rtps.value / 1000000,
             get_label=lambda row: row.disk.name,
             data=result_table.rows
         )
@@ -908,13 +907,12 @@ class IoAnalysisCommand(Command):
 
     def _print_log_row(self, row):
         fmt = '{:<40} {:<16} {:>16} {:>11}  {:<24} {:<8} {:<14}'
-        begin_time = common.ns_to_hour_nsec(row.time_range.begin,
-                                            self._args.multi_day,
-                                            self._args.gmt)
-        end_time = common.ns_to_hour_nsec(row.time_range.end,
-                                          self._args.multi_day,
-                                          self._args.gmt)
-        time_range_str = '[' + begin_time + ',' + end_time + ']'
+        time_range_str = format_utils.format_time_range(
+            row.time_range.begin,
+            row.time_range.end,
+            self._args.multi_day,
+            self._args.gmt
+        )
         duration_str = '%0.03f' % row.duration.to_us()
 
         if type(row.size) is mi.Empty:
@@ -954,7 +952,7 @@ class IoAnalysisCommand(Command):
         print()
         fmt = '{} {} (usec)'
         print(fmt.format(result_table.title, result_table.subtitle))
-        header_fmt = '{:<19} {:<20} {:<16} {:<23} {:<5} {:<24} {:<8} {:<14}'
+        header_fmt = '{:<20} {:<20} {:<16} {:<23} {:<5} {:<24} {:<8} {:<14}'
         print(header_fmt.format(
             'Begin', 'End', 'Name', 'Duration (usec)', 'Size', 'Proc', 'PID',
             'Filename'))
index facbb2b8ebd5b767175a2b3b3b39e90b24ccda61..6f84456d045591f333dcee963033ec2eee3f082c 100644 (file)
@@ -30,7 +30,7 @@ from . import mi
 from . import termgraph
 from .command import Command
 from ..core import irq as core_irq
-from ..linuxautomaton import common, sv
+from ..linuxautomaton import sv
 
 
 class IrqAnalysisCommand(Command):
@@ -420,9 +420,6 @@ class IrqAnalysisCommand(Command):
 
         return hard_stats_table, soft_stats_table, freq_tables
 
-    def _ns_to_hour_nsec(self, ts):
-        return common.ns_to_hour_nsec(ts, self._args.multi_day, self._args.gmt)
-
     def _print_log(self, result_table):
         fmt = '[{:<18}, {:<18}] {:>15} {:>4}  {:<9} {:>4}  {:<22}'
         title_fmt = '{:<20} {:<19} {:>15} {:>4}  {:<9} {:>4}  {:<22}'
@@ -434,9 +431,9 @@ class IrqAnalysisCommand(Command):
             end_ts = timerange.end
 
             if type(row.raised_ts) is mi.Timestamp:
-                raised_fmt = ' (raised at %s)'
-                raised_ts = \
-                    raised_fmt % self._ns_to_hour_nsec(row.raised_ts.value)
+                raised_ts = ' (raised at {})'.format(
+                    self._format_timestamp(row.raised_ts.value)
+                )
             else:
                 raised_ts = ''
 
@@ -448,8 +445,8 @@ class IrqAnalysisCommand(Command):
             else:
                 irqtype = 'SoftIRQ'
 
-            print(fmt.format(self._ns_to_hour_nsec(begin_ts),
-                             self._ns_to_hour_nsec(end_ts),
+            print(fmt.format(self._format_timestamp(begin_ts),
+                             self._format_timestamp(end_ts),
                              '%0.03f' % ((end_ts - begin_ts) / 1000),
                              '%d' % cpu_id, irqtype, irq_do.nr,
                              irq_do.name + raised_ts))
index 52b529a3072d8abf6289647dc0770455d8e8b469..84a4aa8d42389a38dbcd8df3cfe3725143b30642 100644 (file)
@@ -26,12 +26,10 @@ import math
 import operator
 import statistics
 import collections
-from . import mi
-from . import termgraph
+from . import mi, termgraph
 from ..core import sched
 from .command import Command
 from ..common import format_utils
-from ..linuxautomaton import common
 
 
 _SchedStats = collections.namedtuple('_SchedStats', [
@@ -682,9 +680,6 @@ class SchedAnalysisCommand(Command):
 
         return statistics.stdev(sched_latencies)
 
-    def _ns_to_hour_nsec(self, ts):
-        return common.ns_to_hour_nsec(ts, self._args.multi_day, self._args.gmt)
-
     def _print_sched_events(self, result_table):
         fmt = '[{:<18}, {:<18}] {:>15} {:>10}  {:>3}   {:<25}  {:<25}'
         title_fmt = '{:<20} {:<19} {:>15} {:>10}  {:>3}   {:<25}  {:<25}'
@@ -707,8 +702,8 @@ class SchedAnalysisCommand(Command):
             else:
                 waker_str = '%s (%d)' % (waker_proc.name, waker_proc.tid)
 
-            print(fmt.format(self._ns_to_hour_nsec(wakeup_ts),
-                             self._ns_to_hour_nsec(switch_ts),
+            print(fmt.format(self._format_timestamp(wakeup_ts),
+                             self._format_timestamp(switch_ts),
                              '%0.03f' % (latency / 1000), prio,
                              target_cpu, wakee_str, waker_str))
 
index e74b092d3873de7a2ae8285b6c9cc79d55603258..41d956ba3c1ebea1407156575e0b88c7895ed445 100644 (file)
 # SOFTWARE.
 
 import math
+import socket
+import struct
+import time
+from .time_utils import NSEC_PER_SEC
 
 
 def format_size(size, binary_prefix=True):
-    """Convert an integral number of bytes to a human-readable string
+    """Convert an integral number of bytes to a human-readable string.
 
     Args:
-        size (int): a non-negative number of bytes
+        size (int): a non-negative number of bytes.
+
         binary_prefix (bool, optional): whether to use binary units
-            prefixes, over SI prefixes. default: True
+        prefixes, over SI prefixes (default: True).
 
     Returns:
-        The formatted string comprised of the size and units
+        The formatted string comprised of the size and units.
 
     Raises:
-        ValueError: if size < 0
+        ValueError: if size < 0.
     """
     if size < 0:
         raise ValueError('Cannot format negative size')
 
     if binary_prefix:
         base = 1024
-        units = ['  B', 'KiB', 'MiB', 'GiB','TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
+        units = ['  B', 'KiB', 'MiB', 'GiB', 'TiB', 'PiB', 'EiB', 'ZiB', 'YiB']
     else:
         base = 1000
         units = [' B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
@@ -67,11 +72,12 @@ def format_size(size, binary_prefix=True):
 
     return format_str.format(size, unit)
 
+
 def format_prio_list(prio_list):
-    """Format a list of prios into a string of unique prios with count
+    """Format a list of prios into a string of unique prios with count.
 
     Args:
-        prio_list: a list of PrioEvent objects
+        prio_list (list): a list of PrioEvent objects.
 
     Returns:
         The formatted string containing the unique priorities and
@@ -105,3 +111,99 @@ def format_prio_list(prio_list):
         prio_str += ']'
 
     return prio_str
+
+
+def format_timestamp(timestamp, print_date=False, gmt=False):
+    """Format a timestamp into a human-readable date string
+
+    Args:
+        timestamp (int): nanoseconds since epoch.
+
+        print_date (bool, optional): flag indicating whether to print
+        the full date or just the time of day (default: False).
+
+        gmt (bool, optional): flag indicating whether the timestamp is
+        in the local timezone or gmt (default: False).
+
+    Returns:
+        The formatted date string, containing either the full date or
+        just the time of day.
+    """
+    date_fmt = '{:04}-{:02}-{:02} '
+    time_fmt = '{:02}:{:02}:{:02}.{:09}'
+
+    if gmt:
+        date = time.gmtime(timestamp / NSEC_PER_SEC)
+    else:
+        date = time.localtime(timestamp / NSEC_PER_SEC)
+
+    formatted_ts = time_fmt.format(
+        date.tm_hour, date.tm_min, date.tm_sec,
+        timestamp % NSEC_PER_SEC
+    )
+
+    if print_date:
+        date_str = date_fmt.format(date.tm_year, date.tm_mon, date.tm_mday)
+        formatted_ts = date_str + formatted_ts
+
+    return formatted_ts
+
+
+def format_time_range(begin_ts, end_ts, print_date=False, gmt=False):
+    """Format a pair of timestamps into a human-readable date string.
+
+    Args:
+        begin_ts (int): nanoseconds since epoch to beginning of
+        time range.
+
+        end_ts (int): nanoseconds since epoch to end of time range.
+
+        print_date (bool, optional): flag indicating whether to print
+        the full date or just the time of day (default: False).
+
+        gmt (bool, optional): flag indicating whether the timestamp is
+        in the local timezone or gmt (default: False).
+
+    Returns:
+        The formatted dates string, containing either the full date or
+        just the time of day, enclosed within square brackets and
+        delimited by a comma.
+    """
+    time_range_fmt = '[{}, {}]'
+
+    begin_str = format_timestamp(begin_ts, print_date, gmt)
+    end_str = format_timestamp(end_ts, print_date, gmt)
+
+    return time_range_fmt.format(begin_str, end_str)
+
+
+def format_ipv4(ip, port=None):
+    """Format an ipv4 address into a human-readable string.
+
+    Args:
+        ip (varies): the ip address as extracted in an LTTng event.
+        Either an integer or a list of integers, depending on the
+        tracer version.
+
+        port (int, optional): the port number associated with the
+        address.
+
+    Returns:
+        The formatted string containing the ipv4 address and, optionally,
+        the port number.
+
+    """
+    # depending on the version of lttng-modules, the v4addr is an
+    # integer (< 2.6) or sequence (>= 2.6)
+    try:
+        ip_str = '{}.{}.{}.{}'.format(ip[0], ip[1], ip[2], ip[3])
+    except TypeError:
+        # The format string '!I' tells pack to interpret ip as a
+        # packed structure of network-endian 32-bit unsigned integers,
+        # which inet_ntoa can then convert into the formatted string
+        ip_str = socket.inet_ntoa(struct.pack('!I', ip))
+
+    if port is not None:
+        ip_str += ':{}'.format(port)
+
+    return ip_str
diff --git a/lttnganalyses/common/parse_utils.py b/lttnganalyses/common/parse_utils.py
new file mode 100644 (file)
index 0000000..b8aebaa
--- /dev/null
@@ -0,0 +1,468 @@
+# The MIT License (MIT)
+#
+# Copyright (C) 2016 - Antoine Busque <abusque@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import datetime
+import math
+import re
+from time import timezone
+from . import trace_utils
+from .time_utils import NSEC_PER_SEC
+
+
+def _split_value_units(raw_str):
+    """Take a string with a numerical value and units, and separate the
+    two.
+
+    Args:
+        raw_str (str): the string to parse, with numerical value and
+        (optionally) units.
+
+    Returns:
+        A tuple (value, units), where value is a string and units is
+        either a string or `None` if no units were found.
+    """
+    try:
+        units_index = next(i for i, c in enumerate(raw_str) if c.isalpha())
+    except StopIteration:
+        # no units found
+        return (raw_str, None)
+
+    return (raw_str[:units_index], raw_str[units_index:])
+
+
+def parse_size(size_str):
+    """Convert a human-readable size string to an integral number of
+    bytes.
+
+    Args:
+        size_str (str): the formatted string comprised of the size and
+        units.
+
+    Returns:
+        A non-negative number of bytes.
+
+    Raises:
+        ValueError: if units are unrecognised or the size is not a
+        real number.
+    """
+    binary_units = ['B', 'KiB', 'MiB', 'GiB', 'TiB',
+                    'PiB', 'EiB', 'ZiB', 'YiB']
+    # units as printed by GNU coreutils (e.g. ls or du), using base
+    # 1024 as well
+    coreutils_units = ['B', 'K', 'M', 'G', 'T', 'P', 'E', 'Z', 'Y']
+    si_units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB', 'EB', 'ZB', 'YB']
+
+    size, units = _split_value_units(size_str)
+
+    try:
+        size = float(size)
+    except ValueError:
+        raise ValueError('invalid size: {}'.format(size))
+
+    # If no units have been found, assume bytes
+    if units is not None:
+        if units in binary_units:
+            base = 1024
+            exponent = binary_units.index(units)
+        elif units in coreutils_units:
+            base = 1024
+            exponent = coreutils_units.index(units)
+        elif units in si_units:
+            base = 1000
+            exponent = si_units.index(units)
+        else:
+            raise ValueError('unrecognised units: {}'.format(units))
+
+        size *= math.pow(base, exponent)
+
+    return int(size)
+
+
+def parse_duration(duration_str):
+    """Convert a human-readable duration string to an integral number of
+    nanoseconds.
+
+    Args:
+        duration_str (str): the formatted string comprised of the
+        duration and units.
+
+    Returns:
+        A non-negative number of nanoseconds.
+
+    Raises:
+        ValueError: if units are unrecognised or the size is not a
+        real number.
+    """
+    base = 1000
+
+    try:
+        units_index = next(i for i, c in enumerate(duration_str)
+                           if c.isalpha())
+    except StopIteration:
+        # no units found
+        units_index = None
+
+    if units_index is not None:
+        duration = duration_str[:units_index]
+        units = duration_str[units_index:].lower()
+    else:
+        duration = duration_str
+        units = None
+
+    try:
+        duration = float(duration)
+    except ValueError:
+        raise ValueError('invalid duration: {}'.format(duration))
+
+    if units is not None:
+        if units == 's':
+            exponent = 3
+        elif units == 'ms':
+            exponent = 2
+        elif units in ['us', 'µs']:
+            exponent = 1
+        elif units == 'ns':
+            exponent = 0
+        else:
+            raise ValueError('unrecognised units: {}'.format(units))
+    else:
+        # no units defaults to seconds
+        exponent = 3
+
+    duration *= math.pow(base, exponent)
+
+    return int(duration)
+
+
+def _parse_date_full_with_nsec(date):
+    """Parse full date string with nanosecond resolution.
+
+    This matches either 2014-12-12 17:29:43.802588035 or
+    2014-12-12T17:29:43.802588035.
+
+    Args:
+        date (str): the date string to be parsed.
+
+    Returns:
+        A tuple of the format (date_time, nsec), where date_time is a
+        datetime.datetime object and nsec is an int of the remaining
+        nanoseconds.
+
+    Raises:
+        ValueError: if the date format does not match.
+    """
+    pattern = re.compile(
+        r'^(?P<year>\d{4})-(?P<mon>[01]\d)-(?P<day>[0-3]\d)[\sTt]'
+        r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})\.(?P<nsec>\d{9})$'
+    )
+
+    if not pattern.match(date):
+        raise ValueError('Wrong date format: {}'.format(date))
+
+    year = pattern.search(date).group('year')
+    month = pattern.search(date).group('mon')
+    day = pattern.search(date).group('day')
+    hour = pattern.search(date).group('hour')
+    minute = pattern.search(date).group('min')
+    sec = pattern.search(date).group('sec')
+    nsec = pattern.search(date).group('nsec')
+
+    date_time = datetime.datetime(
+        int(year), int(month), int(day),
+        int(hour), int(minute), int(sec)
+    )
+
+    return date_time, int(nsec)
+
+
+def _parse_date_full(date):
+    """Parse full date string.
+
+    This matches either 2014-12-12 17:29:43 or 2014-12-12T17:29:43.
+
+    Args:
+        date (str): the date string to be parsed.
+
+    Returns:
+        A tuple of the format (date_time, nsec), where date_time is a
+        datetime.datetime object and nsec is 0.
+
+    Raises:
+        ValueError: if the date format does not match.
+    """
+    pattern = re.compile(
+        r'^(?P<year>\d{4})-(?P<mon>[01]\d)-(?P<day>[0-3]\d)[\sTt]'
+        r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})$'
+    )
+
+    if not pattern.match(date):
+        raise ValueError('Wrong date format: {}'.format(date))
+
+    year = pattern.search(date).group('year')
+    month = pattern.search(date).group('mon')
+    day = pattern.search(date).group('day')
+    hour = pattern.search(date).group('hour')
+    minute = pattern.search(date).group('min')
+    sec = pattern.search(date).group('sec')
+    nsec = 0
+
+    date_time = datetime.datetime(
+        int(year), int(month), int(day),
+        int(hour), int(minute), int(sec)
+    )
+
+    return date_time, nsec
+
+
+def _parse_date_time_with_nsec(date):
+    """Parse time string with nanosecond resolution.
+
+    This matches 17:29:43.802588035.
+
+    Args:
+        date (str): the date string to be parsed.
+
+    Returns:
+        A tuple of the format (date_time, nsec), where date_time is a
+        datetime.time object and nsec is an int of the remaining
+        nanoseconds.
+
+    Raises:
+        ValueError: if the date format does not match.
+    """
+    pattern = re.compile(
+        r'^(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})\.(?P<nsec>\d{9})$'
+    )
+
+    if not pattern.match(date):
+        raise ValueError('Wrong date format: {}'.format(date))
+
+    hour = pattern.search(date).group('hour')
+    minute = pattern.search(date).group('min')
+    sec = pattern.search(date).group('sec')
+    nsec = pattern.search(date).group('nsec')
+
+    time = datetime.time(int(hour), int(minute), int(sec))
+
+    return time, int(nsec)
+
+
+def _parse_date_time(date):
+    """Parse time string.
+
+    This matches 17:29:43.
+
+    Args:
+        date (str): the date string to be parsed.
+
+    Returns:
+        A tuple of the format (date_time, nsec), where date_time is a
+        datetime.time object and nsec is 0.
+
+    Raises:
+        ValueError: if the date format does not match.
+    """
+    pattern = re.compile(
+        r'^(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})$'
+    )
+
+    if not pattern.match(date):
+        raise ValueError('Wrong date format: {}'.format(date))
+
+    hour = pattern.search(date).group('hour')
+    minute = pattern.search(date).group('min')
+    sec = pattern.search(date).group('sec')
+    nsec = 0
+
+    time = datetime.time(int(hour), int(minute), int(sec))
+
+    return time, nsec
+
+
+def _parse_date_timestamp(date):
+    """Parse timestamp string in nanoseconds from epoch.
+
+    This matches 93847238974923874.
+
+    Args:
+        date (str): the date string to be parsed.
+
+    Returns:
+        A tuple of the format (date_time, nsec), where date_time is a
+        datetime.datetime object and nsec is an int of the remaining
+        nanoseconds.
+
+    Raises:
+        ValueError: if the date format does not match.
+    """
+    pattern = re.compile(r'^\d+$')
+
+    if not pattern.match(date):
+        raise ValueError('Wrong date format: {}'.format(date))
+
+    timestamp_ns = int(date)
+
+    date_time = datetime.datetime.fromtimestamp(
+        timestamp_ns / NSEC_PER_SEC
+    )
+    nsec = timestamp_ns % NSEC_PER_SEC
+
+    return date_time, nsec
+
+
+def parse_date(date):
+    """Try to parse a date string from one of many formats.
+
+    Args:
+        date (str): the date string to be parsed.
+
+    Returns:
+        A tuple of the format (date_time, nsec), where date_time is
+        one of either datetime.datetime or datetime.time, depending on
+        whether the date string contains full date information or only
+        the time of day. The latter case can still be useful when used
+        in conjuction with a trace collection's date to provide the
+        missing information. The nsec element of the tuple is an int and
+        corresponds to the nanoseconds for the given date/timestamp.
+        This is due to datetime objects only supporting a resolution
+        down to the microsecond.
+
+    Raises:
+        ValueError: if the date does not correspond to any of the
+        supported formats.
+    """
+    parsers = [
+        _parse_date_full_with_nsec, _parse_date_full,
+        _parse_date_time_with_nsec, _parse_date_time,
+        _parse_date_timestamp
+    ]
+
+    date_time = None
+    nsec = None
+
+    for parser in parsers:
+        try:
+            (date_time, nsec) = parser(date)
+        except ValueError:
+            continue
+
+        # If no exception was raised, the parser found a match, so
+        # stop iterating
+        break
+
+    if date_time is None or nsec is None:
+        # None of the parsers were a match
+        raise ValueError('Unrecognised date format: {}'.format(date))
+
+    return date_time, nsec
+
+
+def parse_trace_collection_date(collection, date, gmt=False):
+    """Parse a date string, using a trace collection to disambiguate
+    incomplete dates.
+
+    Args:
+        collection (TraceCollection): a babeltrace TraceCollection
+        instance.
+
+        date (string): the date string to be parsed.
+
+        gmt (bool, optional): flag indicating whether the timestamp is
+        in the local timezone or gmt (default: False).
+
+    Returns:
+        A timestamp (int) in nanoseconds since epoch, corresponding to
+        the parsed date.
+
+    Raises:
+        ValueError: if the date format is unrecognised, or if the date
+        format does not specify the date and the trace collection spans
+        multiple days.
+    """
+    try:
+        date_time, nsec = parse_date(date)
+    except ValueError:
+        # This might raise ValueError if the date is in an invalid
+        # format, so just re-raise the exception to inform the caller
+        # of the problem.
+        raise
+
+    # date_time will either be an actual datetime.datetime object, or
+    # just a datetime.time object, depending on the format. In the
+    # latter case, try and fill out the missing date information from
+    # the trace collection's date.
+    if isinstance(date_time, datetime.time):
+        try:
+            collection_date = trace_utils.get_trace_collection_date(collection)
+        except ValueError:
+            raise ValueError(
+                'Invalid date format for multi-day trace: {}'.format(date)
+            )
+
+        date_time = datetime.datetime.combine(collection_date, date_time)
+
+    if gmt:
+        date_time = date_time + datetime.timedelta(seconds=timezone)
+
+    timestamp_ns = date_time.timestamp() * NSEC_PER_SEC + nsec
+
+    return timestamp_ns
+
+
+def parse_trace_collection_time_range(collection, time_range, gmt=False):
+    """Parse a time range string, using a trace collection to
+    disambiguate incomplete dates.
+
+    Args:
+        collection (TraceCollection): a babeltrace TraceCollection
+        instance.
+
+        time_range (string): the time range string to be parsed.
+
+        gmt (bool, optional): flag indicating whether the timestamps are
+        in the local timezone or gmt (default: False).
+
+    Returns:
+        A tuple (begin, end) of the two timestamps (int) in nanoseconds
+        since epoch, corresponding to the parsed dates.
+
+    Raises:
+        ValueError: if the time range or date format is unrecognised,
+        or if the date format does not specify the date and the trace
+        collection spans multiple days.
+    """
+    pattern = re.compile(r'^\[(?P<begin>.*),(?P<end>.*)\]$')
+    if not pattern.match(time_range):
+        raise ValueError('Invalid time range format: {}'.format(time_range))
+
+    begin_str = pattern.search(time_range).group('begin').strip()
+    end_str = pattern.search(time_range).group('end').strip()
+
+    try:
+        begin = parse_trace_collection_date(collection, begin_str, gmt)
+        end = parse_trace_collection_date(collection, end_str, gmt)
+    except ValueError:
+        # Either of the dates was in the wrong format, propagate the
+        # exception to the caller.
+        raise
+
+    return begin, end
diff --git a/lttnganalyses/common/time_utils.py b/lttnganalyses/common/time_utils.py
new file mode 100644 (file)
index 0000000..591fbe7
--- /dev/null
@@ -0,0 +1,23 @@
+# The MIT License (MIT)
+#
+# Copyright (C) 2016 - Antoine Busque <abusque@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+NSEC_PER_SEC = 1000000000
diff --git a/lttnganalyses/common/trace_utils.py b/lttnganalyses/common/trace_utils.py
new file mode 100644 (file)
index 0000000..f693ecb
--- /dev/null
@@ -0,0 +1,92 @@
+# The MIT License (MIT)
+#
+# Copyright (C) 2016 - Antoine Busque <abusque@efficios.com>
+#
+# Permission is hereby granted, free of charge, to any person obtaining a copy
+# of this software and associated documentation files (the "Software"), to deal
+# in the Software without restriction, including without limitation the rights
+# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+# copies of the Software, and to permit persons to whom the Software is
+# furnished to do so, subject to the following conditions:
+#
+# The above copyright notice and this permission notice shall be included in
+# all copies or substantial portions of the Software.
+#
+# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
+# SOFTWARE.
+
+import datetime
+from .time_utils import NSEC_PER_SEC
+
+
+def is_multi_day_trace_collection(collection):
+    """Check whether a trace collection spans more than one day.
+
+    Args:
+        collection (TraceCollection): a babeltrace TraceCollection
+        instance.
+
+    Returns:
+        True if the trace collection spans more than one day,
+        False otherwise.
+    """
+    date_begin = datetime.date.fromtimestamp(
+        collection.timestamp_begin / NSEC_PER_SEC
+    )
+    date_end = datetime.date.fromtimestamp(
+        collection.timestamp_end / NSEC_PER_SEC
+    )
+
+    return date_begin != date_end
+
+
+def get_trace_collection_date(collection):
+    """Get a trace collection's date.
+
+    Args:
+        collection (TraceCollection): a babeltrace TraceCollection
+        instance.
+
+    Returns:
+        A datetime.date object corresponding to the date at which the
+        trace collection was recorded.
+
+    Raises:
+        ValueError: if the trace collection spans more than one day.
+    """
+    if is_multi_day_trace_collection(collection):
+        raise ValueError('Trace collection spans multiple days')
+
+    trace_date = datetime.date.fromtimestamp(
+        collection.timestamp_begin / NSEC_PER_SEC
+    )
+
+    return trace_date
+
+
+def get_syscall_name(event):
+    """Get the name of a syscall from an event.
+
+    Args:
+        event (Event): an instance of a babeltrace Event for a syscall
+        entry.
+
+    Returns:
+        The name of the syscall, stripped of any superfluous prefix.
+
+    Raises:
+        ValueError: if the event is not a syscall event.
+    """
+    name = event.name
+
+    if name.startswith('sys_'):
+        return name[4:]
+    elif name.startswith('syscall_entry_'):
+        return name[14:]
+    else:
+        raise ValueError('Not a syscall event')
index 6ed7434207346445cd3e732eff1656008ae9fd31..b33a41d47865c59908324b541680ebca98f9a27f 100644 (file)
@@ -100,7 +100,7 @@ class IoAnalysis(Analysis):
         return self._get_io_requests(sv.IORequest.OP_READ_WRITE)
 
     def _get_io_requests(self, io_operation=None):
-        """Create a generator of syscall io requests by operation
+        """Create a generator of syscall io requests by operation.
 
         Args:
             io_operation (IORequest.OP_*, optional): The operation of
@@ -461,7 +461,7 @@ class ProcessIOStats(stats.Process):
 
     @staticmethod
     def _get_fd_by_timestamp(fd_list, timestamp):
-        """Return the FDStats object whose lifetime contains timestamp
+        """Return the FDStats object whose lifetime contains timestamp.
 
         This method performs a recursive binary search on the given
         fd_list argument, and will find the FDStats object for which
@@ -470,7 +470,7 @@ class ProcessIOStats(stats.Process):
 
         Args:
             fd_list (list): list of FDStats object, sorted
-            chronologically by open_ts
+            chronologically by open_ts.
 
             timestamp (int): timestamp in nanoseconds (ns) since unix
             epoch which should be contained in the FD's lifetime.
diff --git a/lttnganalyses/linuxautomaton/common.py b/lttnganalyses/linuxautomaton/common.py
deleted file mode 100644 (file)
index 17b4dca..0000000
+++ /dev/null
@@ -1,305 +0,0 @@
-# The MIT License (MIT)
-#
-# Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
-#
-# Permission is hereby granted, free of charge, to any person obtaining a copy
-# of this software and associated documentation files (the "Software"), to deal
-# in the Software without restriction, including without limitation the rights
-# to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
-# copies of the Software, and to permit persons to whom the Software is
-# furnished to do so, subject to the following conditions:
-#
-# The above copyright notice and this permission notice shall be included in
-# all copies or substantial portions of the Software.
-#
-# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
-# IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
-# FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
-# AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
-# LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
-# OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
-# SOFTWARE.
-
-import re
-import time
-import datetime
-import socket
-import struct
-
-NSEC_PER_SEC = 1000000000
-NSEC_PER_MSEC = 1000000
-NSEC_PER_USEC = 1000
-
-BYTES_PER_TIB = 1099511627776
-BYTES_PER_GIB = 1073741824
-BYTES_PER_MIB = 1048576
-BYTES_PER_KIB = 1024
-
-O_CLOEXEC = 0o2000000
-
-
-def get_syscall_name(event):
-    name = event.name
-
-    if name.startswith('sys_'):
-        # Strip first 4 because sys_ is 4 chars long
-        return name[4:]
-
-    # Name begins with syscall_entry_ (14 chars long)
-    return name[14:]
-
-
-def is_multi_day_trace_collection(handles):
-    time_begin = None
-
-    for handle in handles.values():
-        if time_begin is None:
-            time_begin = time.localtime(handle.timestamp_begin / NSEC_PER_SEC)
-            year_begin = time_begin.tm_year
-            month_begin = time_begin.tm_mon
-            day_begin = time_begin.tm_mday
-
-        time_end = time.localtime(handle.timestamp_end / NSEC_PER_SEC)
-        year_end = time_end.tm_year
-        month_end = time_end.tm_mon
-        day_end = time_end.tm_mday
-
-        if year_begin != year_end:
-            return True
-        elif month_begin != month_end:
-            return True
-        elif day_begin != day_end:
-            return True
-
-    return False
-
-
-def trace_collection_date(handles):
-    if is_multi_day_trace_collection(handles):
-        return None
-
-    for handle in handles.values():
-        trace_time = time.localtime(handle.timestamp_begin / NSEC_PER_SEC)
-        year = trace_time.tm_year
-        month = trace_time.tm_mon
-        day = trace_time.tm_mday
-        return (year, month, day)
-
-
-def extract_timerange(handles, timerange, gmt):
-    pattern = re.compile(r'^\[(?P<begin>.*),(?P<end>.*)\]$')
-    if not pattern.match(timerange):
-        return None, None
-    begin_str = pattern.search(timerange).group('begin').strip()
-    end_str = pattern.search(timerange).group('end').strip()
-    begin = date_to_epoch_nsec(handles, begin_str, gmt)
-    end = date_to_epoch_nsec(handles, end_str, gmt)
-    return (begin, end)
-
-
-def date_to_epoch_nsec(handles, date, gmt):
-    # match 2014-12-12 17:29:43.802588035 or 2014-12-12T17:29:43.802588035
-    pattern1 = re.compile(r'^(?P<year>\d{4})-(?P<mon>[01]\d)-'
-                          r'(?P<day>[0-3]\d)[\sTt]'
-                          r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})\.'
-                          r'(?P<nsec>\d{9})$')
-    # match 2014-12-12 17:29:43 or 2014-12-12T17:29:43
-    pattern2 = re.compile(r'^(?P<year>\d{4})-(?P<mon>[01]\d)-'
-                          r'(?P<day>[0-3]\d)[\sTt]'
-                          r'(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})$')
-    # match 17:29:43.802588035
-    pattern3 = re.compile(r'^(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})\.'
-                          r'(?P<nsec>\d{9})$')
-    # match 17:29:43
-    pattern4 = re.compile(r'^(?P<hour>\d{2}):(?P<min>\d{2}):(?P<sec>\d{2})$')
-
-    # match 93847238974923874
-    pattern5 = re.compile(r'^\d+$')
-
-    if pattern1.match(date):
-        year = pattern1.search(date).group('year')
-        month = pattern1.search(date).group('mon')
-        day = pattern1.search(date).group('day')
-        hour = pattern1.search(date).group('hour')
-        minute = pattern1.search(date).group('min')
-        sec = pattern1.search(date).group('sec')
-        nsec = pattern1.search(date).group('nsec')
-    elif pattern2.match(date):
-        year = pattern2.search(date).group('year')
-        month = pattern2.search(date).group('mon')
-        day = pattern2.search(date).group('day')
-        hour = pattern2.search(date).group('hour')
-        minute = pattern2.search(date).group('min')
-        sec = pattern2.search(date).group('sec')
-        nsec = 0
-    elif pattern3.match(date):
-        collection_date = trace_collection_date(handles)
-        if collection_date is None:
-            print("Use the format 'yyyy-mm-dd hh:mm:ss[.nnnnnnnnn]' "
-                  "for multi-day traces")
-            return None
-        (year, month, day) = collection_date
-        hour = pattern3.search(date).group('hour')
-        minute = pattern3.search(date).group('min')
-        sec = pattern3.search(date).group('sec')
-        nsec = pattern3.search(date).group('nsec')
-    elif pattern4.match(date):
-        collection_date = trace_collection_date(handles)
-        if collection_date is None:
-            print("Use the format 'yyyy-mm-dd hh:mm:ss[.nnnnnnnnn]' "
-                  "for multi-day traces")
-            return None
-        (year, month, day) = collection_date
-        hour = pattern4.search(date).group('hour')
-        minute = pattern4.search(date).group('min')
-        sec = pattern4.search(date).group('sec')
-        nsec = 0
-    elif pattern5.match(date):
-        return int(date)
-    else:
-        return None
-
-    date_time = datetime.datetime(int(year), int(month), int(day), int(hour),
-                                  int(minute), int(sec))
-    if gmt:
-        date_time = date_time + datetime.timedelta(seconds=time.timezone)
-    return int(date_time.timestamp()) * NSEC_PER_SEC + int(nsec)
-
-
-def ns_to_asctime(ns):
-    return time.asctime(time.localtime(ns/NSEC_PER_SEC))
-
-
-def ns_to_hour(ns):
-    date = time.localtime(ns / NSEC_PER_SEC)
-    return '%02d:%02d:%02d' % (date.tm_hour, date.tm_min, date.tm_sec)
-
-
-def ns_to_hour_nsec(ns, multi_day=False, gmt=False):
-    if gmt:
-        date = time.gmtime(ns / NSEC_PER_SEC)
-    else:
-        date = time.localtime(ns / NSEC_PER_SEC)
-    if multi_day:
-        return ('%04d-%02d-%02d %02d:%02d:%02d.%09d' %
-                (date.tm_year, date.tm_mon, date.tm_mday, date.tm_hour,
-                 date.tm_min, date.tm_sec, ns % NSEC_PER_SEC))
-    else:
-        return ('%02d:%02d:%02d.%09d' %
-                (date.tm_hour, date.tm_min, date.tm_sec, ns % NSEC_PER_SEC))
-
-
-def ns_to_sec(ns):
-    return '%lu.%09u' % (ns / NSEC_PER_SEC, ns % NSEC_PER_SEC)
-
-
-def ns_to_day(ns):
-    date = time.localtime(ns/NSEC_PER_SEC)
-    return '%04d-%02d-%02d' % (date.tm_year, date.tm_mon, date.tm_mday)
-
-
-def sec_to_hour(ns):
-    date = time.localtime(ns)
-    return '%02d:%02d:%02d' % (date.tm_hour, date.tm_min, date.tm_sec)
-
-
-def sec_to_nsec(sec):
-    return sec * NSEC_PER_SEC
-
-
-def seq_to_ipv4(ip):
-    return '{}.{}.{}.{}'.format(ip[0], ip[1], ip[2], ip[3])
-
-
-def int_to_ipv4(ip):
-    return socket.inet_ntoa(struct.pack('!I', ip))
-
-
-def size_str_to_bytes(size_str):
-    try:
-        units_index = next(i for i, c in enumerate(size_str) if c.isalpha())
-    except StopIteration:
-        # no units found
-        units_index = None
-
-    if units_index is not None:
-        size = size_str[:units_index]
-        units = size_str[units_index:]
-    else:
-        size = size_str
-        units = None
-
-    try:
-        size = float(size)
-    except ValueError:
-        raise ValueError('invalid size: {}'.format(size))
-
-    # no units defaults to bytes
-    if units is not None:
-        if units in ['t', 'T', 'tB', 'TB']:
-            size *= BYTES_PER_TIB
-        elif units in ['g', 'G', 'gB', 'GB']:
-            size *= BYTES_PER_GIB
-        elif units in ['m', 'M', 'mB', 'MB']:
-            size *= BYTES_PER_MIB
-        elif units in ['k', 'K', 'kB', 'KB']:
-            size *= BYTES_PER_KIB
-        elif units == 'B':
-            # bytes is already the target unit
-            pass
-        else:
-            raise ValueError('unrecognised units: {}'.format(units))
-
-    size = int(size)
-
-    return size
-
-
-def duration_str_to_ns(duration_str):
-    try:
-        units_index = next(i for i, c in enumerate(duration_str)
-                           if c.isalpha())
-    except StopIteration:
-        # no units found
-        units_index = None
-
-    if units_index is not None:
-        duration = duration_str[:units_index]
-        units = duration_str[units_index:].lower()
-    else:
-        duration = duration_str
-        units = None
-
-    try:
-        duration = float(duration)
-    except ValueError:
-        raise ValueError('invalid duration: {}'.format(duration))
-
-    if units is not None:
-        if units == 's':
-            duration *= NSEC_PER_SEC
-        elif units == 'ms':
-            duration *= NSEC_PER_MSEC
-        elif units in ['us', 'µs']:
-            duration *= NSEC_PER_USEC
-        elif units == 'ns':
-            # ns is already the target unit
-            pass
-        else:
-            raise ValueError('unrecognised units: {}'.format(units))
-    else:
-        # no units defaults to seconds
-        duration *= NSEC_PER_SEC
-
-    duration = int(duration)
-
-    return duration
-
-
-def get_v4_addr_str(ip):
-    # depending on the version of lttng-modules, the v4addr is a
-    # string (< 2.6) or sequence (>= 2.6)
-    try:
-        return seq_to_ipv4(ip)
-    except TypeError:
-        return int_to_ipv4(ip)
index c6692c962a0158f6340b524158962ead4fa8a184..e05697fc7062a204a1ec05348dad244e1eb321ed 100644 (file)
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import os
 import socket
 from babeltrace import CTFScope
-from . import sp, sv, common
+from . import sp, sv
+from ..common import format_utils, trace_utils
 
 
 class IoStateProvider(sp.StateProvider):
@@ -41,7 +43,7 @@ class IoStateProvider(sp.StateProvider):
 
     def _process_syscall_entry(self, event):
         # Only handle IO Syscalls
-        name = common.get_syscall_name(event)
+        name = trace_utils.get_syscall_name(event)
         if name not in sv.SyscallConsts.IO_SYSCALLS:
             return
 
@@ -106,9 +108,9 @@ class IoStateProvider(sp.StateProvider):
         if 'family' in event and event['family'] == socket.AF_INET:
             fd = event['fd']
             if fd in parent_proc.fds:
-                parent_proc.fds[fd].filename = (
-                    '%s:%d' % (common.get_v4_addr_str(event['v4addr']),
-                               event['dport']))
+                parent_proc.fds[fd].filename = format_utils.format_ipv4(
+                    event['v4addr'], event['dport']
+                )
 
     def _process_writeback_pages_written(self, event):
         for cpu in self._state.cpus.values():
@@ -203,7 +205,7 @@ class IoStateProvider(sp.StateProvider):
             event, proc.tid, old_file)
 
         if name == 'dup3':
-            cloexec = event['flags'] & common.O_CLOEXEC == common.O_CLOEXEC
+            cloexec = event['flags'] & os.O_CLOEXEC == os.O_CLOEXEC
             current_syscall.io_rq.cloexec = cloexec
 
     def _track_close(self, event, name, proc):
index 9fe789450bfb2a1e7ff59caae04b9f2bf18f325e..2dc0c08996d9fdafaca2e867a767fd3b714c3b11 100644 (file)
@@ -21,7 +21,8 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
-from . import sp, sv, common
+import os
+from . import sp, sv
 
 
 class StatedumpStateProvider(sp.StateProvider):
@@ -77,7 +78,7 @@ class StatedumpStateProvider(sp.StateProvider):
         pid = event['pid']
         fd = event['fd']
         filename = event['filename']
-        cloexec = event['flags'] & common.O_CLOEXEC == common.O_CLOEXEC
+        cloexec = event['flags'] & os.O_CLOEXEC == os.O_CLOEXEC
 
         if pid not in self._state.tids:
             self._state.tids[pid] = sv.Process(tid=pid, pid=pid)
index f77c25dc5c2fec0a1f338da1109c44352db70453..5cead4a90c0cd005a986c8abb9337dc2645d8c55 100644 (file)
@@ -21,8 +21,9 @@
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
+import os
 import socket
-from . import common
+from ..common import format_utils, trace_utils
 
 
 class Process():
@@ -79,7 +80,7 @@ class SyscallEvent():
 
     @classmethod
     def new_from_entry(cls, event):
-        name = common.get_syscall_name(event)
+        name = trace_utils.get_syscall_name(event)
         return cls(name, event.timestamp)
 
 
@@ -258,11 +259,11 @@ class OpenIORequest(SyscallIORequest):
     @classmethod
     def new_from_disk_open(cls, event, tid):
         begin_ts = event.timestamp
-        name = common.get_syscall_name(event)
+        name = trace_utils.get_syscall_name(event)
         filename = event['filename']
 
         req = cls(begin_ts, tid, name, filename, FDType.disk)
-        req.cloexec = event['flags'] & common.O_CLOEXEC == common.O_CLOEXEC
+        req.cloexec = event['flags'] & os.O_CLOEXEC == os.O_CLOEXEC
 
         return req
 
@@ -270,15 +271,16 @@ class OpenIORequest(SyscallIORequest):
     def new_from_accept(cls, event, tid):
         # Handle both accept and accept4
         begin_ts = event.timestamp
-        name = common.get_syscall_name(event)
+        name = trace_utils.get_syscall_name(event)
         req = cls(begin_ts, tid, name, 'socket', FDType.net)
 
         if 'family' in event:
             req.family = event['family']
             # Set filename to ip:port if INET socket
             if req.family == socket.AF_INET:
-                req.filename = '%s:%d' % (common.get_v4_addr_str(
-                    event['v4addr']), event['sport'])
+                req.filename = format_utils.format_ipv4(
+                    event['v4addr'], event['sport']
+                )
 
         return req
 
@@ -295,7 +297,7 @@ class OpenIORequest(SyscallIORequest):
     @classmethod
     def new_from_old_fd(cls, event, tid, old_fd):
         begin_ts = event.timestamp
-        name = common.get_syscall_name(event)
+        name = trace_utils.get_syscall_name(event)
         if old_fd is None:
             filename = 'unknown'
             fd_type = FDType.unknown
@@ -363,7 +365,7 @@ class ReadWriteIORequest(SyscallIORequest):
         else:
             size = None
 
-        syscall_name = common.get_syscall_name(event)
+        syscall_name = trace_utils.get_syscall_name(event)
         if syscall_name in SyscallConsts.READ_SYSCALLS:
             operation = IORequest.OP_READ
         else:
@@ -391,7 +393,7 @@ class SyncIORequest(SyscallIORequest):
         # Also handle fdatasync
         begin_ts = event.timestamp
         size = None
-        syscall_name = common.get_syscall_name(event)
+        syscall_name = trace_utils.get_syscall_name(event)
 
         req = cls(begin_ts, size, tid, syscall_name)
         req.fd = event['fd']
index 31cb33c070e09f7ab730f085e356baecd0cfb222..b59e5b5e4175cf2f5a9f64bb56b44b9273ef896e 100644 (file)
@@ -1,14 +1,14 @@
 Timerange: [1970-01-01 00:00:01.000000000, 1970-01-01 00:00:01.024000000]
 
 Top system call latencies open (usec)
-Begin               End                  Name             Duration (usec)         Size  Proc                     PID      Filename      
-[00:00:01.023000000,00:00:01.024000000]  open                    1000.000          N/A  app3                     101      test/open/file (fd=42)
+Begin                End                  Name             Duration (usec)         Size  Proc                     PID      Filename      
+[00:00:01.023000000, 00:00:01.024000000]  open                    1000.000          N/A  app3                     101      test/open/file (fd=42)
 
 Top system call latencies read (usec)
-Begin               End                  Name             Duration (usec)         Size  Proc                     PID      Filename      
-[00:00:01.008000000,00:00:01.009000000]  read                    1000.000      100   B  app2                     100      testfile (fd=3)
-[00:00:01.012000000,00:00:01.013000000]  read                    1000.000       42   B  app3                     101      unknown (fd=3)
+Begin                End                  Name             Duration (usec)         Size  Proc                     PID      Filename      
+[00:00:01.008000000, 00:00:01.009000000]  read                    1000.000      100   B  app2                     100      testfile (fd=3)
+[00:00:01.012000000, 00:00:01.013000000]  read                    1000.000       42   B  app3                     101      unknown (fd=3)
 
 Top system call latencies write (usec)
-Begin               End                  Name             Duration (usec)         Size  Proc                     PID      Filename      
-[00:00:01.004000000,00:00:01.005000000]  write                   1000.000       10   B  app                      99       unknown (fd=4)
\ No newline at end of file
+Begin                End                  Name             Duration (usec)         Size  Proc                     PID      Filename      
+[00:00:01.004000000, 00:00:01.005000000]  write                   1000.000       10   B  app                      99       unknown (fd=4)
\ No newline at end of file
This page took 0.04412 seconds and 5 git commands to generate.