/* This Source Code Form is subject to the terms of the Mozilla Public * License, v. 2.0. If a copy of the MPL was not distributed with this * file, You can obtain one at https://mozilla.org/MPL/2.0/. */ #ifndef SANDBOX_PROFILER_H #define SANDBOX_PROFILER_H #include #include #include #include #include "mozilla/UniquePtr.h" #include "ProfilerNativeStack.h" #include "MicroGeckoProfiler.h" #include "mozilla/ProfileChunkedBuffer.h" #include "mozilla/ProfilerState.h" #include "mozilla/ArrayUtils.h" #include "mozilla/MPSCQueue.h" #if defined(HAVE_REPORT_UPROFILER_PARENT) && \ defined(HAVE_REPORT_UPROFILER_CHILD) # error Cannot include SandboxProfilerChild.h AND SandboxProfilerParent.h #endif // stolen from GeckoTraceEvent.h which is not public static constexpr uint8_t TRACE_VALUE_TYPE_UINT = 2; static constexpr uint8_t TRACE_VALUE_TYPE_STRING = 6; namespace mozilla { #if defined(DEBUG) extern thread_local Atomic sInSignalContext; class MOZ_STACK_CLASS AutoForbidSignalContext { public: AutoForbidSignalContext(); ~AutoForbidSignalContext(); }; #endif // defined(DEBUG) enum class SandboxProfilerPayloadType : uint8_t { Init, Request, Log }; using SandboxProfilerPayload = struct { NativeStack mStack; uint64_t mId; const char* mOp; int mFlags; char mPath[PATH_MAX]; char mPath2[PATH_MAX]; pid_t mPid; SandboxProfilerPayloadType mType; }; using SandboxProfilerQueue = MPSCQueue; extern struct UprofilerFuncPtrs uprofiler; extern bool uprofiler_initted; class SandboxProfiler final { public: SandboxProfiler(); ~SandboxProfiler(); // Needs to be accessible in both child (within libmozsandbox.so) and parent // (within libxul.so) it's easier and less of a mess if this is done from the // header which can more easily be included by both sides. static bool Active() { if (!uprofiler_initted) { return false; } if (!uprofiler.is_active || uprofiler.is_active == is_active_noop) { return false; } if (!uprofiler.feature_active || uprofiler.feature_active == feature_active_noop) { return false; } return uprofiler.is_active() && uprofiler.feature_active(ProfilerFeature::Sandbox); } static void Shutdown(); // Should NOT BE CALLED UNDER SIGSYS, this is here to make sure we do the // dlopen()/dlsym() on the main thread so it is available for later use on // others threads. We expect that only stack trace would be collected under // SIGSYS context, and the rest of the profiler marker would happen on another // safer thread. static inline bool Init(); static void Create(); static void inline ReportAudit(const char* aKind, const char* aOp, int aFlags, uint64_t aId, int aPerms, const char* aPath, pid_t aPid); static void ReportRequest(const void* top, uint64_t aId, const char* aOp, int aFlags, const char* aPath, const char* aPath2, pid_t aPid); static void ReportInit(const void* top); static void ReportLog(const char* buf); private: std::thread mThreadLogs; std::thread mThreadSyscalls; // For child and parent, same reason as Active() above template static constexpr void Report(const char* aKind, std::array aArgNames, std::array aArgTypes, std::array aArgValues, void* aStack) { if (!Active()) { return; } if (!uprofiler_initted) { fprintf(stderr, "[%d] no uprofiler, skip\n", getpid()); return; } MOZ_ASSERT(!sInSignalContext, "SandboxProfiler::Report called in SIGSYS handler"); if (aStack) { uprofiler.simple_event_marker_with_stack( aKind, 'S', 'I', aArgNames.size(), aArgNames.data(), aArgTypes.data(), aArgValues.data(), aStack); } else { uprofiler.simple_event_marker(aKind, 'S', 'I', aArgNames.size(), aArgNames.data(), aArgTypes.data(), aArgValues.data()); } } // Verify that: // - Not in shutdown // - SandboxProfiler exists // - Profiler is active // - aQueue exists static bool ActiveWithQueue(SandboxProfilerQueue* aQueue); // Rely on semaphores to handle consuming the queue: // - One semaphore per queue (= thread) // - Gets SIGNAL'd when a payload has been pushed to the SandboxProfilerQueue // - SandboxProfiler dedicated thread WAIT's on the semaphore, gets unblocked // on signal // - Semaphore's sem_post() is safe to use in a signal context // - Using semaphores allows to wake the SandboxProfiler dedicated thread // only when needed and avoid using sleep() static void Signal(sem_t* aSem); static int Wait(sem_t* aSem); void ThreadMain(const char* aThreadName, SandboxProfilerQueue* aQueue, sem_t* aRequest); void ReportInitImpl(SandboxProfilerPayload& payload, ProfileChunkedBuffer& buffer); void ReportLogImpl(SandboxProfilerPayload& payload); void ReportRequestImpl(SandboxProfilerPayload& payload, ProfileChunkedBuffer& buffer); }; } // namespace mozilla #endif // SANDBOX_PROFILER_H