Tue, 06 Jan 2015 21:39:09 +0100
Conditionally force memory storage according to privacy.thirdparty.isolate;
This solves Tor bug #9701, complying with disk avoidance documented in
https://www.torproject.org/projects/torbrowser/design/#disk-avoidance.
michael@0 | 1 | // Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. |
michael@0 | 2 | // |
michael@0 | 3 | // Redistribution and use in source and binary forms, with or without |
michael@0 | 4 | // modification, are permitted provided that the following conditions |
michael@0 | 5 | // are met: |
michael@0 | 6 | // * Redistributions of source code must retain the above copyright |
michael@0 | 7 | // notice, this list of conditions and the following disclaimer. |
michael@0 | 8 | // * Redistributions in binary form must reproduce the above copyright |
michael@0 | 9 | // notice, this list of conditions and the following disclaimer in |
michael@0 | 10 | // the documentation and/or other materials provided with the |
michael@0 | 11 | // distribution. |
michael@0 | 12 | // * Neither the name of Google, Inc. nor the names of its contributors |
michael@0 | 13 | // may be used to endorse or promote products derived from this |
michael@0 | 14 | // software without specific prior written permission. |
michael@0 | 15 | // |
michael@0 | 16 | // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS |
michael@0 | 17 | // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT |
michael@0 | 18 | // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS |
michael@0 | 19 | // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE |
michael@0 | 20 | // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, |
michael@0 | 21 | // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, |
michael@0 | 22 | // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS |
michael@0 | 23 | // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED |
michael@0 | 24 | // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, |
michael@0 | 25 | // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT |
michael@0 | 26 | // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF |
michael@0 | 27 | // SUCH DAMAGE. |
michael@0 | 28 | |
michael@0 | 29 | /* |
michael@0 | 30 | # vim: sw=2 |
michael@0 | 31 | */ |
michael@0 | 32 | #include <stdio.h> |
michael@0 | 33 | #include <math.h> |
michael@0 | 34 | |
michael@0 | 35 | #include <pthread.h> |
michael@0 | 36 | #include <semaphore.h> |
michael@0 | 37 | #include <signal.h> |
michael@0 | 38 | #include <sys/time.h> |
michael@0 | 39 | #include <sys/resource.h> |
michael@0 | 40 | #include <sys/syscall.h> |
michael@0 | 41 | #include <sys/types.h> |
michael@0 | 42 | #include <sys/prctl.h> // set name |
michael@0 | 43 | #include <stdlib.h> |
michael@0 | 44 | #include <sched.h> |
michael@0 | 45 | #ifdef ANDROID |
michael@0 | 46 | #include <android/log.h> |
michael@0 | 47 | #else |
michael@0 | 48 | #define __android_log_print(a, ...) |
michael@0 | 49 | #endif |
michael@0 | 50 | #include <ucontext.h> |
michael@0 | 51 | // Ubuntu Dapper requires memory pages to be marked as |
michael@0 | 52 | // executable. Otherwise, OS raises an exception when executing code |
michael@0 | 53 | // in that page. |
michael@0 | 54 | #include <sys/types.h> // mmap & munmap |
michael@0 | 55 | #include <sys/mman.h> // mmap & munmap |
michael@0 | 56 | #include <sys/stat.h> // open |
michael@0 | 57 | #include <fcntl.h> // open |
michael@0 | 58 | #include <unistd.h> // sysconf |
michael@0 | 59 | #include <semaphore.h> |
michael@0 | 60 | #ifdef __GLIBC__ |
michael@0 | 61 | #include <execinfo.h> // backtrace, backtrace_symbols |
michael@0 | 62 | #endif // def __GLIBC__ |
michael@0 | 63 | #include <strings.h> // index |
michael@0 | 64 | #include <errno.h> |
michael@0 | 65 | #include <stdarg.h> |
michael@0 | 66 | #include "platform.h" |
michael@0 | 67 | #include "GeckoProfiler.h" |
michael@0 | 68 | #include "mozilla/Mutex.h" |
michael@0 | 69 | #include "mozilla/Atomics.h" |
michael@0 | 70 | #include "ProfileEntry.h" |
michael@0 | 71 | #include "nsThreadUtils.h" |
michael@0 | 72 | #include "TableTicker.h" |
michael@0 | 73 | #include "UnwinderThread2.h" |
michael@0 | 74 | #if defined(__ARM_EABI__) && defined(MOZ_WIDGET_GONK) |
michael@0 | 75 | // Should also work on other Android and ARM Linux, but not tested there yet. |
michael@0 | 76 | #define USE_EHABI_STACKWALK |
michael@0 | 77 | #include "EHABIStackWalk.h" |
michael@0 | 78 | #endif |
michael@0 | 79 | |
michael@0 | 80 | #include <string.h> |
michael@0 | 81 | #include <stdio.h> |
michael@0 | 82 | #include <list> |
michael@0 | 83 | |
michael@0 | 84 | #ifdef MOZ_NUWA_PROCESS |
michael@0 | 85 | #include "ipc/Nuwa.h" |
michael@0 | 86 | #endif |
michael@0 | 87 | |
michael@0 | 88 | #define SIGNAL_SAVE_PROFILE SIGUSR2 |
michael@0 | 89 | |
michael@0 | 90 | #if defined(__GLIBC__) |
michael@0 | 91 | // glibc doesn't implement gettid(2). |
michael@0 | 92 | #include <sys/syscall.h> |
michael@0 | 93 | pid_t gettid() |
michael@0 | 94 | { |
michael@0 | 95 | return (pid_t) syscall(SYS_gettid); |
michael@0 | 96 | } |
michael@0 | 97 | #endif |
michael@0 | 98 | |
michael@0 | 99 | /* static */ Thread::tid_t |
michael@0 | 100 | Thread::GetCurrentId() |
michael@0 | 101 | { |
michael@0 | 102 | return gettid(); |
michael@0 | 103 | } |
michael@0 | 104 | |
michael@0 | 105 | #if !defined(ANDROID) |
michael@0 | 106 | // Keep track of when any of our threads calls fork(), so we can |
michael@0 | 107 | // temporarily disable signal delivery during the fork() call. Not |
michael@0 | 108 | // doing so appears to cause a kind of race, in which signals keep |
michael@0 | 109 | // getting delivered to the thread doing fork(), which keeps causing |
michael@0 | 110 | // it to fail and be restarted; hence forward progress is delayed a |
michael@0 | 111 | // great deal. A side effect of this is to permanently disable |
michael@0 | 112 | // sampling in the child process. See bug 837390. |
michael@0 | 113 | |
michael@0 | 114 | // Unfortunately this is only doable on non-Android, since Bionic |
michael@0 | 115 | // doesn't have pthread_atfork. |
michael@0 | 116 | |
michael@0 | 117 | // This records the current state at the time we paused it. |
michael@0 | 118 | static bool was_paused = false; |
michael@0 | 119 | |
michael@0 | 120 | // In the parent, just before the fork, record the pausedness state, |
michael@0 | 121 | // and then pause. |
michael@0 | 122 | static void paf_prepare(void) { |
michael@0 | 123 | if (Sampler::GetActiveSampler()) { |
michael@0 | 124 | was_paused = Sampler::GetActiveSampler()->IsPaused(); |
michael@0 | 125 | Sampler::GetActiveSampler()->SetPaused(true); |
michael@0 | 126 | } else { |
michael@0 | 127 | was_paused = false; |
michael@0 | 128 | } |
michael@0 | 129 | } |
michael@0 | 130 | |
michael@0 | 131 | // In the parent, just after the fork, return pausedness to the |
michael@0 | 132 | // pre-fork state. |
michael@0 | 133 | static void paf_parent(void) { |
michael@0 | 134 | if (Sampler::GetActiveSampler()) |
michael@0 | 135 | Sampler::GetActiveSampler()->SetPaused(was_paused); |
michael@0 | 136 | } |
michael@0 | 137 | |
michael@0 | 138 | // Set up the fork handlers. |
michael@0 | 139 | static void* setup_atfork() { |
michael@0 | 140 | pthread_atfork(paf_prepare, paf_parent, NULL); |
michael@0 | 141 | return NULL; |
michael@0 | 142 | } |
michael@0 | 143 | #endif /* !defined(ANDROID) */ |
michael@0 | 144 | |
michael@0 | 145 | struct SamplerRegistry { |
michael@0 | 146 | static void AddActiveSampler(Sampler *sampler) { |
michael@0 | 147 | ASSERT(!SamplerRegistry::sampler); |
michael@0 | 148 | SamplerRegistry::sampler = sampler; |
michael@0 | 149 | } |
michael@0 | 150 | static void RemoveActiveSampler(Sampler *sampler) { |
michael@0 | 151 | SamplerRegistry::sampler = NULL; |
michael@0 | 152 | } |
michael@0 | 153 | static Sampler *sampler; |
michael@0 | 154 | }; |
michael@0 | 155 | |
michael@0 | 156 | Sampler *SamplerRegistry::sampler = NULL; |
michael@0 | 157 | |
michael@0 | 158 | static mozilla::Atomic<ThreadProfile*> sCurrentThreadProfile; |
michael@0 | 159 | static sem_t sSignalHandlingDone; |
michael@0 | 160 | |
michael@0 | 161 | static void ProfilerSaveSignalHandler(int signal, siginfo_t* info, void* context) { |
michael@0 | 162 | Sampler::GetActiveSampler()->RequestSave(); |
michael@0 | 163 | } |
michael@0 | 164 | |
michael@0 | 165 | static void SetSampleContext(TickSample* sample, void* context) |
michael@0 | 166 | { |
michael@0 | 167 | // Extracting the sample from the context is extremely machine dependent. |
michael@0 | 168 | ucontext_t* ucontext = reinterpret_cast<ucontext_t*>(context); |
michael@0 | 169 | mcontext_t& mcontext = ucontext->uc_mcontext; |
michael@0 | 170 | #if V8_HOST_ARCH_IA32 |
michael@0 | 171 | sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_EIP]); |
michael@0 | 172 | sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_ESP]); |
michael@0 | 173 | sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_EBP]); |
michael@0 | 174 | #elif V8_HOST_ARCH_X64 |
michael@0 | 175 | sample->pc = reinterpret_cast<Address>(mcontext.gregs[REG_RIP]); |
michael@0 | 176 | sample->sp = reinterpret_cast<Address>(mcontext.gregs[REG_RSP]); |
michael@0 | 177 | sample->fp = reinterpret_cast<Address>(mcontext.gregs[REG_RBP]); |
michael@0 | 178 | #elif V8_HOST_ARCH_ARM |
michael@0 | 179 | // An undefined macro evaluates to 0, so this applies to Android's Bionic also. |
michael@0 | 180 | #if !defined(ANDROID) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) |
michael@0 | 181 | sample->pc = reinterpret_cast<Address>(mcontext.gregs[R15]); |
michael@0 | 182 | sample->sp = reinterpret_cast<Address>(mcontext.gregs[R13]); |
michael@0 | 183 | sample->fp = reinterpret_cast<Address>(mcontext.gregs[R11]); |
michael@0 | 184 | #ifdef ENABLE_ARM_LR_SAVING |
michael@0 | 185 | sample->lr = reinterpret_cast<Address>(mcontext.gregs[R14]); |
michael@0 | 186 | #endif |
michael@0 | 187 | #else |
michael@0 | 188 | sample->pc = reinterpret_cast<Address>(mcontext.arm_pc); |
michael@0 | 189 | sample->sp = reinterpret_cast<Address>(mcontext.arm_sp); |
michael@0 | 190 | sample->fp = reinterpret_cast<Address>(mcontext.arm_fp); |
michael@0 | 191 | #ifdef ENABLE_ARM_LR_SAVING |
michael@0 | 192 | sample->lr = reinterpret_cast<Address>(mcontext.arm_lr); |
michael@0 | 193 | #endif |
michael@0 | 194 | #endif |
michael@0 | 195 | #elif V8_HOST_ARCH_MIPS |
michael@0 | 196 | // Implement this on MIPS. |
michael@0 | 197 | UNIMPLEMENTED(); |
michael@0 | 198 | #endif |
michael@0 | 199 | } |
michael@0 | 200 | |
michael@0 | 201 | #ifdef ANDROID |
michael@0 | 202 | #define V8_HOST_ARCH_ARM 1 |
michael@0 | 203 | #define SYS_gettid __NR_gettid |
michael@0 | 204 | #define SYS_tgkill __NR_tgkill |
michael@0 | 205 | #else |
michael@0 | 206 | #define V8_HOST_ARCH_X64 1 |
michael@0 | 207 | #endif |
michael@0 | 208 | static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { |
michael@0 | 209 | if (!Sampler::GetActiveSampler()) { |
michael@0 | 210 | sem_post(&sSignalHandlingDone); |
michael@0 | 211 | return; |
michael@0 | 212 | } |
michael@0 | 213 | |
michael@0 | 214 | TickSample sample_obj; |
michael@0 | 215 | TickSample* sample = &sample_obj; |
michael@0 | 216 | sample->context = context; |
michael@0 | 217 | |
michael@0 | 218 | #ifdef ENABLE_SPS_LEAF_DATA |
michael@0 | 219 | // If profiling, we extract the current pc and sp. |
michael@0 | 220 | if (Sampler::GetActiveSampler()->IsProfiling()) { |
michael@0 | 221 | SetSampleContext(sample, context); |
michael@0 | 222 | } |
michael@0 | 223 | #endif |
michael@0 | 224 | sample->threadProfile = sCurrentThreadProfile; |
michael@0 | 225 | sample->timestamp = mozilla::TimeStamp::Now(); |
michael@0 | 226 | |
michael@0 | 227 | Sampler::GetActiveSampler()->Tick(sample); |
michael@0 | 228 | |
michael@0 | 229 | sCurrentThreadProfile = NULL; |
michael@0 | 230 | sem_post(&sSignalHandlingDone); |
michael@0 | 231 | } |
michael@0 | 232 | |
michael@0 | 233 | // If the Nuwa process is enabled, we need to use the wrapper of tgkill() to |
michael@0 | 234 | // perform the mapping of thread ID. |
michael@0 | 235 | #ifdef MOZ_NUWA_PROCESS |
michael@0 | 236 | extern "C" MFBT_API int tgkill(pid_t tgid, pid_t tid, int signalno); |
michael@0 | 237 | #else |
michael@0 | 238 | int tgkill(pid_t tgid, pid_t tid, int signalno) { |
michael@0 | 239 | return syscall(SYS_tgkill, tgid, tid, signalno); |
michael@0 | 240 | } |
michael@0 | 241 | #endif |
michael@0 | 242 | |
michael@0 | 243 | class PlatformData : public Malloced { |
michael@0 | 244 | public: |
michael@0 | 245 | PlatformData() |
michael@0 | 246 | {} |
michael@0 | 247 | }; |
michael@0 | 248 | |
michael@0 | 249 | /* static */ PlatformData* |
michael@0 | 250 | Sampler::AllocPlatformData(int aThreadId) |
michael@0 | 251 | { |
michael@0 | 252 | return new PlatformData; |
michael@0 | 253 | } |
michael@0 | 254 | |
michael@0 | 255 | /* static */ void |
michael@0 | 256 | Sampler::FreePlatformData(PlatformData* aData) |
michael@0 | 257 | { |
michael@0 | 258 | delete aData; |
michael@0 | 259 | } |
michael@0 | 260 | |
michael@0 | 261 | static void* SignalSender(void* arg) { |
michael@0 | 262 | // Taken from platform_thread_posix.cc |
michael@0 | 263 | prctl(PR_SET_NAME, "SamplerThread", 0, 0, 0); |
michael@0 | 264 | |
michael@0 | 265 | #ifdef MOZ_NUWA_PROCESS |
michael@0 | 266 | // If the Nuwa process is enabled, we need to mark and freeze the sampler |
michael@0 | 267 | // thread in the Nuwa process and have this thread recreated in the spawned |
michael@0 | 268 | // child. |
michael@0 | 269 | if(IsNuwaProcess()) { |
michael@0 | 270 | NuwaMarkCurrentThread(nullptr, nullptr); |
michael@0 | 271 | // Freeze the thread here so the spawned child will get the correct tgid |
michael@0 | 272 | // from the getpid() call below. |
michael@0 | 273 | NuwaFreezeCurrentThread(); |
michael@0 | 274 | } |
michael@0 | 275 | #endif |
michael@0 | 276 | |
michael@0 | 277 | int vm_tgid_ = getpid(); |
michael@0 | 278 | |
michael@0 | 279 | while (SamplerRegistry::sampler->IsActive()) { |
michael@0 | 280 | SamplerRegistry::sampler->HandleSaveRequest(); |
michael@0 | 281 | |
michael@0 | 282 | if (!SamplerRegistry::sampler->IsPaused()) { |
michael@0 | 283 | mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); |
michael@0 | 284 | std::vector<ThreadInfo*> threads = |
michael@0 | 285 | SamplerRegistry::sampler->GetRegisteredThreads(); |
michael@0 | 286 | |
michael@0 | 287 | for (uint32_t i = 0; i < threads.size(); i++) { |
michael@0 | 288 | ThreadInfo* info = threads[i]; |
michael@0 | 289 | |
michael@0 | 290 | // This will be null if we're not interested in profiling this thread. |
michael@0 | 291 | if (!info->Profile()) |
michael@0 | 292 | continue; |
michael@0 | 293 | |
michael@0 | 294 | PseudoStack::SleepState sleeping = info->Stack()->observeSleeping(); |
michael@0 | 295 | if (sleeping == PseudoStack::SLEEPING_AGAIN) { |
michael@0 | 296 | info->Profile()->DuplicateLastSample(); |
michael@0 | 297 | //XXX: This causes flushes regardless of jank-only mode |
michael@0 | 298 | info->Profile()->flush(); |
michael@0 | 299 | continue; |
michael@0 | 300 | } |
michael@0 | 301 | |
michael@0 | 302 | // We use sCurrentThreadProfile the ThreadProfile for the |
michael@0 | 303 | // thread we're profiling to the signal handler |
michael@0 | 304 | sCurrentThreadProfile = info->Profile(); |
michael@0 | 305 | |
michael@0 | 306 | int threadId = info->ThreadId(); |
michael@0 | 307 | |
michael@0 | 308 | if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) { |
michael@0 | 309 | printf_stderr("profiler failed to signal tid=%d\n", threadId); |
michael@0 | 310 | #ifdef DEBUG |
michael@0 | 311 | abort(); |
michael@0 | 312 | #endif |
michael@0 | 313 | continue; |
michael@0 | 314 | } |
michael@0 | 315 | |
michael@0 | 316 | // Wait for the signal handler to run before moving on to the next one |
michael@0 | 317 | sem_wait(&sSignalHandlingDone); |
michael@0 | 318 | } |
michael@0 | 319 | } |
michael@0 | 320 | |
michael@0 | 321 | // Convert ms to us and subtract 100 us to compensate delays |
michael@0 | 322 | // occuring during signal delivery. |
michael@0 | 323 | // TODO measure and confirm this. |
michael@0 | 324 | int interval = floor(SamplerRegistry::sampler->interval() * 1000 + 0.5) - 100; |
michael@0 | 325 | if (interval <= 0) { |
michael@0 | 326 | interval = 1; |
michael@0 | 327 | } |
michael@0 | 328 | OS::SleepMicro(interval); |
michael@0 | 329 | } |
michael@0 | 330 | return 0; |
michael@0 | 331 | } |
michael@0 | 332 | |
michael@0 | 333 | Sampler::Sampler(double interval, bool profiling, int entrySize) |
michael@0 | 334 | : interval_(interval), |
michael@0 | 335 | profiling_(profiling), |
michael@0 | 336 | paused_(false), |
michael@0 | 337 | active_(false), |
michael@0 | 338 | entrySize_(entrySize) { |
michael@0 | 339 | } |
michael@0 | 340 | |
michael@0 | 341 | Sampler::~Sampler() { |
michael@0 | 342 | ASSERT(!signal_sender_launched_); |
michael@0 | 343 | } |
michael@0 | 344 | |
michael@0 | 345 | |
michael@0 | 346 | void Sampler::Start() { |
michael@0 | 347 | LOG("Sampler started"); |
michael@0 | 348 | |
michael@0 | 349 | #ifdef USE_EHABI_STACKWALK |
michael@0 | 350 | mozilla::EHABIStackWalkInit(); |
michael@0 | 351 | #endif |
michael@0 | 352 | SamplerRegistry::AddActiveSampler(this); |
michael@0 | 353 | |
michael@0 | 354 | // Initialize signal handler communication |
michael@0 | 355 | sCurrentThreadProfile = NULL; |
michael@0 | 356 | if (sem_init(&sSignalHandlingDone, /* pshared: */ 0, /* value: */ 0) != 0) { |
michael@0 | 357 | LOG("Error initializing semaphore"); |
michael@0 | 358 | return; |
michael@0 | 359 | } |
michael@0 | 360 | |
michael@0 | 361 | // Request profiling signals. |
michael@0 | 362 | LOG("Request signal"); |
michael@0 | 363 | struct sigaction sa; |
michael@0 | 364 | sa.sa_sigaction = ProfilerSignalHandler; |
michael@0 | 365 | sigemptyset(&sa.sa_mask); |
michael@0 | 366 | sa.sa_flags = SA_RESTART | SA_SIGINFO; |
michael@0 | 367 | if (sigaction(SIGPROF, &sa, &old_sigprof_signal_handler_) != 0) { |
michael@0 | 368 | LOG("Error installing signal"); |
michael@0 | 369 | return; |
michael@0 | 370 | } |
michael@0 | 371 | |
michael@0 | 372 | // Request save profile signals |
michael@0 | 373 | struct sigaction sa2; |
michael@0 | 374 | sa2.sa_sigaction = ProfilerSaveSignalHandler; |
michael@0 | 375 | sigemptyset(&sa2.sa_mask); |
michael@0 | 376 | sa2.sa_flags = SA_RESTART | SA_SIGINFO; |
michael@0 | 377 | if (sigaction(SIGNAL_SAVE_PROFILE, &sa2, &old_sigsave_signal_handler_) != 0) { |
michael@0 | 378 | LOG("Error installing start signal"); |
michael@0 | 379 | return; |
michael@0 | 380 | } |
michael@0 | 381 | LOG("Signal installed"); |
michael@0 | 382 | signal_handler_installed_ = true; |
michael@0 | 383 | |
michael@0 | 384 | // Start a thread that sends SIGPROF signal to VM thread. |
michael@0 | 385 | // Sending the signal ourselves instead of relying on itimer provides |
michael@0 | 386 | // much better accuracy. |
michael@0 | 387 | SetActive(true); |
michael@0 | 388 | if (pthread_create( |
michael@0 | 389 | &signal_sender_thread_, NULL, SignalSender, NULL) == 0) { |
michael@0 | 390 | signal_sender_launched_ = true; |
michael@0 | 391 | } |
michael@0 | 392 | LOG("Profiler thread started"); |
michael@0 | 393 | } |
michael@0 | 394 | |
michael@0 | 395 | |
michael@0 | 396 | void Sampler::Stop() { |
michael@0 | 397 | SetActive(false); |
michael@0 | 398 | |
michael@0 | 399 | // Wait for signal sender termination (it will exit after setting |
michael@0 | 400 | // active_ to false). |
michael@0 | 401 | if (signal_sender_launched_) { |
michael@0 | 402 | pthread_join(signal_sender_thread_, NULL); |
michael@0 | 403 | signal_sender_launched_ = false; |
michael@0 | 404 | } |
michael@0 | 405 | |
michael@0 | 406 | SamplerRegistry::RemoveActiveSampler(this); |
michael@0 | 407 | |
michael@0 | 408 | // Restore old signal handler |
michael@0 | 409 | if (signal_handler_installed_) { |
michael@0 | 410 | sigaction(SIGNAL_SAVE_PROFILE, &old_sigsave_signal_handler_, 0); |
michael@0 | 411 | sigaction(SIGPROF, &old_sigprof_signal_handler_, 0); |
michael@0 | 412 | signal_handler_installed_ = false; |
michael@0 | 413 | } |
michael@0 | 414 | } |
michael@0 | 415 | |
michael@0 | 416 | bool Sampler::RegisterCurrentThread(const char* aName, |
michael@0 | 417 | PseudoStack* aPseudoStack, |
michael@0 | 418 | bool aIsMainThread, void* stackTop) |
michael@0 | 419 | { |
michael@0 | 420 | if (!Sampler::sRegisteredThreadsMutex) |
michael@0 | 421 | return false; |
michael@0 | 422 | |
michael@0 | 423 | mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); |
michael@0 | 424 | |
michael@0 | 425 | int id = gettid(); |
michael@0 | 426 | for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { |
michael@0 | 427 | ThreadInfo* info = sRegisteredThreads->at(i); |
michael@0 | 428 | if (info->ThreadId() == id) { |
michael@0 | 429 | // Thread already registered. This means the first unregister will be |
michael@0 | 430 | // too early. |
michael@0 | 431 | ASSERT(false); |
michael@0 | 432 | return false; |
michael@0 | 433 | } |
michael@0 | 434 | } |
michael@0 | 435 | |
michael@0 | 436 | set_tls_stack_top(stackTop); |
michael@0 | 437 | |
michael@0 | 438 | ThreadInfo* info = new ThreadInfo(aName, id, |
michael@0 | 439 | aIsMainThread, aPseudoStack, stackTop); |
michael@0 | 440 | |
michael@0 | 441 | if (sActiveSampler) { |
michael@0 | 442 | sActiveSampler->RegisterThread(info); |
michael@0 | 443 | } |
michael@0 | 444 | |
michael@0 | 445 | sRegisteredThreads->push_back(info); |
michael@0 | 446 | |
michael@0 | 447 | uwt__register_thread_for_profiling(stackTop); |
michael@0 | 448 | return true; |
michael@0 | 449 | } |
michael@0 | 450 | |
michael@0 | 451 | void Sampler::UnregisterCurrentThread() |
michael@0 | 452 | { |
michael@0 | 453 | if (!Sampler::sRegisteredThreadsMutex) |
michael@0 | 454 | return; |
michael@0 | 455 | |
michael@0 | 456 | tlsStackTop.set(nullptr); |
michael@0 | 457 | |
michael@0 | 458 | mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); |
michael@0 | 459 | |
michael@0 | 460 | int id = gettid(); |
michael@0 | 461 | |
michael@0 | 462 | for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { |
michael@0 | 463 | ThreadInfo* info = sRegisteredThreads->at(i); |
michael@0 | 464 | if (info->ThreadId() == id) { |
michael@0 | 465 | delete info; |
michael@0 | 466 | sRegisteredThreads->erase(sRegisteredThreads->begin() + i); |
michael@0 | 467 | break; |
michael@0 | 468 | } |
michael@0 | 469 | } |
michael@0 | 470 | |
michael@0 | 471 | uwt__unregister_thread_for_profiling(); |
michael@0 | 472 | } |
michael@0 | 473 | |
michael@0 | 474 | #ifdef ANDROID |
michael@0 | 475 | static struct sigaction old_sigstart_signal_handler; |
michael@0 | 476 | const int SIGSTART = SIGUSR2; |
michael@0 | 477 | |
michael@0 | 478 | static void freeArray(const char** array, int size) { |
michael@0 | 479 | for (int i = 0; i < size; i++) { |
michael@0 | 480 | free((void*) array[i]); |
michael@0 | 481 | } |
michael@0 | 482 | } |
michael@0 | 483 | |
michael@0 | 484 | static uint32_t readCSVArray(char* csvList, const char** buffer) { |
michael@0 | 485 | uint32_t count; |
michael@0 | 486 | char* savePtr; |
michael@0 | 487 | int newlinePos = strlen(csvList) - 1; |
michael@0 | 488 | if (csvList[newlinePos] == '\n') { |
michael@0 | 489 | csvList[newlinePos] = '\0'; |
michael@0 | 490 | } |
michael@0 | 491 | |
michael@0 | 492 | char* item = strtok_r(csvList, ",", &savePtr); |
michael@0 | 493 | for (count = 0; item; item = strtok_r(NULL, ",", &savePtr)) { |
michael@0 | 494 | int length = strlen(item) + 1; // Include \0 |
michael@0 | 495 | char* newBuf = (char*) malloc(sizeof(char) * length); |
michael@0 | 496 | buffer[count] = newBuf; |
michael@0 | 497 | strncpy(newBuf, item, length); |
michael@0 | 498 | count++; |
michael@0 | 499 | } |
michael@0 | 500 | |
michael@0 | 501 | return count; |
michael@0 | 502 | } |
michael@0 | 503 | |
michael@0 | 504 | // Currently support only the env variables |
michael@0 | 505 | // reported in read_profiler_env |
michael@0 | 506 | static void ReadProfilerVars(const char* fileName, const char** features, |
michael@0 | 507 | uint32_t* featureCount, const char** threadNames, uint32_t* threadCount) { |
michael@0 | 508 | FILE* file = fopen(fileName, "r"); |
michael@0 | 509 | const int bufferSize = 1024; |
michael@0 | 510 | char line[bufferSize]; |
michael@0 | 511 | char* feature; |
michael@0 | 512 | char* value; |
michael@0 | 513 | char* savePtr; |
michael@0 | 514 | |
michael@0 | 515 | if (file) { |
michael@0 | 516 | while (fgets(line, bufferSize, file) != NULL) { |
michael@0 | 517 | feature = strtok_r(line, "=", &savePtr); |
michael@0 | 518 | value = strtok_r(NULL, "", &savePtr); |
michael@0 | 519 | |
michael@0 | 520 | if (strncmp(feature, PROFILER_MODE, bufferSize) == 0) { |
michael@0 | 521 | set_profiler_mode(value); |
michael@0 | 522 | } else if (strncmp(feature, PROFILER_INTERVAL, bufferSize) == 0) { |
michael@0 | 523 | set_profiler_interval(value); |
michael@0 | 524 | } else if (strncmp(feature, PROFILER_ENTRIES, bufferSize) == 0) { |
michael@0 | 525 | set_profiler_entries(value); |
michael@0 | 526 | } else if (strncmp(feature, PROFILER_STACK, bufferSize) == 0) { |
michael@0 | 527 | set_profiler_scan(value); |
michael@0 | 528 | } else if (strncmp(feature, PROFILER_FEATURES, bufferSize) == 0) { |
michael@0 | 529 | *featureCount = readCSVArray(value, features); |
michael@0 | 530 | } else if (strncmp(feature, "threads", bufferSize) == 0) { |
michael@0 | 531 | *threadCount = readCSVArray(value, threadNames); |
michael@0 | 532 | } |
michael@0 | 533 | } |
michael@0 | 534 | |
michael@0 | 535 | fclose(file); |
michael@0 | 536 | } |
michael@0 | 537 | } |
michael@0 | 538 | |
michael@0 | 539 | |
michael@0 | 540 | static void StartSignalHandler(int signal, siginfo_t* info, void* context) { |
michael@0 | 541 | // XXX: Everything we do here is NOT async signal safe. We risk nasty things |
michael@0 | 542 | // like deadlocks but we typically only do this once so it tends to be ok. |
michael@0 | 543 | // See bug 909403 |
michael@0 | 544 | uint32_t featureCount = 0; |
michael@0 | 545 | uint32_t threadCount = 0; |
michael@0 | 546 | |
michael@0 | 547 | // Just allocate 10 features for now |
michael@0 | 548 | // FIXME: these don't really point to const chars* |
michael@0 | 549 | // So we free them later, but we don't want to change the const char** |
michael@0 | 550 | // declaration in profiler_start. Annoying but ok for now. |
michael@0 | 551 | const char* threadNames[10]; |
michael@0 | 552 | const char* features[10]; |
michael@0 | 553 | const char* profilerConfigFile = "/data/local/tmp/profiler.options"; |
michael@0 | 554 | |
michael@0 | 555 | ReadProfilerVars(profilerConfigFile, features, &featureCount, threadNames, &threadCount); |
michael@0 | 556 | MOZ_ASSERT(featureCount < 10); |
michael@0 | 557 | MOZ_ASSERT(threadCount < 10); |
michael@0 | 558 | |
michael@0 | 559 | profiler_start(PROFILE_DEFAULT_ENTRY, 1, |
michael@0 | 560 | features, featureCount, |
michael@0 | 561 | threadNames, threadCount); |
michael@0 | 562 | |
michael@0 | 563 | freeArray(threadNames, threadCount); |
michael@0 | 564 | freeArray(features, featureCount); |
michael@0 | 565 | } |
michael@0 | 566 | |
michael@0 | 567 | void OS::Startup() |
michael@0 | 568 | { |
michael@0 | 569 | LOG("Registering start signal"); |
michael@0 | 570 | struct sigaction sa; |
michael@0 | 571 | sa.sa_sigaction = StartSignalHandler; |
michael@0 | 572 | sigemptyset(&sa.sa_mask); |
michael@0 | 573 | sa.sa_flags = SA_RESTART | SA_SIGINFO; |
michael@0 | 574 | if (sigaction(SIGSTART, &sa, &old_sigstart_signal_handler) != 0) { |
michael@0 | 575 | LOG("Error installing signal"); |
michael@0 | 576 | } |
michael@0 | 577 | } |
michael@0 | 578 | |
michael@0 | 579 | #else |
michael@0 | 580 | |
michael@0 | 581 | void OS::Startup() { |
michael@0 | 582 | // Set up the fork handlers. |
michael@0 | 583 | setup_atfork(); |
michael@0 | 584 | } |
michael@0 | 585 | |
michael@0 | 586 | #endif |
michael@0 | 587 | |
michael@0 | 588 | |
michael@0 | 589 | |
michael@0 | 590 | void TickSample::PopulateContext(void* aContext) |
michael@0 | 591 | { |
michael@0 | 592 | MOZ_ASSERT(aContext); |
michael@0 | 593 | ucontext_t* pContext = reinterpret_cast<ucontext_t*>(aContext); |
michael@0 | 594 | if (!getcontext(pContext)) { |
michael@0 | 595 | context = pContext; |
michael@0 | 596 | SetSampleContext(this, aContext); |
michael@0 | 597 | } |
michael@0 | 598 | } |
michael@0 | 599 | |
michael@0 | 600 | // WARNING: Works with values up to 1 second |
michael@0 | 601 | void OS::SleepMicro(int microseconds) |
michael@0 | 602 | { |
michael@0 | 603 | struct timespec ts; |
michael@0 | 604 | ts.tv_sec = 0; |
michael@0 | 605 | ts.tv_nsec = microseconds * 1000UL; |
michael@0 | 606 | |
michael@0 | 607 | while (true) { |
michael@0 | 608 | // in the case of interrupt we keep waiting |
michael@0 | 609 | // nanosleep puts the remaining to back into ts |
michael@0 | 610 | if (!nanosleep(&ts, &ts) || errno != EINTR) { |
michael@0 | 611 | return; |
michael@0 | 612 | } |
michael@0 | 613 | } |
michael@0 | 614 | } |
michael@0 | 615 |