1# This Source Code Form is subject to the terms of the Mozilla Public
2# License, v. 2.0. If a copy of the MPL was not distributed with this file,
3# You can obtain one at http://mozilla.org/MPL/2.0/.
4
5"""
6All jitted code is allocated via the ExecutableAllocator class. Make GDB aware
7of them, such that we can query for pages which are containing code which are
8allocated by the Jits.
9"""
10
11import gdb
12import mozilla.prettyprinters
13from mozilla.prettyprinters import pretty_printer, ptr_pretty_printer
14
15# Forget any printers from previous loads of this module.
16mozilla.prettyprinters.clear_module_printers(__name__)
17
18
19class jsjitExecutableAllocatorCache(object):
20    """Cache information about the ExecutableAllocator type for this objfile."""
21
22    def __init__(self):
23        self.d = None
24
25    def __getattr__(self, name):
26        if self.d is None:
27            self.initialize()
28        return self.d[name]
29
30    def initialize(self):
31        self.d = {}
32        self.d["ExecutableAllocator"] = gdb.lookup_type("js::jit::ExecutableAllocator")
33        self.d["ExecutablePool"] = gdb.lookup_type("js::jit::ExecutablePool")
34        self.d["HashNumber"] = gdb.lookup_type("mozilla::HashNumber")
35
36
37@pretty_printer("js::jit::ExecutableAllocator")
38class jsjitExecutableAllocator(object):
39    def __init__(self, value, cache):
40        if not cache.mod_ExecutableAllocator:
41            cache.mod_ExecutableAllocator = jsjitExecutableAllocatorCache()
42        self.value = value
43        self.cache = cache.mod_ExecutableAllocator
44
45    def to_string(self):
46        return "ExecutableAllocator([%s])" % ", ".join([str(x) for x in self])
47
48    def __iter__(self):
49        return self.PoolIterator(self)
50
51    class PoolIterator(object):
52        def __init__(self, allocator):
53            self.allocator = allocator
54            self.entryType = allocator.cache.ExecutablePool.pointer()
55            self.hashNumType = allocator.cache.HashNumber
56            # Emulate the HashSet::Range
57            self.table = allocator.value["m_pools"]["mImpl"]["mTable"]
58            self.index = 0
59            kHashNumberBits = 32
60            hashShift = allocator.value["m_pools"]["mImpl"]["mHashShift"]
61            self.capacity = 1 << (kHashNumberBits - hashShift)
62            if self.table == 0:
63                self.capacity = 0
64            # auto hashes = reinterpret_cast<HashNumber*>(mTable);
65            self.hashes = self.table.cast(self.hashNumType.pointer())
66            # auto entries = reinterpret_cast<Entry*>(&hashes[capacity()]);
67            self.entries = (self.hashes + self.capacity).cast(self.entryType.pointer())
68
69        def __iter__(self):
70            return self
71
72        def next(self):
73            return self.__next__()
74
75        def __next__(self):
76            cur = self.index
77            if cur >= self.capacity:
78                raise StopIteration()
79            self.index = self.index + 1
80            if self.hashes[cur] > 1:  # table[i]->isLive()
81                return self.entries[cur]
82            return self.__next__()
83
84
85@ptr_pretty_printer("js::jit::ExecutablePool")
86class jsjitExecutablePool(mozilla.prettyprinters.Pointer):
87    def __init__(self, value, cache):
88        if not cache.mod_ExecutableAllocator:
89            cache.mod_ExecutableAllocator = jsjitExecutableAllocatorCache()
90        self.value = value
91        self.cache = cache.mod_ExecutableAllocator
92
93    def to_string(self):
94        pages = self.value["m_allocation"]["pages"]
95        size = self.value["m_allocation"]["size"]
96        return "ExecutablePool %08x-%08x" % (pages, pages + size)
97