Bug 893363 - Ionmonkey: improve support for the Linux perf performance tool. r=bbouvier
authorDouglas Crosher <dtc-moz@scieneer.com>
Wed, 04 Sep 2013 11:57:24 +1000
changeset 145468 7850adf40329e17565cab0a26e7f93e1f7ef3100
parent 145467 48952adc1a955e189aa7586a3242f3c4785ef564
child 145469 907989350527f6f73e5b107cce519453afd34006
push id25213
push userkwierso@gmail.com
push dateWed, 04 Sep 2013 23:18:26 +0000
treeherdermozilla-central@dffedf20a02d [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersbbouvier
bugs893363
milestone26.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 893363 - Ionmonkey: improve support for the Linux perf performance tool. r=bbouvier
js/src/jit/AsmJS.cpp
js/src/jit/AsmJSLink.cpp
js/src/jit/AsmJSModule.h
js/src/jit/BaselineCompiler.cpp
js/src/jit/BaselineIC.cpp
js/src/jit/CodeGenerator.cpp
js/src/jit/CodeGenerator.h
js/src/jit/IonCaches.cpp
js/src/jit/MIRGenerator.h
js/src/jit/PerfSpewer.cpp
js/src/jit/PerfSpewer.h
js/src/jit/arm/CodeGenerator-arm.cpp
js/src/jit/arm/Trampoline-arm.cpp
js/src/jit/x64/Trampoline-x64.cpp
js/src/jit/x86/Trampoline-x86.cpp
--- a/js/src/jit/AsmJS.cpp
+++ b/js/src/jit/AsmJS.cpp
@@ -1486,25 +1486,27 @@ class MOZ_STACK_CLASS ModuleCompiler
         unsigned startCodeOffset = func.code()->offset();
         return module_->trackProfiledFunction(func.name(), startCodeOffset, endCodeOffset);
     }
 #endif
 #ifdef JS_ION_PERF
     bool trackPerfProfiledFunction(const Func &func, unsigned endCodeOffset) {
         unsigned lineno = 0U, columnIndex = 0U;
         parser().tokenStream.srcCoords.lineNumAndColumnIndex(func.srcOffset(), &lineno, &columnIndex);
-
         unsigned startCodeOffset = func.code()->offset();
-        return module_->trackPerfProfiledFunction(func.name(), startCodeOffset, endCodeOffset, lineno, columnIndex);
+        return module_->trackPerfProfiledFunction(func.name(), startCodeOffset, endCodeOffset,
+                                                  lineno, columnIndex);
     }
 
     bool trackPerfProfiledBlocks(AsmJSPerfSpewer &perfSpewer, const Func &func, unsigned endCodeOffset) {
         unsigned startCodeOffset = func.code()->offset();
-        perfSpewer.noteBlocksOffsets(masm_);
-        return module_->trackPerfProfiledBlocks(func.name(), startCodeOffset, endCodeOffset, perfSpewer.basicBlocks());
+        perfSpewer.noteBlocksOffsets();
+        unsigned endInlineCodeOffset = perfSpewer.endInlineCode.offset();
+        return module_->trackPerfProfiledBlocks(func.name(), startCodeOffset, endInlineCodeOffset,
+                                                endCodeOffset, perfSpewer.basicBlocks());
     }
 #endif
     bool addFunctionCounts(IonScriptCounts *counts) {
         return module_->addFunctionCounts(counts);
     }
 
     void finishFunctionBodies() {
         JS_ASSERT(!finishedFunctionBodies_);
@@ -1580,16 +1582,37 @@ class MOZ_STACK_CLASS ModuleCompiler
         masm_.processCodeLabels(code);
         JS_ASSERT(masm_.jumpRelocationTableBytes() == 0);
         JS_ASSERT(masm_.dataRelocationTableBytes() == 0);
         JS_ASSERT(masm_.preBarrierTableBytes() == 0);
         JS_ASSERT(!masm_.hasEnteredExitFrame());
 
         // Patch everything that needs an absolute address:
 
+#ifdef JS_ION_PERF
+        // Fix up the code offsets.  Note the endCodeOffset should not be filtered through
+        // 'actualOffset' as it is generated using 'size()' rather than a label.
+        for (unsigned i = 0; i < module_->numPerfFunctions(); i++) {
+            AsmJSModule::ProfiledFunction &func = module_->perfProfiledFunction(i);
+            func.startCodeOffset = masm_.actualOffset(func.startCodeOffset);
+        }
+
+        for (unsigned i = 0; i < module_->numPerfBlocksFunctions(); i++) {
+            AsmJSModule::ProfiledBlocksFunction &func = module_->perfProfiledBlocksFunction(i);
+            func.startCodeOffset = masm_.actualOffset(func.startCodeOffset);
+            func.endInlineCodeOffset = masm_.actualOffset(func.endInlineCodeOffset);
+            BasicBlocksVector &basicBlocks = func.blocks;
+            for (uint32_t i = 0; i < basicBlocks.length(); i++) {
+                Record &r = basicBlocks[i];
+                r.startOffset = masm_.actualOffset(r.startOffset);
+                r.endOffset = masm_.actualOffset(r.endOffset);
+            }
+        }
+#endif
+
         // Exit points
         for (unsigned i = 0; i < module_->numExits(); i++) {
             module_->exitIndexToGlobalDatum(i).exit = module_->interpExitTrampoline(module_->exit(i));
             module_->exitIndexToGlobalDatum(i).fun = NULL;
         }
         module_->setOperationCallbackExit(code + masm_.actualOffset(operationCallbackLabel_.offset()));
 
         // Function-pointer table entries
--- a/js/src/jit/AsmJSLink.cpp
+++ b/js/src/jit/AsmJSLink.cpp
@@ -12,19 +12,17 @@
 
 #include "jscntxt.h"
 #include "jsmath.h"
 #include "jswrapper.h"
 
 #include "frontend/BytecodeCompiler.h"
 #include "jit/AsmJSModule.h"
 #include "jit/Ion.h"
-#ifdef JS_ION_PERF
-# include "jit/PerfSpewer.h"
-#endif
+#include "jit/PerfSpewer.h"
 
 #include "jsfuninlines.h"
 #include "jsobjinlines.h"
 #include "jsscriptinlines.h"
 
 using namespace js;
 using namespace js::jit;
 
@@ -496,81 +494,77 @@ SendFunctionsToVTune(JSContext *cx, AsmJ
 
 #ifdef JS_ION_PERF
 static bool
 SendFunctionsToPerf(JSContext *cx, AsmJSModule &module)
 {
     if (!PerfFuncEnabled())
         return true;
 
-    AsmJSPerfSpewer perfSpewer;
-
-    unsigned long base = (unsigned long) module.functionCode();
-
+    uintptr_t base = (uintptr_t) module.functionCode();
     const char *filename = module.sourceDesc().scriptSource()->filename();
 
     for (unsigned i = 0; i < module.numPerfFunctions(); i++) {
         const AsmJSModule::ProfiledFunction &func = module.perfProfiledFunction(i);
-
-        unsigned long start = base + (unsigned long) func.startCodeOffset;
-        unsigned long end   = base + (unsigned long) func.endCodeOffset;
+        uintptr_t start = base + (unsigned long) func.startCodeOffset;
+        uintptr_t end   = base + (unsigned long) func.endCodeOffset;
         JS_ASSERT(end >= start);
-
-        unsigned long size = (end - start);
+        size_t size = end - start;
 
         JSAutoByteString bytes;
-        const char *method_name = AtomToPrintableString(cx, func.name, &bytes);
-        if (!method_name)
+        const char *name = AtomToPrintableString(cx, func.name, &bytes);
+        if (!name)
             return false;
 
-        unsigned lineno = func.lineno;
-        unsigned columnIndex = func.columnIndex;
-
-        perfSpewer.writeFunctionMap(start, size, filename, lineno, columnIndex, method_name);
+        writePerfSpewerAsmJSFunctionMap(start, size, filename, func.lineno, func.columnIndex, name);
     }
 
     return true;
 }
 
 static bool
 SendBlocksToPerf(JSContext *cx, AsmJSModule &module)
 {
     if (!PerfBlockEnabled())
         return true;
 
-    AsmJSPerfSpewer spewer;
     unsigned long funcBaseAddress = (unsigned long) module.functionCode();
-
     const char *filename = module.sourceDesc().scriptSource()->filename();
 
     for (unsigned i = 0; i < module.numPerfBlocksFunctions(); i++) {
         const AsmJSModule::ProfiledBlocksFunction &func = module.perfProfiledBlocksFunction(i);
 
-        unsigned long size = (unsigned long)func.endCodeOffset - (unsigned long)func.startCodeOffset;
+        size_t size = func.endCodeOffset - func.startCodeOffset;
+
         JSAutoByteString bytes;
-        const char *method_name = AtomToPrintableString(cx, func.name, &bytes);
-        if (!method_name)
+        const char *name = AtomToPrintableString(cx, func.name, &bytes);
+        if (!name)
             return false;
 
-        spewer.writeBlocksMap(funcBaseAddress, func.startCodeOffset, size, filename, method_name, func.blocks);
+        writePerfSpewerAsmJSBlocksMap(funcBaseAddress, func.startCodeOffset,
+                                      func.endInlineCodeOffset, size, filename, name, func.blocks);
     }
 
     return true;
 }
 #endif
 
 static bool
 SendModuleToAttachedProfiler(JSContext *cx, AsmJSModule &module)
 {
 #if defined(MOZ_VTUNE)
     if (IsVTuneProfilingActive() && !SendFunctionsToVTune(cx, module))
         return false;
 #endif
 
 #if defined(JS_ION_PERF)
+    if (module.numExportedFunctions() > 0) {
+        size_t firstEntryCode = (size_t) module.entryTrampoline(module.exportedFunction(0));
+        writePerfSpewerAsmJSEntriesAndExits(firstEntryCode, (size_t) module.globalData() - firstEntryCode);
+    }
     if (!SendBlocksToPerf(cx, module))
         return false;
     if (!SendFunctionsToPerf(cx, module))
         return false;
 #endif
 
     return true;
 }
--- a/js/src/jit/AsmJSModule.h
+++ b/js/src/jit/AsmJSModule.h
@@ -11,19 +11,17 @@
 
 #include "mozilla/PodOperations.h"
 
 #include "jsscript.h"
 
 #include "gc/Marking.h"
 #include "jit/AsmJS.h"
 #include "jit/IonMacroAssembler.h"
-#if defined(JS_ION_PERF)
-# include "jit/PerfSpewer.h"
-#endif
+#include "jit/PerfSpewer.h"
 #include "jit/RegisterSets.h"
 
 namespace js {
 
 // These EcmaScript-defined coercions form the basis of the asm.js type system.
 enum AsmJSCoercion
 {
     AsmJS_ToInt32,
@@ -275,26 +273,30 @@ class AsmJSModule
             MarkStringUnbarriered(trc, &name, "asm.js profiled function name");
         }
     };
 #endif
 
 #if defined(JS_ION_PERF)
     struct ProfiledBlocksFunction : public ProfiledFunction
     {
-        jit::PerfSpewer::BasicBlocksVector blocks;
+        unsigned endInlineCodeOffset;
+        jit::BasicBlocksVector blocks;
 
-        ProfiledBlocksFunction(JSAtom *name, unsigned start, unsigned end,
-                               jit::PerfSpewer::BasicBlocksVector &blocksVector)
-          : ProfiledFunction(name, start, end), blocks(mozilla::OldMove(blocksVector))
-        { }
+        ProfiledBlocksFunction(JSAtom *name, unsigned start, unsigned endInline, unsigned end,
+                               jit::BasicBlocksVector &blocksVector)
+          : ProfiledFunction(name, start, end), endInlineCodeOffset(endInline),
+            blocks(mozilla::OldMove(blocksVector))
+        {
+            JS_ASSERT(name->isTenured());
+        }
 
         ProfiledBlocksFunction(const ProfiledBlocksFunction &copy)
           : ProfiledFunction(copy.name, copy.startCodeOffset, copy.endCodeOffset),
-            blocks(mozilla::OldMove(copy.blocks))
+            endInlineCodeOffset(copy.endInlineCodeOffset), blocks(mozilla::OldMove(copy.blocks))
         { }
     };
 #endif
 
   private:
     typedef Vector<ExportedFunction, 0, SystemAllocPolicy> ExportedFunctionVector;
     typedef Vector<Global, 0, SystemAllocPolicy> GlobalVector;
     typedef Vector<Exit, 0, SystemAllocPolicy> ExitVector;
@@ -471,42 +473,43 @@ class AsmJSModule
 #ifdef MOZ_VTUNE
     bool trackProfiledFunction(JSAtom *name, unsigned startCodeOffset, unsigned endCodeOffset) {
         ProfiledFunction func(name, startCodeOffset, endCodeOffset);
         return profiledFunctions_.append(func);
     }
     unsigned numProfiledFunctions() const {
         return profiledFunctions_.length();
     }
-    const ProfiledFunction &profiledFunction(unsigned i) const {
+    ProfiledFunction &profiledFunction(unsigned i) {
         return profiledFunctions_[i];
     }
 #endif
 #ifdef JS_ION_PERF
     bool trackPerfProfiledFunction(JSAtom *name, unsigned startCodeOffset, unsigned endCodeOffset,
                                    unsigned line, unsigned column)
     {
         ProfiledFunction func(name, startCodeOffset, endCodeOffset, line, column);
         return profiledFunctions_.append(func);
     }
     unsigned numPerfFunctions() const {
         return profiledFunctions_.length();
     }
-    const ProfiledFunction &perfProfiledFunction(unsigned i) const {
+    ProfiledFunction &perfProfiledFunction(unsigned i) {
         return profiledFunctions_[i];
     }
 
-    bool trackPerfProfiledBlocks(JSAtom *name, unsigned startCodeOffset, unsigned endCodeOffset, jit::PerfSpewer::BasicBlocksVector &basicBlocks) {
-        ProfiledBlocksFunction func(name, startCodeOffset, endCodeOffset, basicBlocks);
+    bool trackPerfProfiledBlocks(JSAtom *name, unsigned startCodeOffset, unsigned endInlineCodeOffset,
+                                 unsigned endCodeOffset, jit::BasicBlocksVector &basicBlocks) {
+        ProfiledBlocksFunction func(name, startCodeOffset, endInlineCodeOffset, endCodeOffset, basicBlocks);
         return perfProfiledBlocksFunctions_.append(func);
     }
     unsigned numPerfBlocksFunctions() const {
         return perfProfiledBlocksFunctions_.length();
     }
-    const ProfiledBlocksFunction perfProfiledBlocksFunction(unsigned i) const {
+    ProfiledBlocksFunction &perfProfiledBlocksFunction(unsigned i) {
         return perfProfiledBlocksFunctions_[i];
     }
 #endif
     bool hasArrayView() const {
         return pod.hasArrayView_;
     }
     unsigned numFFIs() const {
         return pod.numFFIs_;
--- a/js/src/jit/BaselineCompiler.cpp
+++ b/js/src/jit/BaselineCompiler.cpp
@@ -7,16 +7,17 @@
 #include "jit/BaselineCompiler.h"
 
 #include "jit/BaselineHelpers.h"
 #include "jit/BaselineIC.h"
 #include "jit/BaselineJIT.h"
 #include "jit/FixedList.h"
 #include "jit/IonLinker.h"
 #include "jit/IonSpewer.h"
+#include "jit/PerfSpewer.h"
 #include "jit/VMFunctions.h"
 
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
 
 using namespace js;
 using namespace js::jit;
@@ -160,16 +161,20 @@ BaselineCompiler::compile()
     baselineScript->setMethod(code);
 
     script->setBaselineScript(baselineScript);
 
     IonSpew(IonSpew_BaselineScripts, "Created BaselineScript %p (raw %p) for %s:%d",
             (void *) script->baselineScript(), (void *) code->raw(),
             script->filename(), script->lineno);
 
+#ifdef JS_ION_PERF
+    writePerfSpewerBaselineProfile(script, code);
+#endif
+
     JS_ASSERT(pcMappingIndexEntries.length() > 0);
     baselineScript->copyPCMappingIndexEntries(&pcMappingIndexEntries[0]);
 
     JS_ASSERT(pcEntries.length() > 0);
     baselineScript->copyPCMappingEntries(pcEntries);
 
     // Copy IC entries
     if (icEntries_.length())
--- a/js/src/jit/BaselineIC.cpp
+++ b/js/src/jit/BaselineIC.cpp
@@ -10,16 +10,17 @@
 #include "jslibmath.h"
 
 #include "builtin/Eval.h"
 #include "jit/BaselineCompiler.h"
 #include "jit/BaselineHelpers.h"
 #include "jit/BaselineJIT.h"
 #include "jit/IonLinker.h"
 #include "jit/IonSpewer.h"
+#include "jit/PerfSpewer.h"
 #include "jit/VMFunctions.h"
 
 #include "jsboolinlines.h"
 #include "jsscriptinlines.h"
 
 #include "vm/Interpreter-inl.h"
 #include "vm/ScopeObject-inl.h"
 
@@ -567,16 +568,20 @@ ICStubCompiler::getStubCode()
         newStubCode->togglePreBarriers(true);
 
     // Cache newly compiled stubcode.
     if (!ion->putStubCode(stubKey, newStubCode))
         return NULL;
 
     JS_ASSERT(entersStubFrame_ == ICStub::CanMakeCalls(kind));
 
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(newStubCode, "BaselineIC");
+#endif
+
     return newStubCode;
 }
 
 bool
 ICStubCompiler::tailCallVM(const VMFunction &fun, MacroAssembler &masm)
 {
     IonCode *code = cx->runtime()->ionRuntime()->getVMWrapper(fun);
     if (!code)
--- a/js/src/jit/CodeGenerator.cpp
+++ b/js/src/jit/CodeGenerator.cpp
@@ -4347,17 +4347,23 @@ IonCompartment::generateStringConcatStub
     masm.pop(temp2);
     masm.pop(temp1);
 
     masm.bind(&failure);
     masm.movePtr(ImmWord((void *)NULL), output);
     masm.ret();
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "StringConcatStub");
+#endif
+
+    return code;
 }
 
 typedef bool (*CharCodeAtFn)(JSContext *, HandleString, int32_t, uint32_t *);
 static const VMFunction CharCodeAtInfo = FunctionInfo<CharCodeAtFn>(jit::CharCodeAt);
 
 bool
 CodeGenerator::visitCharCodeAt(LCharCodeAt *lir)
 {
@@ -5438,16 +5444,20 @@ CodeGenerator::generateAsmJS()
     // Thus, there is nothing special to do in the prologue except (possibly)
     // bump the stack.
     if (!generatePrologue())
         return false;
     if (!generateBody())
         return false;
     if (!generateEpilogue())
         return false;
+#if defined(JS_ION_PERF)
+    // Note the end of the inline code and start of the OOL code.
+    gen->perfSpewer().noteEndInlineCode(masm);
+#endif
     if (!generateOutOfLineCode())
         return false;
 
     // The only remaining work needed to compile this function is to patch the
     // switch-statement jump tables (the entries of the table need the absolute
     // address of the cases). These table entries are accmulated as CodeLabels
     // in the MacroAssembler's codeLabels_ list and processed all at once at in
     // the "static-link" phase of module compilation. It is critical that there
@@ -5505,16 +5515,20 @@ CodeGenerator::generate()
     if (!generatePrologue())
         return false;
     if (!generateBody())
         return false;
     if (!generateEpilogue())
         return false;
     if (!generateInvalidateEpilogue())
         return false;
+#if defined(JS_ION_PERF)
+    // Note the end of the inline code and start of the OOL code.
+    perfSpewer_.noteEndInlineCode(masm);
+#endif
     if (!generateOutOfLineCode())
         return false;
 
     return !masm.oom();
 }
 
 bool
 CodeGenerator::link()
@@ -5604,18 +5618,20 @@ CodeGenerator::link()
     ionScript->setInvalidationEpilogueDataOffset(invalidateEpilogueData_.offset());
     ionScript->setOsrPc(gen->info().osrPc());
     ionScript->setOsrEntryOffset(getOsrEntryOffset());
     ptrdiff_t real_invalidate = masm.actualOffset(invalidate_.offset());
     ionScript->setInvalidationEpilogueOffset(real_invalidate);
 
     ionScript->setDeoptTable(deoptTable_);
 
+#if defined(JS_ION_PERF)
     if (PerfEnabled())
         perfSpewer_.writeProfile(script, code, masm);
+#endif
 
     // for generating inline caches during the execution.
     if (runtimeData_.length())
         ionScript->copyRuntimeData(&runtimeData_[0]);
     if (cacheList_.length())
         ionScript->copyCacheEntries(&cacheList_[0], masm);
 
     // for marking during GC.
--- a/js/src/jit/CodeGenerator.h
+++ b/js/src/jit/CodeGenerator.h
@@ -356,15 +356,17 @@ class CodeGenerator : public CodeGenerat
     bool emitStoreHoleCheck(Register elements, const LAllocation *index, LSnapshot *snapshot);
 
     bool emitAssertRangeI(Range *r, Register input);
     bool emitAssertRangeD(Range *r, FloatRegister input, FloatRegister temp);
 
     // Script counts created when compiling code with no associated JSScript.
     IonScriptCounts *unassociatedScriptCounts_;
 
+#if defined(JS_ION_PERF)
     PerfSpewer perfSpewer_;
+#endif
 };
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_CodeGenerator_h */
--- a/js/src/jit/IonCaches.cpp
+++ b/js/src/jit/IonCaches.cpp
@@ -403,16 +403,21 @@ IonCache::linkAndAttachStub(JSContext *c
     if (pc_) {
         IonSpew(IonSpew_InlineCaches, "Cache %p(%s:%d/%d) generated %s %s stub at %p",
                 this, script_->filename(), script_->lineno, pc_ - script_->code,
                 attachKind, CacheName(kind()), code->raw());
     } else {
         IonSpew(IonSpew_InlineCaches, "Cache %p generated %s %s stub at %p",
                 this, attachKind, CacheName(kind()), code->raw());
     }
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "IonCache");
+#endif
+
     return true;
 }
 
 void
 IonCache::updateBaseAddress(IonCode *code, MacroAssembler &masm)
 {
     fallbackLabel_.repoint(code, &masm);
 }
--- a/js/src/jit/MIRGenerator.h
+++ b/js/src/jit/MIRGenerator.h
@@ -13,19 +13,17 @@
 #include <stdarg.h>
 
 #include "jscntxt.h"
 #include "jscompartment.h"
 
 #include "jit/CompileInfo.h"
 #include "jit/IonAllocPolicy.h"
 #include "jit/IonCompartment.h"
-#if defined(JS_ION_PERF)
-# include "jit/PerfSpewer.h"
-#endif
+#include "jit/PerfSpewer.h"
 #include "jit/RegisterSets.h"
 
 namespace js {
 namespace jit {
 
 class MBasicBlock;
 class MIRGraph;
 class MStart;
--- a/js/src/jit/PerfSpewer.cpp
+++ b/js/src/jit/PerfSpewer.cpp
@@ -12,28 +12,69 @@
 
 #include "jit/IonSpewer.h"
 #include "jit/LinearScan.h"
 #include "jit/LIR.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
 #include "jit/RangeAnalysis.h"
 
+// perf expects its data to be in a file /tmp/perf-PID.map, but for Android
+// and B2G the map files are written to /data/local/tmp/perf-PID.map
+//
+// Except that Android 4.3 no longer allows the browser to write to /data/local/tmp/
+// so also try /sdcard/.
+
+#ifndef PERF_SPEW_DIR
+# if defined(__ANDROID__)
+#  define PERF_SPEW_DIR "/data/local/tmp/"
+#  define PERF_SPEW_DIR_2 "/sdcard/"
+# else
+#  define PERF_SPEW_DIR "/tmp/"
+# endif
+#endif
+
 using namespace js;
 using namespace js::jit;
 
 #define PERF_MODE_NONE  1
 #define PERF_MODE_FUNC  2
 #define PERF_MODE_BLOCK 3
 
 #ifdef JS_ION_PERF
+
 static uint32_t PerfMode = 0;
 
 static bool PerfChecked = false;
 
+static FILE *PerfFilePtr = NULL;
+
+#ifdef JS_THREADSAFE
+# include "jslock.h"
+static PRLock *PerfMutex;
+#endif
+
+static bool
+openPerfMap(const char *dir)
+{
+    const ssize_t bufferSize = 256;
+    char filenameBuffer[bufferSize];
+
+    if (snprintf(filenameBuffer, bufferSize, "%sperf-%d.map", dir, getpid()) >= bufferSize)
+        return false;
+
+    JS_ASSERT(!PerfFilePtr);
+    PerfFilePtr = fopen(filenameBuffer, "a");
+
+    if (!PerfFilePtr)
+        return false;
+
+    return true;
+}
+
 void
 js::jit::CheckPerf() {
     if (!PerfChecked) {
         const char *env = getenv("IONPERF");
         if (env == NULL) {
             PerfMode = PERF_MODE_NONE;
             fprintf(stderr, "Warning: JIT perf reporting requires IONPERF set to \"block\" or \"func\". ");
             fprintf(stderr, "Perf mapping will be deactivated.\n");
@@ -46,70 +87,85 @@ js::jit::CheckPerf() {
         } else {
             fprintf(stderr, "Use IONPERF=func to record at function granularity\n");
             fprintf(stderr, "Use IONPERF=block to record at basic block granularity\n");
             fprintf(stderr, "\n");
             fprintf(stderr, "Be advised that using IONPERF will cause all scripts\n");
             fprintf(stderr, "to be leaked.\n");
             exit(0);
         }
+
+        if (PerfMode != PERF_MODE_NONE) {
+#ifdef JS_THREADSAFE
+            PerfMutex = PR_NewLock();
+            if (!PerfMutex)
+                MOZ_CRASH();
+#endif
+
+            if (openPerfMap(PERF_SPEW_DIR)) {
+                PerfChecked = true;
+                return;
+            }
+
+#if defined(__ANDROID__)
+            if (openPerfMap(PERF_SPEW_DIR_2)) {
+                PerfChecked = true;
+                return;
+            }
+#endif
+            fprintf(stderr, "Failed to open perf map file.  Disabling IONPERF.\n");
+            PerfMode = PERF_MODE_NONE;
+        }
         PerfChecked = true;
     }
 }
 
 bool
 js::jit::PerfBlockEnabled() {
     JS_ASSERT(PerfMode);
     return PerfMode == PERF_MODE_BLOCK;
 }
 
 bool
 js::jit::PerfFuncEnabled() {
     JS_ASSERT(PerfMode);
     return PerfMode == PERF_MODE_FUNC;
 }
 
-#endif
-
-uint32_t PerfSpewer::nextFunctionIndex = 0;
-
-PerfSpewer::PerfSpewer()
-  : fp_(NULL)
+static bool
+lockPerfMap(void)
 {
     if (!PerfEnabled())
-        return;
+        return false;
 
-#   if defined(__linux__)
-    // perf expects its data to be in a file /tmp/perf-PID.map
-    const ssize_t bufferSize = 256;
-    char filenameBuffer[bufferSize];
-    if (snprintf(filenameBuffer, bufferSize,
-                 "/tmp/perf-%d.map",
-                 getpid()) >= bufferSize)
-        return;
+#ifdef JS_THREADSAFE
+    PR_Lock(PerfMutex);
+#endif
 
-    fp_ = fopen(filenameBuffer, "a");
-    if (!fp_)
-        return;
-#   else
-    fprintf(stderr, "Warning: PerfEnabled, but not running on linux\n");
-#   endif
+    JS_ASSERT(PerfFilePtr);
+    return true;
 }
 
-PerfSpewer::~PerfSpewer()
+static void
+unlockPerfMap()
 {
-    if (fp_)
-        fclose(fp_);
+    JS_ASSERT(PerfFilePtr);
+    fflush(PerfFilePtr);
+#ifdef JS_THREADSAFE
+    PR_Unlock(PerfMutex);
+#endif
 }
 
+uint32_t PerfSpewer::nextFunctionIndex = 0;
+
 bool
 PerfSpewer::startBasicBlock(MBasicBlock *blk,
                             MacroAssembler &masm)
 {
-    if (!PerfBlockEnabled() || !fp_)
+    if (!PerfBlockEnabled())
         return true;
 
     const char *filename = blk->info().script()->filename();
     unsigned lineNumber, columnNumber;
     if (blk->pc()) {
         lineNumber = PCToLineNumber(blk->info().script(),
                                     blk->pc(),
                                     &columnNumber);
@@ -120,176 +176,268 @@ PerfSpewer::startBasicBlock(MBasicBlock 
     Record r(filename, lineNumber, columnNumber, blk->id());
     masm.bind(&r.start);
     return basicBlocks_.append(r);
 }
 
 bool
 PerfSpewer::endBasicBlock(MacroAssembler &masm)
 {
-    if (!PerfBlockEnabled() || !fp_)
+    if (!PerfBlockEnabled())
         return true;
 
     masm.bind(&basicBlocks_[basicBlocks_.length() - 1].end);
     return true;
 }
 
+bool
+PerfSpewer::noteEndInlineCode(MacroAssembler &masm)
+{
+    if (!PerfBlockEnabled())
+        return true;
+
+    masm.bind(&endInlineCode);
+    return true;
+}
+
 void
 PerfSpewer::writeProfile(JSScript *script,
                          IonCode *code,
                          MacroAssembler &masm)
 {
-    if (!fp_)
-        return;
-
-    uint32_t thisFunctionIndex = nextFunctionIndex++;
+    if (PerfFuncEnabled()) {
+        if (!lockPerfMap())
+            return;
 
-    if (PerfFuncEnabled()) {
-        unsigned long size = (unsigned long) code->instructionsSize();
+        uint32_t thisFunctionIndex = nextFunctionIndex++;
+
+        size_t size = code->instructionsSize();
         if (size > 0) {
-            fprintf(fp_,
-                    "%lx %lx %s:%d: Func%02d\n",
+            fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d\n",
                     reinterpret_cast<uintptr_t>(code->raw()),
                     size,
                     script->filename(),
                     script->lineno,
                     thisFunctionIndex);
         }
-    } else if (PerfBlockEnabled()) {
+        unlockPerfMap();
+        return;
+    }
+
+    if (PerfBlockEnabled() && basicBlocks_.length() > 0) {
+        if (!lockPerfMap())
+            return;
+
+        uint32_t thisFunctionIndex = nextFunctionIndex++;
         uintptr_t funcStart = uintptr_t(code->raw());
+        uintptr_t funcEndInlineCode = funcStart + masm.actualOffset(endInlineCode.offset());
         uintptr_t funcEnd = funcStart + code->instructionsSize();
 
-        uintptr_t cur = funcStart;
+        // function begins with the prologue, which is located before the first basic block
+        size_t prologueSize = masm.actualOffset(basicBlocks_[0].start.offset());
+
+        if (prologueSize > 0) {
+            fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Prologue\n",
+                    funcStart, prologueSize, script->filename(), script->lineno, thisFunctionIndex);
+        }
+
+        uintptr_t cur = funcStart + prologueSize;
         for (uint32_t i = 0; i < basicBlocks_.length(); i++) {
             Record &r = basicBlocks_[i];
 
             uintptr_t blockStart = funcStart + masm.actualOffset(r.start.offset());
             uintptr_t blockEnd = funcStart + masm.actualOffset(r.end.offset());
 
             JS_ASSERT(cur <= blockStart);
             if (cur < blockStart) {
-                fprintf(fp_,
-                        "%lx %lx %s:%d: Func%02d-Block?\n",
-                        static_cast<unsigned long>(cur),
-                        static_cast<unsigned long>(blockStart - cur),
+                fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Block?\n",
+                        static_cast<uintptr_t>(cur),
+                        static_cast<uintptr_t>(blockStart - cur),
                         script->filename(), script->lineno,
                         thisFunctionIndex);
             }
             cur = blockEnd;
 
-            unsigned long size = blockEnd - blockStart;
+            size_t size = blockEnd - blockStart;
 
             if (size > 0) {
-                fprintf(fp_,
-                        "%lx %lx %s:%d:%d: Func%02d-Block%d\n",
-                        static_cast<unsigned long>(blockStart), size,
+                fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Func%02d-Block%d\n",
+                        static_cast<uintptr_t>(blockStart), size,
                         r.filename, r.lineNumber, r.columnNumber,
                         thisFunctionIndex, r.id);
             }
         }
 
-        // Any stuff after the basic blocks is presumably OOL code,
-        // which I do not currently categorize.
-        JS_ASSERT(cur <= funcEnd);
-        if (cur < funcEnd) {
-            fprintf(fp_,
-                    "%lx %lx %s:%d: Func%02d-OOL\n",
-                    static_cast<unsigned long>(cur),
-                    static_cast<unsigned long>(funcEnd - cur),
+        JS_ASSERT(cur <= funcEndInlineCode);
+        if (cur < funcEndInlineCode) {
+            fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-Epilogue\n",
+                    cur, funcEndInlineCode - cur,
+                    script->filename(), script->lineno,
+                    thisFunctionIndex);
+        }
+
+        JS_ASSERT(funcEndInlineCode <= funcEnd);
+        if (funcEndInlineCode < funcEnd) {
+            fprintf(PerfFilePtr, "%zx %zx %s:%d: Func%02d-OOL\n",
+                    funcEndInlineCode, funcEnd - funcEndInlineCode,
                     script->filename(), script->lineno,
                     thisFunctionIndex);
         }
+
+        unlockPerfMap();
+        return;
     }
 }
 
-#if defined(JS_ION_PERF)
 void
-AsmJSPerfSpewer::writeFunctionMap(unsigned long base, unsigned long size, const char *filename, unsigned lineno, unsigned colIndex, const char *funcName)
+js::jit::writePerfSpewerBaselineProfile(JSScript *script, IonCode *code)
 {
-    if (!fp_ || !PerfFuncEnabled() || size == 0U)
+    if (!PerfEnabled())
+        return;
+
+    if (!lockPerfMap())
+        return;
+
+    size_t size = code->instructionsSize();
+    if (size > 0) {
+        fprintf(PerfFilePtr, "%zx %zx %s:%d: Baseline\n",
+                reinterpret_cast<uintptr_t>(code->raw()),
+                size, script->filename(), script->lineno);
+    }
+
+    unlockPerfMap();
+}
+
+void
+js::jit::writePerfSpewerIonCodeProfile(IonCode *code, const char *msg)
+{
+    if (!code || !PerfEnabled())
         return;
 
-    fprintf(fp_,
-            "%lx %lx %s:%d:%d: Function %s\n",
-            base, size,
-            filename, lineno, colIndex, funcName);
+    if (!lockPerfMap())
+        return;
+
+    size_t size = code->instructionsSize();
+    if (size > 0) {
+        fprintf(PerfFilePtr, "%zx %zx %s (%p 0x%zx)\n",
+                reinterpret_cast<uintptr_t>(code->raw()),
+                size, msg, code->raw(), size);
+    }
+
+    unlockPerfMap();
+}
+
+void
+js::jit::writePerfSpewerAsmJSFunctionMap(uintptr_t base, uintptr_t size,
+                                         const char *filename, unsigned lineno, unsigned colIndex,
+                                         const char *funcName)
+{
+    if (!PerfFuncEnabled() || size == 0U)
+        return;
+
+    if (!lockPerfMap())
+        return;
+
+    fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Function %s\n", base, size, filename, lineno, colIndex, funcName);
+
+    unlockPerfMap();
 }
 
 bool
 AsmJSPerfSpewer::startBasicBlock(MBasicBlock *blk, MacroAssembler &masm)
 {
-    if (!PerfBlockEnabled() || !fp_)
+    if (!PerfBlockEnabled())
         return true;
 
     Record r("", blk->lineno(), blk->columnIndex(), blk->id()); // filename is retrieved later
     masm.bind(&r.start);
     return basicBlocks_.append(r);
 }
 
 void
-AsmJSPerfSpewer::noteBlocksOffsets(MacroAssembler &masm)
+AsmJSPerfSpewer::noteBlocksOffsets()
 {
-    if (!PerfBlockEnabled() || !fp_)
+    if (!PerfBlockEnabled())
         return;
 
     for (uint32_t i = 0; i < basicBlocks_.length(); i++) {
         Record &r = basicBlocks_[i];
-        r.startOffset = masm.actualOffset(r.start.offset());
-        r.endOffset = masm.actualOffset(r.end.offset());
+        r.startOffset = r.start.offset();
+        r.endOffset = r.end.offset();
     }
 }
 
 void
-AsmJSPerfSpewer::writeBlocksMap(unsigned long baseAddress, unsigned long funcStartOffset, unsigned long funcSize,
-                                const char *filename, const char *funcName, const BasicBlocksVector &basicBlocks)
+js::jit::writePerfSpewerAsmJSBlocksMap(uintptr_t baseAddress, size_t funcStartOffset,
+                                       size_t funcEndInlineOffset, size_t funcSize,
+                                       const char *filename, const char *funcName,
+                                       const js::jit::BasicBlocksVector &basicBlocks)
 {
-    if (!fp_ || !PerfBlockEnabled() || basicBlocks.length() == 0)
+    if (!PerfBlockEnabled() || basicBlocks.length() == 0)
+        return;
+
+    if (!lockPerfMap())
         return;
 
     // function begins with the prologue, which is located before the first basic block
-    unsigned long prologueSize = basicBlocks[0].startOffset - funcStartOffset;
+    size_t prologueSize = basicBlocks[0].startOffset - funcStartOffset;
+    size_t cur = baseAddress + funcStartOffset + prologueSize;
+    size_t funcEndInlineCode = baseAddress + funcEndInlineOffset;
+    size_t funcEnd = baseAddress + funcStartOffset + funcSize;
 
-    unsigned long cur = baseAddress + funcStartOffset + prologueSize;
-    unsigned long funcEnd = baseAddress + funcStartOffset + funcSize - prologueSize;
+    if (prologueSize > 0) {
+        fprintf(PerfFilePtr, "%zx %zx %s: Function %s - Prologue\n",
+                baseAddress + funcStartOffset, prologueSize, filename, funcName);
+    }
 
     for (uint32_t i = 0; i < basicBlocks.length(); i++) {
         const Record &r = basicBlocks[i];
 
-        unsigned long blockStart = baseAddress + (unsigned long) r.startOffset;
-        unsigned long blockEnd = baseAddress + (unsigned long) r.endOffset;
-
-        if (i == basicBlocks.length() - 1) {
-            // for the last block, manually add the ret instruction
-            blockEnd += 1u;
-        }
+        size_t blockStart = baseAddress + r.startOffset;
+        size_t blockEnd = baseAddress + r.endOffset;
 
         JS_ASSERT(cur <= blockStart);
         if (cur < blockStart) {
-            fprintf(fp_,
-                    "%lx %lx %s: Function %s - unknown block\n",
+            fprintf(PerfFilePtr, "%zx %zx %s: Function %s - unknown block\n",
                     cur, blockStart - cur,
                     filename,
                     funcName);
         }
         cur = blockEnd;
 
-        unsigned long size = blockEnd - blockStart;
+        size_t size = blockEnd - blockStart;
         if (size > 0) {
-            fprintf(fp_,
-                    "%lx %lx %s:%d:%d: Function %s - Block %d\n",
+            fprintf(PerfFilePtr, "%zx %zx %s:%d:%d: Function %s - Block %d\n",
                     blockStart, size,
                     filename, r.lineNumber, r.columnNumber,
                     funcName, r.id);
         }
     }
 
-    // Any stuff after the basic blocks is presumably OOL code,
-    // which I do not currently categorize.
-    JS_ASSERT(cur <= funcEnd);
-    if (cur < funcEnd) {
-        fprintf(fp_,
-                "%lx %lx %s: Function %s - OOL\n",
-                cur, funcEnd - cur,
-                filename,
-                funcName);
+    JS_ASSERT(cur <= funcEndInlineCode);
+    if (cur < funcEndInlineCode)
+        fprintf(PerfFilePtr, "%zx %zx %s: Function %s - Epilogue\n",
+                cur, funcEndInlineCode - cur, filename, funcName);
+
+    JS_ASSERT(funcEndInlineCode <= funcEnd);
+    if (funcEndInlineCode < funcEnd) {
+        fprintf(PerfFilePtr, "%zx %zx %s: Function %s - OOL\n",
+                funcEndInlineCode, funcEnd - funcEndInlineCode, filename, funcName);
     }
+
+    unlockPerfMap();
 }
+
+void
+js::jit::writePerfSpewerAsmJSEntriesAndExits(uintptr_t base, size_t size)
+{
+    if (size == 0)
+        return;
+
+    if (!lockPerfMap())
+        return;
+
+    fprintf(PerfFilePtr, "%zx %zx AsmJS Entries and Exits (0x%zx 0x%zx)\n", base, size, base, size);
+
+    unlockPerfMap();
+}
+
 #endif // defined (JS_ION_PERF)
--- a/js/src/jit/PerfSpewer.h
+++ b/js/src/jit/PerfSpewer.h
@@ -26,67 +26,77 @@ static inline bool PerfEnabled() {
 }
 #else
 static inline void CheckPerf() {}
 static inline bool PerfBlockEnabled() { return false; }
 static inline bool PerfFuncEnabled() { return false; }
 static inline bool PerfEnabled() { return false; }
 #endif
 
+#ifdef JS_ION_PERF
+
+struct Record {
+    const char *filename;
+    unsigned lineNumber;
+    unsigned columnNumber;
+    uint32_t id;
+    Label start, end;
+    size_t startOffset, endOffset;
+
+    Record(const char *filename,
+           unsigned lineNumber,
+           unsigned columnNumber,
+           uint32_t id)
+      : filename(filename), lineNumber(lineNumber),
+        columnNumber(columnNumber), id(id),
+        startOffset(0u), endOffset(0u)
+    {}
+};
+
+typedef Vector<Record, 1, SystemAllocPolicy> BasicBlocksVector;
+
 class PerfSpewer
 {
   protected:
     static uint32_t nextFunctionIndex;
-    FILE *fp_;
 
   public:
-    struct Record {
-        const char *filename;
-        unsigned lineNumber;
-        unsigned columnNumber;
-        uint32_t id;
-        Label start, end;
-        unsigned startOffset, endOffset;
+    Label endInlineCode;
 
-        Record(const char *filename,
-               unsigned lineNumber,
-               unsigned columnNumber,
-               uint32_t id)
-          : filename(filename), lineNumber(lineNumber),
-            columnNumber(columnNumber), id(id),
-            startOffset(0u), endOffset(0u)
-        {}
-    };
-
-    typedef Vector<Record, 1, SystemAllocPolicy> BasicBlocksVector;
   protected:
     BasicBlocksVector basicBlocks_;
 
   public:
-    PerfSpewer();
-    virtual ~PerfSpewer();
-
     virtual bool startBasicBlock(MBasicBlock *blk, MacroAssembler &masm);
     bool endBasicBlock(MacroAssembler &masm);
-    void writeProfile(JSScript *script,
-                      IonCode *code,
-                      MacroAssembler &masm);
+    bool noteEndInlineCode(MacroAssembler &masm);
+
+    void writeProfile(JSScript *script, IonCode *code, MacroAssembler &masm);
 };
 
+void writePerfSpewerBaselineProfile(JSScript *script, IonCode *code);
+void writePerfSpewerIonCodeProfile(IonCode *code, const char *msg);
+
 class AsmJSPerfSpewer : public PerfSpewer
 {
   public:
     bool startBasicBlock(MBasicBlock *blk, MacroAssembler &masm);
 
-    void noteBlocksOffsets(MacroAssembler &masm);
+    void noteBlocksOffsets();
     BasicBlocksVector &basicBlocks() { return basicBlocks_; }
+};
+
+void writePerfSpewerAsmJSFunctionMap(uintptr_t base, uintptr_t size, const char *filename,
+                                     unsigned lineno, unsigned colIndex, const char *funcName);
 
-    void writeBlocksMap(unsigned long baseAddress, unsigned long funcStartOffset,
-                        unsigned long funcSize, const char *filename, const char *funcName,
-                        const BasicBlocksVector &basicBlocks);
-    void writeFunctionMap(unsigned long base, unsigned long size, const char *filename,
-                          unsigned lineno, unsigned colIndex, const char *funcName);
-};
+void writePerfSpewerAsmJSBlocksMap(uintptr_t baseAddress, size_t funcStartOffset,
+                                   size_t funcStartOOLOffset, size_t funcSize,
+                                   const char *filename, const char *funcName,
+                                   const BasicBlocksVector &basicBlocks);
+
+void writePerfSpewerAsmJSEntriesAndExits(uintptr_t base, size_t size);
+
+#endif // JS_ION_PERF
 
 } // namespace jit
 } // namespace js
 
 #endif /* jit_PerfSpewer_h */
--- a/js/src/jit/arm/CodeGenerator-arm.cpp
+++ b/js/src/jit/arm/CodeGenerator-arm.cpp
@@ -12,17 +12,16 @@
 #include "jscompartment.h"
 #include "jsnum.h"
 
 #include "jit/CodeGenerator.h"
 #include "jit/IonCompartment.h"
 #include "jit/IonFrames.h"
 #include "jit/MIR.h"
 #include "jit/MIRGraph.h"
-#include "jit/PerfSpewer.h"
 #include "vm/Shape.h"
 
 #include "jsscriptinlines.h"
 
 #include "jit/shared/CodeGenerator-shared-inl.h"
 #include "vm/Shape-inl.h"
 
 using namespace js;
--- a/js/src/jit/arm/Trampoline-arm.cpp
+++ b/js/src/jit/arm/Trampoline-arm.cpp
@@ -9,16 +9,17 @@
 #include "assembler/assembler/MacroAssembler.h"
 #include "jit/arm/BaselineHelpers-arm.h"
 #include "jit/Bailouts.h"
 #include "jit/ExecutionModeInlines.h"
 #include "jit/IonCompartment.h"
 #include "jit/IonFrames.h"
 #include "jit/IonLinker.h"
 #include "jit/IonSpewer.h"
+#include "jit/PerfSpewer.h"
 #include "jit/VMFunctions.h"
 
 using namespace js;
 using namespace js::jit;
 
 static const FloatRegisterSet NonVolatileFloatRegs =
     FloatRegisterSet((1 << FloatRegisters::d8) |
                      (1 << FloatRegisters::d9) |
@@ -312,17 +313,23 @@ IonRuntime::generateEnterJIT(JSContext *
     //   ASSERT(JSReturnReg_Type.code() == JSReturnReg_Data.code()+1);
     //   aasm->as_extdtr(IsStore, 64, true, Offset,
     //                   JSReturnReg_Data, EDtrAddr(r5, EDtrOffImm(0)));
 
     // Restore non-volatile registers and return.
     GenerateReturn(masm, true);
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "EnterJIT");
+#endif
+
+    return code;
 }
 
 IonCode *
 IonRuntime::generateInvalidator(JSContext *cx)
 {
     // See large comment in x86's IonRuntime::generateInvalidator.
     MacroAssembler masm(cx);
     //masm.as_bkpt();
@@ -369,16 +376,21 @@ IonRuntime::generateInvalidator(JSContex
 
     // Jump to shared bailout tail. The BailoutInfo pointer has to be in r2.
     IonCode *bailoutTail = cx->runtime()->ionRuntime()->getBailoutTail();
     masm.branch(bailoutTail);
 
     Linker linker(masm);
     IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
     IonSpew(IonSpew_Invalidate, "   invalidation thunk created at %p", (void *) code->raw());
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "Invalidator");
+#endif
+
     return code;
 }
 
 IonCode *
 IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut)
 {
     MacroAssembler masm(cx);
     // ArgumentsRectifierReg contains the |nargs| pushed onto the current frame.
@@ -471,16 +483,21 @@ IonRuntime::generateArgumentsRectifier(J
     masm.ret();
     Linker linker(masm);
     IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
 
     CodeOffsetLabel returnLabel(returnOffset);
     returnLabel.fixup(&masm);
     if (returnAddrOut)
         *returnAddrOut = (void *) (code->raw() + returnLabel.offset());
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "ArgumentsRectifier");
+#endif
+
     return code;
 }
 
 static void
 GenerateBailoutThunk(JSContext *cx, MacroAssembler &masm, uint32_t frameClass)
 {
     // the stack should look like:
     // [IonFrame]
@@ -584,27 +601,39 @@ IonRuntime::generateBailoutTable(JSConte
     Label bailout;
     for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++)
         masm.ma_bl(&bailout);
     masm.bind(&bailout);
 
     GenerateBailoutThunk(cx, masm, frameClass);
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "BailoutTable");
+#endif
+
+    return code;
 }
 
 IonCode *
 IonRuntime::generateBailoutHandler(JSContext *cx)
 {
     MacroAssembler masm(cx);
     GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "BailoutHandler");
+#endif
+
+    return code;
 }
 
 IonCode *
 IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
 {
     typedef MoveResolver::MoveOperand MoveOperand;
 
     JS_ASSERT(functionWrappers_);
@@ -772,16 +801,20 @@ IonRuntime::generateVMWrapper(JSContext 
     if (!wrapper)
         return NULL;
 
     // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to
     // use relookupOrAdd instead of add.
     if (!functionWrappers_->relookupOrAdd(p, &f, wrapper))
         return NULL;
 
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(wrapper, "VMWrapper");
+#endif
+
     return wrapper;
 }
 
 IonCode *
 IonRuntime::generatePreBarrier(JSContext *cx, MIRType type)
 {
     MacroAssembler masm(cx);
 
@@ -807,17 +840,23 @@ IonRuntime::generatePreBarrier(JSContext
         JS_ASSERT(type == MIRType_Shape);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon));
     }
 
     masm.PopRegsInMask(save);
     masm.ret();
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "PreBarrier");
+#endif
+
+    return code;
 }
 
 typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *);
 static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap);
 
 IonCode *
 IonRuntime::generateDebugTrapHandler(JSContext *cx)
 {
@@ -856,32 +895,50 @@ IonRuntime::generateDebugTrapHandler(JSC
     masm.bind(&forcedReturn);
     masm.loadValue(Address(r11, BaselineFrame::reverseOffsetOfReturnValue()),
                    JSReturnOperand);
     masm.mov(r11, sp);
     masm.pop(r11);
     masm.ret();
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *codeDbg = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(codeDbg, "DebugTrapHandler");
+#endif
+
+    return codeDbg;
 }
 
 IonCode *
 IonRuntime::generateExceptionTailStub(JSContext *cx)
 {
     MacroAssembler masm;
 
     masm.handleFailureWithHandlerTail();
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "ExceptionTailStub");
+#endif
+
+    return code;
 }
 
 IonCode *
 IonRuntime::generateBailoutTailStub(JSContext *cx)
 {
     MacroAssembler masm;
 
     masm.generateBailoutTail(r1, r2);
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "BailoutTailStub");
+#endif
+
+    return code;
 }
--- a/js/src/jit/x64/Trampoline-x64.cpp
+++ b/js/src/jit/x64/Trampoline-x64.cpp
@@ -6,16 +6,17 @@
 
 #include "assembler/assembler/MacroAssembler.h"
 #include "jit/Bailouts.h"
 #include "jit/ExecutionModeInlines.h"
 #include "jit/IonCompartment.h"
 #include "jit/IonFrames.h"
 #include "jit/IonLinker.h"
 #include "jit/IonSpewer.h"
+#include "jit/PerfSpewer.h"
 #include "jit/VMFunctions.h"
 #include "jit/x64/BaselineHelpers-x64.h"
 
 using namespace js;
 using namespace js::jit;
 
 // All registers to save and restore. This includes the stack pointer, since we
 // use the ability to reference register values on the stack by index.
@@ -266,17 +267,23 @@ IonRuntime::generateEnterJIT(JSContext *
     masm.pop(r12);
     masm.pop(rbx);
 
     // Restore frame pointer and return.
     masm.pop(rbp);
     masm.ret();
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "EnterJIT");
+#endif
+
+    return code;
 }
 
 IonCode *
 IonRuntime::generateInvalidator(JSContext *cx)
 {
     AutoIonContextAlloc aica(cx);
     MacroAssembler masm(cx);
 
@@ -309,17 +316,23 @@ IonRuntime::generateInvalidator(JSContex
     // Pop the machine state and the dead frame.
     masm.lea(Operand(rsp, rbx, TimesOne, sizeof(InvalidationBailoutStack)), rsp);
 
     // Jump to shared bailout tail. The BailoutInfo pointer has to be in r9.
     IonCode *bailoutTail = cx->runtime()->ionRuntime()->getBailoutTail();
     masm.jmp(bailoutTail);
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "Invalidator");
+#endif
+
+    return code;
 }
 
 IonCode *
 IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut)
 {
     // Do not erase the frame pointer in this function.
 
     MacroAssembler masm(cx);
@@ -390,16 +403,20 @@ IonRuntime::generateArgumentsRectifier(J
     masm.pop(r11);            // Discard numActualArgs.
     masm.addq(r9, rsp);       // Discard pushed arguments.
 
     masm.ret();
 
     Linker linker(masm);
     IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
 
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "ArgumentsRectifier");
+#endif
+
     CodeOffsetLabel returnLabel(returnOffset);
     returnLabel.fixup(&masm);
     if (returnAddrOut)
         *returnAddrOut = (void *) (code->raw() + returnLabel.offset());
     return code;
 }
 
 static void
@@ -450,17 +467,23 @@ IonRuntime::generateBailoutTable(JSConte
 IonCode *
 IonRuntime::generateBailoutHandler(JSContext *cx)
 {
     MacroAssembler masm;
 
     GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "BailoutHandler");
+#endif
+
+    return code;
 }
 
 IonCode *
 IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
 {
     typedef MoveResolver::MoveOperand MoveOperand;
 
     JS_ASSERT(!StackKeptAligned);
@@ -631,16 +654,20 @@ IonRuntime::generateVMWrapper(JSContext 
                     f.explicitStackSlots() * sizeof(void *) +
                     f.extraValuesToPop * sizeof(Value)));
 
     Linker linker(masm);
     IonCode *wrapper = linker.newCode(cx, JSC::OTHER_CODE);
     if (!wrapper)
         return NULL;
 
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(wrapper, "VMWrapper");
+#endif
+
     // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to
     // use relookupOrAdd instead of add.
     if (!functionWrappers_->relookupOrAdd(p, &f, wrapper))
         return NULL;
 
     return wrapper;
 }
 
@@ -665,17 +692,23 @@ IonRuntime::generatePreBarrier(JSContext
         JS_ASSERT(type == MIRType_Shape);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon));
     }
 
     masm.PopRegsInMask(regs);
     masm.ret();
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "PreBarrier");
+#endif
+
+    return code;
 }
 
 typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *);
 static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap);
 
 IonCode *
 IonRuntime::generateDebugTrapHandler(JSContext *cx)
 {
@@ -718,32 +751,50 @@ IonRuntime::generateDebugTrapHandler(JSC
     masm.bind(&forcedReturn);
     masm.loadValue(Address(ebp, BaselineFrame::reverseOffsetOfReturnValue()),
                    JSReturnOperand);
     masm.mov(rbp, rsp);
     masm.pop(rbp);
     masm.ret();
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *codeDbg = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(codeDbg, "DebugTrapHandler");
+#endif
+
+    return codeDbg;
 }
 
 IonCode *
 IonRuntime::generateExceptionTailStub(JSContext *cx)
 {
     MacroAssembler masm;
 
     masm.handleFailureWithHandlerTail();
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "ExceptionTailStub");
+#endif
+
+    return code;
 }
 
 IonCode *
 IonRuntime::generateBailoutTailStub(JSContext *cx)
 {
     MacroAssembler masm;
 
     masm.generateBailoutTail(rdx, r9);
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "BailoutTailStub");
+#endif
+
+    return code;
 }
--- a/js/src/jit/x86/Trampoline-x86.cpp
+++ b/js/src/jit/x86/Trampoline-x86.cpp
@@ -14,16 +14,20 @@
 #include "jit/IonFrames.h"
 #include "jit/IonLinker.h"
 #include "jit/IonSpewer.h"
 #include "jit/VMFunctions.h"
 #include "jit/x86/BaselineHelpers-x86.h"
 
 #include "jsscriptinlines.h"
 
+#ifdef JS_ION_PERF
+# include "jit/PerfSpewer.h"
+#endif
+
 using namespace js;
 using namespace js::jit;
 
 // All registers to save and restore. This includes the stack pointer, since we
 // use the ability to reference register values on the stack by index.
 static const RegisterSet AllRegs =
   RegisterSet(GeneralRegisterSet(Registers::AllMask),
               FloatRegisterSet(FloatRegisters::AllMask));
@@ -246,17 +250,23 @@ IonRuntime::generateEnterJIT(JSContext *
     masm.pop(esi);
     masm.pop(ebx);
 
     // Restore old stack frame pointer
     masm.pop(ebp);
     masm.ret();
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "EnterJIT");
+#endif
+
+    return code;
 }
 
 IonCode *
 IonRuntime::generateInvalidator(JSContext *cx)
 {
     AutoIonContextAlloc aica(cx);
     MacroAssembler masm(cx);
 
@@ -299,16 +309,21 @@ IonRuntime::generateInvalidator(JSContex
 
     // Jump to shared bailout tail. The BailoutInfo pointer has to be in ecx.
     IonCode *bailoutTail = cx->runtime()->ionRuntime()->getBailoutTail();
     masm.jmp(bailoutTail);
 
     Linker linker(masm);
     IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
     IonSpew(IonSpew_Invalidate, "   invalidation thunk created at %p", (void *) code->raw());
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "Invalidator");
+#endif
+
     return code;
 }
 
 IonCode *
 IonRuntime::generateArgumentsRectifier(JSContext *cx, ExecutionMode mode, void **returnAddrOut)
 {
     MacroAssembler masm(cx);
 
@@ -392,16 +407,20 @@ IonRuntime::generateArgumentsRectifier(J
     masm.lea(Operand(unwind), esp);
 
     masm.pop(FramePointer);
     masm.ret();
 
     Linker linker(masm);
     IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
 
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "ArgumentsRectifier");
+#endif
+
     CodeOffsetLabel returnLabel(returnOffset);
     returnLabel.fixup(&masm);
     if (returnAddrOut)
         *returnAddrOut = (void *) (code->raw() + returnLabel.offset());
     return code;
 }
 
 static void
@@ -466,28 +485,40 @@ IonRuntime::generateBailoutTable(JSConte
     Label bailout;
     for (size_t i = 0; i < BAILOUT_TABLE_SIZE; i++)
         masm.call(&bailout);
     masm.bind(&bailout);
 
     GenerateBailoutThunk(cx, masm, frameClass);
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "BailoutHandler");
+#endif
+
+    return code;
 }
 
 IonCode *
 IonRuntime::generateBailoutHandler(JSContext *cx)
 {
     MacroAssembler masm;
 
     GenerateBailoutThunk(cx, masm, NO_FRAME_SIZE_CLASS_ID);
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "BailoutHandler");
+#endif
+
+    return code;
 }
 
 IonCode *
 IonRuntime::generateVMWrapper(JSContext *cx, const VMFunction &f)
 {
     typedef MoveResolver::MoveOperand MoveOperand;
 
     JS_ASSERT(!StackKeptAligned);
@@ -653,16 +684,20 @@ IonRuntime::generateVMWrapper(JSContext 
                     f.explicitStackSlots() * sizeof(void *) +
                     f.extraValuesToPop * sizeof(Value)));
 
     Linker linker(masm);
     IonCode *wrapper = linker.newCode(cx, JSC::OTHER_CODE);
     if (!wrapper)
         return NULL;
 
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(wrapper, "VMWrapper");
+#endif
+
     // linker.newCode may trigger a GC and sweep functionWrappers_ so we have to
     // use relookupOrAdd instead of add.
     if (!functionWrappers_->relookupOrAdd(p, &f, wrapper))
         return NULL;
 
     return wrapper;
 }
 
@@ -694,17 +729,23 @@ IonRuntime::generatePreBarrier(JSContext
         JS_ASSERT(type == MIRType_Shape);
         masm.callWithABI(JS_FUNC_TO_DATA_PTR(void *, MarkShapeFromIon));
     }
 
     masm.PopRegsInMask(save);
     masm.ret();
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "PreBarrier");
+#endif
+
+    return code;
 }
 
 typedef bool (*HandleDebugTrapFn)(JSContext *, BaselineFrame *, uint8_t *, bool *);
 static const VMFunction HandleDebugTrapInfo = FunctionInfo<HandleDebugTrapFn>(HandleDebugTrap);
 
 IonCode *
 IonRuntime::generateDebugTrapHandler(JSContext *cx)
 {
@@ -747,32 +788,50 @@ IonRuntime::generateDebugTrapHandler(JSC
     masm.bind(&forcedReturn);
     masm.loadValue(Address(ebp, BaselineFrame::reverseOffsetOfReturnValue()),
                    JSReturnOperand);
     masm.mov(ebp, esp);
     masm.pop(ebp);
     masm.ret();
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *codeDbg = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(codeDbg, "DebugTrapHandler");
+#endif
+
+    return codeDbg;
 }
 
 IonCode *
 IonRuntime::generateExceptionTailStub(JSContext *cx)
 {
     MacroAssembler masm;
 
     masm.handleFailureWithHandlerTail();
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "ExceptionTailStub");
+#endif
+
+    return code;
 }
 
 IonCode *
 IonRuntime::generateBailoutTailStub(JSContext *cx)
 {
     MacroAssembler masm;
 
     masm.generateBailoutTail(edx, ecx);
 
     Linker linker(masm);
-    return linker.newCode(cx, JSC::OTHER_CODE);
+    IonCode *code = linker.newCode(cx, JSC::OTHER_CODE);
+
+#ifdef JS_ION_PERF
+    writePerfSpewerIonCodeProfile(code, "BailoutTailStub");
+#endif
+
+    return code;
 }