Web   ·   Wiki   ·   Activities   ·   Blog   ·   Lists   ·   Chat   ·   Meeting   ·   Bugs   ·   Git   ·   Translate   ·   Archive   ·   People   ·   Donate
summaryrefslogtreecommitdiffstats
path: root/shell/view/home/proc_smaps.py
blob: c7a81ec8946b5769ad9d7e7af1af8d0c4f0e5ce6 (plain)
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
####################################################################
# This class open the /proc/PID/maps and /proc/PID/smaps files
# to get useful information about the real memory usage
####################################################################

import os
import logging

_smaps_has_references = None

# Parse the /proc/PID/smaps file
class ProcSmaps:

    mappings = [] # Devices information
    
    def __init__(self, pid):
        global _smaps_has_references
        if _smaps_has_references is None:
            _smaps_has_references = os.path.isfile('/proc/%s/clear_refs' %
                                                   os.getpid())

        smapfile = "/proc/%s/smaps" % pid
        self.mappings = []
        
        # Coded by Federico Mena (script)
        infile = open(smapfile, "r")
        input = infile.read()
        infile.close()
        
        lines = input.splitlines()

        num_lines = len (lines)
        line_idx = 0
    
        # 08065000-08067000 rw-p 0001c000 03:01 147613     /opt/gnome/bin/evolution-2.6
        # Size:                 8 kB
        # Rss:                  8 kB
        # Shared_Clean:         0 kB
        # Shared_Dirty:         0 kB
        # Private_Clean:        8 kB
        # Private_Dirty:        0 kB
        # Referenced:           4 kb -> Introduced in kernel 2.6.22

        while num_lines > 0:
            fields = lines[line_idx].split (" ", 5)
            if len (fields) == 6:
                (offsets, permissions, bin_permissions, device, inode, name) = fields
            else:
                (offsets, permissions, bin_permissions, device, inode) = fields
                name = ""
    
            size          = self.parse_smaps_size_line (lines[line_idx + 1])
            rss           = self.parse_smaps_size_line (lines[line_idx + 2])
            shared_clean  = self.parse_smaps_size_line (lines[line_idx + 3])
            shared_dirty  = self.parse_smaps_size_line (lines[line_idx + 4])
            private_clean = self.parse_smaps_size_line (lines[line_idx + 5])
            private_dirty = self.parse_smaps_size_line (lines[line_idx + 6])
            if _smaps_has_references:
                referenced = self.parse_smaps_size_line (lines[line_idx + 7])
            else:
                referenced = None
            name = name.strip ()

            mapping = Mapping (size, rss, shared_clean, shared_dirty, \
                private_clean, private_dirty, referenced, permissions, name)
            self.mappings.append (mapping)

            if _smaps_has_references:
                num_lines -= 8
                line_idx += 8
            else:
                num_lines -= 7
                line_idx += 7
        
        if _smaps_has_references:
            self._clear_reference(pid)

    def _clear_reference(self, pid):
      os.system("echo 1 > /proc/%s/clear_refs" % pid)

    # Parses a line of the form "foo: 42 kB" and returns an integer for the "42" field
    def parse_smaps_size_line (self, line):
        # Rss:                  8 kB
        fields = line.split ()
        return int(fields[1])

class Mapping:
    def __init__ (self, size, rss, shared_clean, shared_dirty, \
            private_clean, private_dirty, referenced, permissions, name):
        self.size = size
        self.rss = rss
        self.shared_clean = shared_clean
        self.shared_dirty = shared_dirty
        self.private_clean = private_clean
        self.private_dirty = private_dirty
        self.referenced  = referenced
        self.permissions = permissions
        self.name = name

# Parse /proc/PID/maps file to get the clean memory usage by process,
# we avoid lines with backed-files
class ProcMaps:
    
    clean_size = 0
    
    def __init__(self, pid):
        mapfile = "/proc/%s/maps" % pid

        try:
            infile = open(mapfile, "r")
        except:
            print "Error trying " + mapfile
            return None
            
        sum = 0
        to_data_do = {
            "[anon]": self.parse_size_line,
            "[heap]": self.parse_size_line
        }
        
        for line in infile:
            arr = line.split()
            
            # Just parse writable mapped areas
            if arr[1][1] != "w":
                continue
            
            if len(arr) == 6:                
                # if we got a backed-file we skip this info
                if os.path.isfile(arr[5]):
                    continue
                else:
                    line_size = to_data_do.get(arr[5], self.skip)(line)
                    sum += line_size
            else:
                line_size = self.parse_size_line(line)
                sum += line_size
                    
        infile.close()
        self.clean_size = sum
        
    def skip(self, line):
        return 0
    
    # Parse a maps line and return the mapped size
    def parse_size_line(self, line):
        start, end = line.split()[0].split('-')
        size = int(end, 16) - int(start, 16)            
        return size