LCOV - code coverage report
Current view: top level - js/public - ProfilingStack.h (source / functions) Hit Total Coverage
Test: output.info Lines: 48 61 78.7 %
Date: 2017-07-14 16:53:18 Functions: 12 18 66.7 %
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 js_ProfilingStack_h
       8             : #define js_ProfilingStack_h
       9             : 
      10             : #include <algorithm>
      11             : #include <stdint.h>
      12             : 
      13             : #include "jsbytecode.h"
      14             : #include "jstypes.h"
      15             : #include "js/TypeDecls.h"
      16             : #include "js/Utility.h"
      17             : 
      18             : struct JSRuntime;
      19             : class JSTracer;
      20             : 
      21             : class PseudoStack;
      22             : 
      23             : namespace js {
      24             : 
      25             : // A call stack can be specified to the JS engine such that all JS entry/exits
      26             : // to functions push/pop an entry to/from the specified stack.
      27             : //
      28             : // For more detailed information, see vm/GeckoProfiler.h.
      29             : //
      30             : class ProfileEntry
      31             : {
      32             :     // A ProfileEntry represents either a C++ profile entry or a JS one.
      33             : 
      34             :     // Descriptive label for this entry. Must be a static string! Can be an
      35             :     // empty string, but not a null pointer.
      36             :     const char* label_;
      37             : 
      38             :     // An additional descriptive string of this entry which is combined with
      39             :     // |label_| in profiler output. Need not be (and usually isn't) static. Can
      40             :     // be null.
      41             :     const char* dynamicString_;
      42             : 
      43             :     // Stack pointer for non-JS entries, the script pointer otherwise.
      44             :     void* spOrScript;
      45             : 
      46             :     // Line number for non-JS entries, the bytecode offset otherwise.
      47             :     int32_t lineOrPcOffset;
      48             : 
      49             :     // Bits 0..1 hold the Kind. Bits 2..3 are unused. Bits 4..12 hold the
      50             :     // Category.
      51             :     uint32_t kindAndCategory_;
      52             : 
      53             :     static int32_t pcToOffset(JSScript* aScript, jsbytecode* aPc);
      54             : 
      55             :   public:
      56             :     enum class Kind : uint32_t {
      57             :         // A normal C++ frame.
      58             :         CPP_NORMAL = 0,
      59             : 
      60             :         // A special C++ frame indicating the start of a run of JS pseudostack
      61             :         // entries. CPP_MARKER_FOR_JS frames are ignored, except for the sp
      62             :         // field.
      63             :         CPP_MARKER_FOR_JS = 1,
      64             : 
      65             :         // A normal JS frame.
      66             :         JS_NORMAL = 2,
      67             : 
      68             :         // An interpreter JS frame that has OSR-ed into baseline. JS_NORMAL
      69             :         // frames can be converted to JS_OSR and back. JS_OSR frames are
      70             :         // ignored.
      71             :         JS_OSR = 3,
      72             : 
      73             :         KIND_MASK = 0x3,
      74             :     };
      75             : 
      76             :     // Keep these in sync with devtools/client/performance/modules/categories.js
      77             :     enum class Category : uint32_t {
      78             :         OTHER    = 1u << 4,
      79             :         CSS      = 1u << 5,
      80             :         JS       = 1u << 6,
      81             :         GC       = 1u << 7,
      82             :         CC       = 1u << 8,
      83             :         NETWORK  = 1u << 9,
      84             :         GRAPHICS = 1u << 10,
      85             :         STORAGE  = 1u << 11,
      86             :         EVENTS   = 1u << 12,
      87             : 
      88             :         FIRST    = OTHER,
      89             :         LAST     = EVENTS,
      90             : 
      91             :         CATEGORY_MASK = ~uint32_t(Kind::KIND_MASK),
      92             :     };
      93             : 
      94             :     static_assert((uint32_t(Category::FIRST) & uint32_t(Kind::KIND_MASK)) == 0,
      95             :                   "Category overlaps with Kind");
      96             : 
      97       36216 :     bool isCpp() const
      98             :     {
      99       36216 :         Kind k = kind();
     100       36217 :         return k == Kind::CPP_NORMAL || k == Kind::CPP_MARKER_FOR_JS;
     101             :     }
     102             : 
     103       17773 :     bool isJs() const
     104             :     {
     105       17773 :         Kind k = kind();
     106       17773 :         return k == Kind::JS_NORMAL || k == Kind::JS_OSR;
     107             :     }
     108             : 
     109             :     void setLabel(const char* aLabel) { label_ = aLabel; }
     110           0 :     const char* label() const { return label_; }
     111             : 
     112           0 :     const char* dynamicString() const { return dynamicString_; }
     113             : 
     114       36217 :     void initCppFrame(const char* aLabel, const char* aDynamicString, void* sp, uint32_t aLine,
     115             :                       Kind aKind, Category aCategory)
     116             :     {
     117       36217 :         label_ = aLabel;
     118       36217 :         dynamicString_ = aDynamicString;
     119       36217 :         spOrScript = sp;
     120       36217 :         lineOrPcOffset = static_cast<int32_t>(aLine);
     121       36217 :         kindAndCategory_ = uint32_t(aKind) | uint32_t(aCategory);
     122       36217 :         MOZ_ASSERT(isCpp());
     123       36217 :     }
     124             : 
     125       17386 :     void initJsFrame(const char* aLabel, const char* aDynamicString, JSScript* aScript,
     126             :                      jsbytecode* aPc)
     127             :     {
     128       17386 :         label_ = aLabel;
     129       17386 :         dynamicString_ = aDynamicString;
     130       17386 :         spOrScript = aScript;
     131       17386 :         lineOrPcOffset = pcToOffset(aScript, aPc);
     132       17386 :         kindAndCategory_ = uint32_t(Kind::JS_NORMAL) | uint32_t(Category::JS);
     133       17386 :         MOZ_ASSERT(isJs());
     134       17386 :     }
     135             : 
     136           0 :     void setKind(Kind aKind) {
     137           0 :         kindAndCategory_ = uint32_t(aKind) | uint32_t(category());
     138           0 :     }
     139             : 
     140       54070 :     Kind kind() const {
     141       54070 :         return Kind(kindAndCategory_ & uint32_t(Kind::KIND_MASK));
     142             :     }
     143             : 
     144           0 :     Category category() const {
     145           0 :         return Category(kindAndCategory_ & uint32_t(Category::CATEGORY_MASK));
     146             :     }
     147             : 
     148           0 :     void* stackAddress() const {
     149           0 :         MOZ_ASSERT(!isJs());
     150           0 :         return spOrScript;
     151             :     }
     152             : 
     153             :     JS_PUBLIC_API(JSScript*) script() const;
     154             : 
     155           0 :     uint32_t line() const {
     156           0 :         MOZ_ASSERT(!isJs());
     157           0 :         return static_cast<uint32_t>(lineOrPcOffset);
     158             :     }
     159             : 
     160             :     // Note that the pointer returned might be invalid.
     161         101 :     JSScript* rawScript() const {
     162         101 :         MOZ_ASSERT(isJs());
     163         101 :         return (JSScript*)spOrScript;
     164             :     }
     165             : 
     166             :     // We can't know the layout of JSScript, so look in vm/GeckoProfiler.cpp.
     167             :     JS_FRIEND_API(jsbytecode*) pc() const;
     168             :     void setPC(jsbytecode* pc);
     169             : 
     170             :     void trace(JSTracer* trc);
     171             : 
     172             :     // The offset of a pc into a script's code can actually be 0, so to
     173             :     // signify a nullptr pc, use a -1 index. This is checked against in
     174             :     // pc() and setPC() to set/get the right pc.
     175             :     static const int32_t NullPCOffset = -1;
     176             : };
     177             : 
     178             : JS_FRIEND_API(void)
     179             : SetContextProfilingStack(JSContext* cx, PseudoStack* pseudoStack);
     180             : 
     181             : JS_FRIEND_API(void)
     182             : EnableContextProfilingStack(JSContext* cx, bool enabled);
     183             : 
     184             : JS_FRIEND_API(void)
     185             : RegisterContextProfilingEventMarker(JSContext* cx, void (*fn)(const char*));
     186             : 
     187             : } // namespace js
     188             : 
     189             : // Each thread has its own PseudoStack. That thread modifies the PseudoStack,
     190             : // pushing and popping elements as necessary.
     191             : //
     192             : // The PseudoStack is also read periodically by the profiler's sampler thread.
     193             : // This happens only when the thread that owns the PseudoStack is suspended. So
     194             : // there are no genuine parallel accesses.
     195             : //
     196             : // However, it is possible for pushing/popping to be interrupted by a periodic
     197             : // sample. Because of this, we need pushing/popping to be effectively atomic.
     198             : //
     199             : // - When pushing a new entry, we increment the stack pointer -- making the new
     200             : //   entry visible to the sampler thread -- only after the new entry has been
     201             : //   fully written. The stack pointer is Atomic<> (with SequentiallyConsistent
     202             : //   semantics) to ensure the incrementing is not reordered before the writes.
     203             : //
     204             : // - When popping an old entry, the only operation is the decrementing of the
     205             : //   stack pointer, which is obviously atomic.
     206             : //
     207             : class PseudoStack
     208             : {
     209             :   public:
     210          75 :     PseudoStack()
     211          75 :       : stackPointer(0)
     212          75 :     {}
     213             : 
     214           2 :     ~PseudoStack() {
     215             :         // The label macros keep a reference to the PseudoStack to avoid a TLS
     216             :         // access. If these are somehow not all cleared we will get a
     217             :         // use-after-free so better to crash now.
     218           1 :         MOZ_RELEASE_ASSERT(stackPointer == 0);
     219           1 :     }
     220             : 
     221       36219 :     void pushCppFrame(const char* label, const char* dynamicString, void* sp, uint32_t line,
     222             :                       js::ProfileEntry::Kind kind, js::ProfileEntry::Category category) {
     223       36219 :         if (stackPointer < MaxEntries) {
     224       36220 :             entries[stackPointer].initCppFrame(label, dynamicString, sp, line, kind, category);
     225             :         }
     226             : 
     227             :         // This must happen at the end! The compiler will not reorder this
     228             :         // update because stackPointer is Atomic.
     229       36218 :         stackPointer++;
     230       36222 :     }
     231             : 
     232       17386 :     void pushJsFrame(const char* label, const char* dynamicString, JSScript* script,
     233             :                      jsbytecode* pc) {
     234       17386 :         if (stackPointer < MaxEntries) {
     235       17386 :             entries[stackPointer].initJsFrame(label, dynamicString, script, pc);
     236             :         }
     237             : 
     238             :         // This must happen at the end! The compiler will not reorder this
     239             :         // update because stackPointer is Atomic.
     240       17386 :         stackPointer++;
     241       17386 :     }
     242             : 
     243       53668 :     void pop() {
     244       53668 :         MOZ_ASSERT(stackPointer > 0);
     245       53668 :         stackPointer--;
     246       53669 :     }
     247             : 
     248          22 :     uint32_t stackSize() const { return std::min(uint32_t(stackPointer), uint32_t(MaxEntries)); }
     249             : 
     250             :   private:
     251             :     // No copying.
     252             :     PseudoStack(const PseudoStack&) = delete;
     253             :     void operator=(const PseudoStack&) = delete;
     254             : 
     255             :   public:
     256             :     static const uint32_t MaxEntries = 1024;
     257             : 
     258             :     // The stack entries.
     259             :     js::ProfileEntry entries[MaxEntries];
     260             : 
     261             :     // This may exceed MaxEntries, so instead use the stackSize() method to
     262             :     // determine the number of valid samples in entries. When this is less
     263             :     // than MaxEntries, it refers to the first free entry past the top of the
     264             :     // in-use stack (i.e. entries[stackPointer - 1] is the top stack entry).
     265             :     mozilla::Atomic<uint32_t, mozilla::SequentiallyConsistent> stackPointer;
     266             : };
     267             : 
     268             : #endif  /* js_ProfilingStack_h */

Generated by: LCOV version 1.13