SandboxProfiler.cpp (11218B)
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 #include <time.h> 6 #include <unistd.h> 7 #include <cstring> 8 9 #include "SandboxInfo.h" 10 11 #include "SandboxProfilerChild.h" 12 #include "SandboxProfiler.h" 13 14 #include "mozilla/Atomics.h" 15 #include "mozilla/StaticPtr.h" 16 #include "mozilla/PodOperations.h" 17 18 namespace mozilla { 19 20 #if defined(DEBUG) 21 thread_local Atomic<bool> sInSignalContext = Atomic<bool>(false); 22 23 AutoForbidSignalContext::AutoForbidSignalContext() { sInSignalContext = true; } 24 25 AutoForbidSignalContext::~AutoForbidSignalContext() { 26 sInSignalContext = false; 27 } 28 #endif // defined(DEBUG) 29 30 static StaticAutoPtr<SandboxProfiler> gProfiler; 31 static StaticAutoPtr<SandboxProfilerQueue> gSyscallsQueue; 32 static StaticAutoPtr<SandboxProfilerQueue> gLogsQueue; 33 34 static Atomic<bool> isShutdown = Atomic<bool>(false); 35 36 // Those function pointers are set by the main thread, and subsequently read by 37 // all other thread, in particular in SIGSYS handlers. 38 struct UprofilerFuncPtrs uprofiler; 39 bool uprofiler_initted = false; 40 41 static bool const SANDBOX_PROFILER_DEBUG = false; 42 43 // Semaphores that we use to signal the SandboxProfilerEmitter thread when data 44 // has been pushed to the SandboxProfilerQueue 45 static sem_t gSyscallRequest; 46 static sem_t gLogsRequest; 47 48 // This is only be called on main thread, and not within SIGSYS context 49 // 50 // We might be called either from the profiler-started notification observer in 51 // which case the !Active() call is not useful, but also directly from Sandbox' 52 // SandboxLateInit where we want to verify if we are not already active: that 53 // can happen if the user started the profiler via MOZ_PROFILER_STARTUP=1 54 55 /* static */ 56 void SandboxProfiler::Create() { 57 MOZ_ASSERT(!sInSignalContext, 58 "SandboxProfiler::Create called in SIGSYS handler"); 59 60 if (!Init()) { 61 return; 62 } 63 64 if (!Active()) { 65 return; 66 } 67 68 if (!gSyscallsQueue) { 69 gSyscallsQueue = new SandboxProfilerQueue(); 70 } 71 72 if (!gLogsQueue) { 73 gLogsQueue = new SandboxProfilerQueue(); 74 } 75 76 if (!gProfiler) { 77 gProfiler = new SandboxProfiler(); 78 } 79 } 80 81 SandboxProfiler::SandboxProfiler() { 82 mThreadLogs = std::thread(&SandboxProfiler::ThreadMain, this, 83 "SandboxProfilerEmitterLogs", gLogsQueue.get(), 84 &gLogsRequest); 85 mThreadSyscalls = std::thread(&SandboxProfiler::ThreadMain, this, 86 "SandboxProfilerEmitterSyscalls", 87 gSyscallsQueue.get(), &gSyscallRequest); 88 } 89 90 /* static */ 91 void SandboxProfiler::Shutdown() { 92 isShutdown = true; 93 94 if (gProfiler) { 95 // Unblock remaining 96 SandboxProfiler::Signal(&gSyscallRequest); 97 SandboxProfiler::Signal(&gLogsRequest); 98 } 99 100 gProfiler = nullptr; 101 gSyscallsQueue = nullptr; 102 gLogsQueue = nullptr; 103 } 104 105 SandboxProfiler::~SandboxProfiler() { 106 if (mThreadLogs.joinable()) { 107 mThreadLogs.join(); 108 } 109 110 if (mThreadSyscalls.joinable()) { 111 mThreadSyscalls.join(); 112 } 113 } 114 115 /* static */ 116 bool SandboxProfiler::ActiveWithQueue(SandboxProfilerQueue* aQueue) { 117 return !isShutdown && gProfiler && Active() && aQueue; 118 } 119 120 /* static */ 121 void SandboxProfiler::Signal(sem_t* aSem) { 122 if (sem_post(aSem) < 0) { 123 if constexpr (SANDBOX_PROFILER_DEBUG) { 124 fprintf(stderr, "[%d] %s SEM_POST errno=%d\n", getpid(), 125 __PRETTY_FUNCTION__, errno); 126 } 127 } 128 } 129 130 /* static */ 131 int SandboxProfiler::Wait(sem_t* aSem) { return sem_wait(aSem); } 132 133 /* static */ 134 void SandboxProfiler::ReportInit(const void* top) { 135 if (!ActiveWithQueue(gSyscallsQueue)) { 136 return; 137 } 138 139 SandboxProfilerPayload payload = { 140 .mStack = NativeStack{.mCount = 0}, 141 .mType = SandboxProfilerPayloadType::Init, 142 }; 143 uprofiler.native_backtrace(top, &payload.mStack); 144 145 MOZ_ASSERT(gSyscallsQueue, "Queue is valid for Send() from ReportInit()"); 146 if (!gSyscallsQueue) { 147 if constexpr (SANDBOX_PROFILER_DEBUG) { 148 fprintf(stderr, 149 "[%d] WARNING: Hello PRODUCER: gSyscallsQueue disappeared\n", 150 getpid()); 151 } 152 return; 153 } 154 155 int rv = gSyscallsQueue->Send(payload); 156 if (rv == 0) { 157 if constexpr (SANDBOX_PROFILER_DEBUG) { 158 fprintf(stderr, 159 "[%d] WARNING: Hello PRODUCER: one stack mCount=%zu DROPPED\n", 160 getpid(), payload.mStack.mCount); 161 } 162 } 163 164 SandboxProfiler::Signal(&gSyscallRequest); 165 } 166 167 void SandboxProfiler::ReportInitImpl(SandboxProfilerPayload& payload, 168 ProfileChunkedBuffer& buffer) { 169 const char buf[] = "uprofiler init"; 170 std::array arg_names = {"init"}; 171 std::array arg_types = { 172 TRACE_VALUE_TYPE_STRING, 173 }; 174 std::array arg_values = {reinterpret_cast<unsigned long long>(buf)}; 175 176 Report("SandboxBroker::InitWithStack", arg_names, arg_types, arg_values, 177 &buffer); 178 } 179 180 /* static */ 181 void SandboxProfiler::ReportLog(const char* aBuf) { 182 if (!ActiveWithQueue(gLogsQueue)) { 183 return; 184 } 185 186 if (!SandboxInfo::Get().Test(SandboxInfo::kVerbose) && 187 !SandboxInfo::Get().Test(SandboxInfo::kVerboseTests)) { 188 return; 189 } 190 191 SandboxProfilerPayload payload = { 192 .mStack = NativeStack{.mCount = 0}, 193 .mType = SandboxProfilerPayloadType::Log, 194 }; 195 196 const size_t bufLen = strnlen(aBuf, PATH_MAX); 197 PodCopy(payload.mPath, aBuf, bufLen); 198 199 MOZ_ASSERT(gLogsQueue, "Queue is valid for Send() from ReportLog()"); 200 if (!gLogsQueue) { 201 if constexpr (SANDBOX_PROFILER_DEBUG) { 202 fprintf(stderr, "[%d] WARNING: Hello PRODUCER: gLogsQueue disappeared\n", 203 getpid()); 204 } 205 return; 206 } 207 208 int rv = gLogsQueue->Send(payload); 209 if (rv == 0) { 210 if constexpr (SANDBOX_PROFILER_DEBUG) { 211 fprintf(stderr, "[%d] WARNING: Hello PRODUCER: one log stack DROPPED\n", 212 getpid()); 213 } 214 } 215 216 SandboxProfiler::Signal(&gLogsRequest); 217 } 218 219 void SandboxProfiler::ReportLogImpl(SandboxProfilerPayload& payload) { 220 std::array arg_names = {"log"}; 221 std::array arg_types = { 222 TRACE_VALUE_TYPE_STRING, 223 }; 224 std::array arg_values = { 225 reinterpret_cast<unsigned long long>(payload.mPath), 226 }; 227 228 Report("SandboxBroker::Log", arg_names, arg_types, arg_values, nullptr); 229 } 230 231 /* static */ 232 void SandboxProfiler::ReportRequest(const void* top, uint64_t aId, 233 const char* aOp, int aFlags, 234 const char* aPath, const char* aPath2, 235 pid_t aPid) { 236 if (!ActiveWithQueue(gSyscallsQueue)) { 237 return; 238 } 239 240 // Take a stack, this should be safe to do in the context of SIGSYS 241 SandboxProfilerPayload payload = { 242 .mStack = NativeStack{.mCount = 0}, 243 .mId = aId, 244 .mOp = aOp, 245 .mFlags = aFlags, 246 .mPid = aPid, 247 .mType = SandboxProfilerPayloadType::Request, 248 }; 249 250 if (aPath) { 251 const size_t pathLen = strnlen(aPath, PATH_MAX); 252 PodCopy(payload.mPath, aPath, pathLen); 253 } else { 254 payload.mPath[0] = '\0'; 255 } 256 257 if (aPath2) { 258 const size_t path2Len = strnlen(aPath2, PATH_MAX); 259 PodCopy(payload.mPath2, aPath2, path2Len); 260 } else { 261 payload.mPath2[0] = '\0'; 262 } 263 264 uprofiler.native_backtrace(top, &payload.mStack); 265 266 MOZ_ASSERT(gSyscallsQueue, "Queue is valid for Send() from ReportRequest()"); 267 if (!gSyscallsQueue) { 268 if constexpr (SANDBOX_PROFILER_DEBUG) { 269 fprintf(stderr, 270 "[%d] WARNING: Hello PRODUCER: gSyscallsQueue disappeared\n", 271 getpid()); 272 } 273 return; 274 } 275 276 int rv = gSyscallsQueue->Send(payload); 277 if (rv == 0) { 278 if constexpr (SANDBOX_PROFILER_DEBUG) { 279 fprintf(stderr, 280 "[%d] WARNING: Hello PRODUCER: one stack mCount=%zu DROPPED\n", 281 getpid(), payload.mStack.mCount); 282 } 283 } 284 285 SandboxProfiler::Signal(&gSyscallRequest); 286 } 287 288 void SandboxProfiler::ReportRequestImpl(SandboxProfilerPayload& payload, 289 ProfileChunkedBuffer& buffer) { 290 std::array arg_names = {"id", "op", "rflags", "path", "path2", "pid"}; 291 std::array arg_types = { 292 TRACE_VALUE_TYPE_UINT, // id 293 TRACE_VALUE_TYPE_STRING, // op 294 TRACE_VALUE_TYPE_UINT, // rflags 295 TRACE_VALUE_TYPE_STRING, // path 296 TRACE_VALUE_TYPE_STRING, // path2 297 TRACE_VALUE_TYPE_UINT // pid 298 }; 299 300 std::array arg_values = {static_cast<unsigned long long>(payload.mId), 301 reinterpret_cast<unsigned long long>(payload.mOp), 302 static_cast<unsigned long long>(payload.mFlags), 303 reinterpret_cast<unsigned long long>(payload.mPath), 304 reinterpret_cast<unsigned long long>(payload.mPath2), 305 static_cast<unsigned long long>(payload.mPid)}; 306 307 Report("SandboxBrokerClient", arg_names, arg_types, arg_values, &buffer); 308 } 309 310 void SandboxProfiler::ThreadMain(const char* aThreadName, 311 SandboxProfilerQueue* aQueue, 312 sem_t* aRequest) { 313 uprofiler.register_thread(aThreadName, CallerPC()); 314 SandboxProfilerPayload p; 315 316 DebugOnly<int> sem_init_rv = 317 sem_init(aRequest, /* pshared */ 0, /* value */ 0); 318 MOZ_ASSERT(sem_init_rv == 0, "Failure to initialize semaphore"); 319 320 while (!isShutdown) { 321 errno = 0; 322 if (SandboxProfiler::Wait(aRequest) < 0) { 323 int _errno = errno; 324 MOZ_ASSERT(_errno != EINVAL, "sem_wait() returned EINVAL"); 325 if (_errno == EAGAIN || _errno == EINTR) { 326 continue; 327 } 328 } 329 330 MOZ_ASSERT(aQueue, "Syscalls queue is valid for Recv()"); 331 if (!aQueue) { 332 if constexpr (SANDBOX_PROFILER_DEBUG) { 333 fprintf(stderr, 334 "[%d] WARNING: Hello CONSUMER [%s]: aQueue disappeared\n", 335 getpid(), aThreadName); 336 } 337 continue; 338 } 339 340 int deq = aQueue->Recv(&p); 341 if (deq > 0) { 342 switch (p.mType) { 343 case SandboxProfilerPayloadType::Init: 344 case SandboxProfilerPayloadType::Request: { 345 ProfileBufferChunkManagerSingle chunkManager{ 346 mozilla::ProfileBufferChunkManager::scExpectedMaximumStackSize}; 347 ProfileChunkedBuffer chunkedBuffer{ 348 ProfileChunkedBuffer::ThreadSafety::WithoutMutex, chunkManager}; 349 uprofiler.backtrace_into_buffer(&p.mStack, &chunkedBuffer); 350 351 switch (p.mType) { 352 case SandboxProfilerPayloadType::Init: 353 ReportInitImpl(p, chunkedBuffer); 354 break; 355 356 case SandboxProfilerPayloadType::Request: 357 ReportRequestImpl(p, chunkedBuffer); 358 break; 359 360 default: 361 // impossible? 362 MOZ_ASSERT_UNREACHABLE("Should have been Init/Request"); 363 break; 364 } 365 } break; 366 367 case SandboxProfilerPayloadType::Log: 368 ReportLogImpl(p); 369 break; 370 371 default: 372 fprintf(stderr, "[%d] mType=%hhu\n", getpid(), 373 static_cast<uint8_t>(p.mType)); 374 MOZ_CRASH("Unsupported type"); 375 break; 376 } 377 } 378 } 379 380 sem_destroy(aRequest); 381 382 uprofiler.unregister_thread(); 383 } 384 385 } // namespace mozilla