aboutsummaryrefslogtreecommitdiff
path: root/kernel/mm/memcheck.py
blob: 49f40fd1b5c2d1dbfc40bec283623833f734e7ed (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
150
151
152
153
154
155
156
157
158
import gdb

import string

import weenix
import weenix.kmem
import weenix.stack


class SlabAllocation:
    def __init__(self, addr, stack, allocator, initialization):
        self.addr = addr
        self.stack = stack
        self.allocator = allocator
        self.initialization = initialization


class PageAllocation:
    def __init__(self, addr, stack, npages, slabdata, initialization):
        self.addr = addr
        self.stack = stack
        self.npages = npages
        self.slabdata = slabdata
        self.initialization = initialization


class Memcheck:
    def __init__(self):
        self._slab_alloc_count = 0
        self._slab_free_count = 0
        self._slab_invalid_free = 0
        self._slab_allocated = {}
        self._page_alloc_count = 0
        self._page_free_count = 0
        self._page_invalid_free = 0
        self._page_allocated = {}
        self._initialized = False
        weenix.Hook("slab_obj_alloc", self._slab_alloc_callback)
        weenix.Hook("slab_obj_free", self._slab_free_callback)
        weenix.Hook("page_alloc", self._page_alloc_callback)
        weenix.Hook("page_free", self._page_free_callback)
        weenix.Hook("initialized", self._initialized_callback)
        weenix.Hook("shutdown", self._shutdown_callback)

    def _slab_alloc_callback(self, args):
        addr = args["addr"]
        if string.atol(addr, 16) == 0:
            return False
        stack = weenix.stack.Stack(gdb.newest_frame().older())
        allocator = weenix.kmem.SlabAllocator(
            gdb.Value(string.atol(args["allocator"].split(" ")[0], 16)).cast(
                gdb.lookup_type("void").pointer()
            )
        )
        self._slab_allocated[addr] = SlabAllocation(
            addr, stack, allocator, not self._initialized
        )
        if self._initialized:
            self._slab_alloc_count += 1
        return False

    def _slab_free_callback(self, args):
        if not args["addr"] in self._slab_allocated:
            self._slab_invalid_free += 1
            print("Invalid free of address " + args["addr"] + ":")
            print(weenix.stack.Stack(gdb.newest_frame().older()))
        else:
            if not self._slab_allocated[args["addr"]].initialization:
                self._slab_free_count += 1
            del self._slab_allocated[args["addr"]]
        return False

    def _page_alloc_callback(self, args):
        addr = args["addr"]
        if string.atol(addr, 16) == 0:
            return False
        stack = weenix.stack.Stack(gdb.newest_frame().older())
        slabdata = stack.contains("_slab_allocator_grow")
        self._page_allocated[addr] = PageAllocation(
            addr, stack, string.atoi(args["npages"]), slabdata, not self._initialized
        )
        if self._initialized and not slabdata:
            self._page_alloc_count += 1
        return False

    def _page_free_callback(self, args):
        if not args["addr"] in self._page_allocated:
            self._page_invalid_free += 1
            print("Invalid free of address " + args["addr"] + ":")
            print(weenix.stack.Stack(gdb.newest_frame().older()))
        elif self._page_allocated[args["addr"]].npages != string.atoi(args["npages"]):
            self._page_invalid_free += 1
            print(
                "Address "
                + args["addr"]
                + " allocated for "
                + str(self._page_allocated[args["addr"]].npages)
                + " pages:"
            )
            print(self._page_allocated[args["addr"]].stack)
            print("but freed with " + args["npages"] + " pages:")
            print(weenix.stack.Stack(gdb.newest_frame().older()))
            del self._page_allocated[args["addr"]]
        else:
            if (
                not self._page_allocated[args["addr"]].initialization
                and not self._page_allocated[args["addr"]].slabdata
            ):
                self._page_free_count += 1
            del self._page_allocated[args["addr"]]
        return False

    def _initialized_callback(self, args):
        self._initialized = True
        return False

    def _shutdown_callback(self, args):
        size = 0
        for alloc in self._slab_allocated.itervalues():
            if not alloc.initialization:
                size += alloc.allocator.size()
                print(
                    'Leaked {0} bytes from "{1}" at {2}:'.format(
                        alloc.allocator.size(), alloc.allocator.name(), alloc.addr
                    )
                )
                print(alloc.stack)
        npages = 0
        for page in self._page_allocated.itervalues():
            if not page.initialization and not page.slabdata:
                npages += page.npages
                print("Leaked {0} pages at {1}:".format(page.npages, page.addr))
                print(page.stack)
        print(
            "{0} slab allocs, {1} frees ({2} bytes leaked)".format(
                self._slab_alloc_count, self._slab_free_count, size
            )
        )
        print(
            "{0} page allocs, {1} frees ({2} pages leaked)".format(
                self._page_alloc_count, self._page_free_count, npages
            )
        )
        print("{0} invalid slab frees".format(self._slab_invalid_free))
        print("{0} invalid page frees".format(self._page_invalid_free))
        return False


class MemcheckFlag(weenix.Flag):
    def __init__(self):
        weenix.Flag.__init__(self, "memcheck", gdb.COMMAND_DATA)

    def callback(self, value):
        if value:
            Memcheck()


MemcheckFlag()