Bumping manifests a=b2g-bump
[gecko.git] / tools / trace-malloc / lib / nsTypeInfo.cpp
blob2a4ae4bb7ef19e14b9feae0df38189eb37abcbe9
1 /* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
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/. */
7 /*
8 typeinfo.cpp
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.
14 by Patrick C. Beard.
17 #include <exception> /* Needed for MSVC2010 due to bug 1055675 */
18 #include <typeinfo>
19 #include <ctype.h>
21 #include "nsTypeInfo.h"
23 extern "C" void NS_TraceMallocShutdown();
25 struct TraceMallocShutdown {
26 TraceMallocShutdown() {}
27 ~TraceMallocShutdown() {
28 NS_TraceMallocShutdown();
32 extern "C" void RegisterTraceMallocShutdown() {
33 // This instanciates the dummy class above, and will trigger the class
34 // destructor when libxul is unloaded. This is equivalent to atexit(),
35 // but gracefully handles dlclose().
36 static TraceMallocShutdown t;
39 class IUnknown {
40 public:
41 virtual long QueryInterface() = 0;
42 virtual long AddRef() = 0;
43 virtual long Release() = 0;
46 #if defined(MACOS)
48 #include <Processes.h>
50 class AddressSpace {
51 public:
52 AddressSpace();
53 Boolean contains(void* ptr);
54 private:
55 ProcessInfoRec mInfo;
58 AddressSpace::AddressSpace()
60 ProcessSerialNumber psn = { 0, kCurrentProcess };
61 mInfo.processInfoLength = sizeof(mInfo);
62 ::GetProcessInformation(&psn, &mInfo);
65 Boolean AddressSpace::contains(void* ptr)
67 UInt32 start = UInt32(mInfo.processLocation);
68 return (UInt32(ptr) >= start && UInt32(ptr) < (start + mInfo.processSize));
71 const char* nsGetTypeName(void* ptr)
73 // construct only one of these per process.
74 static AddressSpace space;
76 // sanity check the vtable pointer, before trying to use RTTI on the object.
77 void** vt = *(void***)ptr;
78 if (vt && !(unsigned(vt) & 0x3) && space.contains(vt) && space.contains(*vt)) {
79 IUnknown* u = static_cast<IUnknown*>(ptr);
80 const char* type = typeid(*u).name();
81 // make sure it looks like a C++ identifier.
82 if (type && (isalnum(type[0]) || type[0] == '_'))
83 return type;
85 return "void*";
88 #endif
90 // New, more "portable" Linux code is below, but this might be a useful
91 // model for other platforms, so keeping.
92 //#if defined(linux)
93 #if 0
95 #include <signal.h>
96 #include <setjmp.h>
98 static jmp_buf context;
100 static void handler(int signum)
102 longjmp(context, signum);
105 #define attempt() setjmp(context)
107 class Signaller {
108 public:
109 Signaller(int signum);
110 ~Signaller();
112 private:
113 typedef void (*handler_t) (int signum);
114 int mSignal;
115 handler_t mOldHandler;
118 Signaller::Signaller(int signum)
119 : mSignal(signum), mOldHandler(signal(signum, &handler))
123 Signaller::~Signaller()
125 signal(mSignal, mOldHandler);
128 // The following are pointers that bamboozle our otherwise feeble
129 // attempts to "safely" collect type names.
131 // XXX this kind of sucks because it means that anyone trying to use
132 // this without NSPR will get unresolved symbols when this library
133 // loads. It's also not very extensible. Oh well: FIX ME!
134 extern "C" {
135 // from nsprpub/pr/src/io/priometh.c (libnspr4.so)
136 extern void* _pr_faulty_methods;
139 static inline int
140 sanity_check_vtable_i386(void** vt)
142 // Now that we're "safe" inside the signal handler, we can
143 // start poking around. If we're really an object with
144 // RTTI, then the second entry in the vtable should point
145 // to a function.
147 // Let's see if the second entry:
149 // 1) looks like a 4-byte aligned pointer
151 // 2) points to something that looks like the following
152 // i386 instructions:
154 // 55 push %ebp
155 // 89e5 mov %esp,%ebp
156 // 53 push %ebx
158 // or
160 // 55 push %ebp
161 // 89e5 mov %esp,%ebp
162 // 56 push %esi
164 // (which is the standard function prologue generated
165 // by egcs, plus a ``signature'' instruction that appears
166 // in the typeid() function's implementation).
167 unsigned char** fp1 = reinterpret_cast<unsigned char**>(vt) + 1;
169 // Does it look like an address?
170 unsigned char* ip = *fp1;
171 if ((unsigned(ip) & 3) != 0)
172 return 0;
174 // Does it look like it refers to the standard prologue?
175 static unsigned char prologue[] = { 0x55, 0x89, 0xE5 };
176 for (unsigned i = 0; i < sizeof(prologue); ++i)
177 if (*ip++ != prologue[i])
178 return 0;
180 // Is the next instruction a `push %ebx' or `push %esi'?
181 if (*ip == 0x53 || *ip == 0x56) {
182 return 1;
185 // Nope. There's another variant that has a `sub' instruction,
186 // followed by a `cmpl' and a `jne'. Check for that.
187 if (ip[0] == 0x83 && ip[1] == 0xec // sub
188 && ip[3] == 0x83 && ip[4] == 0x3d // cmpl
189 && ip[10] == 0x75 // jne
191 return 1;
194 return 0;
197 static inline int
198 sanity_check_vtable_ppc(void** vt)
200 // XXX write me!
201 return 1;
204 #if defined(__i386)
205 # define SANITY_CHECK_VTABLE(vt) (sanity_check_vtable_i386(vt))
206 #elif defined(PPC)
207 # define SANITY_CHECK_VTABLE(vt) (sanity_check_vtable_ppc(vt))
208 #else
209 # define SANITY_CHECK_VTABLE(vt) (1)
210 #endif
212 const char* nsGetTypeName(void* ptr)
214 // sanity check the vtable pointer, before trying to use RTTI on the object.
215 void** vt = *(void***)ptr;
216 if (vt && !(unsigned(vt) & 3) && (vt != &_pr_faulty_methods)) {
217 Signaller s1(SIGSEGV);
218 if (attempt() == 0) {
219 if (SANITY_CHECK_VTABLE(vt)) {
220 // Looks like a function: what the hell, let's call it.
221 IUnknown* u = static_cast<IUnknown*>(ptr);
222 const char* type = typeid(*u).name();
223 // EGCS seems to prefix a length string.
224 while (isdigit(*type)) ++type;
225 return type;
229 return "void*";
232 #endif
234 #if defined(linux) || defined(XP_MACOSX)
236 #define __USE_GNU
237 #include <dlfcn.h>
238 #include <ctype.h>
239 #include <string.h>
241 const char* nsGetTypeName(void* ptr)
243 #if defined(__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100 /* G++ V3 ABI */
244 const int expected_offset = 8;
245 const char vtable_sym_start[] = "_ZTV";
246 const int vtable_sym_start_length = sizeof(vtable_sym_start) - 1;
247 #else
248 const int expected_offset = 0;
249 const char vtable_sym_start[] = "__vt_";
250 const int vtable_sym_start_length = sizeof(vtable_sym_start) - 1;
251 #endif
252 void* vt = *(void**)ptr;
253 Dl_info info;
254 // If dladdr fails, if we're not at the expected offset in the vtable,
255 // or if the symbol name isn't a vtable symbol name, return "void*".
256 if ( !dladdr(vt, &info) ||
257 ((char*)info.dli_saddr) + expected_offset != vt ||
258 !info.dli_sname ||
259 strncmp(info.dli_sname, vtable_sym_start, vtable_sym_start_length))
260 return "void*";
262 // skip the garbage at the beginning of things like
263 // __vt_14nsRootBoxFrame (gcc 2.96) or _ZTV14nsRootBoxFrame (gcc 3.0)
264 const char* rv = info.dli_sname + vtable_sym_start_length;
265 while (*rv && isdigit(*rv))
266 ++rv;
267 return rv;
270 #endif
272 #ifdef XP_WIN32
273 const char* nsGetTypeName(void* ptr)
275 //TODO: COMPLETE THIS
276 return "void*";
279 #endif //XP_WIN32