michael@0: // Copyright (c) 2006-2011 The Chromium Authors. All rights reserved. michael@0: // michael@0: // Redistribution and use in source and binary forms, with or without michael@0: // modification, are permitted provided that the following conditions michael@0: // are met: michael@0: // * Redistributions of source code must retain the above copyright michael@0: // notice, this list of conditions and the following disclaimer. michael@0: // * Redistributions in binary form must reproduce the above copyright michael@0: // notice, this list of conditions and the following disclaimer in michael@0: // the documentation and/or other materials provided with the michael@0: // distribution. michael@0: // * Neither the name of Google, Inc. nor the names of its contributors michael@0: // may be used to endorse or promote products derived from this michael@0: // software without specific prior written permission. michael@0: // michael@0: // THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS michael@0: // "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT michael@0: // LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS michael@0: // FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE michael@0: // COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, michael@0: // INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, michael@0: // BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS michael@0: // OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED michael@0: // AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, michael@0: // OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT michael@0: // OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF michael@0: // SUCH DAMAGE. michael@0: michael@0: /* michael@0: # vim: sw=2 michael@0: */ michael@0: #include michael@0: #include michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include michael@0: #include // set name michael@0: #include michael@0: #include michael@0: #ifdef ANDROID michael@0: #include michael@0: #else michael@0: #define __android_log_print(a, ...) michael@0: #endif michael@0: #include michael@0: // Ubuntu Dapper requires memory pages to be marked as michael@0: // executable. Otherwise, OS raises an exception when executing code michael@0: // in that page. michael@0: #include // mmap & munmap michael@0: #include // mmap & munmap michael@0: #include // open michael@0: #include // open michael@0: #include // sysconf michael@0: #include michael@0: #ifdef __GLIBC__ michael@0: #include // backtrace, backtrace_symbols michael@0: #endif // def __GLIBC__ michael@0: #include // index michael@0: #include michael@0: #include michael@0: #include "platform.h" michael@0: #include "GeckoProfiler.h" michael@0: #include "mozilla/Mutex.h" michael@0: #include "mozilla/Atomics.h" michael@0: #include "ProfileEntry.h" michael@0: #include "nsThreadUtils.h" michael@0: #include "TableTicker.h" michael@0: #include "UnwinderThread2.h" michael@0: #if defined(__ARM_EABI__) && defined(MOZ_WIDGET_GONK) michael@0: // Should also work on other Android and ARM Linux, but not tested there yet. michael@0: #define USE_EHABI_STACKWALK michael@0: #include "EHABIStackWalk.h" michael@0: #endif michael@0: michael@0: #include michael@0: #include michael@0: #include michael@0: michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: #include "ipc/Nuwa.h" michael@0: #endif michael@0: michael@0: #define SIGNAL_SAVE_PROFILE SIGUSR2 michael@0: michael@0: #if defined(__GLIBC__) michael@0: // glibc doesn't implement gettid(2). michael@0: #include michael@0: pid_t gettid() michael@0: { michael@0: return (pid_t) syscall(SYS_gettid); michael@0: } michael@0: #endif michael@0: michael@0: /* static */ Thread::tid_t michael@0: Thread::GetCurrentId() michael@0: { michael@0: return gettid(); michael@0: } michael@0: michael@0: #if !defined(ANDROID) michael@0: // Keep track of when any of our threads calls fork(), so we can michael@0: // temporarily disable signal delivery during the fork() call. Not michael@0: // doing so appears to cause a kind of race, in which signals keep michael@0: // getting delivered to the thread doing fork(), which keeps causing michael@0: // it to fail and be restarted; hence forward progress is delayed a michael@0: // great deal. A side effect of this is to permanently disable michael@0: // sampling in the child process. See bug 837390. michael@0: michael@0: // Unfortunately this is only doable on non-Android, since Bionic michael@0: // doesn't have pthread_atfork. michael@0: michael@0: // This records the current state at the time we paused it. michael@0: static bool was_paused = false; michael@0: michael@0: // In the parent, just before the fork, record the pausedness state, michael@0: // and then pause. michael@0: static void paf_prepare(void) { michael@0: if (Sampler::GetActiveSampler()) { michael@0: was_paused = Sampler::GetActiveSampler()->IsPaused(); michael@0: Sampler::GetActiveSampler()->SetPaused(true); michael@0: } else { michael@0: was_paused = false; michael@0: } michael@0: } michael@0: michael@0: // In the parent, just after the fork, return pausedness to the michael@0: // pre-fork state. michael@0: static void paf_parent(void) { michael@0: if (Sampler::GetActiveSampler()) michael@0: Sampler::GetActiveSampler()->SetPaused(was_paused); michael@0: } michael@0: michael@0: // Set up the fork handlers. michael@0: static void* setup_atfork() { michael@0: pthread_atfork(paf_prepare, paf_parent, NULL); michael@0: return NULL; michael@0: } michael@0: #endif /* !defined(ANDROID) */ michael@0: michael@0: struct SamplerRegistry { michael@0: static void AddActiveSampler(Sampler *sampler) { michael@0: ASSERT(!SamplerRegistry::sampler); michael@0: SamplerRegistry::sampler = sampler; michael@0: } michael@0: static void RemoveActiveSampler(Sampler *sampler) { michael@0: SamplerRegistry::sampler = NULL; michael@0: } michael@0: static Sampler *sampler; michael@0: }; michael@0: michael@0: Sampler *SamplerRegistry::sampler = NULL; michael@0: michael@0: static mozilla::Atomic sCurrentThreadProfile; michael@0: static sem_t sSignalHandlingDone; michael@0: michael@0: static void ProfilerSaveSignalHandler(int signal, siginfo_t* info, void* context) { michael@0: Sampler::GetActiveSampler()->RequestSave(); michael@0: } michael@0: michael@0: static void SetSampleContext(TickSample* sample, void* context) michael@0: { michael@0: // Extracting the sample from the context is extremely machine dependent. michael@0: ucontext_t* ucontext = reinterpret_cast(context); michael@0: mcontext_t& mcontext = ucontext->uc_mcontext; michael@0: #if V8_HOST_ARCH_IA32 michael@0: sample->pc = reinterpret_cast
(mcontext.gregs[REG_EIP]); michael@0: sample->sp = reinterpret_cast
(mcontext.gregs[REG_ESP]); michael@0: sample->fp = reinterpret_cast
(mcontext.gregs[REG_EBP]); michael@0: #elif V8_HOST_ARCH_X64 michael@0: sample->pc = reinterpret_cast
(mcontext.gregs[REG_RIP]); michael@0: sample->sp = reinterpret_cast
(mcontext.gregs[REG_RSP]); michael@0: sample->fp = reinterpret_cast
(mcontext.gregs[REG_RBP]); michael@0: #elif V8_HOST_ARCH_ARM michael@0: // An undefined macro evaluates to 0, so this applies to Android's Bionic also. michael@0: #if !defined(ANDROID) && (__GLIBC__ < 2 || (__GLIBC__ == 2 && __GLIBC_MINOR__ <= 3)) michael@0: sample->pc = reinterpret_cast
(mcontext.gregs[R15]); michael@0: sample->sp = reinterpret_cast
(mcontext.gregs[R13]); michael@0: sample->fp = reinterpret_cast
(mcontext.gregs[R11]); michael@0: #ifdef ENABLE_ARM_LR_SAVING michael@0: sample->lr = reinterpret_cast
(mcontext.gregs[R14]); michael@0: #endif michael@0: #else michael@0: sample->pc = reinterpret_cast
(mcontext.arm_pc); michael@0: sample->sp = reinterpret_cast
(mcontext.arm_sp); michael@0: sample->fp = reinterpret_cast
(mcontext.arm_fp); michael@0: #ifdef ENABLE_ARM_LR_SAVING michael@0: sample->lr = reinterpret_cast
(mcontext.arm_lr); michael@0: #endif michael@0: #endif michael@0: #elif V8_HOST_ARCH_MIPS michael@0: // Implement this on MIPS. michael@0: UNIMPLEMENTED(); michael@0: #endif michael@0: } michael@0: michael@0: #ifdef ANDROID michael@0: #define V8_HOST_ARCH_ARM 1 michael@0: #define SYS_gettid __NR_gettid michael@0: #define SYS_tgkill __NR_tgkill michael@0: #else michael@0: #define V8_HOST_ARCH_X64 1 michael@0: #endif michael@0: static void ProfilerSignalHandler(int signal, siginfo_t* info, void* context) { michael@0: if (!Sampler::GetActiveSampler()) { michael@0: sem_post(&sSignalHandlingDone); michael@0: return; michael@0: } michael@0: michael@0: TickSample sample_obj; michael@0: TickSample* sample = &sample_obj; michael@0: sample->context = context; michael@0: michael@0: #ifdef ENABLE_SPS_LEAF_DATA michael@0: // If profiling, we extract the current pc and sp. michael@0: if (Sampler::GetActiveSampler()->IsProfiling()) { michael@0: SetSampleContext(sample, context); michael@0: } michael@0: #endif michael@0: sample->threadProfile = sCurrentThreadProfile; michael@0: sample->timestamp = mozilla::TimeStamp::Now(); michael@0: michael@0: Sampler::GetActiveSampler()->Tick(sample); michael@0: michael@0: sCurrentThreadProfile = NULL; michael@0: sem_post(&sSignalHandlingDone); michael@0: } michael@0: michael@0: // If the Nuwa process is enabled, we need to use the wrapper of tgkill() to michael@0: // perform the mapping of thread ID. michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: extern "C" MFBT_API int tgkill(pid_t tgid, pid_t tid, int signalno); michael@0: #else michael@0: int tgkill(pid_t tgid, pid_t tid, int signalno) { michael@0: return syscall(SYS_tgkill, tgid, tid, signalno); michael@0: } michael@0: #endif michael@0: michael@0: class PlatformData : public Malloced { michael@0: public: michael@0: PlatformData() michael@0: {} michael@0: }; michael@0: michael@0: /* static */ PlatformData* michael@0: Sampler::AllocPlatformData(int aThreadId) michael@0: { michael@0: return new PlatformData; michael@0: } michael@0: michael@0: /* static */ void michael@0: Sampler::FreePlatformData(PlatformData* aData) michael@0: { michael@0: delete aData; michael@0: } michael@0: michael@0: static void* SignalSender(void* arg) { michael@0: // Taken from platform_thread_posix.cc michael@0: prctl(PR_SET_NAME, "SamplerThread", 0, 0, 0); michael@0: michael@0: #ifdef MOZ_NUWA_PROCESS michael@0: // If the Nuwa process is enabled, we need to mark and freeze the sampler michael@0: // thread in the Nuwa process and have this thread recreated in the spawned michael@0: // child. michael@0: if(IsNuwaProcess()) { michael@0: NuwaMarkCurrentThread(nullptr, nullptr); michael@0: // Freeze the thread here so the spawned child will get the correct tgid michael@0: // from the getpid() call below. michael@0: NuwaFreezeCurrentThread(); michael@0: } michael@0: #endif michael@0: michael@0: int vm_tgid_ = getpid(); michael@0: michael@0: while (SamplerRegistry::sampler->IsActive()) { michael@0: SamplerRegistry::sampler->HandleSaveRequest(); michael@0: michael@0: if (!SamplerRegistry::sampler->IsPaused()) { michael@0: mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); michael@0: std::vector threads = michael@0: SamplerRegistry::sampler->GetRegisteredThreads(); michael@0: michael@0: for (uint32_t i = 0; i < threads.size(); i++) { michael@0: ThreadInfo* info = threads[i]; michael@0: michael@0: // This will be null if we're not interested in profiling this thread. michael@0: if (!info->Profile()) michael@0: continue; michael@0: michael@0: PseudoStack::SleepState sleeping = info->Stack()->observeSleeping(); michael@0: if (sleeping == PseudoStack::SLEEPING_AGAIN) { michael@0: info->Profile()->DuplicateLastSample(); michael@0: //XXX: This causes flushes regardless of jank-only mode michael@0: info->Profile()->flush(); michael@0: continue; michael@0: } michael@0: michael@0: // We use sCurrentThreadProfile the ThreadProfile for the michael@0: // thread we're profiling to the signal handler michael@0: sCurrentThreadProfile = info->Profile(); michael@0: michael@0: int threadId = info->ThreadId(); michael@0: michael@0: if (tgkill(vm_tgid_, threadId, SIGPROF) != 0) { michael@0: printf_stderr("profiler failed to signal tid=%d\n", threadId); michael@0: #ifdef DEBUG michael@0: abort(); michael@0: #endif michael@0: continue; michael@0: } michael@0: michael@0: // Wait for the signal handler to run before moving on to the next one michael@0: sem_wait(&sSignalHandlingDone); michael@0: } michael@0: } michael@0: michael@0: // Convert ms to us and subtract 100 us to compensate delays michael@0: // occuring during signal delivery. michael@0: // TODO measure and confirm this. michael@0: int interval = floor(SamplerRegistry::sampler->interval() * 1000 + 0.5) - 100; michael@0: if (interval <= 0) { michael@0: interval = 1; michael@0: } michael@0: OS::SleepMicro(interval); michael@0: } michael@0: return 0; michael@0: } michael@0: michael@0: Sampler::Sampler(double interval, bool profiling, int entrySize) michael@0: : interval_(interval), michael@0: profiling_(profiling), michael@0: paused_(false), michael@0: active_(false), michael@0: entrySize_(entrySize) { michael@0: } michael@0: michael@0: Sampler::~Sampler() { michael@0: ASSERT(!signal_sender_launched_); michael@0: } michael@0: michael@0: michael@0: void Sampler::Start() { michael@0: LOG("Sampler started"); michael@0: michael@0: #ifdef USE_EHABI_STACKWALK michael@0: mozilla::EHABIStackWalkInit(); michael@0: #endif michael@0: SamplerRegistry::AddActiveSampler(this); michael@0: michael@0: // Initialize signal handler communication michael@0: sCurrentThreadProfile = NULL; michael@0: if (sem_init(&sSignalHandlingDone, /* pshared: */ 0, /* value: */ 0) != 0) { michael@0: LOG("Error initializing semaphore"); michael@0: return; michael@0: } michael@0: michael@0: // Request profiling signals. michael@0: LOG("Request signal"); michael@0: struct sigaction sa; michael@0: sa.sa_sigaction = ProfilerSignalHandler; michael@0: sigemptyset(&sa.sa_mask); michael@0: sa.sa_flags = SA_RESTART | SA_SIGINFO; michael@0: if (sigaction(SIGPROF, &sa, &old_sigprof_signal_handler_) != 0) { michael@0: LOG("Error installing signal"); michael@0: return; michael@0: } michael@0: michael@0: // Request save profile signals michael@0: struct sigaction sa2; michael@0: sa2.sa_sigaction = ProfilerSaveSignalHandler; michael@0: sigemptyset(&sa2.sa_mask); michael@0: sa2.sa_flags = SA_RESTART | SA_SIGINFO; michael@0: if (sigaction(SIGNAL_SAVE_PROFILE, &sa2, &old_sigsave_signal_handler_) != 0) { michael@0: LOG("Error installing start signal"); michael@0: return; michael@0: } michael@0: LOG("Signal installed"); michael@0: signal_handler_installed_ = true; michael@0: michael@0: // Start a thread that sends SIGPROF signal to VM thread. michael@0: // Sending the signal ourselves instead of relying on itimer provides michael@0: // much better accuracy. michael@0: SetActive(true); michael@0: if (pthread_create( michael@0: &signal_sender_thread_, NULL, SignalSender, NULL) == 0) { michael@0: signal_sender_launched_ = true; michael@0: } michael@0: LOG("Profiler thread started"); michael@0: } michael@0: michael@0: michael@0: void Sampler::Stop() { michael@0: SetActive(false); michael@0: michael@0: // Wait for signal sender termination (it will exit after setting michael@0: // active_ to false). michael@0: if (signal_sender_launched_) { michael@0: pthread_join(signal_sender_thread_, NULL); michael@0: signal_sender_launched_ = false; michael@0: } michael@0: michael@0: SamplerRegistry::RemoveActiveSampler(this); michael@0: michael@0: // Restore old signal handler michael@0: if (signal_handler_installed_) { michael@0: sigaction(SIGNAL_SAVE_PROFILE, &old_sigsave_signal_handler_, 0); michael@0: sigaction(SIGPROF, &old_sigprof_signal_handler_, 0); michael@0: signal_handler_installed_ = false; michael@0: } michael@0: } michael@0: michael@0: bool Sampler::RegisterCurrentThread(const char* aName, michael@0: PseudoStack* aPseudoStack, michael@0: bool aIsMainThread, void* stackTop) michael@0: { michael@0: if (!Sampler::sRegisteredThreadsMutex) michael@0: return false; michael@0: michael@0: mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); michael@0: michael@0: int id = gettid(); michael@0: for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { michael@0: ThreadInfo* info = sRegisteredThreads->at(i); michael@0: if (info->ThreadId() == id) { michael@0: // Thread already registered. This means the first unregister will be michael@0: // too early. michael@0: ASSERT(false); michael@0: return false; michael@0: } michael@0: } michael@0: michael@0: set_tls_stack_top(stackTop); michael@0: michael@0: ThreadInfo* info = new ThreadInfo(aName, id, michael@0: aIsMainThread, aPseudoStack, stackTop); michael@0: michael@0: if (sActiveSampler) { michael@0: sActiveSampler->RegisterThread(info); michael@0: } michael@0: michael@0: sRegisteredThreads->push_back(info); michael@0: michael@0: uwt__register_thread_for_profiling(stackTop); michael@0: return true; michael@0: } michael@0: michael@0: void Sampler::UnregisterCurrentThread() michael@0: { michael@0: if (!Sampler::sRegisteredThreadsMutex) michael@0: return; michael@0: michael@0: tlsStackTop.set(nullptr); michael@0: michael@0: mozilla::MutexAutoLock lock(*Sampler::sRegisteredThreadsMutex); michael@0: michael@0: int id = gettid(); michael@0: michael@0: for (uint32_t i = 0; i < sRegisteredThreads->size(); i++) { michael@0: ThreadInfo* info = sRegisteredThreads->at(i); michael@0: if (info->ThreadId() == id) { michael@0: delete info; michael@0: sRegisteredThreads->erase(sRegisteredThreads->begin() + i); michael@0: break; michael@0: } michael@0: } michael@0: michael@0: uwt__unregister_thread_for_profiling(); michael@0: } michael@0: michael@0: #ifdef ANDROID michael@0: static struct sigaction old_sigstart_signal_handler; michael@0: const int SIGSTART = SIGUSR2; michael@0: michael@0: static void freeArray(const char** array, int size) { michael@0: for (int i = 0; i < size; i++) { michael@0: free((void*) array[i]); michael@0: } michael@0: } michael@0: michael@0: static uint32_t readCSVArray(char* csvList, const char** buffer) { michael@0: uint32_t count; michael@0: char* savePtr; michael@0: int newlinePos = strlen(csvList) - 1; michael@0: if (csvList[newlinePos] == '\n') { michael@0: csvList[newlinePos] = '\0'; michael@0: } michael@0: michael@0: char* item = strtok_r(csvList, ",", &savePtr); michael@0: for (count = 0; item; item = strtok_r(NULL, ",", &savePtr)) { michael@0: int length = strlen(item) + 1; // Include \0 michael@0: char* newBuf = (char*) malloc(sizeof(char) * length); michael@0: buffer[count] = newBuf; michael@0: strncpy(newBuf, item, length); michael@0: count++; michael@0: } michael@0: michael@0: return count; michael@0: } michael@0: michael@0: // Currently support only the env variables michael@0: // reported in read_profiler_env michael@0: static void ReadProfilerVars(const char* fileName, const char** features, michael@0: uint32_t* featureCount, const char** threadNames, uint32_t* threadCount) { michael@0: FILE* file = fopen(fileName, "r"); michael@0: const int bufferSize = 1024; michael@0: char line[bufferSize]; michael@0: char* feature; michael@0: char* value; michael@0: char* savePtr; michael@0: michael@0: if (file) { michael@0: while (fgets(line, bufferSize, file) != NULL) { michael@0: feature = strtok_r(line, "=", &savePtr); michael@0: value = strtok_r(NULL, "", &savePtr); michael@0: michael@0: if (strncmp(feature, PROFILER_MODE, bufferSize) == 0) { michael@0: set_profiler_mode(value); michael@0: } else if (strncmp(feature, PROFILER_INTERVAL, bufferSize) == 0) { michael@0: set_profiler_interval(value); michael@0: } else if (strncmp(feature, PROFILER_ENTRIES, bufferSize) == 0) { michael@0: set_profiler_entries(value); michael@0: } else if (strncmp(feature, PROFILER_STACK, bufferSize) == 0) { michael@0: set_profiler_scan(value); michael@0: } else if (strncmp(feature, PROFILER_FEATURES, bufferSize) == 0) { michael@0: *featureCount = readCSVArray(value, features); michael@0: } else if (strncmp(feature, "threads", bufferSize) == 0) { michael@0: *threadCount = readCSVArray(value, threadNames); michael@0: } michael@0: } michael@0: michael@0: fclose(file); michael@0: } michael@0: } michael@0: michael@0: michael@0: static void StartSignalHandler(int signal, siginfo_t* info, void* context) { michael@0: // XXX: Everything we do here is NOT async signal safe. We risk nasty things michael@0: // like deadlocks but we typically only do this once so it tends to be ok. michael@0: // See bug 909403 michael@0: uint32_t featureCount = 0; michael@0: uint32_t threadCount = 0; michael@0: michael@0: // Just allocate 10 features for now michael@0: // FIXME: these don't really point to const chars* michael@0: // So we free them later, but we don't want to change the const char** michael@0: // declaration in profiler_start. Annoying but ok for now. michael@0: const char* threadNames[10]; michael@0: const char* features[10]; michael@0: const char* profilerConfigFile = "/data/local/tmp/profiler.options"; michael@0: michael@0: ReadProfilerVars(profilerConfigFile, features, &featureCount, threadNames, &threadCount); michael@0: MOZ_ASSERT(featureCount < 10); michael@0: MOZ_ASSERT(threadCount < 10); michael@0: michael@0: profiler_start(PROFILE_DEFAULT_ENTRY, 1, michael@0: features, featureCount, michael@0: threadNames, threadCount); michael@0: michael@0: freeArray(threadNames, threadCount); michael@0: freeArray(features, featureCount); michael@0: } michael@0: michael@0: void OS::Startup() michael@0: { michael@0: LOG("Registering start signal"); michael@0: struct sigaction sa; michael@0: sa.sa_sigaction = StartSignalHandler; michael@0: sigemptyset(&sa.sa_mask); michael@0: sa.sa_flags = SA_RESTART | SA_SIGINFO; michael@0: if (sigaction(SIGSTART, &sa, &old_sigstart_signal_handler) != 0) { michael@0: LOG("Error installing signal"); michael@0: } michael@0: } michael@0: michael@0: #else michael@0: michael@0: void OS::Startup() { michael@0: // Set up the fork handlers. michael@0: setup_atfork(); michael@0: } michael@0: michael@0: #endif michael@0: michael@0: michael@0: michael@0: void TickSample::PopulateContext(void* aContext) michael@0: { michael@0: MOZ_ASSERT(aContext); michael@0: ucontext_t* pContext = reinterpret_cast(aContext); michael@0: if (!getcontext(pContext)) { michael@0: context = pContext; michael@0: SetSampleContext(this, aContext); michael@0: } michael@0: } michael@0: michael@0: // WARNING: Works with values up to 1 second michael@0: void OS::SleepMicro(int microseconds) michael@0: { michael@0: struct timespec ts; michael@0: ts.tv_sec = 0; michael@0: ts.tv_nsec = microseconds * 1000UL; michael@0: michael@0: while (true) { michael@0: // in the case of interrupt we keep waiting michael@0: // nanosleep puts the remaining to back into ts michael@0: if (!nanosleep(&ts, &ts) || errno != EINTR) { michael@0: return; michael@0: } michael@0: } michael@0: } michael@0: