SandboxProfiler.h (5265B)
1 /* This Source Code Form is subject to the terms of the Mozilla Public 2 * License, v. 2.0. If a copy of the MPL was not distributed with this 3 * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ 4 5 #ifndef SANDBOX_PROFILER_H 6 #define SANDBOX_PROFILER_H 7 8 #include <thread> 9 #include <linux/limits.h> 10 #include <semaphore.h> 11 12 #include "base/trace_event/common/trace_event_common.h" 13 14 #include "ProfilerNativeStack.h" 15 #include "MicroGeckoProfiler.h" 16 17 #include "mozilla/ProfileChunkedBuffer.h" 18 #include "mozilla/ProfilerState.h" 19 20 #include "mozilla/BoundedMPSCQueue.h" 21 22 #if defined(HAVE_REPORT_UPROFILER_PARENT) && \ 23 defined(HAVE_REPORT_UPROFILER_CHILD) 24 # error Cannot include SandboxProfilerChild.h AND SandboxProfilerParent.h 25 #endif 26 27 namespace mozilla { 28 29 #if defined(DEBUG) 30 extern thread_local Atomic<bool> sInSignalContext; 31 32 class MOZ_STACK_CLASS AutoForbidSignalContext { 33 public: 34 AutoForbidSignalContext(); 35 ~AutoForbidSignalContext(); 36 }; 37 #endif // defined(DEBUG) 38 39 enum class SandboxProfilerPayloadType : uint8_t { Init, Request, Log }; 40 41 using SandboxProfilerPayload = struct { 42 NativeStack mStack; 43 uint64_t mId; 44 const char* mOp; 45 int mFlags; 46 char mPath[PATH_MAX]; 47 char mPath2[PATH_MAX]; 48 pid_t mPid; 49 SandboxProfilerPayloadType mType; 50 }; 51 52 using SandboxProfilerQueue = BoundedMPSCQueue<SandboxProfilerPayload, 15>; 53 54 extern struct UprofilerFuncPtrs uprofiler; 55 extern bool uprofiler_initted; 56 57 class SandboxProfiler final { 58 public: 59 SandboxProfiler(); 60 ~SandboxProfiler(); 61 62 // Needs to be accessible in both child (within libmozsandbox.so) and parent 63 // (within libxul.so) it's easier and less of a mess if this is done from the 64 // header which can more easily be included by both sides. 65 static bool Active() { 66 if (!uprofiler_initted) { 67 return false; 68 } 69 70 if (!uprofiler.is_active || uprofiler.is_active == is_active_noop) { 71 return false; 72 } 73 74 if (!uprofiler.feature_active || 75 uprofiler.feature_active == feature_active_noop) { 76 return false; 77 } 78 79 return uprofiler.is_active() && 80 uprofiler.feature_active(ProfilerFeature::Sandbox); 81 } 82 83 static void Shutdown(); 84 85 // Should NOT BE CALLED UNDER SIGSYS, this is here to make sure we do the 86 // dlopen()/dlsym() on the main thread so it is available for later use on 87 // others threads. We expect that only stack trace would be collected under 88 // SIGSYS context, and the rest of the profiler marker would happen on another 89 // safer thread. 90 static inline bool Init(); 91 92 static void Create(); 93 static void inline ReportAudit(const char* aKind, const char* aOp, int aFlags, 94 uint64_t aId, int aPerms, const char* aPath, 95 pid_t aPid); 96 static void ReportRequest(const void* top, uint64_t aId, const char* aOp, 97 int aFlags, const char* aPath, const char* aPath2, 98 pid_t aPid); 99 static void ReportInit(const void* top); 100 static void ReportLog(const char* buf); 101 102 private: 103 std::thread mThreadLogs; 104 std::thread mThreadSyscalls; 105 106 // For child and parent, same reason as Active() above 107 template <typename N, typename T, typename V, size_t len> 108 static constexpr void Report(const char* aKind, std::array<N, len> aArgNames, 109 std::array<T, len> aArgTypes, 110 std::array<V, len> aArgValues, void* aStack) { 111 if (!Active()) { 112 return; 113 } 114 115 if (!uprofiler_initted) { 116 fprintf(stderr, "[%d] no uprofiler, skip\n", getpid()); 117 return; 118 } 119 120 MOZ_ASSERT(!sInSignalContext, 121 "SandboxProfiler::Report called in SIGSYS handler"); 122 123 if (aStack) { 124 uprofiler.simple_event_marker_with_stack( 125 aKind, 'S', 'I', aArgNames.size(), aArgNames.data(), aArgTypes.data(), 126 aArgValues.data(), aStack); 127 } else { 128 uprofiler.simple_event_marker(aKind, 'S', 'I', aArgNames.size(), 129 aArgNames.data(), aArgTypes.data(), 130 aArgValues.data()); 131 } 132 } 133 134 // Verify that: 135 // - Not in shutdown 136 // - SandboxProfiler exists 137 // - Profiler is active 138 // - aQueue exists 139 static bool ActiveWithQueue(SandboxProfilerQueue* aQueue); 140 141 // Rely on semaphores to handle consuming the queue: 142 // - One semaphore per queue (= thread) 143 // - Gets SIGNAL'd when a payload has been pushed to the SandboxProfilerQueue 144 // - SandboxProfiler dedicated thread WAIT's on the semaphore, gets unblocked 145 // on signal 146 // - Semaphore's sem_post() is safe to use in a signal context 147 // - Using semaphores allows to wake the SandboxProfiler dedicated thread 148 // only when needed and avoid using sleep() 149 static void Signal(sem_t* aSem); 150 static int Wait(sem_t* aSem); 151 152 void ThreadMain(const char* aThreadName, SandboxProfilerQueue* aQueue, 153 sem_t* aRequest); 154 void ReportInitImpl(SandboxProfilerPayload& payload, 155 ProfileChunkedBuffer& buffer); 156 void ReportLogImpl(SandboxProfilerPayload& payload); 157 void ReportRequestImpl(SandboxProfilerPayload& payload, 158 ProfileChunkedBuffer& buffer); 159 }; 160 161 } // namespace mozilla 162 #endif // SANDBOX_PROFILER_H