summaryrefslogtreecommitdiffstats
path: root/node-admin/scripts/pyroute2/netlink/taskstats/__init__.py
blob: 86a8d20464c4fa83e4917a0606b21625a40cdfda (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
# By Peter V. Saveliev https://pypi.python.org/pypi/pyroute2. Dual licensed under the Apache 2 and GPLv2+ see https://github.com/svinota/pyroute2 for License details.
'''
TaskStats module
================

All that you should know about TaskStats, is that you should not
use it. But if you have to, ok::

    import os
    from pyroute2 import TaskStats
    ts = TaskStats()
    ts.get_pid_stat(os.getpid())

It is not implemented normally yet, but some methods are already
usable.
'''

from pyroute2.netlink import NLM_F_REQUEST
from pyroute2.netlink import nla
from pyroute2.netlink import genlmsg
from pyroute2.netlink.generic import GenericNetlinkSocket

TASKSTATS_CMD_UNSPEC = 0      # Reserved
TASKSTATS_CMD_GET = 1         # user->kernel request/get-response
TASKSTATS_CMD_NEW = 2


class tcmd(genlmsg):
    nla_map = (('TASKSTATS_CMD_ATTR_UNSPEC', 'none'),
               ('TASKSTATS_CMD_ATTR_PID', 'uint32'),
               ('TASKSTATS_CMD_ATTR_TGID', 'uint32'),
               ('TASKSTATS_CMD_ATTR_REGISTER_CPUMASK', 'asciiz'),
               ('TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK', 'asciiz'))


class tstats(nla):
    pack = "struct"
    fields = (('version', 'H'),                           # 2
              ('ac_exitcode', 'I'),                       # 4
              ('ac_flag', 'B'),                           # 1
              ('ac_nice', 'B'),                           # 1 --- 10
              ('cpu_count', 'Q'),                         # 8
              ('cpu_delay_total', 'Q'),                   # 8
              ('blkio_count', 'Q'),                       # 8
              ('blkio_delay_total', 'Q'),                 # 8
              ('swapin_count', 'Q'),                      # 8
              ('swapin_delay_total', 'Q'),                # 8
              ('cpu_run_real_total', 'Q'),                # 8
              ('cpu_run_virtual_total', 'Q'),             # 8
              ('ac_comm', '32s'),                         # 32 +++ 112
              ('ac_sched', 'B'),                          # 1
              ('__pad', '3x'),                            # 1 --- 8 (!)
              ('ac_uid', 'I'),                            # 4  +++ 120
              ('ac_gid', 'I'),                            # 4
              ('ac_pid', 'I'),                            # 4
              ('ac_ppid', 'I'),                           # 4
              ('ac_btime', 'I'),                          # 4  +++ 136
              ('ac_etime', 'Q'),                          # 8  +++ 144
              ('ac_utime', 'Q'),                          # 8
              ('ac_stime', 'Q'),                          # 8
              ('ac_minflt', 'Q'),                         # 8
              ('ac_majflt', 'Q'),                         # 8
              ('coremem', 'Q'),                           # 8
              ('virtmem', 'Q'),                           # 8
              ('hiwater_rss', 'Q'),                       # 8
              ('hiwater_vm', 'Q'),                        # 8
              ('read_char', 'Q'),                         # 8
              ('write_char', 'Q'),                        # 8
              ('read_syscalls', 'Q'),                     # 8
              ('write_syscalls', 'Q'),                    # 8
              ('read_bytes', 'Q'),                        # ...
              ('write_bytes', 'Q'),
              ('cancelled_write_bytes', 'Q'),
              ('nvcsw', 'Q'),
              ('nivcsw', 'Q'),
              ('ac_utimescaled', 'Q'),
              ('ac_stimescaled', 'Q'),
              ('cpu_scaled_run_real_total', 'Q'))

    def decode(self):
        nla.decode(self)
        self['ac_comm'] = self['ac_comm'][:self['ac_comm'].find('\0')]


class taskstatsmsg(genlmsg):

    nla_map = (('TASKSTATS_TYPE_UNSPEC', 'none'),
               ('TASKSTATS_TYPE_PID', 'uint32'),
               ('TASKSTATS_TYPE_TGID', 'uint32'),
               ('TASKSTATS_TYPE_STATS', 'stats'),
               ('TASKSTATS_TYPE_AGGR_PID', 'aggr_pid'),
               ('TASKSTATS_TYPE_AGGR_TGID', 'aggr_tgid'))

    class stats(tstats):
        pass  # FIXME: optimize me!

    class aggr_id(nla):
        nla_map = (('TASKSTATS_TYPE_UNSPEC', 'none'),
                   ('TASKSTATS_TYPE_PID', 'uint32'),
                   ('TASKSTATS_TYPE_TGID', 'uint32'),
                   ('TASKSTATS_TYPE_STATS', 'stats'))

        class stats(tstats):
            pass

    class aggr_pid(aggr_id):
        pass

    class aggr_tgid(aggr_id):
        pass


class TaskStats(GenericNetlinkSocket):

    def __init__(self):
        GenericNetlinkSocket.__init__(self)

    def bind(self):
        GenericNetlinkSocket.bind(self, 'TASKSTATS', taskstatsmsg)

    def get_pid_stat(self, pid):
        '''
        Get taskstats for a process. Pid should be an integer.
        '''
        msg = tcmd()
        msg['cmd'] = TASKSTATS_CMD_GET
        msg['version'] = 1
        msg['attrs'].append(['TASKSTATS_CMD_ATTR_PID', pid])
        return self.nlm_request(msg,
                                self.prid,
                                msg_flags=NLM_F_REQUEST)

    def _register_mask(self, cmd, mask):
        msg = tcmd()
        msg['cmd'] = TASKSTATS_CMD_GET
        msg['version'] = 1
        msg['attrs'].append([cmd, mask])
        # there is no response to this request
        self.put(msg,
                 self.prid,
                 msg_flags=NLM_F_REQUEST)

    def register_mask(self, mask):
        '''
        Start the accounting for a processors by a mask. Mask is
        a string, e.g.::
            0,1 -- first two CPUs
            0-4,6-10 -- CPUs from 0 to 4 and from 6 to 10

        When the accounting is turned on, on can receive messages
        with get() routine.

        Though the kernel has a procedure, that cleans up accounting,
        when it is not used, it is recommended to run deregister_mask()
        before process exit.
        '''
        self.monitor(True)
        self._register_mask('TASKSTATS_CMD_ATTR_REGISTER_CPUMASK',
                            mask)

    def deregister_mask(self, mask):
        '''
        Stop the accounting.
        '''
        self.monitor(False)
        self._register_mask('TASKSTATS_CMD_ATTR_DEREGISTER_CPUMASK',
                            mask)