tools/trace-malloc/lib/nsTypeInfo.cpp

changeset 0
6474c204b198
     1.1 --- /dev/null	Thu Jan 01 00:00:00 1970 +0000
     1.2 +++ b/tools/trace-malloc/lib/nsTypeInfo.cpp	Wed Dec 31 06:09:35 2014 +0100
     1.3 @@ -0,0 +1,278 @@
     1.4 +/* -*- Mode: C; tab-width: 4; indent-tabs-mode: nil; c-basic-offset: 4 -*-
     1.5 + *
     1.6 + * This Source Code Form is subject to the terms of the Mozilla Public
     1.7 + * License, v. 2.0. If a copy of the MPL was not distributed with this
     1.8 + * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
     1.9 +
    1.10 +/*
    1.11 +  typeinfo.cpp
    1.12 +	
    1.13 +  Speculatively use RTTI on a random object. If it contains a pointer at offset 0
    1.14 +  that is in the current process' address space, and that so on, then attempt to
    1.15 +  use C++ RTTI's typeid operation to obtain the name of the type.
    1.16 +  
    1.17 +  by Patrick C. Beard.
    1.18 + */
    1.19 +
    1.20 +#include <typeinfo>
    1.21 +#include <ctype.h>
    1.22 +
    1.23 +#include "nsTypeInfo.h"
    1.24 +
    1.25 +extern "C" void NS_TraceMallocShutdown();
    1.26 +
    1.27 +struct TraceMallocShutdown {
    1.28 +    TraceMallocShutdown() {}
    1.29 +    ~TraceMallocShutdown() {
    1.30 +        NS_TraceMallocShutdown();
    1.31 +    }
    1.32 +};
    1.33 +
    1.34 +extern "C" void RegisterTraceMallocShutdown() {
    1.35 +    // This instanciates the dummy class above, and will trigger the class
    1.36 +    // destructor when libxul is unloaded. This is equivalent to atexit(),
    1.37 +    // but gracefully handles dlclose().
    1.38 +    static TraceMallocShutdown t;
    1.39 +}
    1.40 +
    1.41 +class IUnknown {
    1.42 +public:
    1.43 +    virtual long QueryInterface() = 0;
    1.44 +    virtual long AddRef() = 0;
    1.45 +    virtual long Release() = 0;
    1.46 +};
    1.47 +
    1.48 +#if defined(MACOS)
    1.49 +
    1.50 +#include <Processes.h>
    1.51 +
    1.52 +class AddressSpace {
    1.53 +public:
    1.54 +    AddressSpace();
    1.55 +    Boolean contains(void* ptr);
    1.56 +private:
    1.57 +    ProcessInfoRec mInfo;
    1.58 +};
    1.59 +
    1.60 +AddressSpace::AddressSpace()
    1.61 +{
    1.62 +    ProcessSerialNumber psn = { 0, kCurrentProcess };
    1.63 +    mInfo.processInfoLength = sizeof(mInfo);
    1.64 +    ::GetProcessInformation(&psn, &mInfo);
    1.65 +}
    1.66 +
    1.67 +Boolean AddressSpace::contains(void* ptr)
    1.68 +{
    1.69 +    UInt32 start = UInt32(mInfo.processLocation);
    1.70 +    return (UInt32(ptr) >= start && UInt32(ptr) < (start + mInfo.processSize));
    1.71 +}
    1.72 +
    1.73 +const char* nsGetTypeName(void* ptr)
    1.74 +{
    1.75 +    // construct only one of these per process.
    1.76 +    static AddressSpace space;
    1.77 +	
    1.78 +    // sanity check the vtable pointer, before trying to use RTTI on the object.
    1.79 +    void** vt = *(void***)ptr;
    1.80 +    if (vt && !(unsigned(vt) & 0x3) && space.contains(vt) && space.contains(*vt)) {
    1.81 +	IUnknown* u = static_cast<IUnknown*>(ptr);
    1.82 +	const char* type = typeid(*u).name();
    1.83 +        // make sure it looks like a C++ identifier.
    1.84 +	if (type && (isalnum(type[0]) || type[0] == '_'))
    1.85 +	    return type;
    1.86 +    }
    1.87 +    return "void*";
    1.88 +}
    1.89 +
    1.90 +#endif
    1.91 +
    1.92 +// New, more "portable" Linux code is below, but this might be a useful
    1.93 +// model for other platforms, so keeping.
    1.94 +//#if defined(linux)
    1.95 +#if 0
    1.96 +
    1.97 +#include <signal.h>
    1.98 +#include <setjmp.h>
    1.99 +
   1.100 +static jmp_buf context;
   1.101 +
   1.102 +static void handler(int signum)
   1.103 +{
   1.104 +    longjmp(context, signum);
   1.105 +}
   1.106 +
   1.107 +#define attempt() setjmp(context)
   1.108 +
   1.109 +class Signaller {
   1.110 +public:
   1.111 +    Signaller(int signum);
   1.112 +    ~Signaller();
   1.113 +
   1.114 +private:
   1.115 +    typedef void (*handler_t) (int signum);
   1.116 +    int mSignal;
   1.117 +    handler_t mOldHandler;
   1.118 +};
   1.119 +
   1.120 +Signaller::Signaller(int signum)
   1.121 +    : mSignal(signum), mOldHandler(signal(signum, &handler))
   1.122 +{
   1.123 +}
   1.124 +
   1.125 +Signaller::~Signaller()
   1.126 +{
   1.127 +    signal(mSignal, mOldHandler);
   1.128 +}
   1.129 +
   1.130 +// The following are pointers that bamboozle our otherwise feeble
   1.131 +// attempts to "safely" collect type names.
   1.132 +//
   1.133 +// XXX this kind of sucks because it means that anyone trying to use
   1.134 +// this without NSPR will get unresolved symbols when this library
   1.135 +// loads. It's also not very extensible. Oh well: FIX ME!
   1.136 +extern "C" {
   1.137 +    // from nsprpub/pr/src/io/priometh.c (libnspr4.so)
   1.138 +    extern void* _pr_faulty_methods;
   1.139 +};
   1.140 +
   1.141 +static inline int
   1.142 +sanity_check_vtable_i386(void** vt)
   1.143 +{
   1.144 +    // Now that we're "safe" inside the signal handler, we can
   1.145 +    // start poking around. If we're really an object with
   1.146 +    // RTTI, then the second entry in the vtable should point
   1.147 +    // to a function.
   1.148 +    //
   1.149 +    // Let's see if the second entry:
   1.150 +    //
   1.151 +    // 1) looks like a 4-byte aligned pointer
   1.152 +    //
   1.153 +    // 2) points to something that looks like the following
   1.154 +    //    i386 instructions:
   1.155 +    //
   1.156 +    //    55     push %ebp
   1.157 +    //    89e5   mov  %esp,%ebp
   1.158 +    //    53     push %ebx
   1.159 +    //
   1.160 +    //    or
   1.161 +    //
   1.162 +    //    55     push %ebp
   1.163 +    //    89e5   mov  %esp,%ebp
   1.164 +    //    56     push %esi
   1.165 +    //
   1.166 +    //    (which is the standard function prologue generated
   1.167 +    //    by egcs, plus a ``signature'' instruction that appears
   1.168 +    //    in the typeid() function's implementation).
   1.169 +    unsigned char** fp1 = reinterpret_cast<unsigned char**>(vt) + 1;
   1.170 +
   1.171 +    // Does it look like an address?
   1.172 +    unsigned char* ip = *fp1;
   1.173 +    if ((unsigned(ip) & 3) != 0)
   1.174 +        return 0;
   1.175 +
   1.176 +    // Does it look like it refers to the standard prologue?
   1.177 +    static unsigned char prologue[] = { 0x55, 0x89, 0xE5 };
   1.178 +    for (unsigned i = 0; i < sizeof(prologue); ++i)
   1.179 +        if (*ip++ != prologue[i])
   1.180 +            return 0;
   1.181 +
   1.182 +    // Is the next instruction a `push %ebx' or `push %esi'?
   1.183 +    if (*ip == 0x53 || *ip == 0x56) {
   1.184 +        return 1;
   1.185 +    }
   1.186 +
   1.187 +    // Nope.  There's another variant that has a `sub' instruction,
   1.188 +    // followed by a `cmpl' and a `jne'.  Check for that.
   1.189 +    if (ip[0] == 0x83 && ip[1] == 0xec     // sub
   1.190 +        && ip[3] == 0x83 && ip[4] == 0x3d  // cmpl
   1.191 +        && ip[10] == 0x75                  // jne
   1.192 +        ) {
   1.193 +        return 1;
   1.194 +    }
   1.195 +
   1.196 +    return 0;
   1.197 +}
   1.198 +
   1.199 +static inline int
   1.200 +sanity_check_vtable_ppc(void** vt)
   1.201 +{
   1.202 +    // XXX write me!
   1.203 +    return 1;
   1.204 +}
   1.205 +
   1.206 +#if defined(__i386)
   1.207 +#  define SANITY_CHECK_VTABLE(vt) (sanity_check_vtable_i386(vt))
   1.208 +#elif defined(PPC)
   1.209 +#  define SANITY_CHECK_VTABLE(vt) (sanity_check_vtable_ppc(vt))
   1.210 +#else
   1.211 +#  define SANITY_CHECK_VTABLE(vt) (1)
   1.212 +#endif
   1.213 +
   1.214 +const char* nsGetTypeName(void* ptr)
   1.215 +{
   1.216 +    // sanity check the vtable pointer, before trying to use RTTI on the object.
   1.217 +    void** vt = *(void***)ptr;
   1.218 +    if (vt && !(unsigned(vt) & 3) && (vt != &_pr_faulty_methods)) {
   1.219 +        Signaller s1(SIGSEGV);
   1.220 +        if (attempt() == 0) {
   1.221 +            if (SANITY_CHECK_VTABLE(vt)) {
   1.222 +                // Looks like a function: what the hell, let's call it.
   1.223 +                IUnknown* u = static_cast<IUnknown*>(ptr);
   1.224 +                const char* type = typeid(*u).name();
   1.225 +                // EGCS seems to prefix a length string.
   1.226 +                while (isdigit(*type)) ++type;
   1.227 +                return type;
   1.228 +            }
   1.229 +        }
   1.230 +    }
   1.231 +    return "void*";
   1.232 +}
   1.233 +
   1.234 +#endif
   1.235 +
   1.236 +#if defined(linux) || defined(XP_MACOSX)
   1.237 +
   1.238 +#define __USE_GNU
   1.239 +#include <dlfcn.h>
   1.240 +#include <ctype.h>
   1.241 +#include <string.h>
   1.242 +
   1.243 +const char* nsGetTypeName(void* ptr)
   1.244 +{
   1.245 +#if defined(__GXX_ABI_VERSION) && __GXX_ABI_VERSION >= 100 /* G++ V3 ABI */
   1.246 +    const int expected_offset = 8;
   1.247 +    const char vtable_sym_start[] = "_ZTV";
   1.248 +    const int vtable_sym_start_length = sizeof(vtable_sym_start) - 1;
   1.249 +#else
   1.250 +    const int expected_offset = 0;
   1.251 +    const char vtable_sym_start[] = "__vt_";
   1.252 +    const int vtable_sym_start_length = sizeof(vtable_sym_start) - 1;
   1.253 +#endif
   1.254 +    void* vt = *(void**)ptr;
   1.255 +    Dl_info info;
   1.256 +    // If dladdr fails, if we're not at the expected offset in the vtable,
   1.257 +    // or if the symbol name isn't a vtable symbol name, return "void*".
   1.258 +    if ( !dladdr(vt, &info) ||
   1.259 +         ((char*)info.dli_saddr) + expected_offset != vt ||
   1.260 +         !info.dli_sname ||
   1.261 +         strncmp(info.dli_sname, vtable_sym_start, vtable_sym_start_length))
   1.262 +        return "void*";
   1.263 +
   1.264 +    // skip the garbage at the beginning of things like
   1.265 +    // __vt_14nsRootBoxFrame (gcc 2.96) or _ZTV14nsRootBoxFrame (gcc 3.0)
   1.266 +    const char* rv = info.dli_sname + vtable_sym_start_length;
   1.267 +    while (*rv && isdigit(*rv))
   1.268 +        ++rv;
   1.269 +    return rv;
   1.270 +}
   1.271 +
   1.272 +#endif
   1.273 +
   1.274 +#ifdef XP_WIN32
   1.275 +const char* nsGetTypeName(void* ptr)
   1.276 +{
   1.277 +  //TODO: COMPLETE THIS
   1.278 +    return "void*";
   1.279 +}
   1.280 +
   1.281 +#endif //XP_WIN32

mercurial