tools/trace-malloc/lib/nsTypeInfo.cpp

changeset 0
6474c204b198
equal deleted inserted replaced
-1:000000000000 0:8a93f4f98a04
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
2 *
3 * This Source Code Form is subject to the terms of the Mozilla Public
4 * License, v. 2.0. If a copy of the MPL was not distributed with this
5 * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
6
7 /*
8 typeinfo.cpp
9
10 Speculatively use RTTI on a random object. If it contains a pointer at offset 0
11 that is in the current process' address space, and that so on, then attempt to
12 use C++ RTTI's typeid operation to obtain the name of the type.
13
14 by Patrick C. Beard.
15 */
16
17 #include <typeinfo>
18 #include <ctype.h>
19
20 #include "nsTypeInfo.h"
21
22 extern "C" void NS_TraceMallocShutdown();
23
24 struct TraceMallocShutdown {
25 TraceMallocShutdown() {}
26 ~TraceMallocShutdown() {
27 NS_TraceMallocShutdown();
28 }
29 };
30
31 extern "C" void RegisterTraceMallocShutdown() {
32 // This instanciates the dummy class above, and will trigger the class
33 // destructor when libxul is unloaded. This is equivalent to atexit(),
34 // but gracefully handles dlclose().
35 static TraceMallocShutdown t;
36 }
37
38 class IUnknown {
39 public:
40 virtual long QueryInterface() = 0;
41 virtual long AddRef() = 0;
42 virtual long Release() = 0;
43 };
44
45 #if defined(MACOS)
46
47 #include <Processes.h>
48
49 class AddressSpace {
50 public:
51 AddressSpace();
52 Boolean contains(void* ptr);
53 private:
54 ProcessInfoRec mInfo;
55 };
56
57 AddressSpace::AddressSpace()
58 {
59 ProcessSerialNumber psn = { 0, kCurrentProcess };
60 mInfo.processInfoLength = sizeof(mInfo);
61 ::GetProcessInformation(&psn, &mInfo);
62 }
63
64 Boolean AddressSpace::contains(void* ptr)
65 {
66 UInt32 start = UInt32(mInfo.processLocation);
67 return (UInt32(ptr) >= start && UInt32(ptr) < (start + mInfo.processSize));
68 }
69
70 const char* nsGetTypeName(void* ptr)
71 {
72 // construct only one of these per process.
73 static AddressSpace space;
74
75 // sanity check the vtable pointer, before trying to use RTTI on the object.
76 void** vt = *(void***)ptr;
77 if (vt && !(unsigned(vt) & 0x3) && space.contains(vt) && space.contains(*vt)) {
78 IUnknown* u = static_cast<IUnknown*>(ptr);
79 const char* type = typeid(*u).name();
80 // make sure it looks like a C++ identifier.
81 if (type && (isalnum(type[0]) || type[0] == '_'))
82 return type;
83 }
84 return "void*";
85 }
86
87 #endif
88
89 // New, more "portable" Linux code is below, but this might be a useful
90 // model for other platforms, so keeping.
91 //#if defined(linux)
92 #if 0
93
94 #include <signal.h>
95 #include <setjmp.h>
96
97 static jmp_buf context;
98
99 static void handler(int signum)
100 {
101 longjmp(context, signum);
102 }
103
104 #define attempt() setjmp(context)
105
106 class Signaller {
107 public:
108 Signaller(int signum);
109 ~Signaller();
110
111 private:
112 typedef void (*handler_t) (int signum);
113 int mSignal;
114 handler_t mOldHandler;
115 };
116
117 Signaller::Signaller(int signum)
118 : mSignal(signum), mOldHandler(signal(signum, &handler))
119 {
120 }
121
122 Signaller::~Signaller()
123 {
124 signal(mSignal, mOldHandler);
125 }
126
127 // The following are pointers that bamboozle our otherwise feeble
128 // attempts to "safely" collect type names.
129 //
130 // XXX this kind of sucks because it means that anyone trying to use
131 // this without NSPR will get unresolved symbols when this library
132 // loads. It's also not very extensible. Oh well: FIX ME!
133 extern "C" {
134 // from nsprpub/pr/src/io/priometh.c (libnspr4.so)
135 extern void* _pr_faulty_methods;
136 };
137
138 static inline int
139 sanity_check_vtable_i386(void** vt)
140 {
141 // Now that we're "safe" inside the signal handler, we can
142 // start poking around. If we're really an object with
143 // RTTI, then the second entry in the vtable should point
144 // to a function.
145 //
146 // Let's see if the second entry:
147 //
148 // 1) looks like a 4-byte aligned pointer
149 //
150 // 2) points to something that looks like the following
151 // i386 instructions:
152 //
153 // 55 push %ebp
154 // 89e5 mov %esp,%ebp
155 // 53 push %ebx
156 //
157 // or
158 //
159 // 55 push %ebp
160 // 89e5 mov %esp,%ebp
161 // 56 push %esi
162 //
163 // (which is the standard function prologue generated
164 // by egcs, plus a ``signature'' instruction that appears
165 // in the typeid() function's implementation).
166 unsigned char** fp1 = reinterpret_cast<unsigned char**>(vt) + 1;
167
168 // Does it look like an address?
169 unsigned char* ip = *fp1;
170 if ((unsigned(ip) & 3) != 0)
171 return 0;
172
173 // Does it look like it refers to the standard prologue?
174 static unsigned char prologue[] = { 0x55, 0x89, 0xE5 };
175 for (unsigned i = 0; i < sizeof(prologue); ++i)
176 if (*ip++ != prologue[i])
177 return 0;
178
179 // Is the next instruction a `push %ebx' or `push %esi'?
180 if (*ip == 0x53 || *ip == 0x56) {
181 return 1;
182 }
183
184 // Nope. There's another variant that has a `sub' instruction,
185 // followed by a `cmpl' and a `jne'. Check for that.
186 if (ip[0] == 0x83 && ip[1] == 0xec // sub
187 && ip[3] == 0x83 && ip[4] == 0x3d // cmpl
188 && ip[10] == 0x75 // jne
189 ) {
190 return 1;
191 }
192
193 return 0;
194 }
195
196 static inline int
197 sanity_check_vtable_ppc(void** vt)
198 {
199 // XXX write me!
200 return 1;
201 }
202
203 #if defined(__i386)
204 # define SANITY_CHECK_VTABLE(vt) (sanity_check_vtable_i386(vt))
205 #elif defined(PPC)
206 # define SANITY_CHECK_VTABLE(vt) (sanity_check_vtable_ppc(vt))
207 #else
208 # define SANITY_CHECK_VTABLE(vt) (1)
209 #endif
210
211 const char* nsGetTypeName(void* ptr)
212 {
213 // sanity check the vtable pointer, before trying to use RTTI on the object.
214 void** vt = *(void***)ptr;
215 if (vt && !(unsigned(vt) & 3) && (vt != &_pr_faulty_methods)) {
216 Signaller s1(SIGSEGV);
217 if (attempt() == 0) {
218 if (SANITY_CHECK_VTABLE(vt)) {
219 // Looks like a function: what the hell, let's call it.
220 IUnknown* u = static_cast<IUnknown*>(ptr);
221 const char* type = typeid(*u).name();
222 // EGCS seems to prefix a length string.
223 while (isdigit(*type)) ++type;
224 return type;
225 }
226 }
227 }
228 return "void*";
229 }
230
231 #endif
232
233 #if defined(linux) || defined(XP_MACOSX)
234
235 #define __USE_GNU
236 #include <dlfcn.h>
237 #include <ctype.h>
238 #include <string.h>
239
240 const char* nsGetTypeName(void* ptr)
241 {
242 #if defined(__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100 /* G++ V3 ABI */
243 const int expected_offset = 8;
244 const char vtable_sym_start[] = "_ZTV";
245 const int vtable_sym_start_length = sizeof(vtable_sym_start) - 1;
246 #else
247 const int expected_offset = 0;
248 const char vtable_sym_start[] = "__vt_";
249 const int vtable_sym_start_length = sizeof(vtable_sym_start) - 1;
250 #endif
251 void* vt = *(void**)ptr;
252 Dl_info info;
253 // If dladdr fails, if we're not at the expected offset in the vtable,
254 // or if the symbol name isn't a vtable symbol name, return "void*".
255 if ( !dladdr(vt, &info) ||
256 ((char*)info.dli_saddr) + expected_offset != vt ||
257 !info.dli_sname ||
258 strncmp(info.dli_sname, vtable_sym_start, vtable_sym_start_length))
259 return "void*";
260
261 // skip the garbage at the beginning of things like
262 // __vt_14nsRootBoxFrame (gcc 2.96) or _ZTV14nsRootBoxFrame (gcc 3.0)
263 const char* rv = info.dli_sname + vtable_sym_start_length;
264 while (*rv && isdigit(*rv))
265 ++rv;
266 return rv;
267 }
268
269 #endif
270
271 #ifdef XP_WIN32
272 const char* nsGetTypeName(void* ptr)
273 {
274 //TODO: COMPLETE THIS
275 return "void*";
276 }
277
278 #endif //XP_WIN32

mercurial