Separate syscalls and io analyses
authorAntoine Busque <antoinebusque@gmail.com>
Wed, 18 Mar 2015 19:46:49 +0000 (15:46 -0400)
committerAntoine Busque <antoinebusque@gmail.com>
Wed, 18 Mar 2015 19:46:49 +0000 (15:46 -0400)
linuxautomaton/linuxautomaton/sv.py
linuxautomaton/linuxautomaton/syscalls.py
lttnganalyses/lttnganalyses/syscalls.py
lttnganalysescli/lttnganalysescli/syscallstats.py

index 2c2074e27887a5a79fdb4fdb139b3da70437619e..78b5a83a6f8de14bc9158952d79a7eefda1fbfe9 100644 (file)
@@ -64,10 +64,6 @@ class Process():
         self.write = 0
         # the process scheduled before this one
         self.prev_tid = None
-        # indexed by syscall_name
-        self.syscalls = {}
-        self.dirty = 0
-        self.total_syscalls = 0
         # array of IORequest objects for freq analysis later (block and
         # syscalls with no FD like sys_sync)
         self.iorequests = []
index 8a19bd03a164c8d5f8cd13b8e2dd8ff59b7d43fd..4235e5268af74b02dca38c22da868dc6b3d0af9c 100644 (file)
@@ -3,6 +3,7 @@
 # The MIT License (MIT)
 #
 # Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
+#               2015 - 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
 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
 # SOFTWARE.
 
-import socket
-from linuxautomaton import sp, sv, common
-from babeltrace import CTFScope
+from linuxautomaton import sp, sv
 
 
 class SyscallsStateProvider(sp.StateProvider):
     def __init__(self, state):
-        self.state = state
-        self.cpus = state.cpus
-        self.tids = state.tids
-        self.syscalls = state.syscalls
-        self.pending_syscalls = state.pending_syscalls
-        self.syscalls['total'] = 0
+        self._state = state
         cbs = {
             'syscall_entry': self._process_syscall_entry,
-            'syscall_exit': self._process_syscall_exit,
-            'writeback_pages_written': self._process_writeback_pages_written,
-            'mm_vmscan_wakeup_kswapd': self._process_mm_vmscan_wakeup_kswapd,
-            'mm_page_free': self._process_mm_page_free,
+            'syscall_exit': self._process_syscall_exit
         }
         self._register_cbs(cbs)
 
     def process_event(self, ev):
         self._process_event_cb(ev)
 
-    def get_fd_type(self, name, family):
-        if name in sv.SyscallConsts.NET_OPEN_SYSCALLS:
-            if family in sv.SyscallConsts.INET_FAMILIES:
-                return sv.FDType.net
-            if family in sv.SyscallConsts.DISK_FAMILIES:
-                return sv.FDType.disk
-
-        if name in sv.SyscallConsts.DISK_OPEN_SYSCALLS:
-            return sv.FDType.disk
-
-        return sv.FDType.unknown
-
-    def global_syscall_entry(self, name):
-        if name not in self.syscalls:
-            s = sv.Syscall()
-            s.name = name
-            s.count = 0
-            self.syscalls[name] = s
-        else:
-            s = self.syscalls[name]
-        s.count += 1
-        self.syscalls['total'] += 1
-
-    def override_name(self, name, event):
-        if name in ['syscall_entry_epoll_ctl']:
-            if event['op'] == 1:
-                name = '%s-ADD' % (name)
-            elif event['op'] == 2:
-                name = '%s-DEL' % (name)
-            elif event['op'] == 3:
-                name = '%s-MODE' % (name)
-        return name
-
-    def per_tid_syscall_entry(self, name, cpu_id, event):
-        # we don't know which process is currently on this CPU
-        if cpu_id not in self.cpus:
-            return
-        c = self.cpus[cpu_id]
-        if c.current_tid is None:
-            return
-        t = self.tids[c.current_tid]
-        t.total_syscalls += 1
-        name = self.override_name(name, event)
-        if name not in t.syscalls:
-            s = sv.Syscall()
-            s.name = name
-            t.syscalls[name] = s
-        else:
-            s = t.syscalls[name]
-        s.count += 1
-        current_syscall = t.current_syscall
-        current_syscall['name'] = name
-        current_syscall['start'] = event.timestamp
-        self.global_syscall_entry(name)
-
-    def track_open(self, name, proc, event, cpu):
-        self.tids[cpu.current_tid].current_syscall = {}
-        current_syscall = self.tids[cpu.current_tid].current_syscall
-        if name in sv.SyscallConsts.DISK_OPEN_SYSCALLS:
-            current_syscall['filename'] = event['filename']
-            if event['flags'] & common.O_CLOEXEC == common.O_CLOEXEC:
-                current_syscall['cloexec'] = True
-        elif name in ['sys_accept', 'syscall_entry_accept',
-                      'sys_accept4', 'syscall_entry_accept4']:
-            if 'family' in event.keys() and event['family'] == socket.AF_INET:
-                ipport = '%s:%d' % (common.get_v4_addr_str(event['v4addr']),
-                                    event['sport'])
-                current_syscall['filename'] = ipport
-            else:
-                current_syscall['filename'] = 'socket'
-        elif name in sv.SyscallConsts.NET_OPEN_SYSCALLS:
-            current_syscall['filename'] = 'socket'
-        elif name in ['sys_dup2', 'syscall_entry_dup2']:
-            newfd = event['newfd']
-            oldfd = event['oldfd']
-            if newfd in proc.fds.keys():
-                self.close_fd(proc, newfd)
-            if oldfd in proc.fds.keys():
-                current_syscall['filename'] = proc.fds[oldfd].filename
-                current_syscall['fdtype'] = proc.fds[oldfd].fdtype
-            else:
-                current_syscall['filename'] = ''
-        elif name in ['sys_fcntl', 'syscall_entry_fcntl']:
-            # F_DUPsv.FD
-            if event['cmd'] != 0:
-                return
-            oldfd = event['fd']
-            if oldfd in proc.fds.keys():
-                current_syscall['filename'] = proc.fds[oldfd].filename
-                current_syscall['fdtype'] = proc.fds[oldfd].fdtype
-            else:
-                current_syscall['filename'] = ''
-
-        if name in sv.SyscallConsts.NET_OPEN_SYSCALLS and \
-                'family' in event.keys():
-            family = event['family']
-            current_syscall['family'] = family
-        else:
-            family = socket.AF_UNSPEC
-            current_syscall['family'] = family
-
-        current_syscall['name'] = name
-        current_syscall['start'] = event.timestamp
-        current_syscall['fdtype'] = self.get_fd_type(name, family)
-
-    def close_fd(self, proc, fd):
-        filename = proc.fds[fd].filename
-        if filename not in sv.SyscallConsts.GENERIC_NAMES \
-           and filename in proc.closed_fds.keys():
-            f = proc.closed_fds[filename]
-            f.close += 1
-            f.net_read += proc.fds[fd].net_read
-            f.disk_read += proc.fds[fd].disk_read
-            f.net_write += proc.fds[fd].net_write
-            f.disk_write += proc.fds[fd].disk_write
-        else:
-            proc.closed_fds[filename] = proc.fds[fd]
-            proc.closed_fds[filename].close = 1
-
-        proc.fds.pop(fd, None)
-
-    def track_close(self, name, proc, event, cpu):
-        fd = event['fd']
-        if fd not in proc.fds.keys():
-            return
-
-        tid = self.tids[cpu.current_tid]
-        tid.current_syscall = {}
-        current_syscall = tid.current_syscall
-        current_syscall['filename'] = proc.fds[fd].filename
-        current_syscall['name'] = name
-        current_syscall['start'] = event.timestamp
-
-        self.close_fd(proc, fd)
-
-    def _fix_context_pid(self, event, t):
-        for context in event.field_list_with_scope(
-                CTFScope.STREAM_EVENT_CONTEXT):
-            if context != 'pid':
-                continue
-            # make sure the 'pid' field is not also in the event
-            # payload, otherwise we might clash
-            for context in event.field_list_with_scope(
-                    CTFScope.EVENT_FIELDS):
-                if context == 'pid':
-                    return
-            if t.pid is None:
-                t.pid == event['pid']
-                if event['pid'] != t.tid:
-                    t.pid = event['pid']
-                    p = sv.Process()
-                    p.tid = t.pid
-                    p.pid = t.pid
-                    p.comm = t.comm
-                    self.tids[p.pid] = p
-
-    def track_fds(self, name, event, cpu_id):
-        # we don't know which process is currently on this CPU
-        if cpu_id not in self.cpus:
-            return
-        c = self.cpus[cpu_id]
-        if c.current_tid is None:
-            return
-        t = self.tids[c.current_tid]
-        # check if we can fix the pid from a context
-        self._fix_context_pid(event, t)
-        # if it's a thread, we want the parent
-        if t.pid is not None and t.tid != t.pid:
-            t = self.tids[t.pid]
-        if name in sv.SyscallConsts.OPEN_SYSCALLS:
-            self.track_open(name, t, event, c)
-        elif name in sv.SyscallConsts.CLOSE_SYSCALLS:
-            self.track_close(name, t, event, c)
-        # when a connect occurs, no new sv.FD is returned, but we can fix
-        # the 'filename' if we have the destination info
-        elif name in ['sys_connect', 'syscall_entry_connect'] \
-                and 'family' in event.keys():
-            if event['family'] == socket.AF_INET:
-                fd = self.get_fd(t, event['fd'], event)
-                ipport = '%s:%d' % (common.get_v4_addr_str(event['v4addr']),
-                                    event['dport'])
-                fd.filename = ipport
-
-    def get_fd(self, proc, fd, event):
-        if fd not in proc.fds.keys():
-            f = sv.FD()
-            f.fd = fd
-            f.filename = 'unknown (origin not found)'
-            proc.fds[fd] = f
-        else:
-            f = proc.fds[fd]
-
-        proc.track_chrono_fd(fd, f.filename, f.fdtype, event.timestamp)
-
-        return f
-
-    def track_sync(self, name, event, cpu_id):
-        # we don't know which process is currently on this CPU
-        if cpu_id not in self.cpus:
-            return
-        c = self.cpus[cpu_id]
-        if c.current_tid is None:
-            return
-        t = self.tids[c.current_tid]
-        self.pending_syscalls.append(t)
-        # if it's a thread, we want the parent
-        if t.pid is not None and t.tid != t.pid:
-            t = self.tids[t.pid]
-        current_syscall = self.tids[c.current_tid].current_syscall
-        current_syscall['name'] = name
-        current_syscall['start'] = event.timestamp
-        if name not in ['sys_sync', 'syscall_entry_sync']:
-            fd = event['fd']
-            f = self.get_fd(t, fd, event)
-            current_syscall['fd'] = f
-            current_syscall['filename'] = f.filename
-
-    def track_read_write(self, name, event, cpu_id):
-        # we don't know which process is currently on this CPU
-        if cpu_id not in self.cpus:
-            return
-        c = self.cpus[cpu_id]
-        if c.current_tid is None:
-            return
-        t = self.tids[c.current_tid]
-        self.pending_syscalls.append(t)
-        # if it's a thread, we want the parent
-        if t.pid is not None and t.tid != t.pid:
-            t = self.tids[t.pid]
-        current_syscall = self.tids[c.current_tid].current_syscall
-        current_syscall['name'] = name
-        current_syscall['start'] = event.timestamp
-        if name in ['sys_splice', 'syscall_entry_splice']:
-            current_syscall['fd_in'] = self.get_fd(t, event['fd_in'], event)
-            current_syscall['fd_out'] = self.get_fd(t, event['fd_out'], event)
-            current_syscall['count'] = event['len']
-            current_syscall['filename'] = current_syscall['fd_in'].filename
-            return
-        elif name in ['sys_sendfile64', 'syscall_entry_sendfile64']:
-            current_syscall['fd_in'] = self.get_fd(t, event['in_fd'], event)
-            current_syscall['fd_out'] = self.get_fd(t, event['out_fd'], event)
-            current_syscall['count'] = event['count']
-            current_syscall['filename'] = current_syscall['fd_in'].filename
-            return
-        fd = event['fd']
-        f = self.get_fd(t, fd, event)
-        current_syscall['fd'] = f
-        if name in ['sys_writev', 'syscall_entry_writev',
-                    'sys_readv', 'syscall_entry_readv']:
-            current_syscall['count'] = event['vlen']
-        elif name in ['sys_recvfrom', 'syscall_entry_recvfrom']:
-            current_syscall['count'] = event['size']
-        elif name in ['sys_recvmsg', 'syscall_entry_recvmsg',
-                      'sys_sendmsg', 'syscall_entry_sendmsg']:
-            current_syscall['count'] = ''
-        elif name in ['sys_sendto', 'syscall_entry_sendto']:
-            current_syscall['count'] = event['len']
-        else:
-            try:
-                current_syscall['count'] = event['count']
-            except:
-                print('Missing count argument for syscall',
-                      current_syscall['name'])
-                current_syscall['count'] = 0
-
-        current_syscall['filename'] = f.filename
-
-    def add_tid_fd(self, event, cpu):
-        ret = event['ret']
-        t = self.tids[cpu.current_tid]
-        # if it's a thread, we want the parent
-        if t.pid is not None and t.tid != t.pid:
-            t = self.tids[t.pid]
-        current_syscall = self.tids[cpu.current_tid].current_syscall
+    def _process_syscall_entry(self, event):
+        name = event.name
+        cpu_id = event['cpu_id']
 
-        name = current_syscall['filename']
-        if name not in sv.SyscallConsts.GENERIC_NAMES \
-           and name in t.closed_fds.keys():
-            fd = t.closed_fds[name]
-            fd.open += 1
-        else:
-            fd = sv.FD()
-            fd.filename = name
-            if current_syscall['name'] in sv.SyscallConsts.NET_OPEN_SYSCALLS:
-                fd.family = current_syscall['family']
-                if fd.family in sv.SyscallConsts.INET_FAMILIES:
-                    fd.fdtype = sv.FDType.net
-            fd.open = 1
-        if ret >= 0:
-            fd.fd = ret
-        else:
+        if cpu_id not in self._state.cpus:
             return
-        if 'cloexec' in current_syscall.keys():
-            fd.cloexec = True
-        t.fds[fd.fd] = fd
-
-        t.track_chrono_fd(fd.fd, fd.filename, fd.fdtype, event.timestamp)
-
-    def read_append(self, fd, proc, count, rq):
-        rq.operation = sv.IORequest.OP_READ
-        rq.size = count
-        if fd.fdtype in [sv.FDType.net, sv.FDType.maybe_net]:
-            fd.net_read += count
-            proc.net_read += count
-        elif fd.fdtype == sv.FDType.disk:
-            fd.disk_read += count
-            proc.disk_read += count
-        else:
-            fd.unk_read += count
-            proc.unk_read += count
-        fd.read += count
-        proc.read += count
 
-    def write_append(self, fd, proc, count, rq):
-        rq.operation = sv.IORequest.OP_WRITE
-        rq.size = count
-        if fd.fdtype in [sv.FDType.net, sv.FDType.maybe_net]:
-            fd.net_write += count
-            proc.net_write += count
-        elif fd.fdtype == sv.FDType.disk:
-            fd.disk_write += count
-            proc.disk_write += count
-        else:
-            fd.unk_write += count
-            proc.unk_write += count
-        fd.write += count
-        proc.write += count
-
-    def track_read_write_return(self, name, ret, cpu):
-        if ret < 0:
-            # TODO: track errors
+        cpu = self._state.cpus[cpu_id]
+        if cpu.current_tid is None:
             return
-        proc = self.tids[cpu.current_tid]
-        # if it's a thread, we want the parent
-        if proc.pid is not None and proc.tid != proc.pid:
-            proc = self.tids[proc.pid]
-        current_syscall = self.tids[cpu.current_tid].current_syscall
-        if name in ['sys_splice', 'syscall_entry_splice',
-                    'sys_sendfile64', 'syscall_entry_sendfile64']:
-            self.read_append(current_syscall['fd_in'], proc, ret,
-                             current_syscall['iorequest'])
-            self.write_append(current_syscall['fd_out'], proc, ret,
-                              current_syscall['iorequest'])
-        elif name in sv.SyscallConsts.READ_SYSCALLS:
-            if ret > 0:
-                self.read_append(current_syscall['fd'], proc, ret,
-                                 current_syscall['iorequest'])
-        elif name in sv.SyscallConsts.WRITE_SYSCALLS:
-            if ret > 0:
-                self.write_append(current_syscall['fd'], proc, ret,
-                                  current_syscall['iorequest'])
-
-    def track_rw_latency(self, name, ret, c, ts, event):
-        current_syscall = self.tids[c.current_tid].current_syscall
-        rq = current_syscall['iorequest']
-        rq.duration = (event.timestamp - current_syscall['start'])
-        rq.begin = current_syscall['start']
-        rq.end = event.timestamp
-        rq.proc = self.tids[c.current_tid]
-        if 'fd' in current_syscall.keys():
-            rq.fd = current_syscall['fd']
-            r = current_syscall['fd'].iorequests
-            r.append(current_syscall['iorequest'])
-        elif 'fd_in' in current_syscall.keys():
-            rq.fd = current_syscall['fd_in']
-        # pages written during the latency
-        if 'pages_written' in current_syscall.keys():
-            rq.page_written = current_syscall['pages_written']
-        # dirty buffers during the latency
-        if 'dirty' in current_syscall.keys():
-            rq.dirty = current_syscall['dirty']
-        # allocated pages during the latency
-        if 'pages_allocated' in current_syscall.keys():
-            rq.page_alloc = current_syscall['pages_allocated']
-        # wakeup_kswapd during the latency
-        if 'page_free' in current_syscall.keys():
-            rq.page_free = current_syscall['page_free']
-        if 'wakeup_kswapd' in current_syscall.keys():
-            rq.woke_kswapd = True
-        if name in sv.SyscallConsts.SYNC_SYSCALLS:
-            if 'pages_cleared' in current_syscall.keys():
-                rq.page_cleared = len(current_syscall['pages_cleared'])
 
-    def _per_tid_syscall_exit(self, name, ret, event, c):
-        t = self.tids[c.current_tid]
-        if name not in t.syscalls:
-            return
-        s = sv.SyscallEvent()
-        s.ret = ret
-        s.entry_ts = t.current_syscall['start']
-        s.exit_ts = event.timestamp
-        s.duration = s.exit_ts - s.entry_ts
-        t_syscall = t.syscalls[name]
-        if t_syscall.min is None or t_syscall.min > s.duration:
-            t_syscall.min = s.duration
-        if t_syscall.max < s.duration:
-            t_syscall.max = s.duration
-        t_syscall.total_duration += s.duration
-        t_syscall.rq.append(s)
-
-    def _process_syscall_entry(self, event):
-        name = event.name
-        cpu_id = event['cpu_id']
-        self.per_tid_syscall_entry(name, cpu_id, event)
-        self.track_fds(name, event, cpu_id)
-        if name in sv.SyscallConsts.READ_SYSCALLS or \
-                name in sv.SyscallConsts.WRITE_SYSCALLS:
-            self.track_read_write(name, event, cpu_id)
-        if name in sv.SyscallConsts.SYNC_SYSCALLS:
-            self.track_sync(name, event, cpu_id)
+        proc = self._state.tids[cpu.current_tid]
+        proc.current_syscall['name'] = name
+        proc.current_syscall['start'] = event.timestamp
 
     def _process_syscall_exit(self, event):
         cpu_id = event['cpu_id']
-        if cpu_id not in self.cpus:
-            return
-        c = self.cpus[cpu_id]
-        if c.current_tid is None:
-            return
-        current_syscall = self.tids[c.current_tid].current_syscall
-        if not current_syscall:
+        if cpu_id not in self._state.cpus:
             return
-        name = current_syscall['name']
-        ret = event['ret']
-        self._per_tid_syscall_exit(name, ret, event, c)
 
-        if name not in sv.SyscallConsts.IO_SYSCALLS:
+        cpu = self._state.cpus[cpu_id]
+        if cpu.current_tid is None:
             return
 
-        current_syscall['iorequest'] = sv.IORequest()
-        current_syscall['iorequest'].iotype = sv.IORequest.IO_SYSCALL
-        current_syscall['iorequest'].name = name
-        if name in sv.SyscallConsts.OPEN_SYSCALLS:
-            self.add_tid_fd(event, c)
-            if ret < 0:
-                return
-            t = self.tids[c.current_tid]
-            current_syscall['fd'] = self.get_fd(t, ret, event)
-            current_syscall['count'] = 0
-            current_syscall['fd'].fdtype = current_syscall['fdtype']
-            current_syscall['iorequest'].operation = sv.IORequest.OP_OPEN
-            self.track_rw_latency(name, ret, c,
-                                  event.timestamp, event)
-        elif name in sv.SyscallConsts.READ_SYSCALLS or \
-                name in sv.SyscallConsts.WRITE_SYSCALLS:
-            self.track_read_write_return(name, ret, c)
-            self.track_rw_latency(name, ret, c, event.timestamp, event)
-        elif name in sv.SyscallConsts.SYNC_SYSCALLS:
-            current_syscall['iorequest'].operation = sv.IORequest.OP_SYNC
-            self.track_rw_latency(name, ret, c, event.timestamp, event)
-            if name in ['sys_sync', 'syscall_entry_sync']:
-                t = self.tids[c.current_tid]
-                t.iorequests.append(current_syscall['iorequest'])
-        self.tids[c.current_tid].current_syscall = {}
-        if self.tids[c.current_tid] in self.pending_syscalls:
-            self.pending_syscalls.remove(self.tids[c.current_tid])
+        proc = self._state.tids[cpu.current_tid]
+        current_syscall = proc.current_syscall
+        if not current_syscall:
+            return
 
-    def _process_writeback_pages_written(self, event):
-        """writeback_pages_written"""
-        for c in self.cpus.values():
-            if c.current_tid is None:
-                continue
-            current_syscall = self.tids[c.current_tid].current_syscall
-            if not current_syscall:
-                continue
-            current_syscall['pages_written'] = event['pages']
+        current_syscall['duration'] = event.timestamp - \
+                                      current_syscall['start']
+        current_syscall['ret'] = event['ret']
 
-    def _process_mm_vmscan_wakeup_kswapd(self, event):
-        """mm_vmscan_wakeup_kswapd"""
-        cpu_id = event['cpu_id']
-        if cpu_id not in self.cpus:
-            return
-        c = self.cpus[cpu_id]
-        if c.current_tid is None:
-            return
-        current_syscall = self.tids[c.current_tid].current_syscall
-        if current_syscall:
-            return
-        current_syscall['wakeup_kswapd'] = 1
+        self._state.send_notification_cb('syscall_exit',
+                                        proc=proc,
+                                        event=event)
 
-    def _process_mm_page_free(self, event):
-        """mm_page_free"""
-        for c in self.cpus.values():
-            if c.current_tid is None:
-                continue
-            p = self.tids[c.current_tid]
-            # if the current process is kswapd0, we need to
-            # attribute the page freed to the process that
-            # woke it up.
-            if p.comm == 'kswapd0' and p.prev_tid > 0:
-                p = self.tids[p.prev_tid]
-            current_syscall = p.current_syscall
-            if current_syscall:
-                continue
-            if 'wakeup_kswapd' in current_syscall.keys():
-                if 'page_free' in current_syscall.keys():
-                    current_syscall['page_free'] += 1
-                else:
-                    current_syscall['page_free'] = 1
+        self._state.tids[cpu.current_tid].current_syscall = {}
index cee2853ea20c9c26f262b2fdf283f6794ba11674..9cf202d889a687dbeb5091ed07f7ceec8b4f8154 100644 (file)
@@ -2,7 +2,7 @@
 #
 # The MIT License (MIT)
 #
-# Copyright (C) 2015 - Julien Desfossez <jdesfossez@efficios.com>
+# Copyright (C) 2015 - 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
@@ -27,10 +27,73 @@ from .analysis import Analysis
 
 class SyscallsAnalysis(Analysis):
     def __init__(self, state):
+        notification_cbs = {
+            'syscall_exit': self._process_syscall_exit
+        }
+
         self._state = state
+        self._state.register_notification_cbs(notification_cbs)
+        self.tids = {}
+        self.total_syscalls = 0
 
     def process_event(self, ev):
         pass
 
     def reset(self):
         pass
+
+    def _process_syscall_exit(self, **kwargs):
+        proc = kwargs['proc']
+        tid = proc.tid
+        current_syscall = proc.current_syscall
+        name = current_syscall['name']
+
+        if tid not in self.tids:
+            self.tids[tid] = ProcessSyscallStats.new_from_process(proc)
+
+        proc_stats = self.tids[tid]
+        if name not in proc_stats.syscalls:
+            proc_stats.syscalls[name] = SyscallStats(name)
+
+        proc_stats.syscalls[name].update_stats(current_syscall)
+        proc_stats.total_syscalls += 1
+        self.total_syscalls += 1
+
+
+class ProcessSyscallStats():
+    def __init__(self, pid, tid, comm):
+        self.pid = pid
+        self.tid = tid
+        self.comm = comm
+        # indexed by syscall name
+        self.syscalls = {}
+        self.total_syscalls = 0
+
+    @classmethod
+    def new_from_process(cls, proc):
+        return cls(proc.pid, proc.tid, proc.comm)
+
+
+class SyscallStats():
+    def __init__(self, name):
+        self.name = name
+        # duration min/max
+        self.min = None
+        self.max = None
+        self.total_duration = 0
+        self.syscalls_list = []
+
+    @property
+    def count(self):
+        return len(self.syscalls_list)
+
+    def update_stats(self, syscall):
+        duration = syscall['duration']
+
+        if self.min is None or self.min > duration:
+            self.min = duration
+        if self.max is None or self.max < duration:
+            self.max = duration
+        self.total_duration += duration
+        self.syscalls_list.append(syscall)
+
index 325f04e60fccec0c29181416ef9996c0cee51723..84ee4f67be8aee7b66bb2fe9f52fb6eca393a8ce 100644 (file)
@@ -80,32 +80,40 @@ class SyscallsAnalysis(Command):
 
     def _print_results(self, begin_ns, end_ns):
         self._print_date(begin_ns, end_ns)
-        strformat = '{:<28} {:>14} {:>14} {:>14} {:>12} {:>10}  {:<14}'
+        strformat = '{:<38} {:>14} {:>14} {:>14} {:>12} {:>10}  {:<14}'
         print('Per-TID syscalls statistics (usec)')
-        for tid in sorted(self.state.tids.values(),
+        for tid in sorted(self._analysis.tids.values(),
                           key=operator.attrgetter('total_syscalls'),
                           reverse=True):
             if not self._filter_process(tid):
                 continue
             if tid.total_syscalls == 0:
                 continue
-            print(strformat.format('%s (%d, tid = %d)' % (
-                tid.comm, tid.pid, tid.tid),
+
+            pid = tid.pid
+            if pid is None:
+                pid = '?'
+            else:
+                pid = str(pid)
+
+            print(strformat.format(
+                '%s (%s, tid = %d)' % (tid.comm, pid, tid.tid),
                 'Count', 'Min', 'Average', 'Max', 'Stdev', 'Return values'))
+
             for syscall in sorted(tid.syscalls.values(),
                                   key=operator.attrgetter('count'),
                                   reverse=True):
                 sysvalues = []
                 rets = {}
-                for s in syscall.rq:
-                    sysvalues.append(s.duration)
-                    if s.ret >= 0:
+                for s in syscall.syscalls_list:
+                    sysvalues.append(s['duration'])
+                    if s['ret'] >= 0:
                         key = 'success'
                     else:
                         try:
-                            key = errno.errorcode[-s.ret]
+                            key = errno.errorcode[-s['ret']]
                         except:
-                            key = str(s.ret)
+                            key = str(s['ret'])
                     if key in rets.keys():
                         rets[key] += 1
                     else:
@@ -114,7 +122,10 @@ class SyscallsAnalysis(Command):
                     syscallmin = '?'
                 else:
                     syscallmin = '%0.03f' % (syscall.min / 1000)
-                syscallmax = '%0.03f' % (syscall.max / 1000)
+                if syscall.max is None:
+                    syscallmax = '?'
+                else:
+                    syscallmax = '%0.03f' % (syscall.max / 1000)
                 syscallavg = '%0.03f' % \
                     (syscall.total_duration/(syscall.count*1000))
                 if len(sysvalues) > 2:
@@ -130,7 +141,7 @@ class SyscallsAnalysis(Command):
                                    '', ''))
             print('-' * 113)
 
-        print('\nTotal syscalls: %d' % (self.state.syscalls['total']))
+        print('\nTotal syscalls: %d' % (self._analysis.total_syscalls))
 
     def _reset_total(self, start_ts):
         pass
This page took 0.033035 seconds and 5 git commands to generate.