Home | History | Annotate | Download | only in memory_replay
      1 /*
      2  * Copyright (C) 2015 The Android Open Source Project
      3  *
      4  * Licensed under the Apache License, Version 2.0 (the "License");
      5  * you may not use this file except in compliance with the License.
      6  * You may obtain a copy of the License at
      7  *
      8  *      http://www.apache.org/licenses/LICENSE-2.0
      9  *
     10  * Unless required by applicable law or agreed to in writing, software
     11  * distributed under the License is distributed on an "AS IS" BASIS,
     12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
     13  * See the License for the specific language governing permissions and
     14  * limitations under the License.
     15  */
     16 
     17 #include <err.h>
     18 #include <errno.h>
     19 #include <pthread.h>
     20 #include <stdint.h>
     21 #include <stdio.h>
     22 #include <stdlib.h>
     23 #include <string.h>
     24 #include <sys/mman.h>
     25 #include <unistd.h>
     26 
     27 #include <new>
     28 
     29 #include "Action.h"
     30 #include "Thread.h"
     31 #include "Threads.h"
     32 
     33 void* ThreadRunner(void* data) {
     34   Thread* thread = reinterpret_cast<Thread*>(data);
     35   while (true) {
     36     thread->WaitForPending();
     37     Action* action = thread->GetAction();
     38     thread->AddTimeNsecs(action->Execute(thread->pointers()));
     39     bool end_thread = action->EndThread();
     40     thread->ClearPending();
     41     if (end_thread) {
     42       break;
     43     }
     44   }
     45   return nullptr;
     46 }
     47 
     48 Threads::Threads(Pointers* pointers, size_t max_threads)
     49     : pointers_(pointers), max_threads_(max_threads) {
     50   size_t pagesize = getpagesize();
     51   data_size_ = (max_threads_ * sizeof(Thread) + pagesize - 1) & ~(pagesize - 1);
     52   max_threads_ = data_size_ / sizeof(Thread);
     53 
     54   void* memory = mmap(nullptr, data_size_, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANON, -1, 0);
     55   if (memory == MAP_FAILED) {
     56     err(1, "Failed to map in memory for Threads: map size %zu, max threads %zu\n",
     57         data_size_, max_threads_);
     58   }
     59 
     60   if (Thread::ACTION_SIZE < Action::MaxActionSize()) {
     61     err(1, "Thread action size is too small: ACTION_SIZE %zu, max size %zu\n",
     62         Thread::ACTION_SIZE, Action::MaxActionSize());
     63   }
     64 
     65   threads_ = new (memory) Thread[max_threads_];
     66 }
     67 
     68 Threads::~Threads() {
     69   if (threads_) {
     70     munmap(threads_, data_size_);
     71     threads_ = nullptr;
     72     data_size_ = 0;
     73   }
     74 }
     75 
     76 Thread* Threads::CreateThread(pid_t tid) {
     77   if (num_threads_ == max_threads_) {
     78     err(1, "Too many threads created, current max %zu.\n", num_threads_);
     79   }
     80   Thread* thread = FindEmptyEntry(tid);
     81   if (thread == nullptr) {
     82     err(1, "No empty entries found, current max %zu, num threads %zu\n",
     83           max_threads_, num_threads_);
     84   }
     85   thread->tid_ = tid;
     86   thread->pointers_ = pointers_;
     87   thread->total_time_nsecs_ = 0;
     88   if (pthread_create(&thread->thread_id_, nullptr, ThreadRunner, thread) == -1) {
     89     err(1, "Failed to create thread %d: %s\n", tid, strerror(errno));
     90   }
     91 
     92   num_threads_++;
     93   return thread;
     94 }
     95 
     96 Thread* Threads::FindThread(pid_t tid) {
     97   size_t index = GetHashEntry(tid);
     98   for (size_t entries = num_threads_; entries != 0; ) {
     99     pid_t cur_tid = threads_[index].tid_;
    100     if (cur_tid == tid) {
    101       return threads_ + index;
    102     }
    103     if (cur_tid != 0) {
    104       entries--;
    105     }
    106     if (++index == max_threads_) {
    107       index = 0;
    108     }
    109   }
    110   return nullptr;
    111 }
    112 
    113 void Threads::WaitForAllToQuiesce() {
    114   for (size_t i = 0, threads = 0; threads < num_threads_; i++) {
    115     pid_t cur_tid = threads_[i].tid_;
    116     if (cur_tid != 0) {
    117       threads++;
    118       threads_[i].WaitForReady();
    119     }
    120   }
    121 }
    122 
    123 size_t Threads::GetHashEntry(pid_t tid) {
    124   return tid % max_threads_;
    125 }
    126 
    127 Thread* Threads::FindEmptyEntry(pid_t tid) {
    128   size_t index = GetHashEntry(tid);
    129   for (size_t entries = 0; entries < max_threads_; entries++) {
    130     if (threads_[index].tid_ == 0) {
    131       return threads_ + index;
    132     }
    133     if (++index == max_threads_) {
    134       index = 0;
    135     }
    136   }
    137   return nullptr;
    138 }
    139 
    140 void Threads::Finish(Thread* thread) {
    141   int ret = pthread_join(thread->thread_id_, nullptr);
    142   if (ret != 0) {
    143     fprintf(stderr, "pthread_join failed: %s\n", strerror(ret));
    144     exit(1);
    145   }
    146   total_time_nsecs_ += thread->total_time_nsecs_;
    147   thread->tid_ = 0;
    148   num_threads_--;
    149 }
    150 
    151 void Threads::FinishAll() {
    152   for (size_t i = 0; i < max_threads_; i++) {
    153     if (threads_[i].tid_ != 0) {
    154       threads_[i].CreateAction(0, "thread_done", nullptr);
    155       threads_[i].SetPending();
    156       Finish(threads_ + i);
    157     }
    158   }
    159 }
    160