Bug 727955 - Support shark jit profiling via DL trick (r=wmaddox+)
authorEdwin Smith <edwsmith@adobe.com>
Thu, 01 Mar 2012 01:45:55 +0000
changeset 7242 c57eb8acfee0903c17ce990ee4be0e7708ea4f75
parent 7241 9023e2bb2ef3e0c3cbb6a487bb4dc5998c03d11a
child 7243 24feadfe7df228a2908098002c50faf5f67fc42d
push id4176
push userfklockii@adobe.com
push dateThu, 01 Mar 2012 17:44:36 +0000
reviewerswmaddox
bugs727955
Bug 727955 - Support shark jit profiling via DL trick (r=wmaddox+)
build/avmfeatures.py
core/AvmCore.cpp
core/AvmCore.h
core/CodegenLIR.cpp
core/CodegenLIR.h
core/InvokerCompiler.cpp
core/LirHelper.cpp
core/LirHelper.h
core/avmfeatures.as
core/avmfeatures.cpp
core/avmfeatures.h
core/exec-jit.cpp
core/exec.cpp
core/exec.h
nanojit/Assembler.cpp
nanojit/Assembler.h
nanojit/CodeAlloc.cpp
nanojit/CodeAlloc.h
nanojit/LIR.cpp
nanojit/LIRopcode.tbl
shell/ShellCore.cpp
shell/ShellCore.h
shell/avmshell-features.h
shell/avmshell.cpp
utils/sharkprof/.cproject
utils/sharkprof/.project
utils/sharkprof/Makefile
utils/sharkprof/README-2.txt
utils/sharkprof/README.txt
utils/sharkprof/dinkuuid.cpp
utils/sharkprof/jit.s
utils/sharkprof/log2s.cpp
utils/sharkprof/logconv.cpp
utils/sharkprof/mksyms
--- a/build/avmfeatures.py
+++ b/build/avmfeatures.py
@@ -60,16 +60,21 @@ def featureSettings(o):
         args += "-DAVMFEATURE_ALLOCATION_SAMPLER=1 "
     if (arg == False):
         args += "-DAVMFEATURE_ALLOCATION_SAMPLER=0 "
     arg = o.getBoolArg("vtune")
     if (arg == True):
         args += "-DAVMFEATURE_VTUNE=1 "
     if (arg == False):
         args += "-DAVMFEATURE_VTUNE=0 "
+    arg = o.getBoolArg("shark")
+    if (arg == True):
+        args += "-DAVMFEATURE_SHARK=1 "
+    if (arg == False):
+        args += "-DAVMFEATURE_SHARK=0 "
     if o.getBoolArg("jit"):
         args += "-DAVMFEATURE_JIT=1 -DAVMFEATURE_WORDCODE_INTERP=0 -DAVMFEATURE_AOT=0 "
     arg = o.getBoolArg("float")
     if (arg == True):
         args += "-DAVMFEATURE_FLOAT=1 "
     if (arg == False):
         args += "-DAVMFEATURE_FLOAT=0 "
     arg = o.getBoolArg("osr")
--- a/core/AvmCore.cpp
+++ b/core/AvmCore.cpp
@@ -188,16 +188,17 @@ namespace avmplus
     const uint32_t AvmCore::verbose_default = 0; // all off
     const bool AvmCore::methodNames_default = true;
     const bool AvmCore::oldVectorMethodNames_default = true;
     const bool AvmCore::verifyall_default = false;
     const bool AvmCore::verifyonly_default = false;
     const bool AvmCore::verifyquiet_default = false;
     const Runmode AvmCore::runmode_default = RM_mixed;
     const uint32_t AvmCore::osr_threshold_default = OSR_THRESHOLD_DEFAULT;
+    const uint32_t AvmCore::jitprof_level_default = 0; // no logging.
     const bool AvmCore::interrupts_default = false;
     const bool AvmCore::jitordie_default = false;
 
 #ifdef AVMPLUS_VERBOSE
     #ifdef VMCFG_NANOJIT
         const uint32_t AvmCore::DEFAULT_VERBOSE_ON = (uint32_t)~0 & ~(nanojit::LC_FragProfile); // LC_FragProfile changes generated code!?!
     #else
         const uint32_t AvmCore::DEFAULT_VERBOSE_ON = (uint32_t)~0;
@@ -405,16 +406,17 @@ namespace avmplus
         config.oldVectorMethodNames = oldVectorMethodNames_default;
 
         config.verifyall = verifyall_default;
         config.verifyonly = verifyonly_default;
 
         // jit flag forces use of jit-compiler instead of interpreter
         config.runmode = runmode_default;
         config.osr_threshold = osr_threshold_default;
+        config.jitprof_level = jitprof_level_default;
         config.jitordie = jitordie_default;
 
         config.interrupts = interrupts_default;
 
         gcInterface.SetCore(this);
         xmlEntities                 = NULL;
         exceptionFrame              = NULL;
         exceptionAddr               = NULL;
--- a/core/AvmCore.h
+++ b/core/AvmCore.h
@@ -134,16 +134,17 @@ const int kBufferPadding = 16;
         // if false, don't (Function.toString will return things like "Function-21")
         bool methodNames;
 
         // give "Vector.<*>" instead of "Vector$object", etc
         bool oldVectorMethodNames;
 
         enum Runmode runmode;
         uint32_t osr_threshold;
+        uint32_t jitprof_level;
         const char* compilePolicyRules; // JIT compilation override
 
         /**
          * If this switch is set, executing code will check the
          * "interrupted" flag to see whether an interrupt needs
          * to be handled.
          */
         bool interrupts;
@@ -623,16 +624,17 @@ const int kBufferPadding = 16;
         static const uint32_t verbose_default;
         static const bool methodNames_default;
         static const bool oldVectorMethodNames_default;
         static const bool verifyall_default;
         static const bool verifyonly_default;
         static const bool verifyquiet_default;
         static const Runmode runmode_default;
         static const uint32_t osr_threshold_default;
+        static const uint32_t jitprof_level_default;
         static const bool interrupts_default;
         static const bool jitordie_default;
 
 #ifdef AVMPLUS_VERBOSE
         // default set of flags to enable for "verbose" with no specific qualifiers
         static const uint32_t DEFAULT_VERBOSE_ON;
 #endif
 
--- a/core/CodegenLIR.cpp
+++ b/core/CodegenLIR.cpp
@@ -668,17 +668,18 @@ namespace avmplus
         get_cache_builder(*alloc1, *pool->codeMgr),
         set_cache_builder(*alloc1, *pool->codeMgr),
         prolog(NULL),
         skip_ins(NULL),
         specializedCallHashMap(NULL),
         builtinFunctionOptimizerHashMap(NULL),
         blockLabels(NULL),
         cseFilter(NULL),
-        noise()
+        noise(),
+        jit_debug_info(NULL)
         DEBUGGER_ONLY(, haveDebugger(core->debugger() != NULL) )
     {
         #ifdef AVMPLUS_MAC_CARBON
         setjmpInit();
         #endif
 
         verbose_only(
             if (pool->isVerbose(VB_jit, i)) {
@@ -2719,25 +2720,63 @@ FLOAT_ONLY(           !(v.sst_mask == (1
         case OP_lookupswitch:
             emit(opcode, uintptr_t(pc + imm24), imm30b /*count*/);
             break;
         case OP_throw:
         case OP_returnvalue:
         case OP_returnvoid:
             emit(opcode, sp);
             break;
-        case OP_debugfile:
-        {
-#if defined VMCFG_VTUNE
-            emit(opcode, (uintptr_t)pool->getString(imm30));
-#elif defined DEBUGGER
-            if (haveDebugger)
-                emit(opcode, (uintptr_t)pool->getString(imm30));
-#endif
-           break;
+        case OP_debugfile: {
+            if (haveDebugger || haveVTune || jit_observer) {
+              String* filename = pool->getString(imm30); (void)filename;
+#ifdef DEBUGGER
+              if (haveDebugger) {
+                  LIns* debugger = loadIns(LIR_ldp, offsetof(AvmCore, _debugger),
+                                           coreAddr, ACCSET_OTHER, LOAD_CONST);
+                  callIns(FUNCTIONID(debugFile), 2,
+                          debugger,
+                          InsConstPtr(filename));
+              }
+#endif // DEBUGGER
+#ifdef VMCFG_VTUNE
+              Ins(LIR_file, InsConstPtr(filename));
+#endif /* VMCFG_VTUNE */
+              if (jit_observer) {
+                  JITDebugInfo* jdi = initJitDebugInfo();
+                  JITDebugInfo::Info* info = new (*lir_alloc) JITDebugInfo::Info(JITDebugInfo::kFile);
+                  info->file = imm30;
+                  jdi->add(info);
+                  Ins(LIR_pc, InsConstPtr(&info->pc));
+              }
+            }
+            break;
+        }
+        case OP_debugline: {
+            if (haveDebugger || haveVTune || jit_observer) {
+                // we actually do generate code for these, in debugger mode
+#ifdef DEBUGGER
+                if (haveDebugger) {
+                    LIns* debugger = loadIns(LIR_ldp, offsetof(AvmCore, _debugger),
+                                             coreAddr, ACCSET_OTHER, LOAD_CONST);
+                    callIns(FUNCTIONID(debugLine), 2, debugger, InsConst(imm30));
+                }
+#endif // DEBUGGER
+#ifdef VMCFG_VTUNE
+                Ins(LIR_line, InsConst(imm30));
+#endif /* VMCFG_VTUNE */
+                if (jit_observer) {
+                    JITDebugInfo* jdi = initJitDebugInfo();
+                    JITDebugInfo::Info* info = new (*lir_alloc) JITDebugInfo::Info(JITDebugInfo::kLine);
+                    info->line = imm30; // filename pool index
+                    jdi->add(info);
+                    Ins(LIR_pc, InsConstPtr(&info->pc));
+                }
+            }
+            break;
         }
         case OP_dxns:
         {
             Stringp str = pool->getString(imm30);  // assume been checked already
             emit(opcode, (uintptr_t)str);
             break;
         }
         case OP_dxnslate:
@@ -2985,28 +3024,16 @@ FLOAT_ONLY(           !(v.sst_mask == (1
         case OP_bitnot:
             emit(opcode, sp, 0, INT_TYPE);
             break;
 
         case OP_typeof:
             emit(opcode, sp, 0, STRING_TYPE);
             break;
 
-        case OP_debugline:
-        {
-#if defined VMCFG_VTUNE
-            emit(opcode, imm30);
-#elif defined DEBUGGER
-            if (haveDebugger) {
-                // we actually do generate code for these, in debugger mode
-                emit(opcode, imm30);
-            }
-#endif
-           break;
-        }
         case OP_nextvalue:
         case OP_nextname:
             emit(opcode, 0, 0, NULL);
             break;
 
         case OP_hasnext:
             emit(opcode, 0, 0, INT_TYPE);
             break;
@@ -7253,53 +7280,16 @@ FLOAT_ONLY(           !(v.sst_mask == (1
             case OP_dxnslate:
             {
                 int32_t index = (int32_t) op1;
                 LIns* atom = loadAtomRep(index);
                 callIns(FUNCTIONID(setDxnsLate), 3, coreAddr, methodFrame, atom);
                 break;
             }
 
-            /*
-             * debugger instructions
-             */
-            case OP_debugfile:
-            {
-            #ifdef DEBUGGER
-            if (haveDebugger) {
-                // todo refactor api's so we don't have to pass argv/argc
-                LIns* debugger = loadIns(LIR_ldp, offsetof(AvmCore, _debugger), coreAddr, ACCSET_OTHER, LOAD_CONST);
-                callIns(FUNCTIONID(debugFile), 2,
-                        debugger,
-                        InsConstPtr((String*)op1));
-            }
-            #endif // DEBUGGER
-            #ifdef VMCFG_VTUNE
-                Ins(LIR_file, InsConstPtr((String*)op1));
-            #endif /* VMCFG_VTUNE */
-                break;
-            }
-
-            case OP_debugline:
-            {
-            #ifdef DEBUGGER
-            if (haveDebugger) {
-                // todo refactor api's so we don't have to pass argv/argc
-                LIns* debugger = loadIns(LIR_ldp, offsetof(AvmCore, _debugger), coreAddr, ACCSET_OTHER, LOAD_CONST);
-                callIns(FUNCTIONID(debugLine), 2,
-                        debugger,
-                        InsConst((int32_t)op1));
-            }
-            #endif // DEBUGGER
-            #ifdef VMCFG_VTUNE
-                Ins(LIR_line, InsConst((int32_t)op1));
-            #endif /* VMCFG_VTUNE */
-                break;
-            }
-
             default:
             {
                 AvmAssert(false); // unsupported
             }
         }
 
     } // emit()
 
@@ -8813,16 +8803,26 @@ FLOAT_ONLY(           !(v.sst_mask == (1
         {}
 
         cfg.printGmlCfg(f, frag->lirbuf->printer, ignore);
         fclose(f);
     }
 
 #endif
 
+    JITDebugInfo* CodegenLIR::initJitDebugInfo()
+    {
+        // allocate JITDebugInfo for any JITObserver
+        if (!jit_debug_info) {
+            jit_debug_info = new (*lir_alloc) JITDebugInfo();
+            jit_debug_info->info = 0;
+        }
+        return jit_debug_info;
+    }
+
     // return pointer to generated code on success, NULL on failure (frame size too large)
     GprMethodProc CodegenLIR::emitMD()
     {
         deadvars();  // deadvars_kill() will add livep(vars) or livep(tags) if necessary
 
         // do this very last so it's after livep(vars)
         frag->lastIns = livep(undefConst);
 
@@ -8915,16 +8915,18 @@ FLOAT_ONLY(           !(v.sst_mask == (1
         PERFM_NVPROF("IR", frag->lirbuf->insCount());
 
         GprMethodProc code;
         bool keep = !assm->error();
         if (keep) {
             // save pointer to generated code
             code = (GprMethodProc) frag->code();
             PERFM_NVPROF("JIT method bytes", CodeAlloc::size(assm->codeList));
+            if (jit_observer)
+                jit_observer->notifyMethodJITed(info, assm->codeList, jit_debug_info);
         } else {
             verbose_only (if (pool->isVerbose(VB_execpolicy))
                 AvmLog("execpolicy revert to interp (%d) compiler error %d \n", info->unique_method_id(), assm->error());
             )
             // assm puked, or we did something untested, so interpret.
             code = NULL;
             PERFM_NVPROF("lir-error",1);
         }
--- a/core/CodegenLIR.h
+++ b/core/CodegenLIR.h
@@ -352,21 +352,28 @@ namespace avmplus
         HashMap<LIns*, LIns*> *specializedCallHashMap;
         HashMap<uint32_t, uint32_t> *builtinFunctionOptimizerHashMap;
         HashMap<const uint8_t*, CodegenLabel*> *blockLabels;
         LirWriter* redirectWriter;
         CseFilter* cseFilter; // The CseFilter instance for this method, or NULL if none.
         JITNoise noise;
         verbose_only(VerboseWriter *vbWriter;)
         verbose_only(LInsPrinter* vbNames;)
+        JITDebugInfo *jit_debug_info;
+
 #ifdef DEBUGGER
         bool haveDebugger;
 #else
         static const bool haveDebugger = false;
 #endif
+#if defined VMCFG_VTUNE
+        static const bool haveVTune = true;
+#else
+        static const bool haveVTune = false;
+#endif
 #ifdef DEBUG
         /** jit_sst is an array of sst_mask bytes, used to double check that we
          *  are modeling storage types the same way the verifier did for us.
          *  Mismatches are caught in writeOpcodeVerified() after the Verifier has
          *  updated FrameValue.sst_mask. */
         uint16_t *jit_sst;   // array of SST masks to sanity check with FrameState
         ValidateWriter* validate3; // ValidateWriter for method body.
 #endif
@@ -603,16 +610,18 @@ namespace avmplus
         void emitFloat4Less(Traits* result);
         void emitFloat4LessOrEqual(Traits* result);
         void emitFloat4Equal(Traits* result);
         void emitFloat4NotEqual(Traits* result);
 #endif
 
         LIns *optimizeIndexArgumentType(int32_t sp, Traits** indexType);
 
+        JITDebugInfo* initJitDebugInfo();
+
     public:
         ~CodegenLIR();
         CodegenLIR(MethodInfo* info, MethodSignaturep ms, Toplevel* toplevel,
                    OSR *osr_state);
         GprMethodProc emitMD();
 
         // May return true if JIT will always fail based on information known prior to invocation.
         static bool jitWillFail(const MethodSignaturep ms);
--- a/core/InvokerCompiler.cpp
+++ b/core/InvokerCompiler.cpp
@@ -303,16 +303,18 @@ namespace avmplus
         assm->endAssembly(frag);
 
         verbose_only(
             assm->_outputCache = 0;
             for (Seq<char*>* p = asmOutput.get(); p != NULL; p = p->tail)
                 assm->outputf("%s", p->head);
         );
         if (!assm->error()) {
+            if (jit_observer)
+                jit_observer->notifyInvokerJITed(method, assm->codeList);
             if (method->isNative()) {
                 PERFM_NVPROF("C++ invoker bytes", CodeAlloc::size(assm->codeList));
             } else {
                 PERFM_NVPROF("JIT invoker bytes", CodeAlloc::size(assm->codeList));
             }
             return frag->code();
         } else {
             return NULL;
--- a/core/LirHelper.cpp
+++ b/core/LirHelper.cpp
@@ -39,16 +39,20 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "avmplus.h"
 
 #ifdef VMCFG_NANOJIT
 
 #include "LirHelper.h"
 
+#ifdef VMCFG_SHARK
+#include <dlfcn.h> // for dlopen and dlsym
+#endif
+
 namespace avmplus
 {
 
 bool neverReturns(const CallInfo* call)
 {
     return call == FUNCTIONID(throwAtom) ||
         call == FUNCTIONID(npe) ||
         call == FUNCTIONID(upe) ||
@@ -69,16 +73,17 @@ CodeMgr* initCodeMgr(PoolObject *pool)
     return pool->codeMgr;
 }
 
 LirHelper::LirHelper(PoolObject* pool) :
     pool(pool),
     core(pool->core),
     alloc1(mmfx_new(Allocator())),
     lir_alloc(mmfx_new(Allocator())),
+    jit_observer(((BaseExecMgr*)core->exec)->jit_observer),
 #ifdef NANOJIT_IA32
     use_cmov(pool->core->config.njconfig.i386_use_cmov)
 #else
     use_cmov(true)
 #endif
 { }
 
 LirHelper::~LirHelper()
@@ -371,17 +376,18 @@ LIns* LirHelper::IncrementLIREmitter::op
             const CallInfo* addfunc = _lh->pool->hasFloatSupport() ? FUNCTIONID(op_add) : FUNCTIONID(op_add_nofloat);
             return _lh->callIns( addfunc, 3, _lh->coreAddr, oper, _lh->InsConstAtom(_shouldDecrement? _lh->core->kFltMinusOne:_lh->core->kFltOne ));
         }
     default: return TODO("Unexpected builtin type in IncrementLIREmitter");
     }
 }
 
 #endif
-}
+
+} // end namespace avmplus
 
 //
 // The following methods implement Service Provider API's defined in
 // nanojit, which must be implemented by the nanojit embedder.
 //
 namespace nanojit
 {
     int StackFilter::getTop(LIns* /*br*/) {
@@ -414,16 +420,88 @@ namespace nanojit
 
     void Allocator::freeChunk(void* p) {
         mmfx_free(p);
     }
 
     void Allocator::postReset() {
     }
 
+#ifdef VMCFG_SHARK
+
+#if defined VMCFG_64BIT
+    static const char* JIT_SO_PATH = "/tmp/jit64.so";
+#elif defined VMCFG_32BIT
+    static const char* JIT_SO_PATH = "/tmp/jit32.so";
+#else
+#   error "unsupported system"
+#endif
+
+    // TODO: lock access to these statics.
+    static void *sHandle = NULL;
+    static char *sStart = NULL;
+    static char *sEnd = NULL;
+    static char *sCur = NULL;
+    size_t overage = 0;
+
+    void* CodeAlloc::allocCodeChunk(size_t nbytes)
+    {
+        if (!sHandle) {
+            sHandle = dlopen(JIT_SO_PATH, RTLD_NOW);
+            if (sHandle) {
+                // Get the bounds of the code area by looking for known symbols,
+                // then make that area writable.
+                sStart = (char*) dlsym(sHandle, "_jitStart");
+                sEnd = (char*) dlsym(sHandle, "_jitEnd");
+                if (!mprotect(sStart, sEnd - sStart,
+                              PROT_READ | PROT_WRITE | PROT_EXEC))
+                    sCur = sStart;
+            } else {
+                const char *err = dlerror();
+                fprintf(stderr, "dlopen error: %s\n", err);
+                // will fall through to AVMPI_allocateCodeMemory below.
+            }
+        }
+
+        if (sCur) {
+            char *next = sCur + nbytes;
+            if (next < sEnd) {
+                char *alloc = sCur;
+                sCur = next;
+                return alloc;
+            }
+        }
+
+        // Out of space in jit.so, fall back to AVMPI to get code mem.
+        overage += nbytes;
+        fprintf(stderr, "out of jit.so memory, allocated %luKB from system\n",
+               overage / 1024);
+        char* mem = (char*) AVMPI_allocateCodeMemory(nbytes);
+        mprotect((maddr_ptr) mem, (unsigned int) nbytes,
+                 PROT_EXEC | PROT_READ | PROT_WRITE);
+
+        return mem;
+    }
+
+    void CodeAlloc::freeCodeChunk(void* addr, size_t nbytes)
+    {
+        if (addr < sStart || addr >= sEnd) {
+            // Did not come from DL region.
+            AVMPI_freeCodeMemory(addr, nbytes);
+        }
+    }
+
+    void CodeAlloc::markCodeChunkExec(void* /*addr*/, size_t /*nbytes*/) {
+    }
+
+    void CodeAlloc::markCodeChunkWrite(void* /*addr*/, size_t /*nbytes*/) {
+    }
+
+#else // !VMCFG_SHARK
+
     void* CodeAlloc::allocCodeChunk(size_t nbytes) {
         return AVMPI_allocateCodeMemory(nbytes);
     }
 
     void CodeAlloc::freeCodeChunk(void* addr, size_t nbytes) {
         AVMPI_freeCodeMemory(addr, nbytes);
     }
 
@@ -431,16 +509,17 @@ namespace nanojit
         //printf("protect   %d %p\n", (int)nbytes, addr);
         AVMPI_makeCodeMemoryExecutable(addr, nbytes, true); // RX
     }
 
     void CodeAlloc::markCodeChunkWrite(void* addr, size_t nbytes) {
         //printf("unprotect %d %p\n", (int)nbytes, addr);
         AVMPI_makeCodeMemoryExecutable(addr, nbytes, false); // RW
     }
+#endif
 
 #ifdef DEBUG
     // Note this method should only be called during debug builds.
     bool CodeAlloc::checkChunkMark(void* addr, size_t nbytes, bool isExec) {
         bool b = true;
 
         // iterate over each page checking permission bits
         size_t psize = VMPI_getVMPageSize();
@@ -451,17 +530,17 @@ namespace nanojit
             /* windows */
             MEMORY_BASIC_INFORMATION buf;
             VMPI_memset(&buf, 0, sizeof(MEMORY_BASIC_INFORMATION));
             SIZE_T sz = VirtualQuery((LPCVOID)n, &buf, sizeof(buf));
             NanoAssert(sz > 0);
             b = isExec ? buf.Protect == PAGE_EXECUTE_READ
                        : buf.Protect == PAGE_READWRITE;
             NanoAssert(b);
-#elif defined(__MACH30__)
+#elif defined(__MACH30__) && !defined(VMCFG_SHARK)
             /* mach / osx */
             vm_address_t vmaddr = (vm_address_t)addr;
             vm_size_t vmsize = psize;
             vm_region_basic_info_data_64_t inf;
             mach_msg_type_number_t infoCnt = sizeof(vm_region_basic_info_data_64_t);
             mach_port_t port;
             VMPI_memset(&inf, 0, infoCnt);
             kern_return_t err = vm_region_64(mach_task_self(), &vmaddr, &vmsize, VM_REGION_BASIC_INFO_64, (vm_region_info_t)&inf, &infoCnt, &port);
--- a/core/LirHelper.h
+++ b/core/LirHelper.h
@@ -201,16 +201,17 @@ namespace avmplus
     protected: // data
         LirWriter *lirout;
         Fragment *frag;
         PoolObject* pool;
         AvmCore *core;
         LIns *coreAddr;
         Allocator* alloc1;    // allocator used in first pass, while writing LIR
         Allocator* lir_alloc; // allocator with LIR buffer lifetime
+        JITObserver* jit_observer;
         bool const use_cmov;
         debug_only(ValidateWriter* validate1;)
         debug_only(ValidateWriter* validate2;)
 
 #ifdef VMCFG_FLOAT
 #include "LirHelper-Emitters.h"
 #endif
     };
--- a/core/avmfeatures.as
+++ b/core/avmfeatures.as
@@ -440,30 +440,40 @@ var FEATURES =
            It is known that the Flash Player wants to enable this if SCRIPT_DEBUGGER
            is enabled in the Player code. </desc>
 
     <name> AVMFEATURE_ALLOCATION_SAMPLER </name>
     <defines> AVMPLUS_SAMPLER </defines>
   </feature>
 
   <feature>
-    <desc> Selects vtune profiling of jit'd code.  Requires Windows x86,
+    <desc> Selects VTune profiling of jit'd code.  Requires Windows x86,
            and could support windows x64 after more testing.
            turns on AVMPLUS_VERBOSE solely to get method/class names for profiling
     </desc>
 
     <name>     AVMFEATURE_VTUNE  </name>
     <requires> AVMSYSTEM_WIN32 </requires>
     <requires> AVMSYSTEM_IA32 </requires>
     <defines>  VMCFG_VTUNE </defines>
 
     <defines>  AVMPLUS_VERBOSE </defines>
   </feature>
 
   <feature>
+    <desc> Selects Shark profiling of jit'd code.  MacOS 10.6.  This technique
+           should work for oprofile on linux and/or android, with more tweaks.
+           See README in utils/sharkprof.
+    </desc>
+    <name>     AVMFEATURE_SHARK  </name>
+    <requires> AVMSYSTEM_MAC </requires>
+    <defines>  VMCFG_SHARK </defines>
+  </feature>
+
+  <feature>
     <desc> Enables the just-in-time compiler.  This will typically increase performance
            significantly but may result in significantly higher memory consumption. </desc>
 
     <name> AVMFEATURE_JIT  </name>
     <requires>
      <exactly-one>
       <name> AVMSYSTEM_IA32 </name>
       <name> AVMSYSTEM_AMD64 </name>
--- a/core/avmfeatures.cpp
+++ b/core/avmfeatures.cpp
@@ -111,16 +111,19 @@ const char * const avmfeatures = ""
     "AVMFEATURE_DEBUGGER_STUB;"
   #endif
   #if AVMFEATURE_ALLOCATION_SAMPLER
     "AVMFEATURE_ALLOCATION_SAMPLER;"
   #endif
   #if AVMFEATURE_VTUNE
     "AVMFEATURE_VTUNE;"
   #endif
+  #if AVMFEATURE_SHARK
+    "AVMFEATURE_SHARK;"
+  #endif
   #if AVMFEATURE_JIT
     "AVMFEATURE_JIT;"
   #endif
   #if AVMFEATURE_FLOAT
     "AVMFEATURE_FLOAT;"
   #endif
   #if AVMFEATURE_OSR
     "AVMFEATURE_OSR;"
--- a/core/avmfeatures.h
+++ b/core/avmfeatures.h
@@ -79,16 +79,17 @@
 #undef VMCFG_DEBUGGER
 #undef VMCFG_VERIFYALL
 #undef AVMPLUS_VERBOSE
 #undef DEBUGGER
 #undef VMCFG_DEBUGGER_STUB
 #undef AVMPLUS_SAMPLER
 #undef VMCFG_VTUNE
 #undef AVMPLUS_VERBOSE
+#undef VMCFG_SHARK
 #undef VMCFG_NANOJIT
 #undef FEATURE_NANOJIT
 #undef VMCFG_FLOAT
 #undef VMCFG_GENERIC_FLOAT4
 #undef VMCFG_OSR
 #undef VMCFG_COMPILEPOLICY
 #undef VMCFG_AOT
 #undef VMCFG_AOTSHELL
@@ -405,25 +406,36 @@
  */
 #if !defined AVMFEATURE_ALLOCATION_SAMPLER || AVMFEATURE_ALLOCATION_SAMPLER != 0 && AVMFEATURE_ALLOCATION_SAMPLER != 1
 #  error "AVMFEATURE_ALLOCATION_SAMPLER must be defined and 0 or 1 (only)."
 #endif
 
 
 /* AVMFEATURE_VTUNE
  *
- * Selects vtune profiling of jit'd code.  Requires Windows x86,
+ * Selects VTune profiling of jit'd code.  Requires Windows x86,
  * and could support windows x64 after more testing.
  * turns on AVMPLUS_VERBOSE solely to get method/class names for profiling
  */
 #if !defined AVMFEATURE_VTUNE || AVMFEATURE_VTUNE != 0 && AVMFEATURE_VTUNE != 1
 #  error "AVMFEATURE_VTUNE must be defined and 0 or 1 (only)."
 #endif
 
 
+/* AVMFEATURE_SHARK
+ *
+ * Selects Shark profiling of jit'd code.  MacOS 10.6.  This technique
+ * should work for oprofile on linux and/or android, with more tweaks.
+ * See README in utils/sharkprof.
+ */
+#if !defined AVMFEATURE_SHARK || AVMFEATURE_SHARK != 0 && AVMFEATURE_SHARK != 1
+#  error "AVMFEATURE_SHARK must be defined and 0 or 1 (only)."
+#endif
+
+
 /* AVMFEATURE_JIT
  *
  * Enables the just-in-time compiler.  This will typically increase performance
  * significantly but may result in significantly higher memory consumption.
  */
 #if !defined AVMFEATURE_JIT || AVMFEATURE_JIT != 0 && AVMFEATURE_JIT != 1
 #  error "AVMFEATURE_JIT must be defined and 0 or 1 (only)."
 #endif
@@ -901,16 +913,21 @@
 #if AVMFEATURE_VTUNE
 #  if !AVMSYSTEM_WIN32
 #    error "AVMSYSTEM_WIN32 is required for AVMFEATURE_VTUNE"
 #  endif
 #  if !AVMSYSTEM_IA32
 #    error "AVMSYSTEM_IA32 is required for AVMFEATURE_VTUNE"
 #  endif
 #endif
+#if AVMFEATURE_SHARK
+#  if !AVMSYSTEM_MAC
+#    error "AVMSYSTEM_MAC is required for AVMFEATURE_SHARK"
+#  endif
+#endif
 #if AVMFEATURE_JIT
 #if AVMSYSTEM_IA32+AVMSYSTEM_AMD64+AVMSYSTEM_ARM+AVMSYSTEM_PPC+AVMSYSTEM_SPARC+AVMSYSTEM_MIPS+AVMSYSTEM_SH4 != 1
 #  error "Exactly one of AVMSYSTEM_IA32,AVMSYSTEM_AMD64,AVMSYSTEM_ARM,AVMSYSTEM_PPC,AVMSYSTEM_SPARC,AVMSYSTEM_MIPS,AVMSYSTEM_SH4 must be defined."
 #endif
 
 #  if !AVMFEATURE_ABC_INTERP
 #    error "AVMFEATURE_ABC_INTERP is required for AVMFEATURE_JIT"
 #  endif
@@ -1175,16 +1192,19 @@
 #  define AVMPLUS_SAMPLER
 #endif
 #if AVMFEATURE_VTUNE
 #  define VMCFG_VTUNE
 #endif
 #if AVMFEATURE_VTUNE
 #  define AVMPLUS_VERBOSE
 #endif
+#if AVMFEATURE_SHARK
+#  define VMCFG_SHARK
+#endif
 #if AVMFEATURE_JIT
 #  define VMCFG_NANOJIT
 #endif
 #if AVMFEATURE_JIT
 #  define FEATURE_NANOJIT
 #endif
 #if AVMFEATURE_FLOAT
 #  define VMCFG_FLOAT
--- a/core/exec-jit.cpp
+++ b/core/exec-jit.cpp
@@ -39,16 +39,20 @@
  * ***** END LICENSE BLOCK ***** */
 
 #include "avmplus.h"
 #include "Interpreter.h"
 
 #ifdef VMCFG_NANOJIT
 #include "CodegenLIR.h"
 
+#ifdef VMCFG_SHARK
+#include <dlfcn.h> // dl apis for JITLoggingObserver
+#endif
+
 namespace avmplus {
 
 #ifdef VMCFG_COMPILEPOLICY
 BaseExecMgr::JitInterpRuleSet::JitInterpRuleSet(MMgc::GC* gc)
     : jit(gc,0)
     , interp(gc,0)
 {
 }
@@ -729,10 +733,242 @@ void BaseExecMgr::resolveImtSlotFull(VTa
     // the imt stub down as we go.
     uint32_t size = work_stack.length();
     for (uint32_t i = 0; i < size; ++i) {
         cur = work_stack[size-i-1];
         resolveImtSlotFromBase(cur, slot);
     }
 }
 
+/**
+ * JITLoggingObserver receives notifications after each function is
+ * JIT-compiled and generates a log file containing symbolic information
+ * that we can then post-process into a symbol-rich library for use with
+ * shark or oprofile.
+ */
+class JITLoggingObserver: public JITObserver
+{
+    FILE *_log;
+    AvmCore *_core;
+    int _logLevel;
+    const void *_curBase;
+
+public:
+    /**
+     * logLevel <= 1    log function names and ranges only
+     * logLevel > 1     include code.
+     */
+    JITLoggingObserver(AvmCore *core, int logLevel);
+    virtual ~JITLoggingObserver();
+
+    // implements JITObserver
+    void notifyMethodJITed(MethodInfo *method, const CodeList *code_info,
+                           JITDebugInfo *debug_info);
+    void notifyInvokerJITed(MethodInfo* method, const CodeList* code_info);
+private:
+    void log(uint32_t tag, size_t byte_count, const void *data);
+    void flush();
+    void updateBase(const void *addr);
+    void logMethod(const char* name, int len, const CodeList* code);
+};
+
+void BaseExecMgr::setupJit(AvmCore* core)
+{
+#ifdef VMCFG_OSR_ENV_VAR
+    if (config.osr_threshold == AvmCore::osr_threshold_default) {
+        // If osr_threshold hasn't been set, check for OSR environment var.
+        const char* osr_config = VMPI_getenv("OSR");
+        if (osr_config) {
+            int threshold = OSR::parseConfig(osr_config);
+            if (threshold != -1) {
+                // A valid threshold was found.
+                core->config.osr_threshold = threshold;
+                core->console << "USING ENV OSR THRESHOLD " << threshold << "\n";
+            }
+        }
+    }
+#endif
+
+#ifdef VMCFG_SHARK
+    // Set up JITObserver
+    if (core->config.jitprof_level)
+        jit_observer = new JITLoggingObserver(core, core->config.jitprof_level);
+#endif
+
+    (void)core;
+}
+
+#ifdef VMCFG_SHARK
+
+JITLoggingObserver::JITLoggingObserver(AvmCore *core, int logLevel)
+: _log(NULL)
+, _core(core)
+, _logLevel(logLevel)
+, _curBase(NULL)
+{
+    char path[256]; // plenty of space because %d produces at most 10 chars.
+    snprintf(path, sizeof(path) - 1, "/tmp/%d-jit.log", getpid());
+    _log = fopen(path, "w");
+}
+
+JITLoggingObserver::~JITLoggingObserver()
+{
+    if (_log)
+        fclose(_log);
+    _log = NULL;
+}
+
+/**
+ * Write and flush one log record formatted as:
+ *   uint32_t tag
+ *   uint32_t byte_count
+ *   uint8_t  bytes[byte_count]
+ */
+void JITLoggingObserver::log(uint32_t tag, size_t byte_count, const void *data)
+{
+    uint32_t size32 = (uint32_t) byte_count;
+    flockfile(_log);
+    fwrite(&tag, sizeof(tag), 1, _log);
+    fwrite(&size32, sizeof(size32), 1, _log);
+    fwrite(data, 1, size32, _log);
+    funlockfile(_log);
+}
+
+void JITLoggingObserver::flush()
+{
+    fflush(_log);
+}
+
+void JITLoggingObserver::updateBase(const void *addr)
+{
+    const void *base = NULL;
+    const char *path = NULL;
+    Dl_info dlInfo;
+    if (dladdr(addr, &dlInfo)) {
+        base = dlInfo.dli_fbase;
+        path = dlInfo.dli_fname;
+    }
+    if (base != _curBase) {
+        struct {
+            uint64_t base;
+            char fileName[4096];
+        } baseInfo = { (uint64_t) base, "" };
+        size_t len = path ? strlen(path) : 0;
+        if (len > sizeof(baseInfo.fileName))
+            len = sizeof(baseInfo.fileName);
+        memcpy(baseInfo.fileName, path, len);
+        log('base', sizeof(baseInfo) - sizeof(baseInfo.fileName) + len, &baseInfo);
+        _curBase = base;
+    }
+}
+
+/**
+ * Log data about jit-compiling this method.  Log record sequence is:
+ *
+ *   meth (blok [code])* (line|file)*
+ */
+void JITLoggingObserver::notifyMethodJITed(MethodInfo *method,
+                                           const CodeList* code_info,
+                                           JITDebugInfo *debug_info)
+{
+    if (!_log)
+        return;
+
+    Stringp name = method->getMethodName();
+    char nameBuf[1024];
+    int len;
+    if (!name) {
+        // anonymous method
+        len = snprintf(nameBuf, sizeof(nameBuf), "{pool=%d:method=%d}",
+                       method->pool()->uniqueId(), method->method_id());
+    } else {
+        // named method
+        StUTF8String name8(name);
+        len = snprintf(nameBuf, sizeof(nameBuf), "%s{pool=%d:method=%d}",
+                       name8.c_str(), method->pool()->uniqueId(),
+                       method->method_id());
+    }
+
+    logMethod(nameBuf, len, code_info);
+
+    for (JITDebugInfo::Info* info = debug_info ? debug_info->info : 0;
+            info != NULL;
+            info = info->next) {
+        switch (info->kind) {
+        case JITDebugInfo::kLine: {
+            struct {
+                uint64_t pc;
+                uint64_t lineNo;
+            } lineInfo = { (uint64_t) info->pc, info->line };
+            log('line', sizeof(lineInfo), &lineInfo);
+            break;
+        }
+        case JITDebugInfo::kFile: {
+            StUTF8String fileName8(method->pool()->getString(info->file));
+            struct {
+                uint64_t pc;
+                char fileName[4096];
+            } fileInfo = { (uint64_t) info->pc, "" };
+            size_t len = fileName8.length();
+            if (len > sizeof(fileInfo.fileName))
+                len = sizeof(fileInfo.fileName);
+            memcpy(fileInfo.fileName, fileName8.c_str(), len);
+            log('file', sizeof(fileInfo) - sizeof(fileInfo.fileName) + len, &fileInfo);
+            break;
+        }
+        default:
+            AvmAssert(false && "bad atom kind for debug info");
+            break;
+        }
+    }
+
+    flush();
+}
+
+void JITLoggingObserver::notifyInvokerJITed(MethodInfo* method,
+                                            const CodeList* code_info)
+{
+    if (!_log)
+        return;
+
+    Stringp name = method->getMethodName();
+    char nameBuf[1024];
+    int len;
+    if (!name) {
+        // anonymous method
+        len = snprintf(nameBuf, sizeof(nameBuf), "invoke {pool=%d:method=%d}",
+                       method->pool()->uniqueId(), method->method_id());
+    } else {
+        // named method
+        StUTF8String name8(name);
+        len = snprintf(nameBuf, sizeof(nameBuf),
+                       "invoke-%s {pool=%d:method=%d}", name8.c_str(),
+                       method->pool()->uniqueId(), method->method_id());
+    }
+
+    logMethod(nameBuf, len, code_info);
+    flush();
+}
+
+void JITLoggingObserver::logMethod(const char* nameBuf, int len,
+                                   const CodeList* code_info)
+{
+    CodeRange r(code_info);
+    log('meth', len, nameBuf);
+    for (; !r.empty(); r.popFront()) {
+        // write out range (and possibly code) for each block of code
+        struct {
+            uint64_t start, end;
+        } block = {
+                (uint64_t) r.frontStart(),
+                (uint64_t) r.frontEnd()
+        };
+        updateBase(r.frontStart());
+        log('blok', sizeof(block), &block);
+        if (_logLevel > 1)
+            log('code', block.end - block.start, (void *)block.start);
+    }
+}
+#endif // VMCFG_SHARK
+
 } // namespace avmplus
+
 #endif // VMCFG_NANOJIT
--- a/core/exec.cpp
+++ b/core/exec.cpp
@@ -73,45 +73,39 @@ BaseExecMgr::BaseExecMgr(AvmCore* core)
     , _ruleSet(NULL)
 #endif
 #ifdef VMCFG_VERIFYALL
     , verifyFunctionQueue(core->gc, 0)
     , verifyTraitsQueue(core->gc, 0)
 #endif
 #ifdef VMCFG_NANOJIT
     , current_osr(NULL)
+    , jit_observer(NULL)
 #endif
 {
 #ifdef SUPERWORD_PROFILING
     WordcodeTranslator::swprofStart();
 #endif
 #ifdef VMCFG_COMPILEPOLICY
     prepPolicyRules();
 #endif
-#if defined VMCFG_NANOJIT && defined VMCFG_OSR_ENV_VAR
-    if (config.osr_threshold == AvmCore::osr_threshold_default) {
-        // If osr_threshold hasn't been set, check for OSR environment var.
-        const char* osr_config = VMPI_getenv("OSR");
-        if (osr_config) {
-            int threshold = OSR::parseConfig(osr_config);
-            if (threshold != -1) {
-                // A valid threshold was found.
-                core->config.osr_threshold = threshold;
-                core->console << "USING ENV OSR THRESHOLD " << threshold << "\n";
-            }
-        }
-    }
+#ifdef VMCFG_NANOJIT
+    setupJit(core);
 #endif
 }
 
 BaseExecMgr::~BaseExecMgr()
 {
 #ifdef SUPERWORD_PROFILING
     WordcodeTranslator::swprofStop();
 #endif
+#ifdef VMCFG_NANOJIT
+    delete jit_observer;
+    jit_observer = NULL;
+#endif
 }
 
 // Called when MethodInfo is constructed.
 void BaseExecMgr::init(MethodInfo* m, const NativeMethodInfo* native_info)
 {
 #ifndef MEMORY_INFO
 //    MMGC_STATIC_ASSERT(offsetof(MethodInfo, _implGPR) == 0);
 #endif
--- a/core/exec.h
+++ b/core/exec.h
@@ -37,16 +37,20 @@
  *
  * ***** END LICENSE BLOCK ***** */
 
 #ifndef __avmplus_exec__
 #define __avmplus_exec__
 
 #include "exec-osr.h"
 
+namespace nanojit {
+  class CodeList; // Forward declaration for JITObserver
+}
+
 namespace avmplus {
 
 /**
  *  Execution manager pure virtual interface.  An execution manager implementation
  *  is responsible for all aspects of AS3 execution, including invocation,
  *  policy decisions for how to execute, when to verify, and when to translate,
  *  if needed.
  *
@@ -141,16 +145,69 @@ extern const VecrThunkProc thunkEnterVEC
  * Compute number of bytes needed for the unboxed representation
  * of this argument value when passed on the stack.
  */
 int32_t argSize(Traits*);
 
 class MethodRecognizer;
 
 /**
+ * Associates debugfile/debugline information with locations in JITted code
+ */
+struct JITDebugInfo
+{
+    enum Kind {
+        kLine, kFile
+    };
+    struct Info {
+        Info(Kind kind) : kind(kind) {}
+        const void* pc; // location in native code.
+        Kind kind;      // kLine or kFile
+        union {
+            int line;
+            int file;   // pool index of filename string
+        };
+        Info* next; // next record.
+    };
+
+    JITDebugInfo() : info(0), last(0) {}
+
+    void add(Info* r) {
+        r->next = 0;
+        if (last)
+            last->next = r;
+        else
+            info = r;
+        last = r;
+    }
+
+    Info* info;
+    Info* last;
+};
+
+/** JITObserver is an interface that is notified for each JITted method */
+class JITObserver
+{
+public:
+    virtual ~JITObserver() {}
+    /**
+     * Notify the observer that the given method was just compiled.
+     * codeInfo and debugInfo contain code and debug symbol data about the
+     * method.  After notifyMethodJITted is called, the codeInfo and debugInfo
+     * buffers may be recycled.  The observer must copy out any required info.
+     */
+    virtual void notifyMethodJITed(MethodInfo* method,
+                                   const nanojit::CodeList* code,
+                                   JITDebugInfo *debugInfo) = 0;
+
+    virtual void notifyInvokerJITed(MethodInfo* method,
+                                    const nanojit::CodeList* code) = 0;
+};
+
+/**
  * BaseExecMgr implements for all policies, and encapsulates
  * jit+abc, abc-only, and wordcode-only mechanisms.  This could be improved
  * by factoring into multiple implementations.
  *
  * Extends GCFinalizedObject because instances contain GC object references
  * and have a destructor that needs to run.
  */
 class BaseExecMgr: public MMgc::GCFinalizedObject
@@ -315,16 +372,17 @@ private:
     bool isVerified(const MethodInfo*) const;
     bool isVerifyPending(const MethodInfo*) const;
     void setVerified(MethodInfo*) const;
     void setVerifyPending(MethodInfo*) const;
 
     //
     // Support for JIT Compilation:
     //
+    void setupJit(AvmCore*);
 
     /** Return true if we should eagerly JIT.  False means use interpreter. */
     bool shouldJitFirst(const AbcEnv*, const MethodInfo*, MethodSignaturep) const;
 
     /** True if the JIT is enabled */
     bool isJitEnabled() const;
 
     /** Run the verifier with the JIT attached. */
@@ -418,20 +476,21 @@ private:
 #ifdef VMCFG_COMPILEPOLICY
     JitInterpRuleSet* _ruleSet;
 #endif
 #ifdef VMCFG_VERIFYALL
     GCList<MethodInfo> verifyFunctionQueue;
     GCList<Traits> verifyTraitsQueue;
 #endif
 #ifdef VMCFG_NANOJIT
-    // OSR support
     friend class OSR;
     friend class CodegenLIR;
+    friend class LirHelper;
     OSR *current_osr;
+    JITObserver *jit_observer; // Current JITObserver or NULL if not profiling.
 #endif
 };
 
 /**
  * CodeWriter instance to hook into opcodes OP_newfunction, OP_newclass, and
  * OP_newactivation so we can populate verifyFunctionQueue and
  * verifyTraitsQueue in verifyall mode.
  */
--- a/nanojit/Assembler.cpp
+++ b/nanojit/Assembler.cpp
@@ -2178,17 +2178,24 @@ namespace nanojit
                      // to so we need to add it to the update table too
                      // note the alloc, actual act is delayed; see above
                     if (vtuneHandle) {
                         uint32_t currentLine = (uint32_t) ins->oprnd1()->immI();
                         vtuneLine(vtuneHandle, currentLine, _nIns);
                     }
                     break;
                 }
-               #endif // VMCFG_VTUNE
+                #endif // VMCFG_VTUNE
+
+                case LIR_pc: {
+                    // record the current PC
+                    void **dest = (void **)ins->oprnd1()->immP();
+                    *dest = _nIns;
+                    break;
+                }
 
                 case LIR_comment:
                     // Do nothing.
                     break;
             }
 
 #ifdef NJ_VERBOSE
             // We do final LIR printing inside this loop to avoid printing
--- a/nanojit/Assembler.h
+++ b/nanojit/Assembler.h
@@ -167,17 +167,17 @@ namespace nanojit
             }
         }
         return n;
     }
 
     inline uint32_t AR::stackSlotsNeeded() const
     {
         // NB: _highWaterMark is an index, not a count
-        return _highWaterMark+1;
+        return _highWaterMark + 1;
     }
 
     #ifndef AVMPLUS_ALIGN16
         #ifdef _MSC_VER
             #define AVMPLUS_ALIGN16(type) __declspec(align(16)) type
         #else
             #define AVMPLUS_ALIGN16(type) type __attribute__ ((aligned (16)))
         #endif
@@ -432,17 +432,19 @@ namespace nanojit
             // code chunks (for exit stubs).  We use a hack to avoid having to
             // parameterize the code that does the generating -- we let that
             // code assume that it's always generating into a normal code
             // chunk (most of the time it is), and when we instead need to
             // generate into an exit code chunk, we set _inExit to true and
             // temporarily swap all the code/exit variables below (using
             // swapCodeChunks()).  Afterwards we swap them all back and set
             // _inExit to false again.
+        public:
             CodeList*   codeList;               // finished blocks of code.
+        private:
             bool        _inExit, vpad2[3];
             NIns        *codeStart, *codeEnd;   // current normal code chunk
             NIns        *exitStart, *exitEnd;   // current exit code chunk
             NIns*       _nIns;                  // current instruction in current normal code chunk
             NIns*       _nExitIns;              // current instruction in current exit code chunk
                                                 // note: _nExitIns == NULL until the first side exit is seen.
         #ifdef NJ_VERBOSE
             NIns*       _nInsAfter;             // next instruction (ascending) in current normal/exit code chunk (for verbose output)
--- a/nanojit/CodeAlloc.cpp
+++ b/nanojit/CodeAlloc.cpp
@@ -42,20 +42,20 @@
 //#define DOPROF
 #include "../vprof/vprof.h"
 
 #ifdef FEATURE_NANOJIT
 
 namespace nanojit
 {
     static const bool verbose = false;
-#ifdef VMCFG_VTUNE
-    // vtune jit profiling api can't handle non-contiguous methods,
+#if defined VMCFG_VTUNE || defined VMCFG_SHARK
+    // Profilers get confused by non-contiguous functions,
     // so make the allocation size huge to avoid non-contiguous methods
-    static const int pagesPerAlloc = 128; // 512KB
+    static const int pagesPerAlloc = 256; // 1MB
 #elif defined(NANOJIT_ARM)
     // ARM requires single-page allocations, due to the constant pool that
     // lives on each page that must be reachable by a 4KB pc-relative load.
     static const int pagesPerAlloc = 1; // 4KB
 #else
     static const int pagesPerAlloc = 16; // 64KB
 #endif
 
--- a/nanojit/CodeAlloc.h
+++ b/nanojit/CodeAlloc.h
@@ -45,16 +45,17 @@ namespace nanojit
     /**
      * CodeList is a single block of code.  The next field is used to
      * form linked lists of non-contiguous blocks of code.  Clients use CodeList*
      * to point to the first block in a list.
      */
     class CodeList
     {
         friend class CodeAlloc;
+        friend class CodeRange;
 
         /** for making singly linked lists of blocks in any order */
         CodeList* next;
 
         /** adjacent block at lower address.  This field plus higher
             form a doubly linked list of blocks in address order, used
             for splitting and coalescing blocks. */
         CodeList* lower;
@@ -243,11 +244,44 @@ namespace nanojit
         /** unprotect the code chunk containing just this one block */
         void markBlockWrite(CodeList* b);
 
 #ifdef _DEBUG
         /** make sure all the higher/lower pointers are correct for every block */
         void sanity_check();
 #endif
     };
+
+    /** CodeRange is a range-style iterator over CodeList */
+    class CodeRange
+    {
+    public:
+        CodeRange(const CodeList* blocks)
+                : p(blocks) {}
+
+        /** return true if there are no more blocks */
+        bool empty() const {
+            return !p;
+        }
+
+        /** return the start address of the front block */
+        const void *frontStart() const {
+            NanoAssert(!empty());
+            return &p->code[0];
+        }
+
+        /** return the end address of the front block */
+        const void *frontEnd() const {
+            return p->end;
+        }
+
+        /** move to the next block */
+        void popFront() {
+            NanoAssert(!empty());
+            p = p->next;
+        }
+
+    private:
+        const CodeList* p;
+    };
 }
 
 #endif // __nanojit_CodeAlloc__
--- a/nanojit/LIR.cpp
+++ b/nanojit/LIR.cpp
@@ -4315,17 +4315,18 @@ namespace nanojit
         case LIR_f2i:
         case LIR_f2d:
         case LIR_f2f4:
             formals[0] = LTy_F;
             break;
                 
         case LIR_file:
         case LIR_line:
-            // These will never get hit since VTUNE implies !DEBUG.  Ignore for the moment.
+        case LIR_pc:
+            // Ignore for the moment.  really we do know the operand types.
             nArgs = 0;
             break;
 
         default:
             NanoAssertMsgf(0, "%s\n", lirNames[op]);
         }
 
         typeCheckArgs(op, nArgs, formals, args);
--- a/nanojit/LIRopcode.tbl
+++ b/nanojit/LIRopcode.tbl
@@ -137,19 +137,20 @@ OP___(retf,     Op1,  V,    0)  // retur
 OP___(retf4,    Op1,  V,    0)  // return a float4
 
 OP___(livei,    Op1,  V,    0)  // extend live range of an int
 OP_64(liveq,    Op1,  V,    0)  // extend live range of a quad
 OP___(lived,    Op1,  V,    0)  // extend live range of a double
 OP___(livef,    Op1,  V,    0)  // extend live range of a float
 OP___(livef4,   Op1,  V,    0)  // extend live range of a float4
 
-OP___(file,     Op1,  V,    0)  // source filename for debug symbols
-OP___(line,     Op1,  V,    0)  // source line number for debug symbols
-
+OP___(file,     Op1,  V,    0)  // [VTune] source filename for debug symbols
+OP___(line,     Op1,  V,    0)  // [VTune] source line number for debug symbols
+OP___(pc,       Op1,  V,    0)  // [Shark] record the machine address of this instruction
+OP_UN(align_comment)
 OP___(comment,  Op1,  V,    0)  // a comment shown, on its own line, in LIR dumps
 
 //---------------------------------------------------------------------------
 // Loads and stores
 //---------------------------------------------------------------------------
 OP___(ldc2i,    Ld,   I,   -1)  // load char and sign-extend to an int
 OP___(lds2i,    Ld,   I,   -1)  // load short and sign-extend to an int
 OP___(lduc2ui,  Ld,   I,   -1)  // load unsigned char and zero-extend to an unsigned int
--- a/shell/ShellCore.cpp
+++ b/shell/ShellCore.cpp
@@ -71,16 +71,17 @@ namespace avmshell
         , langID(-1)
         , jitordie(avmplus::AvmCore::jitordie_default)
         , do_testSWFHasAS3(false)
         , runmode(avmplus::AvmCore::runmode_default)
 #ifdef VMCFG_NANOJIT
         , njconfig()
         , jitconfig()
         , osr_threshold(avmplus::AvmCore::osr_threshold_default)
+        , jitprof_level(avmplus::AvmCore::jitprof_level_default)
         , policyRulesArg(NULL)
 #endif
         , st_component(NULL)
         , st_category(NULL)
         , st_name(NULL)
         , apiVersionSeries(avmplus::kApiVersionSeries_FP)
         , apiVersion(avmplus::kApiVersionLatest[avmplus::kApiVersionSeries_FP])
         , swfVersion(avmplus::BugCompatibility::kLatest)
@@ -411,37 +412,36 @@ namespace avmshell
         config.verifyall = settings.verifyall;
         config.verifyonly = settings.verifyonly;
 #endif
         config.jitordie = settings.jitordie;
 #ifdef VMCFG_NANOJIT
         config.njconfig = settings.njconfig;
         config.jitconfig = settings.jitconfig;
         config.osr_threshold = settings.osr_threshold;
+        config.jitprof_level = settings.jitprof_level;
         config.compilePolicyRules = settings.policyRulesArg;
 #endif
 
 #ifdef AVMPLUS_VERBOSE
         config.verboseOnlyString = settings.verboseOnlyArg;
 
         if (settings.do_verbose & avmplus::VB_builtins)
             config.verbose_vb = settings.do_verbose;  // ~builtins then skip verbose settings during setup()
 #endif
         config.runmode = settings.runmode;
 
-#if VMCFG_METHOD_NAMES
         // verbose requires methodnames (in avmshell, anyway), before calling initBuiltinPool.
         if (settings.do_verbose)
             config.methodNames = true;
     #ifdef DEBUGGER
         // debugger in avmshell always enables methodnames.
         if (allowDebugger)
             config.methodNames = true;
     #endif
-#endif // VMCFG_METHOD_NAMES
 
 #ifdef DEBUGGER
         langID = settings.langID;
 #endif
 
         TRY(this, avmplus::kCatchAction_ReportAsError)
         {
             setStackLimit();
--- a/shell/ShellCore.h
+++ b/shell/ShellCore.h
@@ -90,16 +90,17 @@ namespace avmshell
         int langID;                     // copy to ShellCore?
         bool jitordie;                  // copy to config
         bool do_testSWFHasAS3;
         avmplus::Runmode runmode;       // copy to config
 #ifdef VMCFG_NANOJIT
         nanojit::Config njconfig;       // copy to config
         avmplus::JitConfig jitconfig;   // copy to config
         uint32_t osr_threshold;         // Invocation count to trigger JIT.
+        uint32_t jitprof_level;          // Log level for jit profiling
         const char* policyRulesArg;     // copy to config (raw unprocessed)
 #endif
         avmplus::AvmCore::CacheSizes cacheSizes; // Default to unlimited
         const char* st_component;
         const char* st_category;
         const char* st_name;
         avmplus::ApiVersionSeries apiVersionSeries;
         avmplus::ApiVersion apiVersion;
--- a/shell/avmshell-features.h
+++ b/shell/avmshell-features.h
@@ -76,16 +76,20 @@
     #define AVMFEATURE_ALLOCATION_SAMPLER 0
   #endif
 #endif
 
 #ifndef AVMFEATURE_VTUNE
   #define AVMFEATURE_VTUNE             0
 #endif
 
+#ifndef AVMFEATURE_SHARK
+  #define AVMFEATURE_SHARK             0
+#endif
+
 #ifndef AVMFEATURE_JIT
   #define AVMFEATURE_JIT               1
 #endif
 
 #ifndef AVMFEATURE_TELEMETRY
   #define AVMFEATURE_TELEMETRY         0
 #endif
 
--- a/shell/avmshell.cpp
+++ b/shell/avmshell.cpp
@@ -866,16 +866,26 @@ namespace avmshell
                     int32_t threshold;
                     if (VMPI_sscanf(arg + 5, "%d", &threshold) != 1 ||
                         threshold < 0) {
                       avmplus::AvmLog("Bad value to -osr: %s\n", arg + 5);
                       usage();
                     }
                     settings.osr_threshold = threshold;
                 }
+                else if (!VMPI_strncmp(arg, "-prof=", 6)) {
+                    // parse jit profiling level
+                    int32_t level;
+                    if (VMPI_sscanf(arg + 6, "%d", &level) != 1 ||
+                        level < 0 || level > 2) {
+                        avmplus::AvmLog("Bad value to -prof: %s", arg + 6);
+                        usage();
+                    }
+                    settings.jitprof_level = level;
+                }
 #endif /* VMCFG_NANOJIT */
                 else if (MMgc::GCHeap::GetGCHeap()->Config().IsGCOptionWithParam(arg) && i+1 < argc ) {
                     const char *val = argv[++i];
                     if (MMgc::GCHeap::GetGCHeap()->Config().ParseAndApplyOption(arg, mmgcSaysArgIsWrong, val)) {
                         if (mmgcSaysArgIsWrong) {
                             avmplus::AvmLog("Invalid GC option: %s %s\n", arg, val);
                             usage();
                         }
@@ -1172,16 +1182,17 @@ namespace avmshell
         avmplus::AvmLog("          [-Dinterp]    do not generate machine code, interpret instead\n");
         avmplus::AvmLog("          [-Ojit]       use jit always, never interp (except when the jit fails)\n");
         avmplus::AvmLog("          [-Djitordie]  use jit always, and abort when the jit fails\n");
         avmplus::AvmLog("          [-Dnocse]     disable CSE optimization\n");
         avmplus::AvmLog("          [-Dnoinline]  disable speculative inlining\n");
         avmplus::AvmLog("          [-jitharden]  enable jit hardening techniques\n");
         avmplus::AvmLog("          [-osr=T]      enable OSR with invocation threshold T; disable with -osr=0; default is -osr=%d\n",
                         avmplus::AvmCore::osr_threshold_default);
+        avmplus::AvmLog("          [-prof=L]     enable jit profile level L; default 0=disabled; 1=function ranges, 2=functions+native asm)\n");
     #ifdef AVMPLUS_IA32
         avmplus::AvmLog("          [-Dnosse]     use FPU stack instead of SSE2 instructions\n");
         avmplus::AvmLog("          [-Dfixedesp]  pre-decrement stack for all needed call usage upon method entry\n");
     #endif
     #ifdef AVMPLUS_ARM
         avmplus::AvmLog("          [-Darm_arch N]  nanojit assumes ARMvN architecture (default=5)\n");
         avmplus::AvmLog("          [-Darm_vfp]     nanojit uses VFP rather than SoftFloat\n");
     #endif
new file mode 100644
--- /dev/null
+++ b/utils/sharkprof/.cproject
@@ -0,0 +1,63 @@
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<?fileVersion 4.0.0?>
+
+<cproject storage_type_id="org.eclipse.cdt.core.XmlProjectDescriptionStorage">
+	<storageModule moduleId="org.eclipse.cdt.core.settings">
+		<cconfiguration id="cdt.managedbuild.toolchain.gnu.macosx.base.1803346242">
+			<storageModule buildSystemId="org.eclipse.cdt.managedbuilder.core.configurationDataProvider" id="cdt.managedbuild.toolchain.gnu.macosx.base.1803346242" moduleId="org.eclipse.cdt.core.settings" name="Default">
+				<externalSettings/>
+				<extensions>
+					<extension id="org.eclipse.cdt.core.MachO64" point="org.eclipse.cdt.core.BinaryParser"/>
+					<extension id="org.eclipse.cdt.core.GmakeErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.CWDLocator" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GCCErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GASErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+					<extension id="org.eclipse.cdt.core.GLDErrorParser" point="org.eclipse.cdt.core.ErrorParser"/>
+				</extensions>
+			</storageModule>
+			<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+				<configuration buildProperties="" description="" id="cdt.managedbuild.toolchain.gnu.macosx.base.1803346242" name="Default" parent="org.eclipse.cdt.build.core.emptycfg">
+					<folderInfo id="cdt.managedbuild.toolchain.gnu.macosx.base.1803346242.1453857131" name="/" resourcePath="">
+						<toolChain id="cdt.managedbuild.toolchain.gnu.macosx.base.1035100856" name="cdt.managedbuild.toolchain.gnu.macosx.base" superClass="cdt.managedbuild.toolchain.gnu.macosx.base">
+							<targetPlatform archList="all" binaryParser="org.eclipse.cdt.core.MachO64" id="cdt.managedbuild.target.gnu.platform.macosx.base.616645248" name="Debug Platform" osList="macosx" superClass="cdt.managedbuild.target.gnu.platform.macosx.base"/>
+							<builder buildPath="${workspace_loc:/sharkprof}" id="cdt.managedbuild.target.gnu.builder.macosx.base.1404187521" keepEnvironmentInBuildfile="false" managedBuildOn="false" name="Gnu Make Builder" superClass="cdt.managedbuild.target.gnu.builder.macosx.base"/>
+							<tool id="cdt.managedbuild.tool.macosx.c.linker.macosx.base.691230257" name="MacOS X C Linker" superClass="cdt.managedbuild.tool.macosx.c.linker.macosx.base"/>
+							<tool id="cdt.managedbuild.tool.macosx.cpp.linker.macosx.base.108392738" name="MacOS X C++ Linker" superClass="cdt.managedbuild.tool.macosx.cpp.linker.macosx.base">
+								<inputType id="cdt.managedbuild.tool.macosx.cpp.linker.input.675511042" superClass="cdt.managedbuild.tool.macosx.cpp.linker.input">
+									<additionalInput kind="additionalinputdependency" paths="$(USER_OBJS)"/>
+									<additionalInput kind="additionalinput" paths="$(LIBS)"/>
+								</inputType>
+							</tool>
+							<tool id="cdt.managedbuild.tool.gnu.assembler.macosx.base.1335941599" name="GCC Assembler" superClass="cdt.managedbuild.tool.gnu.assembler.macosx.base">
+								<inputType id="cdt.managedbuild.tool.gnu.assembler.input.531782196" superClass="cdt.managedbuild.tool.gnu.assembler.input"/>
+							</tool>
+							<tool id="cdt.managedbuild.tool.gnu.archiver.macosx.base.851069164" name="GCC Archiver" superClass="cdt.managedbuild.tool.gnu.archiver.macosx.base"/>
+							<tool id="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.base.1353589694" name="GCC C++ Compiler" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.macosx.base">
+								<inputType id="cdt.managedbuild.tool.gnu.cpp.compiler.input.2045702989" superClass="cdt.managedbuild.tool.gnu.cpp.compiler.input"/>
+							</tool>
+							<tool id="cdt.managedbuild.tool.gnu.c.compiler.macosx.base.381413214" name="GCC C Compiler" superClass="cdt.managedbuild.tool.gnu.c.compiler.macosx.base">
+								<inputType id="cdt.managedbuild.tool.gnu.c.compiler.input.779703065" superClass="cdt.managedbuild.tool.gnu.c.compiler.input"/>
+							</tool>
+						</toolChain>
+					</folderInfo>
+				</configuration>
+			</storageModule>
+			<storageModule moduleId="org.eclipse.cdt.core.externalSettings"/>
+		</cconfiguration>
+	</storageModule>
+	<storageModule moduleId="cdtBuildSystem" version="4.0.0">
+		<project id="sharkprof.null.1542540886" name="sharkprof"/>
+	</storageModule>
+	<storageModule moduleId="refreshScope" versionNumber="1">
+		<resource resourceType="PROJECT" workspacePath="/sharkprof"/>
+	</storageModule>
+	<storageModule moduleId="scannerConfiguration">
+		<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId=""/>
+		<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.macosx.base.1803346242;cdt.managedbuild.toolchain.gnu.macosx.base.1803346242.1453857131;cdt.managedbuild.tool.gnu.c.compiler.macosx.base.381413214;cdt.managedbuild.tool.gnu.c.compiler.input.779703065">
+			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileC"/>
+		</scannerConfigBuildInfo>
+		<scannerConfigBuildInfo instanceId="cdt.managedbuild.toolchain.gnu.macosx.base.1803346242;cdt.managedbuild.toolchain.gnu.macosx.base.1803346242.1453857131;cdt.managedbuild.tool.gnu.cpp.compiler.macosx.base.1353589694;cdt.managedbuild.tool.gnu.cpp.compiler.input.2045702989">
+			<autodiscovery enabled="true" problemReportingEnabled="true" selectedProfileId="org.eclipse.cdt.managedbuilder.core.GCCManagedMakePerProjectProfileCPP"/>
+		</scannerConfigBuildInfo>
+	</storageModule>
+</cproject>
new file mode 100644
--- /dev/null
+++ b/utils/sharkprof/.project
@@ -0,0 +1,83 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<projectDescription>
+	<name>sharkprof</name>
+	<comment></comment>
+	<projects>
+	</projects>
+	<buildSpec>
+		<buildCommand>
+			<name>org.eclipse.cdt.managedbuilder.core.genmakebuilder</name>
+			<triggers>clean,full,incremental,</triggers>
+			<arguments>
+				<dictionary>
+					<key>?name?</key>
+					<value></value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.append_environment</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.autoBuildTarget</key>
+					<value>all</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.buildArguments</key>
+					<value></value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.buildCommand</key>
+					<value>make</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.buildLocation</key>
+					<value>${workspace_loc:/sharkprof}</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.cleanBuildTarget</key>
+					<value>clean</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.contents</key>
+					<value>org.eclipse.cdt.make.core.activeConfigSettings</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.enableAutoBuild</key>
+					<value>false</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.enableCleanBuild</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.enableFullBuild</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.fullBuildTarget</key>
+					<value>all</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.stopOnError</key>
+					<value>true</value>
+				</dictionary>
+				<dictionary>
+					<key>org.eclipse.cdt.make.core.useDefaultBuildCmd</key>
+					<value>true</value>
+				</dictionary>
+			</arguments>
+		</buildCommand>
+		<buildCommand>
+			<name>org.eclipse.cdt.managedbuilder.core.ScannerConfigBuilder</name>
+			<triggers>full,incremental,</triggers>
+			<arguments>
+			</arguments>
+		</buildCommand>
+	</buildSpec>
+	<natures>
+		<nature>org.eclipse.cdt.core.cnature</nature>
+		<nature>org.eclipse.cdt.core.ccnature</nature>
+		<nature>org.eclipse.cdt.managedbuilder.core.managedBuildNature</nature>
+		<nature>org.eclipse.cdt.managedbuilder.core.ScannerConfigNature</nature>
+	</natures>
+</projectDescription>
new file mode 100644
--- /dev/null
+++ b/utils/sharkprof/Makefile
@@ -0,0 +1,60 @@
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is [Open Source Virtual Machine].
+#
+# The Initial Developer of the Original Code is
+# Adobe System Incorporated.
+# Portions created by the Initial Developer are Copyright (C) 2012
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s): Adobe AS3 Team
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+all: log2s-32 log2s-64 dinkuuid jit64.so jit32.so
+
+log2s-32: log2s.cpp
+	g++ -m32 log2s.cpp -o log2s-32
+
+log2s-64: log2s.cpp
+	g++ -m64 log2s.cpp -o log2s-64
+
+dinkuuid: dinkuuid.cpp
+	g++ dinkuuid.cpp -o dinkuuid
+
+jit64.so: jit.s dinkuuid
+	gcc -m64 -shared -nostartfiles -nodefaultlibs jit.s -o jit64.so
+	./dinkuuid jit64.so
+
+jit32.so: jit.s dinkuuid
+	gcc -m32 -shared -nostartfiles -nodefaultlibs jit.s -o jit32.so
+	./dinkuuid jit32.so
+
+clean:
+	rm -f dinkuuid log2s-* jit*.so
+
+install:
+	cp ./jit*.so /tmp
new file mode 100644
--- /dev/null
+++ b/utils/sharkprof/README-2.txt
@@ -0,0 +1,61 @@
+
+[notes copied from wiki]
+
+The following tools are used. They aren't all used by all configurations,
+but it makes sense to describe them together. The tools are in the zip
+file attached to this page.
+
+logconv:	Converts JIT log into symbol-rich object files.
+log2s:		Converts JIT log into an assembly file that can be assembled
+			into a symbol rich object file by gcc.
+dinkuuid:	Sets the UUID of a mach-o executable to a known value. 
+			Shark uses UUIDs during symbolification to check that the object
+			file containing symbols is the "same" as one it has taken sample
+			data from.
+binutils:	from http://macports.org
+
+Mac AIR
+
+	You need binutils, logconv, and dinkuuid.
+
+	sudo port install binutils
+	g++ -I/opt/local/include logconv.cpp -L/opt/local/lib -lbfd -ldl -o logconv
+	g++ dinkuuid.cpp -o dinkuuid
+
+Mac 64-bit Player
+
+	You need binuitls, log2s, and dinkuuid.
+
+	sudo port install binutils
+	g++ log2s.cpp -o log2s
+	g++ dinkuuid.cpp -o dinkuuid
+
+Droid
+
+	You need logconv.
+
+	g++ logconv.cpp -lbfd -ldl -o logconv
+
+Build the dummy shared object
+
+	In the zip file, there is a jit.s. This small bit of assembly can be
+	compiled into a shared object, which the patched player will pretend
+	it is "loading" the JITted instructions from.
+
+Mac AIR
+
+	gcc -m32 -shared -nostartfiles -nodefaultlibs jit.s -o jit.so
+	dinkuuid jit.so
+
+Mac 64-bit Player
+
+	gcc -shared -nostartfiles -nodefaultlibs jit.s -o jit.so
+	dinkuuid jit.so
+
+Droid
+
+	using the arm gcc from the Android NDK
+	http://developer.android.com/sdk/ndk/index.html
+
+	arm-eabi-gcc -shared -nostartfiles -nodefaultlibs jit.s -o jit.so
+
new file mode 100644
--- /dev/null
+++ b/utils/sharkprof/README.txt
@@ -0,0 +1,46 @@
+MacOSX 64-bit plugin with support for profiling JIT'd code in Shark
+Tested on OSX 10.6.
+
+Note that each code chunk returned by CodeAlloc is treated as if it were a separate function,
+so that you may land in the middle of the function and not get to see it all.  This is probably
+infrequent in practice, because with profiling enabled, CodeAlloc grabs 1MB chunks to 
+allocate code from.
+
+Scott's original patch would allow Shark to display AS3 source code, not assembler, if file
+and line information were compiled into the original AS3.  Since I've been workign with examples
+for which source was not available, I haven't yet had the need to try this feature, so it may
+or may not work.  [Ed 2/28/2012: I've tried this feature and still can't get it to work].
+
+To install:
+
+1) build player with #define AVMFEATURE_SHARK 1 or shell with configure.py --enable-shark
+install the player if necessary.  consider saving the old one and making a symlink
+to the plugin you just compiled.
+2) cd utils/sharkprof; make; make install ## creates /tmp/jit-32.so and /tmp/jit-64.so
+
+To profile:
+
+1) Start Safari and visit the content you wish to profile, or run standalone player
+2) Start Shark, select 'PluginProcess' as the target process (or FlashPlayer)
+3) Click Start to begin profiling;  play the game; Stop profiling manually if needed.
+   I just set a limit on the number of samples and play until sampling stops.
+   I get good results with 25us sampling frequency.
+4) run 'mksyms', which will find and convert log files in /tmp.  Or, run mksyms <pid>
+   where <pid> is the pid of the plugin process, which should show up in Shark.
+5) When Shark finishes analyzing samples, do a 'Get Info' and select 'Symbols' on the pane that drops down.
+6) Find jit-32.so or jit-64.so in the list of modules, and click 'Symbolicate'.
+7) Navigate to /tmp/<pid>-jit.so.  Ignore any message regarding mismatched names.
+
+Now the "Unknown Library" addresses from AS3 JIT code show up as "jit-xxx.so"
+with names from AS3 code.  Now you can use shark data mining.  for example:
+ * flatten "FlashPlayer" to charge all player time to entrypoint functions
+ * focus on an interesting function.
+ * exclude time from ignorable functions
+
+caveats:
+ * at high optimization levels, gcc does tail-call optimization.  Some stack
+   samples will show the tail-called function as the jit->player entry point.
+ * you may still see "Unknown Library" addresses.  I suspect these are from
+   software shaders but I have no proof.  
+ * This scheme is untested only tested on MacOS 10.6.  YMMV with Instruments
+   on 10.6 or 10.7.
new file mode 100644
--- /dev/null
+++ b/utils/sharkprof/dinkuuid.cpp
@@ -0,0 +1,99 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
+/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is [Open Source Virtual Machine].
+ *
+ * The Initial Developer of the Original Code is
+ * Adobe System Incorporated.
+ * Portions created by the Initial Developer are Copyright (C) 2012
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Adobe AS3 Team
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+/*
+  Highly simple utility that sets the UUID of a mach-o object
+  to a known value
+*/
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <mach-o/loader.h>
+
+int main(int argc, char **argv)
+{
+    if (argc != 2) {
+        fprintf(stderr, "usage: %s inOutFile\n", argv[0]);
+        exit(-1);
+    }
+
+    FILE *f = fopen(argv[1], "r+");
+
+    uint32_t ncmds = 0;
+
+    mach_header header;
+
+    fread(&header, sizeof(header), 1, f);
+
+    if (header.magic == MH_MAGIC_64) {
+        mach_header_64 header64;
+
+        fseek(f, 0, SEEK_SET);
+        fread(&header64, sizeof(header64), 1, f);
+        ncmds = header64.ncmds;
+    } else
+        ncmds = header.ncmds;
+
+    for (int n = 0; n < ncmds; n++) {
+        load_command lc;
+
+        fread(&lc, sizeof(lc), 1, f);
+
+        if (lc.cmd == LC_UUID) {
+            unsigned uuid[4] = { 'adob',
+            'eado',
+            'bead',
+            'obe ' };
+
+            unsigned ruuid[4];
+
+            fread(&ruuid, sizeof(ruuid), 1, f);
+
+            fseek(f, -sizeof(ruuid), SEEK_CUR);
+
+            fwrite(&uuid, sizeof(uuid), 1, f);
+            printf("found uuid %08x %08x %08x %08x\n", ruuid[0], ruuid[1],
+                   ruuid[2], ruuid[3]);
+        } else {
+            fseek(f, lc.cmdsize - sizeof(lc), SEEK_CUR);
+        }
+    }
+    fclose(f);
+    return 0;
+}
new file mode 100644
--- /dev/null
+++ b/utils/sharkprof/jit.s
@@ -0,0 +1,12 @@
+// something
+.text
+.align 12
+.globl __jitStart
+.globl _jitStart
+__jitStart:
+_jitStart:
+.fill 128 * 1024 * 1024, 1, 0xcc
+.globl __jitEnd
+.globl _jitEnd
+__jitEnd:
+_jitEnd:
new file mode 100644
--- /dev/null
+++ b/utils/sharkprof/log2s.cpp
@@ -0,0 +1,404 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
+/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is [Open Source Virtual Machine].
+ *
+ * The Initial Developer of the Original Code is
+ * Adobe System Incorporated.
+ * Portions created by the Initial Developer are Copyright (C) 2004-2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Adobe AS3 Team
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <sys/mman.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <dlfcn.h>
+
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+
+static void warn(const char *tmpl, ...)
+{
+    va_list ap;
+    va_start(ap, tmpl);
+    vfprintf(stderr, tmpl, ap);
+    fputs("\n", stderr);
+    va_end(ap);
+}
+
+static void fatal(const char *tmpl, ...)
+{
+    va_list ap;
+    va_start(ap, tmpl);
+    vfprintf(stderr, tmpl, ap);
+    fputs("\n", stderr);
+    va_end(ap);
+    exit(-1);
+}
+
+struct Chunk
+{
+    uint32_t tag;
+    uint32_t size;
+    char data[0];
+};
+
+struct DebugInfo
+{
+    const Chunk *info;
+
+    uint64_t pc() const
+    {
+        return ((uint64_t *) info->data)[0];
+    }
+
+    uint32_t tag() const
+    {
+        return info->tag;
+    }
+
+    std::string file() const
+    {
+        if (tag() != 'file')
+            fatal("DebugInfo::file() called on non-file debug info");
+
+        return std::string(info->data + sizeof(uint64_t),
+                           info->size - sizeof(uint64_t));
+    }
+
+    uint64_t line() const
+    {
+        if (tag() != 'line'
+            )
+            fatal("DebugInfo::line() called on non-linedebug info");
+
+        return ((uint64_t *) info->data)[1];
+    }
+
+    bool operator<(const DebugInfo &other) const
+    {
+        int64_t pcPad = pc() - other.pc();
+
+        if (pcPad)
+            return pcPad < 0;
+
+        if (tag() == 'file' && other.tag() != 'file')
+            return true;
+
+        return false;
+    }
+};
+
+struct MethBlock
+{
+    const Chunk *meth;
+    const Chunk *block;
+    const Chunk *code;
+
+    uint64_t start() const
+    {
+        return ((uint64_t *) block->data)[0];
+    }
+
+    uint64_t end() const
+    {
+        return ((uint64_t *) block->data)[1];
+    }
+
+    std::string name() const
+    {
+        return std::string(meth->data, meth->size);
+    }
+
+    const void *codeBytes() const
+    {
+        return code ? code->data : NULL;
+    }
+
+    const uint32_t codeLen() const
+    {
+        return code ? code->size : 0;
+    }
+
+    bool operator<(const MethBlock &other) const
+    {
+        return start() < other.start();
+    }
+};
+
+static std::string quote(const std::string &s)
+{
+#ifdef __MACH__
+    std::string result = "\"";
+    std::string::const_iterator i = s.begin(), e = s.end();
+
+    for (; i != e; i++) {
+        if (*i != '\\' && isprint(*i))
+            result += *i;
+        else {
+            char buf[8];
+
+            sprintf(buf, "\\%03o", *i);
+            result += buf;
+        }
+    }
+    result += "\"";
+    return result;
+#else
+#error foo
+#endif
+}
+
+int main(int argc, char **argv)
+{
+    if (argc != 2)
+        fatal("usage: %s inputLog", argv[0]);
+
+    int fd = open(argv[1], O_RDONLY);
+
+    if (fd < 0)
+        fatal("couldn't open %s", argv[1]);
+
+    off_t len = lseek(fd, 0, SEEK_END);
+    const void *cur = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+    const void *end = (char *) cur + len;
+
+    if (cur == MAP_FAILED)
+        fatal("couldn't mmap %s", argv[1]);
+
+    const Chunk *base = NULL;
+    const Chunk *meth = NULL;
+
+    std::vector<MethBlock> blocks;
+    std::vector<DebugInfo> debugInfos;
+    int pad_count = 0;
+
+    while (cur < end) {
+        const Chunk &chunk = *(const Chunk *) cur;
+
+        switch (chunk.tag) {
+        default:
+            warn("unknown tag %08x", chunk.tag);
+            break;
+        case 'base':
+            base = &chunk;
+            break;
+        case 'meth':
+            meth = &chunk;
+            break;
+        case 'blok': {
+            if (!meth)
+                fatal("'blok' tag without 'meth'");
+
+            MethBlock block;
+            block.meth = meth;
+            block.block = &chunk;
+            block.code = NULL;
+            blocks.push_back(block);
+        }
+            break;
+        case 'code': {
+            if (blocks.size() <= 0)
+                fatal("'code' chunk without 'blok'");
+            blocks.back().code = &chunk;
+        }
+            break;
+        case 'file':
+        case 'line': {
+            DebugInfo info = { &chunk };
+            debugInfos.push_back(info);
+        }
+            break;
+        }
+        cur = chunk.data + chunk.size;
+    }
+
+    printf(".text\n");
+#ifdef __MACH__
+    printf(".align 12\n");
+#else
+    printf(".align 4096\n");
+#endif
+    printf(".globl __jitStart\n");
+    printf(".globl _jitStart\n");
+    printf("__jitStart:\n");
+    printf("_jitStart:\n");
+    printf(".align 0\n");
+
+    std::sort(blocks.begin(), blocks.end());
+    std::sort(debugInfos.begin(), debugInfos.end());
+
+    std::map<std::string, int> namesSeen;
+    std::vector<MethBlock>::const_iterator i, e;
+    std::vector<DebugInfo>::const_iterator di, de;
+
+    if (!base)
+        fatal("no 'base' found");
+
+    uint64_t textOffs = 0x1000;
+    uint64_t textLen = 256 * 1024 * 1024;
+
+    if (base->size <= sizeof(uint64_t))
+        fatal("base info too short");
+
+    char path[4096];
+    int path_len = base->size - sizeof(uint64_t);
+
+    if (path_len >= sizeof(path))
+        fatal("path to base library too long");
+
+    memcpy(path, base->data + sizeof(uint64_t), path_len);
+    path[path_len] = 0;
+    void *hdl = dlopen(path, RTLD_NOW);
+    if (!hdl)
+        fatal("couldn't get info from base library %s", path);
+
+    char *jit_start = (char*) dlsym(hdl, "_jitStart");
+    if (!jit_start)
+        fatal("couldn't find _jitStart in base library");
+    Dl_info dlInfo;
+
+    if (dladdr(jit_start, &dlInfo)) {
+        if ((jit_start - (char *) dlInfo.dli_fbase) != textOffs)
+            warn("_jitStart at unexpected location");
+    } else {
+        warn("couldn't validate _jitStart location");
+    }
+
+    char *jit_end = (char*) dlsym(hdl, "_jitEnd");
+    if (!jit_end)
+        fatal("couldn't find _jitEnd in base library");
+
+    textLen = jit_end - jit_start;
+    dlclose(hdl);
+
+    uint64_t curAddr = *(uint64_t *) base->data + textOffs;
+    uint64_t endAddr = curAddr + textLen;
+
+    int dbgFile = 0;
+
+    di = debugInfos.begin();
+    de = debugInfos.end();
+
+    for (i = blocks.begin(), e = blocks.end(); i != e; ++i) {
+        int curDbgFile = 0;
+
+        uint64_t block_start = i->start();  //###
+        uint64_t block_end = i->end();      //###
+
+        std::string name = i->name();
+        int seen = namesSeen[name]++;
+
+        if (seen) {
+            char buf[16];
+
+            sprintf(buf, " (%d)", seen);
+            name += buf;
+        }
+
+        uint64_t pad = block_start - curAddr;
+
+        if (pad) {
+            printf("pad%d:\n", pad_count++);
+            printf(".fill %lld,1,0xcc\n", pad);
+        }
+
+        curAddr += pad;
+
+        while (di != de && di->pc() < curAddr) {
+            warn("dropped debug info at %llx", di->pc());
+            di++;
+        }
+
+        printf("%s:\n", quote(name).c_str());
+
+        const void *code = i->codeBytes();
+
+        if (code) {
+            uint32_t codeLen = i->codeLen();
+            if (codeLen != block_end - block_start) {
+                printf("FAIL\n");
+            }
+
+            while (codeLen) {
+                while (di != de && di->pc() == curAddr) {
+                    switch (di->tag()) {
+                    default:
+                        warn("unknown debug tag %08x", di->tag());
+                        break;
+                    case 'file':
+                        printf(".file %d %s\n", curDbgFile = ++dbgFile,
+                               quote(di->file()).c_str());
+                        break;
+                    case 'line':
+                        if (curDbgFile)
+                            printf(".loc %d %lld 0\n", dbgFile, di->line());
+                        break;
+                    }
+                    di++;
+                }
+
+                printf(".byte ");
+
+                int nextDebug = (di == de) ? codeLen : di->pc() - curAddr;
+                int nextDebugHere = (nextDebug > codeLen) ? codeLen : nextDebug;
+                int atOnce = (nextDebugHere < 12) ? nextDebugHere : 12;
+
+                while (atOnce--) {
+                    printf("0x%02x%s", *(const unsigned char *) code,
+                           atOnce ? ", " : "\n");
+                    code = (const char *) code + 1;
+                    codeLen--;
+                    curAddr++;
+                }
+            }
+        }
+    }
+
+    uint64_t endPad = endAddr - curAddr;
+
+    if (endPad) {
+        printf("pad%d:\n", pad_count++);
+        printf(".fill %lld,1,0xcc\n", endPad);
+    }
+    printf(".globl __jitEnd\n");
+    printf(".globl _jitEnd\n");
+    printf("__jitEnd:\n");
+    printf("_jitEnd:\n");
+    return 0;
+}
new file mode 100644
--- /dev/null
+++ b/utils/sharkprof/logconv.cpp
@@ -0,0 +1,407 @@
+/* -*- Mode: C++; c-basic-offset: 4; indent-tabs-mode: nil; tab-width: 4 -*- */
+/* vi: set ts=4 sw=4 expandtab: (add to ~/.vimrc: set modeline modelines=5) */
+/* ***** BEGIN LICENSE BLOCK *****
+ * Version: MPL 1.1/GPL 2.0/LGPL 2.1
+ *
+ * The contents of this file are subject to the Mozilla Public License Version
+ * 1.1 (the "License"); you may not use this file except in compliance with
+ * the License. You may obtain a copy of the License at
+ * http://www.mozilla.org/MPL/
+ *
+ * Software distributed under the License is distributed on an "AS IS" basis,
+ * WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+ * for the specific language governing rights and limitations under the
+ * License.
+ *
+ * The Original Code is [Open Source Virtual Machine].
+ *
+ * The Initial Developer of the Original Code is
+ * Adobe System Incorporated.
+ * Portions created by the Initial Developer are Copyright (C) 2004-2007
+ * the Initial Developer. All Rights Reserved.
+ *
+ * Contributor(s):
+ *   Adobe AS3 Team
+ *
+ * Alternatively, the contents of this file may be used under the terms of
+ * either the GNU General Public License Version 2 or later (the "GPL"), or
+ * the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+ * in which case the provisions of the GPL or the LGPL are applicable instead
+ * of those above. If you wish to allow use of your version of this file only
+ * under the terms of either the GPL or the LGPL, and not to allow others to
+ * use your version of this file under the terms of the MPL, indicate your
+ * decision by deleting the provisions above and replace them with the notice
+ * and other provisions required by the GPL or the LGPL. If you do not delete
+ * the provisions above, a recipient may use your version of this file under
+ * the terms of any one of the MPL, the GPL or the LGPL.
+ *
+ * ***** END LICENSE BLOCK ***** */
+
+#include <inttypes.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/mman.h>
+#include <stdarg.h>
+#include <unistd.h>
+#include <fcntl.h>
+#include <stdlib.h>
+#include <ctype.h>
+#include <dlfcn.h>
+
+#include <string>
+#include <vector>
+#include <map>
+#include <algorithm>
+
+#include <bfd.h>
+
+static void warn(const char *tmpl, ...)
+{
+    va_list ap;
+    va_start(ap, tmpl);
+    vfprintf(stderr, tmpl, ap);
+    fputs("\n", stderr);
+    va_end(ap);
+}
+
+static void fatal(const char *tmpl, ...)
+{
+    va_list ap;
+    va_start(ap, tmpl);
+    vfprintf(stderr, tmpl, ap);
+    fputs("\n", stderr);
+    va_end(ap);
+    exit(-1);
+}
+
+struct Chunk
+{
+    uint32_t tag;
+    uint32_t size;
+    char data[0];
+};
+
+struct DebugInfo
+{
+    const Chunk *info;
+
+    uint64_t pc() const
+    {
+        return ((uint64_t *) info->data)[0];
+    }
+
+    uint32_t tag() const
+    {
+        return info->tag;
+    }
+
+    std::string file() const
+    {
+        if (tag() != 'file'
+            )
+            fatal("DebugInfo::file() called on non-file debug info");
+
+        return std::string(info->data + sizeof(uint64_t),
+                           info->size - sizeof(uint64_t));
+    }
+
+    uint64_t line() const
+    {
+        if (tag() != 'line'
+            )
+            fatal("DebugInfo::line() called on non-linedebug info");
+
+        return ((uint64_t *) info->data)[1];
+    }
+
+    bool operator<(const DebugInfo &other) const
+    {
+        int64_t pcPad = pc() - other.pc();
+
+        if (pcPad)
+            return pcPad < 0;
+
+        if (tag() == 'file' && other.tag() != 'file'
+            )
+            return true;
+
+        return false;
+    }
+};
+
+struct MethBlock
+{
+    const Chunk *meth;
+    const Chunk *block;
+    const Chunk *code;
+
+    uint64_t start() const
+    {
+        return ((uint64_t *) block->data)[0];
+    }
+
+    uint64_t end() const
+    {
+        return ((uint64_t *) block->data)[1];
+    }
+
+    std::string name() const
+    {
+        return std::string(meth->data, meth->size);
+    }
+
+    const void *codeBytes() const
+    {
+        return code ? code->data : NULL;
+    }
+
+    const uint32_t codeLen() const
+    {
+        return code ? code->size : 0;
+    }
+
+    bool operator<(const MethBlock &other) const
+    {
+        return start() < other.start();
+    }
+};
+
+static void emit_so_bfd(const Chunk *base, uint64_t textOffs, uint64_t textLen,
+                        std::vector<MethBlock> &blocks,
+                        std::vector<DebugInfo> &debugInfos);
+
+int main(int argc, char **argv)
+{
+    if (argc != 2)
+        fatal("usage: %s inputLog", argv[0]);
+
+    int fd = open(argv[1], O_RDONLY);
+
+    if (fd < 0)
+        fatal("couldn't open %s", argv[1]);
+
+    off_t len = lseek(fd, 0, SEEK_END);
+    const void *start = mmap(NULL, len, PROT_READ, MAP_PRIVATE, fd, 0);
+    const void *cur = start;
+    const void *end = (char *) cur + len;
+
+    if (cur == MAP_FAILED)
+        fatal("couldn't mmap %s", argv[1]);
+
+    const Chunk *base = NULL;
+    const Chunk *meth = NULL;
+
+    std::vector<MethBlock> blocks;
+    std::vector<DebugInfo> debugInfos;
+
+    while (cur < end) {
+        const Chunk &chunk = *(const Chunk *) cur;
+
+        switch (chunk.tag) {
+        default:
+            warn("unknown tag %08x", chunk.tag);
+            break;
+        case 'base':
+            base = &chunk;
+            break;
+        case 'meth':
+            meth = &chunk;
+            break;
+        case 'blok': {
+            if (!meth)
+                fatal("'blok' tag without 'meth'");
+
+            MethBlock block;
+
+            block.meth = meth;
+            block.block = &chunk;
+            block.code = NULL;
+
+            blocks.push_back(block);
+        }
+            break;
+        case 'code': {
+            if (blocks.size() <= 0)
+                fatal("'code' chunk without 'blok'");
+            blocks.back().code = &chunk;
+        }
+            break;
+        case 'file':
+        case 'line': {
+            DebugInfo info = { &chunk };
+
+            debugInfos.push_back(info);
+        }
+            break;
+        }
+        cur = chunk.data + chunk.size;
+    }
+
+    std::sort(blocks.begin(), blocks.end());
+    std::sort(debugInfos.begin(), debugInfos.end());
+
+    if (!base)
+        fatal("no 'base' found");
+
+    uint64_t textOffs = 0x1000;
+    uint64_t textLen = 64 * 1024 * 1024;
+
+    if (base->size > sizeof(uint64_t)) {
+        char path[4096];
+        int len = base->size - sizeof(uint64_t);
+
+        if (len >= sizeof(path))
+            warn("path to base library too long");
+        else {
+            memcpy(path, base->data + sizeof(uint64_t), len);
+            path[len] = 0;
+
+            void *hdl = dlopen(path, RTLD_NOW);
+
+            if (!hdl)
+                warn("couldn't get info from base library %s (using defaults)",
+                     path);
+            else {
+                void *start = dlsym(hdl, "_jitStart");
+
+                if (!start)
+                    warn("couldn't find _jitStart in base library");
+                else {
+                    Dl_info dlInfo;
+
+                    if (dladdr(start, &dlInfo)) {
+                        if (((char *) start - (char *) dlInfo.dli_fbase)
+                                != textOffs)
+                            warn("_jitStart at unexpected location");
+                    } else
+                        warn("couldn't validate _jitStart location");
+
+                    void *end = dlsym(hdl, "_jitEnd");
+
+                    if (!end)
+                        warn("couldn't find _jitEnd in base library");
+                    else
+                        textLen = (char *) end - (char *) start;
+                }
+                dlclose(hdl);
+            }
+        }
+    }
+
+    emit_so_bfd(base, textOffs, textLen, blocks, debugInfos);
+    return 0;
+}
+
+static asymbol *make_glob_symbol(bfd *abfd, const char *name, asection *section,
+                                 uint64_t value)
+{
+    asymbol *result = bfd_make_empty_symbol(abfd);
+    result->name = strdup(name);
+    result->section = section;
+    result->flags = BSF_GLOBAL;
+    result->value = value;
+    return result;
+}
+
+static int l2(uint64_t n)
+{
+    int result = 0;
+
+    while (!(n & 1)) {
+        result++;
+        n >>= 1;
+    }
+    if (n != 1)
+        warn("textOffs not a power of 2");
+    return result;
+}
+
+static void emit_so_bfd(const Chunk *base, uint64_t textOffs, uint64_t textLen,
+                        std::vector<MethBlock> &blocks,
+                        std::vector<DebugInfo> &debugInfos)
+{
+    bfd_init();
+
+    bfd *abfd = bfd_openw("jit.o",
+#ifdef __MACH__
+                          "mach-o-i386"
+#else
+                          "elf32-i386"
+#endif
+)                          ;
+
+    bfd_set_format(abfd, bfd_object);
+#ifdef __MACH__
+    bfd_set_arch_mach(abfd, bfd_arch_i386, bfd_mach_i386_i386);
+#else
+    bfd_set_arch_mach(abfd, bfd_arch_i386, bfd_mach_i386_i386);
+#endif
+
+    asection *textSection = bfd_make_section(abfd, ".text");
+
+    bfd_set_section_flags(
+            abfd, textSection,
+            SEC_ALLOC | SEC_LOAD | SEC_READONLY | SEC_CODE | SEC_HAS_CONTENTS);
+#ifdef __MACH__
+    bfd_set_section_size(abfd, textSection, textLen);
+    // for mach-o, the .o we generate is linked into a .dylib
+    // so just set the alignment to make sure things end up
+    // in the right place after link
+    textSection->alignment_power = l2(textOffs);
+
+    static const uint64_t textAdj = 0;
+#else
+    bfd_set_section_size(abfd, textSection, textLen + textOffs);
+
+    // for linux (and oprofile) the generated object is used
+    // directly, so just offset the symbols
+    uint64_t textAdj = textOffs;
+#endif
+
+    asymbol **symbols = (asymbol **) malloc(
+            sizeof(asymbol *) * (6 + blocks.size()));
+    uint64_t start = ((uint64_t *) base->data)[0] + textOffs;
+
+    int n = 0;
+    std::map<std::string, int> namesSeen;
+
+    std::vector<MethBlock>::const_iterator i, e;
+
+    symbols[n++] = make_glob_symbol(abfd, "__jitStart", textSection, textAdj);
+    symbols[n++] = make_glob_symbol(abfd, "_jitStart", textSection, textAdj);
+
+    for (i = blocks.begin(), e = blocks.end(); i != e; i++, n++) {
+        std::string name = i->name();
+        int seen = namesSeen[name]++;
+
+        if (seen) {
+            char buf[16];
+
+            sprintf(buf, " (%d)", seen);
+            name += buf;
+        }
+        symbols[n] = make_glob_symbol(abfd, name.c_str(), textSection,
+                                      i->start() - start + textAdj);
+    }
+    symbols[n++] = make_glob_symbol(abfd, "__jitUnused", textSection,
+                                    blocks.back().end() - start + textAdj);
+    symbols[n++] = make_glob_symbol(abfd, "_jitUnused", textSection,
+                                    blocks.back().end() - start + textAdj);
+    symbols[n++] = make_glob_symbol(abfd, "__jitEnd", textSection,
+                                    textLen + textAdj);
+    symbols[n++] = make_glob_symbol(abfd, "_jitEnd", textSection,
+                                    textLen + textAdj);
+
+    bfd_set_symtab(abfd, symbols, n);
+
+    for (i = blocks.begin(), e = blocks.end(); i != e; i++, n++)
+        if (i->codeBytes())
+            bfd_set_section_contents(abfd, textSection, i->codeBytes(),
+                                     i->start() - start, i->codeLen());
+
+    bfd_close(abfd);
+
+    for (int n = 0; n < blocks.size(); n++)
+        free((void *) symbols[n]->name);
+    free(symbols);
+}
new file mode 100755
--- /dev/null
+++ b/utils/sharkprof/mksyms
@@ -0,0 +1,79 @@
+#!/bin/sh
+# ***** BEGIN LICENSE BLOCK *****
+# Version: MPL 1.1/GPL 2.0/LGPL 2.1
+#
+# The contents of this file are subject to the Mozilla Public License Version
+# 1.1 (the "License"); you may not use this file except in compliance with
+# the License. You may obtain a copy of the License at
+# http://www.mozilla.org/MPL/
+#
+# Software distributed under the License is distributed on an "AS IS" basis,
+# WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
+# for the specific language governing rights and limitations under the
+# License.
+#
+# The Original Code is [Open Source Virtual Machine].
+#
+# The Initial Developer of the Original Code is
+# Adobe System Incorporated.
+# Portions created by the Initial Developer are Copyright (C) 2012
+# the Initial Developer. All Rights Reserved.
+#
+# Contributor(s): Adobe AS3 Team
+#
+# Alternatively, the contents of this file may be used under the terms of
+# either the GNU General Public License Version 2 or later (the "GPL"), or
+# the GNU Lesser General Public License Version 2.1 or later (the "LGPL"),
+# in which case the provisions of the GPL or the LGPL are applicable instead
+# of those above. If you wish to allow use of your version of this file only
+# under the terms of either the GPL or the LGPL, and not to allow others to
+# use your version of this file under the terms of the MPL, indicate your
+# decision by deleting the provisions above and replace them with the notice
+# and other provisions required by the GPL or the LGPL. If you do not delete
+# the provisions above, a recipient may use your version of this file under
+# the terms of any one of the MPL, the GPL or the LGPL.
+#
+# ***** END LICENSE BLOCK *****
+
+# assume we're in TR's scope
+bindir=`hg root`/utils/sharkprof
+if [ ! -d $bindir ]; then
+	bindir=`hg root`/third_party/avmplus/utils/sharkprof
+fi
+logdir=/tmp
+tmpdir=/tmp
+
+mksym() {
+	logfile=$1
+	sfile=$2
+	sofile=$3
+	echo mksym $logfile $sfile $sofile
+	if ${bindir}/log2s-32 ${logfile} > ${sfile}; then
+		# 32bit worked
+		gcc -m32 -shared -nostartfiles -nodefaultlibs ${sfile} -o ${sofile}
+	else
+		# try 64
+		${bindir}/log2s-64 ${logfile} > ${sfile}
+		gcc -m64 -shared -nostartfiles -nodefaultlibs ${sfile} -o ${sofile}
+	fi
+	${bindir}/dinkuuid ${sofile}
+}
+
+pid=$1
+if [ "$pid" == "" ]; then
+	# no pid given, look for unprocessed log files
+	for f in /tmp/*.log; do
+		s=`echo $f | sed -e 's/log$/s/'`
+		so=`echo $f | sed -e 's/log$/so/'`
+		if [ ! -f $s ]; then
+			# log file exists but not .s file
+			mksym $f $s $so
+		fi
+	done
+else
+	# pid given, use that one
+	log=${logdir}/${pid}-jit.log
+	s=${logdir}/${pid}-jit.s
+	so=${logdir}/${pid}-jit.so
+	mksym $log $s $so
+fi