LCOV - code coverage report
Current view: top level - js/src/vm - GeckoProfiler.h (source / functions) Hit Total Coverage
Test: output.info Lines: 5 16 31.2 %
Date: 2017-07-14 16:53:18 Functions: 4 8 50.0 %
Legend: Lines: hit not hit

          Line data    Source code
       1             : /* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*-
       2             :  * vim: set ts=8 sts=4 et sw=4 tw=99:
       3             :  * This Source Code Form is subject to the terms of the Mozilla Public
       4             :  * License, v. 2.0. If a copy of the MPL was not distributed with this
       5             :  * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
       6             : 
       7             : #ifndef vm_GeckoProfiler_h
       8             : #define vm_GeckoProfiler_h
       9             : 
      10             : #include "mozilla/DebugOnly.h"
      11             : #include "mozilla/GuardObjects.h"
      12             : 
      13             : #include <stddef.h>
      14             : 
      15             : #include "jsscript.h"
      16             : 
      17             : #include "js/ProfilingStack.h"
      18             : #include "threading/ExclusiveData.h"
      19             : #include "vm/MutexIDs.h"
      20             : 
      21             : /*
      22             :  * Gecko Profiler integration with the JS Engine
      23             :  * https://developer.mozilla.org/en/Performance/Profiling_with_the_Built-in_Profiler
      24             :  *
      25             :  * The Gecko Profiler (found in tools/profiler) is an implementation of a
      26             :  * profiler which has the ability to walk the C++ stack as well as use
      27             :  * instrumentation to gather information. When dealing with JS, however, the
      28             :  * profiler needs integration with the engine because otherwise it is very
      29             :  * difficult to figure out what javascript is executing.
      30             :  *
      31             :  * The current method of integration with the profiler is a form of
      32             :  * instrumentation: every time a JS function is entered, a bit of information
      33             :  * is pushed onto a stack that the profiler owns and maintains. This
      34             :  * information is then popped at the end of the JS function. The profiler
      35             :  * informs the JS engine of this stack at runtime, and it can by turned on/off
      36             :  * dynamically. Each stack entry has type ProfileEntry.
      37             :  *
      38             :  * Throughout execution, the size of the stack recorded in memory may exceed the
      39             :  * maximum. The JS engine will not write any information past the maximum limit,
      40             :  * but it will still maintain the size of the stack. Profiler code is aware of
      41             :  * this and iterates the stack accordingly.
      42             :  *
      43             :  * There is some information pushed on the profiler stack for every JS function
      44             :  * that is entered. First is a char* label with a description of what function
      45             :  * was entered. Currently this string is of the form "function (file:line)" if
      46             :  * there's a function name, or just "file:line" if there's no function name
      47             :  * available. The other bit of information is the relevant C++ (native) stack
      48             :  * pointer. This stack pointer is what enables the interleaving of the C++ and
      49             :  * the JS stack. Finally, throughout execution of the function, some extra
      50             :  * information may be updated on the ProfileEntry structure.
      51             :  *
      52             :  * = Profile Strings
      53             :  *
      54             :  * The profile strings' allocations and deallocation must be carefully
      55             :  * maintained, and ideally at a very low overhead cost. For this reason, the JS
      56             :  * engine maintains a mapping of all known profile strings. These strings are
      57             :  * keyed in lookup by a JSScript*, but are serialized with a JSFunction*,
      58             :  * JSScript* pair. A JSScript will destroy its corresponding profile string when
      59             :  * the script is finalized.
      60             :  *
      61             :  * For this reason, a char* pointer pushed on the profiler stack is valid only
      62             :  * while it is on the profiler stack. The profiler uses sampling to read off
      63             :  * information from this instrumented stack, and it therefore copies the string
      64             :  * byte for byte when a JS function is encountered during sampling.
      65             :  *
      66             :  * = Native Stack Pointer
      67             :  *
      68             :  * The actual value pushed as the native pointer is nullptr for most JS
      69             :  * functions. The reason for this is that there's actually very little
      70             :  * correlation between the JS stack and the C++ stack because many JS functions
      71             :  * all run in the same C++ frame, or can even go backwards in C++ when going
      72             :  * from the JIT back to the interpreter.
      73             :  *
      74             :  * To alleviate this problem, all JS functions push nullptr as their "native
      75             :  * stack pointer" to indicate that it's a JS function call. The function
      76             :  * RunScript(), however, pushes an actual C++ stack pointer onto the profiler
      77             :  * stack. This way when interleaving C++ and JS, if the Gecko Profiler sees a
      78             :  * nullptr native stack pointer on the profiler stack, it looks backwards for
      79             :  * the first non-nullptr pointer and uses that for all subsequent nullptr
      80             :  * native stack pointers.
      81             :  *
      82             :  * = Line Numbers
      83             :  *
      84             :  * One goal of sampling is to get both a backtrace of the JS stack, but also
      85             :  * know where within each function on the stack execution currently is. For
      86             :  * this, each ProfileEntry has a 'pc' field to tell where its execution
      87             :  * currently is. This field is updated whenever a call is made to another JS
      88             :  * function, and for the JIT it is also updated whenever the JIT is left.
      89             :  *
      90             :  * This field is in a union with a uint32_t 'line' so that C++ can make use of
      91             :  * the field as well. It was observed that tracking 'line' via PCToLineNumber in
      92             :  * JS was far too expensive, so that is why the pc instead of the translated
      93             :  * line number is stored.
      94             :  *
      95             :  * As an invariant, if the pc is nullptr, then the JIT is currently executing
      96             :  * generated code. Otherwise execution is in another JS function or in C++. With
      97             :  * this in place, only the top entry of the stack can ever have nullptr as its
      98             :  * pc. Additionally with this invariant, it is possible to maintain mappings of
      99             :  * JIT code to pc which can be accessed safely because they will only be
     100             :  * accessed from a signal handler when the JIT code is executing.
     101             :  */
     102             : 
     103             : namespace js {
     104             : 
     105             : // The `ProfileStringMap` weakly holds its `JSScript*` keys and owns its string
     106             : // values. Entries are removed when the `JSScript` is finalized; see
     107             : // `GeckoProfiler::onScriptFinalized`.
     108             : using ProfileStringMap = HashMap<JSScript*,
     109             :                                  UniqueChars,
     110             :                                  DefaultHasher<JSScript*>,
     111             :                                  SystemAllocPolicy>;
     112             : 
     113             : class AutoGeckoProfilerEntry;
     114             : class GeckoProfilerEntryMarker;
     115             : class GeckoProfilerBaselineOSRMarker;
     116             : 
     117           0 : class GeckoProfiler
     118             : {
     119             :     friend class AutoGeckoProfilerEntry;
     120             :     friend class GeckoProfilerEntryMarker;
     121             :     friend class GeckoProfilerBaselineOSRMarker;
     122             : 
     123             :     JSRuntime*           rt;
     124             :     ExclusiveData<ProfileStringMap> strings;
     125             :     PseudoStack*         pseudoStack_;
     126             :     bool                 slowAssertions;
     127             :     uint32_t             enabled_;
     128             :     void                (*eventMarker_)(const char*);
     129             : 
     130             :     UniqueChars allocProfileString(JSScript* script, JSFunction* function);
     131             : 
     132             :   public:
     133             :     explicit GeckoProfiler(JSRuntime* rt);
     134             : 
     135             :     bool init();
     136             : 
     137       47741 :     uint32_t stackPointer() { MOZ_ASSERT(installed()); return pseudoStack_->stackPointer; }
     138           0 :     ProfileEntry* stack() { return pseudoStack_->entries; }
     139             : 
     140             :     /* management of whether instrumentation is on or off */
     141       15109 :     bool enabled() { MOZ_ASSERT_IF(enabled_, installed()); return enabled_; }
     142       71617 :     bool installed() { return pseudoStack_ != nullptr; }
     143             :     MOZ_MUST_USE bool enable(bool enabled);
     144             :     void enableSlowAssertions(bool enabled) { slowAssertions = enabled; }
     145           0 :     bool slowAssertionsEnabled() { return slowAssertions; }
     146             : 
     147             :     /*
     148             :      * Functions which are the actual instrumentation to track run information
     149             :      *
     150             :      *   - enter: a function has started to execute
     151             :      *   - updatePC: updates the pc information about where a function
     152             :      *               is currently executing
     153             :      *   - exit: this function has ceased execution, and no further
     154             :      *           entries/exits will be made
     155             :      */
     156             :     bool enter(JSContext* cx, JSScript* script, JSFunction* maybeFun);
     157             :     void exit(JSScript* script, JSFunction* maybeFun);
     158           0 :     void updatePC(JSScript* script, jsbytecode* pc) {
     159           0 :         if (!enabled())
     160           0 :             return;
     161             : 
     162           0 :         uint32_t sp = pseudoStack_->stackPointer;
     163           0 :         if (sp - 1 < PseudoStack::MaxEntries) {
     164           0 :             MOZ_ASSERT(sp > 0);
     165           0 :             MOZ_ASSERT(pseudoStack_->entries[sp - 1].rawScript() == script);
     166           0 :             pseudoStack_->entries[sp - 1].setPC(pc);
     167             :         }
     168             :     }
     169             : 
     170             :     void setProfilingStack(PseudoStack* pseudoStack);
     171             :     void setEventMarker(void (*fn)(const char*));
     172             :     const char* profileString(JSScript* script, JSFunction* maybeFun);
     173             :     void onScriptFinalized(JSScript* script);
     174             : 
     175             :     void markEvent(const char* event);
     176             : 
     177             :     /* meant to be used for testing, not recommended to call in normal code */
     178             :     size_t stringsCount();
     179             :     void stringsReset();
     180             : 
     181          62 :     uint32_t* addressOfEnabled() {
     182          62 :         return &enabled_;
     183             :     }
     184             : 
     185             :     void trace(JSTracer* trc);
     186             :     void fixupStringsMapAfterMovingGC();
     187             : #ifdef JSGC_HASH_TABLE_CHECKS
     188             :     void checkStringsMapAfterMovingGC();
     189             : #endif
     190             : };
     191             : 
     192             : inline size_t
     193             : GeckoProfiler::stringsCount()
     194             : {
     195             :     return strings.lock()->count();
     196             : }
     197             : 
     198             : inline void
     199             : GeckoProfiler::stringsReset()
     200             : {
     201             :     strings.lock()->clear();
     202             : }
     203             : 
     204             : /*
     205             :  * This class is used in RunScript() to push the marker onto the sampling stack
     206             :  * that we're about to enter JS function calls. This is the only time in which a
     207             :  * valid stack pointer is pushed to the sampling stack.
     208             :  */
     209             : class MOZ_RAII GeckoProfilerEntryMarker
     210             : {
     211             :   public:
     212             :     explicit GeckoProfilerEntryMarker(JSRuntime* rt,
     213             :                                       JSScript* script
     214             :                                       MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     215             :     ~GeckoProfilerEntryMarker();
     216             : 
     217             :   private:
     218             :     GeckoProfiler* profiler;
     219             :     mozilla::DebugOnly<uint32_t> spBefore_;
     220             :     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     221             : };
     222             : 
     223             : /*
     224             :  * RAII class to automatically add Gecko Profiler pseudo frame entries.
     225             :  *
     226             :  * NB: The `label` string must be statically allocated.
     227             :  */
     228             : class MOZ_NONHEAP_CLASS AutoGeckoProfilerEntry
     229             : {
     230             :   public:
     231             :     explicit AutoGeckoProfilerEntry(JSRuntime* rt, const char* label,
     232             :                                     ProfileEntry::Category category = ProfileEntry::Category::JS
     233             :                                     MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     234             :     ~AutoGeckoProfilerEntry();
     235             : 
     236             :   private:
     237             :     GeckoProfiler* profiler_;
     238             :     mozilla::DebugOnly<uint32_t> spBefore_;
     239             :     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     240             : };
     241             : 
     242             : /*
     243             :  * This class is used in the interpreter to bound regions where the baseline JIT
     244             :  * being entered via OSR.  It marks the current top pseudostack entry as
     245             :  * OSR-ed
     246             :  */
     247             : class MOZ_RAII GeckoProfilerBaselineOSRMarker
     248             : {
     249             :   public:
     250             :     explicit GeckoProfilerBaselineOSRMarker(JSRuntime* rt, bool hasProfilerFrame
     251             :                                             MOZ_GUARD_OBJECT_NOTIFIER_PARAM);
     252             :     ~GeckoProfilerBaselineOSRMarker();
     253             : 
     254             :   private:
     255             :     GeckoProfiler* profiler;
     256             :     mozilla::DebugOnly<uint32_t> spBefore_;
     257             :     MOZ_DECL_USE_GUARD_OBJECT_NOTIFIER
     258             : };
     259             : 
     260             : /*
     261             :  * This class manages the instrumentation portion of the profiling for JIT
     262             :  * code.
     263             :  *
     264             :  * The instrumentation tracks entry into functions, leaving those functions via
     265             :  * a function call, reentering the functions from a function call, and exiting
     266             :  * the functions from returning. This class also handles inline frames and
     267             :  * manages the instrumentation which needs to be attached to them as well.
     268             :  *
     269             :  * The basic methods which emit instrumentation are at the end of this class,
     270             :  * and the management functions are all described in the middle.
     271             :  */
     272             : template<class Assembler, class Register>
     273             : class GeckoProfilerInstrumentation
     274             : {
     275             :     GeckoProfiler* profiler_; // Instrumentation location management
     276             : 
     277             :   public:
     278             :     /*
     279             :      * Creates instrumentation which writes information out the the specified
     280             :      * profiler's stack and constituent fields.
     281             :      */
     282             :     explicit GeckoProfilerInstrumentation(GeckoProfiler* profiler) : profiler_(profiler) {}
     283             : 
     284             :     /* Small proxies around GeckoProfiler */
     285             :     bool enabled() { return profiler_ && profiler_->enabled(); }
     286             :     GeckoProfiler* profiler() { MOZ_ASSERT(enabled()); return profiler_; }
     287             :     void disable() { profiler_ = nullptr; }
     288             : };
     289             : 
     290             : } /* namespace js */
     291             : 
     292             : #endif /* vm_GeckoProfiler_h */

Generated by: LCOV version 1.13