<commit_before>#include <windows.h>
#include <iostream>
#include <cassert>
#include <sstream>
#include <numeric>

#include "livehttpposter.hpp"

using namespace std;

extern HANDLE g_handle_quit;

namespace live_webm_streamer {

long LivePoster::size_file_ = 0;
bool LivePoster::init_ = false;
bool LivePoster::codec_private_checked_ = false;
double LivePoster::max_bps_ = 0.0;
double LivePoster::min_bps_ = 0.0;
double LivePoster::cur_bps_ = 0.0;

wchar_t* LivePoster::ip_address_= NULL;
wchar_t* LivePoster::port_ = NULL;
wchar_t* LivePoster::webm_file_ = NULL;

deque<double> LivePoster::dq_;

LivePoster::LivePoster(void)
{
}

LivePoster::~LivePoster(void)
{
}

unsigned LivePoster::ThreadProc(void* pv)
{
  const HANDLE* ha = reinterpret_cast<const HANDLE*>(pv);

  CURL* curl_;
  CURLcode res_;

  const char* const clone_file = "test_clone.webm";

  struct curl_httppost* formpost = NULL;
  struct curl_httppost* lastptr = NULL;
  struct curl_slist* headerlist = NULL;
  static const char buf[] = "Expect:";

  curl_ = curl_easy_init();

  bool post_ready = false;

  for (;;)
  {
    const DWORD dw = WaitForMultipleObjects(2, ha, FALSE, INFINITE);

    if (dw == (WAIT_OBJECT_0 + 0))  // Ctrl+C
    {
      ifstream ifs(webm_file_, ios_base::binary | ios_base::in);

      if (ifs.is_open())
      {
        const bool b = ifs.good();
        assert(b);

        ifs.seekg(0, ios::end);
        const long size_temp = ifs.tellg();
        const long size_diff = size_temp - size_file_;

        ifs.seekg(size_file_, ios::beg);

        char* const buffer = new char[size_diff];
        ifs.read(buffer, size_diff);
        size_file_ = size_temp;
        ifs.close();

#ifdef _DEBUG
        ofstream ofs(clone_file, ios_base::binary | ios_base::out | ios::app);
        ofs.write(buffer, size_diff);
        ofs.close();
#endif
        delete [] buffer;

        curl_easy_cleanup(curl_);
        curl_formfree(formpost);
      }
      return 0;
    }

    if (dw == (WAIT_OBJECT_0 + 1)) // POST event
    {
      ifstream ifs(webm_file_, ios_base::binary | ios_base::in);

      if (ifs.is_open())
      {
        const bool b = ifs.good();
        assert(b);

        ifs.seekg(0, ios::end);
        const long size_temp = ifs.tellg();
        const long size_diff = size_temp - size_file_;

        if (size_diff == 0)
        {
          ifs.close();
          continue;
        }

#ifdef _DEBUG
        ofstream ofs;
#endif
        ofstream ofs_multipart;

        if (size_file_ == 0)
        {
          ifs.seekg(0, ios::beg);
#ifdef _DEBUG
          ofs.open(clone_file, ios_base::binary | ios_base::out);
#endif
        }
        else
        {
          ifs.seekg(size_file_, ios::beg);
#ifdef _DEBUG
          ofs.open(clone_file, ios_base::binary | ios_base::out | ios::app);
#endif
        }

        ofs_multipart.open("multipart.webm", ios_base::binary | ios_base::out);

        char* const buffer = new char[size_diff];
        ifs.read(buffer, size_diff);
        ifs.close();

        if (!codec_private_checked_)
        {
          //TODO(hwasoo): consider A_VORBIS is the last part of the buffer.
          for (int i = 0; i < size_diff - 10; ++i)
          {
            if ((*(buffer + i ) == 65)      // A
             && (*(buffer + i + 1) == 95)   // _
             && (*(buffer + i + 2) == 86)   // V
             && (*(buffer + i + 3) == 79)   // O
             && (*(buffer + i + 4) == 82)   // R
             && (*(buffer + i + 5) == 66)   // B
             && (*(buffer + i + 6) == 73)   // I
             && (*(buffer + i + 7) == 83))  // S
            {
              if ((*(buffer + i + 8) == -20)  // 0xEC - Void Element
               && (*(buffer + i + 9) == 95)   // 0x5F
               && (*(buffer + i + 10) == 61)) // 0x3D
              {
#ifdef _DEBUG
                cout << endl << " A_VORBIS and Void element found" << endl;
#endif
                post_ready = true;
                break;
              }

              if ((*(buffer + i + 8) == 99)     // 0x63
                && (*(buffer + i + 9) == -94))  // 0xA2
              {
#ifdef _DEBUG
                cout << endl << " A_VORBIS and Codec Private found" << endl;
#endif
                codec_private_checked_ = true;
                break;
              }
            }
          } // for loop
        } // if codec_private_checked_

        // Check live cluster size
        for (int i = 0; i < size_diff - 7; ++i)
        //for (int i = 0; i < size_diff - 8; ++i)
        {
          if ((*(buffer + i) == 31)        //0x1F - Cluster
           && (*(buffer + i + 1) == 67)    //0x43 - Cluster
           && (*(buffer + i + 2) == -74)   //0xB6 - Cluster
           && (*(buffer + i + 3) == 117)   //0x75 - Cluster
           && (*(buffer + i + 4) == 31)    //0x1F
           && (*(buffer + i + 5) == -1)    //0xFF
           && (*(buffer + i + 6) == -1)    //0xFF
           && (*(buffer + i + 7) == -1))   //0xFF
          {
#ifdef _DEBUG
            cout << endl << " Cluster size is undefined." << endl;
#endif
            post_ready = true;
            break;
          }
        }

        if (!post_ready)
        {
          size_file_ = size_temp;
#ifdef _DEBUG
          ofs.write(buffer, size_diff);
#endif
          ofs_multipart.write(buffer, size_diff);
          ofs_multipart.close();

          if (curl_)
          {
            curl_formadd(&formpost,
             &lastptr,
             CURLFORM_COPYNAME, "send_webm_stream",
             CURLFORM_FILE, "multipart.webm",
             CURLFORM_END);

            ostringstream os;
            os << "http://";

            const wchar_t* p = ip_address_;
            const wchar_t* q = p + wcslen(p);

            while (p != q)
              os << static_cast<char>(*p++);

            os << ':';

            p = port_;
            q = p + wcslen(p);

            while (p != q)
              os << static_cast<char>(*p++);

            const string str_ = os.str();
            const char* const str = str_.c_str();

            res_ = curl_easy_setopt(curl_, CURLOPT_URL, str);

            cout << "  multipart size(uploading): " << size_diff << " Byte(s)" << endl;
            res_ = curl_easy_setopt(curl_, CURLOPT_HTTPPOST, formpost);

            cout.width(10);
            cout.precision(4);

            if (cur_bps_ != 0.0)
              cout << "  estimated upload time in second : " << (size_diff / 1000) / cur_bps_ << endl;

            __int64 ctr1 = 0, ctr2 = 0, freq = 0;
            QueryPerformanceCounter((LARGE_INTEGER*)&ctr1);

            res_ = curl_easy_perform(curl_);

            if (res_ == CURLE_OK)
            {
              QueryPerformanceCounter((LARGE_INTEGER*)&ctr2);
	            QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
	            const double span = (ctr2 - ctr1)* 1.0 /freq;
	            cout << "  real upload time in second : " << span << endl;

              cur_bps_ = (double)(((double)size_diff / span) / 1000.0);

              if (!init_)
              {
                init_ = true;
                min_bps_ = cur_bps_;
              }

              if (cur_bps_ > max_bps_)
                max_bps_ = cur_bps_;

              if (cur_bps_ < min_bps_)
                min_bps_ = cur_bps_;

	            cout << "  " << cur_bps_ << " KBps  <--- current speed" << endl;
	            dq_.push_back(cur_bps_);

	            if (dq_.size() > 100)
	              dq_.pop_front();

	            const double avg_bps = (accumulate(dq_.begin(), dq_.end(), 0.0)) / dq_.size();

	            cout << "  Max(KBps) : " << max_bps_ << "   Avg(KBps) : " << avg_bps << "   Min(KBps) : " << min_bps_ << endl << endl;

              if (res_ == CURLE_COULDNT_CONNECT)
                cout << " Failed to connect() to host or proxy " << endl;
            }
            else
            {
              //TODO(hwasoo): need to exit out this thread
              ;
            }
          }
        }

        post_ready = false;
#ifdef _DEBUG
        ofs.close();
#endif
        delete [] buffer;
      }
    } // POST event
  } // for(;;)
} // ThreadProc

}<commit_msg>whitespace cleanup<commit_after>#include <windows.h>
#include <iostream>
#include <cassert>
#include <sstream>
#include <numeric>

#include "livehttpposter.hpp"

using namespace std;

extern HANDLE g_handle_quit;

namespace live_webm_streamer {

long LivePoster::size_file_ = 0;
bool LivePoster::init_ = false;
bool LivePoster::codec_private_checked_ = false;
double LivePoster::max_bps_ = 0.0;
double LivePoster::min_bps_ = 0.0;
double LivePoster::cur_bps_ = 0.0;

wchar_t* LivePoster::ip_address_= NULL;
wchar_t* LivePoster::port_ = NULL;
wchar_t* LivePoster::webm_file_ = NULL;

deque<double> LivePoster::dq_;

LivePoster::LivePoster(void)
{
}

LivePoster::~LivePoster(void)
{
}

unsigned LivePoster::ThreadProc(void* pv)
{
  const HANDLE* ha = reinterpret_cast<const HANDLE*>(pv);

  CURL* curl_;
  CURLcode res_;

  const char* const clone_file = "test_clone.webm";

  struct curl_httppost* formpost = NULL;
  struct curl_httppost* lastptr = NULL;
  struct curl_slist* headerlist = NULL;
  static const char buf[] = "Expect:";

  curl_ = curl_easy_init();

  bool post_ready = false;

  for (;;)
  {
    const DWORD dw = WaitForMultipleObjects(2, ha, FALSE, INFINITE);

    if (dw == (WAIT_OBJECT_0 + 0))  // Ctrl+C
    {
      ifstream ifs(webm_file_, ios_base::binary | ios_base::in);

      if (ifs.is_open())
      {
        const bool b = ifs.good();
        assert(b);

        ifs.seekg(0, ios::end);
        const long size_temp = ifs.tellg();
        const long size_diff = size_temp - size_file_;

        ifs.seekg(size_file_, ios::beg);

        char* const buffer = new char[size_diff];
        ifs.read(buffer, size_diff);
        size_file_ = size_temp;
        ifs.close();

#ifdef _DEBUG
        ofstream ofs(clone_file, ios_base::binary | ios_base::out | ios::app);
        ofs.write(buffer, size_diff);
        ofs.close();
#endif
        delete [] buffer;

        curl_easy_cleanup(curl_);
        curl_formfree(formpost);
      }
      return 0;
    }

    if (dw == (WAIT_OBJECT_0 + 1)) // POST event
    {
      ifstream ifs(webm_file_, ios_base::binary | ios_base::in);

      if (ifs.is_open())
      {
        const bool b = ifs.good();
        assert(b);

        ifs.seekg(0, ios::end);
        const long size_temp = ifs.tellg();
        const long size_diff = size_temp - size_file_;

        if (size_diff == 0)
        {
          ifs.close();
          continue;
        }

#ifdef _DEBUG
        ofstream ofs;
#endif
        ofstream ofs_multipart;

        if (size_file_ == 0)
        {
          ifs.seekg(0, ios::beg);
#ifdef _DEBUG
          ofs.open(clone_file, ios_base::binary | ios_base::out);
#endif
        }
        else
        {
          ifs.seekg(size_file_, ios::beg);
#ifdef _DEBUG
          ofs.open(clone_file, ios_base::binary | ios_base::out | ios::app);
#endif
        }

        ofs_multipart.open("multipart.webm", ios_base::binary | ios_base::out);

        char* const buffer = new char[size_diff];
        ifs.read(buffer, size_diff);
        ifs.close();

        if (!codec_private_checked_)
        {
          //TODO(hwasoo): consider A_VORBIS is the last part of the buffer.
          for (int i = 0; i < size_diff - 10; ++i)
          {
            if ((*(buffer + i ) == 65)      // A
             && (*(buffer + i + 1) == 95)   // _
             && (*(buffer + i + 2) == 86)   // V
             && (*(buffer + i + 3) == 79)   // O
             && (*(buffer + i + 4) == 82)   // R
             && (*(buffer + i + 5) == 66)   // B
             && (*(buffer + i + 6) == 73)   // I
             && (*(buffer + i + 7) == 83))  // S
            {
              if ((*(buffer + i + 8) == -20)  // 0xEC - Void Element
               && (*(buffer + i + 9) == 95)   // 0x5F
               && (*(buffer + i + 10) == 61)) // 0x3D
              {
#ifdef _DEBUG
                cout << endl << " A_VORBIS and Void element found" << endl;
#endif
                post_ready = true;
                break;
              }

              if ((*(buffer + i + 8) == 99)     // 0x63
                && (*(buffer + i + 9) == -94))  // 0xA2
              {
#ifdef _DEBUG
                cout << endl << " A_VORBIS and Codec Private found" << endl;
#endif
                codec_private_checked_ = true;
                break;
              }
            }
          } // for loop
        } // if codec_private_checked_

        // Check live cluster size
        for (int i = 0; i < size_diff - 7; ++i)
        //for (int i = 0; i < size_diff - 8; ++i)
        {
          if ((*(buffer + i) == 31)        //0x1F - Cluster
           && (*(buffer + i + 1) == 67)    //0x43 - Cluster
           && (*(buffer + i + 2) == -74)   //0xB6 - Cluster
           && (*(buffer + i + 3) == 117)   //0x75 - Cluster
           && (*(buffer + i + 4) == 31)    //0x1F
           && (*(buffer + i + 5) == -1)    //0xFF
           && (*(buffer + i + 6) == -1)    //0xFF
           && (*(buffer + i + 7) == -1))   //0xFF
          {
#ifdef _DEBUG
            cout << endl << " Cluster size is undefined." << endl;
#endif
            post_ready = true;
            break;
          }
        }

        if (!post_ready)
        {
          size_file_ = size_temp;
#ifdef _DEBUG
          ofs.write(buffer, size_diff);
#endif
          ofs_multipart.write(buffer, size_diff);
          ofs_multipart.close();

          if (curl_)
          {
            curl_formadd(&formpost,
             &lastptr,
             CURLFORM_COPYNAME, "send_webm_stream",
             CURLFORM_FILE, "multipart.webm",
             CURLFORM_END);

            ostringstream os;
            os << "http://";

            const wchar_t* p = ip_address_;
            const wchar_t* q = p + wcslen(p);

            while (p != q)
              os << static_cast<char>(*p++);

            os << ':';

            p = port_;
            q = p + wcslen(p);

            while (p != q)
              os << static_cast<char>(*p++);

            const string str_ = os.str();
            const char* const str = str_.c_str();

            res_ = curl_easy_setopt(curl_, CURLOPT_URL, str);

            cout << "  multipart size(uploading): " << size_diff << " Byte(s)" << endl;
            res_ = curl_easy_setopt(curl_, CURLOPT_HTTPPOST, formpost);

            cout.width(10);
            cout.precision(4);

            if (cur_bps_ != 0.0)
              cout << "  estimated upload time in second : " << (size_diff / 1000) / cur_bps_ << endl;

            __int64 ctr1 = 0, ctr2 = 0, freq = 0;
            QueryPerformanceCounter((LARGE_INTEGER*)&ctr1);

            res_ = curl_easy_perform(curl_);

            if (res_ == CURLE_OK)
            {
              QueryPerformanceCounter((LARGE_INTEGER*)&ctr2);
              QueryPerformanceFrequency((LARGE_INTEGER*)&freq);
              const double span = (ctr2 - ctr1)* 1.0 /freq;
              cout << "  real upload time in second : " << span << endl;

              cur_bps_ = (double)(((double)size_diff / span) / 1000.0);

              if (!init_)
              {
                init_ = true;
                min_bps_ = cur_bps_;
              }

              if (cur_bps_ > max_bps_)
                max_bps_ = cur_bps_;

              if (cur_bps_ < min_bps_)
                min_bps_ = cur_bps_;

              cout << "  " << cur_bps_ << " KBps  <--- current speed" << endl;
              dq_.push_back(cur_bps_);

              if (dq_.size() > 100)
                dq_.pop_front();

              const double avg_bps = (accumulate(dq_.begin(), dq_.end(), 0.0)) / dq_.size();

              cout << "  Max(KBps) : " << max_bps_ << "   Avg(KBps) : " << avg_bps << "   Min(KBps) : " << min_bps_ << endl << endl;

              if (res_ == CURLE_COULDNT_CONNECT)
                cout << " Failed to connect() to host or proxy " << endl;
            }
            else
            {
              //TODO(hwasoo): need to exit out this thread
              ;
            }
          }
        }

        post_ready = false;
#ifdef _DEBUG
        ofs.close();
#endif
        delete [] buffer;
      }
    } // POST event
  } // for(;;)
} // ThreadProc

}<|endoftext|>


<commit_before>#include "TracerThread.h"

#include <thread>

#include "Logging.h"
#include "UprobesUnwindingVisitor.h"

namespace LinuxTracing {

// TODO: Refactor this huge method.
void TracerThread::Run(
    const std::shared_ptr<std::atomic<bool>>& exit_requested) {
  if (listener_ == nullptr) {
    ERROR("No listener set to TracerThread, aborting trace");
    return;
  }

  Reset();

  // perf_event_open refers to cores as "CPUs".

  // Record context switches from all cores for all processes.
  std::vector<int32_t> all_cpus;
  for (int32_t cpu = 0; cpu < GetNumCores(); ++cpu) {
    all_cpus.push_back(cpu);
  }

  // Record calls to dynamically instrumented functions and sample only on cores
  // in this process's cgroup's cpuset, as these are the only cores the process
  // will be scheduled on.
  std::vector<int32_t> cpuset_cpus = GetCpusetCpus(pid_);
  if (cpuset_cpus.empty()) {
    ERROR("Could not read cpuset");
    cpuset_cpus = all_cpus;
  }

  if (trace_context_switches_) {
    for (int32_t cpu : all_cpus) {
      int context_switch_fd = context_switch_event_open(-1, cpu);
      PerfEventRingBuffer context_switch_ring_buffer{context_switch_fd,
                                                     SMALL_RING_BUFFER_SIZE_KB};
      if (context_switch_ring_buffer.IsOpen()) {
        tracing_fds_.push_back(context_switch_fd);
        ring_buffers_.push_back(std::move(context_switch_ring_buffer));
      }
    }
  }

  auto uprobes_unwinding_visitor =
      std::make_unique<UprobesUnwindingVisitor>(ReadMaps(pid_));
  uprobes_unwinding_visitor->SetListener(listener_);
  // Switch between PerfEventProcessor and PerfEventProcessor2 here.
  // PerfEventProcessor2 is supposedly faster but assumes that events from the
  // same perf_event_open ring buffer are already sorted.
  uprobes_event_processor_ = std::make_shared<PerfEventProcessor2>(
      std::move(uprobes_unwinding_visitor));

  if (trace_instrumented_functions_) {
    for (const auto& function : instrumented_functions_) {
      for (int32_t cpu : cpuset_cpus) {
        int uprobes_fd = uprobes_stack_event_open(
            function.BinaryPath().c_str(), function.FileOffset(), -1, cpu);
        PerfEventRingBuffer uprobes_ring_buffer{uprobes_fd,
                                                BIG_RING_BUFFER_SIZE_KB};
        if (uprobes_ring_buffer.IsOpen()) {
          tracing_fds_.push_back(uprobes_fd);
          ring_buffers_.push_back(std::move(uprobes_ring_buffer));
          uprobes_fds_to_function_.emplace(uprobes_fd, &function);
        }

        int uretprobes_fd = uretprobes_event_open(
            function.BinaryPath().c_str(), function.FileOffset(), -1, cpu);
        tracing_fds_.push_back(uretprobes_fd);

        // Redirect uretprobes to the uprobes ring buffer to reduce number of
        // ring buffers and to coalesce closely related events.
        perf_event_redirect(uretprobes_fd, uprobes_fd);
      }
    }
  }

  for (int32_t cpu : cpuset_cpus) {
    int mmap_task_fd = mmap_task_event_open(-1, cpu);
    PerfEventRingBuffer mmap_task_ring_buffer{mmap_task_fd,
                                              SMALL_RING_BUFFER_SIZE_KB};
    if (mmap_task_ring_buffer.IsOpen()) {
      tracing_fds_.push_back(mmap_task_fd);
      ring_buffers_.push_back(std::move(mmap_task_ring_buffer));
    }
  }

  if (trace_callstacks_) {
    for (int32_t cpu : cpuset_cpus) {
      int sampling_fd = sample_event_open(sampling_period_ns_, -1, cpu);
      PerfEventRingBuffer sampling_ring_buffer{sampling_fd,
                                               BIG_RING_BUFFER_SIZE_KB};
      if (sampling_ring_buffer.IsOpen()) {
        tracing_fds_.push_back(sampling_fd);
        ring_buffers_.push_back(std::move(sampling_ring_buffer));
      }
    }
  }

  // Start recording events.
  for (int fd : tracing_fds_) {
    perf_event_enable(fd);
  }

  for (pid_t tid : ListThreads(pid_)) {
    // Keep threads in sync.
    listener_->OnTid(tid);
  }

  stats_.Reset();

  bool last_iteration_saw_events = false;
  std::thread deferred_events_thread(&TracerThread::ProcessDeferredEvents,
                                     this);

  while (!(*exit_requested)) {
    // Sleep if there was no new event in the last iteration so that we are not
    // constantly polling. Don't sleep so long that ring buffers overflow.
    // TODO: Refine this sleeping pattern, possibly using exponential backoff.
    if (!last_iteration_saw_events) {
      usleep(IDLE_TIME_ON_EMPTY_RING_BUFFERS_US);
    }

    last_iteration_saw_events = false;

    // Read and process events from all ring buffers. In order to ensure that no
    // buffer is read constantly while others overflow, we schedule the reading
    // using round-robin like scheduling.
    for (PerfEventRingBuffer& ring_buffer : ring_buffers_) {
      if (*exit_requested) {
        break;
      }

      // Read up to ROUND_ROBIN_POLLING_BATCH_SIZE (5) new events.
      // TODO: Some event types (e.g., stack samples) have a much longer
      //  processing time but are less frequent than others (e.g., context
      //  switches). Take this into account in our scheduling algorithm.
      for (int32_t read_from_this_buffer = 0;
           read_from_this_buffer < ROUND_ROBIN_POLLING_BATCH_SIZE;
           ++read_from_this_buffer) {
        if (*exit_requested) {
          break;
        }
        if (!ring_buffer.HasNewData()) {
          break;
        }

        last_iteration_saw_events = true;
        perf_event_header header;
        ring_buffer.ReadHeader(&header);

        // perf_event_header::type contains the type of record, e.g.,
        // PERF_RECORD_SAMPLE, PERF_RECORD_MMAP, etc., defined in enum
        // perf_event_type in linux/perf_event.h.
        switch (header.type) {
          case PERF_RECORD_SWITCH:
            // Note: as we are recording context switches on CPUs and not on
            // threads, we don't expect this type of record.
            ERROR(
                "Unexpected PERF_RECORD_SWITCH (only "
                "PERF_RECORD_SWITCH_CPU_WIDE are expected)");
            ProcessContextSwitchEvent(header, &ring_buffer);
            break;
          case PERF_RECORD_SWITCH_CPU_WIDE:
            ProcessContextSwitchCpuWideEvent(header, &ring_buffer);
            break;
          case PERF_RECORD_FORK:
            ProcessForkEvent(header, &ring_buffer);
            break;
          case PERF_RECORD_EXIT:
            ProcessExitEvent(header, &ring_buffer);
            break;
          case PERF_RECORD_MMAP:
            ProcessMmapEvent(header, &ring_buffer);
            break;
          case PERF_RECORD_SAMPLE:
            ProcessSampleEvent(header, &ring_buffer);
            break;
          case PERF_RECORD_LOST:
            ProcessLostEvent(header, &ring_buffer);
            break;
          default:
            ERROR("Unexpected perf_event_header::type: %u", header.type);
            ring_buffer.SkipRecord(header);
            break;
        }

        // Periodically print event statistics.
        PrintStatsIfTimerElapsed();
      }
    }
  }

  // Finish processing all deferred events.
  stop_deferred_thread_ = true;
  deferred_events_thread.join();
  uprobes_event_processor_->ProcessAllEvents();

  // Stop recording.
  for (int fd : tracing_fds_) {
    perf_event_disable(fd);
  }

  // Close the ring buffers.
  ring_buffers_.clear();

  // Close the file descriptors.
  for (int fd : tracing_fds_) {
    close(fd);
  }
}

void TracerThread::ProcessContextSwitchEvent(const perf_event_header& header,
                                             PerfEventRingBuffer* ring_buffer) {
  ContextSwitchPerfEvent event;
  ring_buffer->ConsumeRecord(header, &event.ring_buffer_record);
  pid_t tid = event.GetTid();
  uint16_t cpu = static_cast<uint16_t>(event.GetCpu());
  uint64_t time = event.GetTimestamp();

  if (event.IsSwitchOut()) {
    listener_->OnContextSwitchOut(ContextSwitchOut(tid, cpu, time));
  } else {
    listener_->OnContextSwitchIn(ContextSwitchIn(tid, cpu, time));
  }

  ++stats_.sched_switch_count;
}

void TracerThread::ProcessContextSwitchCpuWideEvent(
    const perf_event_header& header, PerfEventRingBuffer* ring_buffer) {
  SystemWideContextSwitchPerfEvent event;
  ring_buffer->ConsumeRecord(header, &event.ring_buffer_record);
  uint16_t cpu = static_cast<uint16_t>(event.GetCpu());
  uint64_t time = event.GetTimestamp();

  if (event.GetPrevTid() != 0) {
    ContextSwitchOut context_switch_out{event.GetPrevTid(), cpu, time};
    listener_->OnContextSwitchOut(context_switch_out);
  }
  if (event.GetNextTid() != 0) {
    ContextSwitchIn context_switch_in{event.GetNextTid(), cpu, time};
    listener_->OnContextSwitchIn(context_switch_in);
  }

  ++stats_.sched_switch_count;
}

void TracerThread::ProcessForkEvent(const perf_event_header& header,
                                    PerfEventRingBuffer* ring_buffer) {
  ForkPerfEvent event;
  ring_buffer->ConsumeRecord(header, &event.ring_buffer_record);

  if (event.GetPid() != pid_) {
    return;
  }

  // A new thread of the sampled process was spawned.
  listener_->OnTid(event.GetTid());
}

void TracerThread::ProcessExitEvent(const perf_event_header& header,
                                    PerfEventRingBuffer* ring_buffer) {
  ExitPerfEvent event;
  ring_buffer->ConsumeRecord(header, &event.ring_buffer_record);

  if (event.GetPid() != pid_) {
    return;
  }

  // Nothing to do.
}

void TracerThread::ProcessMmapEvent(const perf_event_header& header,
                                    PerfEventRingBuffer* ring_buffer) {
  pid_t pid = ReadMmapRecordPid(ring_buffer);
  ring_buffer->SkipRecord(header);

  if (pid != pid_) {
    return;
  }

  // There was a call to mmap with PROT_EXEC, hence refresh the maps.
  // This should happen rarely.
  auto event =
      std::make_unique<MapsPerfEvent>(MonotonicTimestampNs(), ReadMaps(pid_));
  event->SetOriginFileDescriptor(ring_buffer->GetFileDescriptor());
  DeferEvent(std::move(event));
}

void TracerThread::ProcessSampleEvent(const perf_event_header& header,
                                      PerfEventRingBuffer* ring_buffer) {
  int fd = ring_buffer->GetFileDescriptor();
  bool is_probe = uprobes_fds_to_function_.count(fd) > 0;
  constexpr size_t size_of_uretprobe = sizeof(perf_event_empty_sample);
  bool is_uretprobe = is_probe && (header.size == size_of_uretprobe);
  bool is_uprobe = is_probe && !is_uretprobe;

  pid_t pid;
  if (is_uretprobe) {
    pid = ReadUretprobesRecordPid(ring_buffer);
  } else {
    pid = ReadSampleRecordPid(ring_buffer);
  }

  if (pid != pid_) {
    ring_buffer->SkipRecord(header);
    return;
  }

  if (is_uprobe) {
    auto event =
        ConsumeSamplePerfEvent<UprobesWithStackPerfEvent>(ring_buffer, header);
    event->SetFunction(uprobes_fds_to_function_.at(fd));
    event->SetOriginFileDescriptor(fd);
    DeferEvent(std::move(event));
    ++stats_.uprobes_count;

  } else if (is_uretprobe) {
    auto event = make_unique_for_overwrite<UretprobesPerfEvent>();
    ring_buffer->ConsumeRecord(header, &event->ring_buffer_record);
    event->SetFunction(uprobes_fds_to_function_.at(fd));
    event->SetOriginFileDescriptor(fd);
    DeferEvent(std::move(event));
    ++stats_.uprobes_count;

  } else {
    auto event =
        ConsumeSamplePerfEvent<StackSamplePerfEvent>(ring_buffer, header);
    event->SetOriginFileDescriptor(fd);
    DeferEvent(std::move(event));
    ++stats_.sample_count;
  }
}

void TracerThread::ProcessLostEvent(const perf_event_header& header,
                                    PerfEventRingBuffer* ring_buffer) {
  LostPerfEvent event;
  ring_buffer->ConsumeRecord(header, &event.ring_buffer_record);
  LOG("Lost %lu events", event.GetNumLost());
}

void TracerThread::DeferEvent(std::unique_ptr<PerfEvent> event) {
  std::lock_guard<std::mutex> lock(deferred_events_mutex_);
  deferred_events_.emplace_back(std::move(event));
}

std::vector<std::unique_ptr<PerfEvent>> TracerThread::ConsumeDeferredEvents() {
  std::lock_guard<std::mutex> lock(deferred_events_mutex_);
  std::vector<std::unique_ptr<PerfEvent>> events(std::move(deferred_events_));
  deferred_events_.clear();
  return events;
}

void TracerThread::ProcessDeferredEvents() {
  bool should_exit = false;
  while (!should_exit) {
    // When "should_exit" becomes true, we know that we have stopped generating
    // deferred events. The last iteration will consume all remaining events.
    should_exit = stop_deferred_thread_;
    std::vector<std::unique_ptr<PerfEvent>> events = ConsumeDeferredEvents();
    if (events.empty()) {
      // TODO: use a wait/notify mechanism instead of check/sleep.
      usleep(IDLE_TIME_ON_EMPTY_DEFERRED_EVENTS_US);
    } else {
      for (auto& event : events) {
        int fd = event->GetOriginFileDescriptor();
        uprobes_event_processor_->AddEvent(fd, std::move(event));
      }

      uprobes_event_processor_->ProcessOldEvents();
    }
  }
}

void TracerThread::Reset() {
  tracing_fds_.clear();
  ring_buffers_.clear();
  uprobes_fds_to_function_.clear();
  deferred_events_.clear();
  stop_deferred_thread_ = false;
}

void TracerThread::PrintStatsIfTimerElapsed() {
  constexpr uint64_t EVENT_COUNT_WINDOW_S = 5;

  if (stats_.event_count_begin_ns + EVENT_COUNT_WINDOW_S * 1'000'000'000 <
      MonotonicTimestampNs()) {
    LOG("Events per second (last %lu s): "
        "sched switches: %lu; "
        "samples: %lu; "
        "u(ret)probes: %lu",
        EVENT_COUNT_WINDOW_S, stats_.sched_switch_count / EVENT_COUNT_WINDOW_S,
        stats_.sample_count / EVENT_COUNT_WINDOW_S,
        stats_.uprobes_count / EVENT_COUNT_WINDOW_S);
    stats_.Reset();
  }
}

}  // namespace LinuxTracing
<commit_msg>Increase size of context switch ring buffers<commit_after>#include "TracerThread.h"

#include <thread>

#include "Logging.h"
#include "UprobesUnwindingVisitor.h"

namespace LinuxTracing {

// TODO: Refactor this huge method.
void TracerThread::Run(
    const std::shared_ptr<std::atomic<bool>>& exit_requested) {
  if (listener_ == nullptr) {
    ERROR("No listener set to TracerThread, aborting trace");
    return;
  }

  Reset();

  // perf_event_open refers to cores as "CPUs".

  // Record context switches from all cores for all processes.
  std::vector<int32_t> all_cpus;
  for (int32_t cpu = 0; cpu < GetNumCores(); ++cpu) {
    all_cpus.push_back(cpu);
  }

  // Record calls to dynamically instrumented functions and sample only on cores
  // in this process's cgroup's cpuset, as these are the only cores the process
  // will be scheduled on.
  std::vector<int32_t> cpuset_cpus = GetCpusetCpus(pid_);
  if (cpuset_cpus.empty()) {
    ERROR("Could not read cpuset");
    cpuset_cpus = all_cpus;
  }

  if (trace_context_switches_) {
    for (int32_t cpu : all_cpus) {
      int context_switch_fd = context_switch_event_open(-1, cpu);
      PerfEventRingBuffer context_switch_ring_buffer{context_switch_fd,
                                                     SMALL_RING_BUFFER_SIZE_KB};
      if (context_switch_ring_buffer.IsOpen()) {
        tracing_fds_.push_back(context_switch_fd);
        ring_buffers_.push_back(std::move(context_switch_ring_buffer));
      }
    }
  }

  auto uprobes_unwinding_visitor =
      std::make_unique<UprobesUnwindingVisitor>(ReadMaps(pid_));
  uprobes_unwinding_visitor->SetListener(listener_);
  // Switch between PerfEventProcessor and PerfEventProcessor2 here.
  // PerfEventProcessor2 is supposedly faster but assumes that events from the
  // same perf_event_open ring buffer are already sorted.
  uprobes_event_processor_ = std::make_shared<PerfEventProcessor2>(
      std::move(uprobes_unwinding_visitor));

  if (trace_instrumented_functions_) {
    for (const auto& function : instrumented_functions_) {
      for (int32_t cpu : cpuset_cpus) {
        int uprobes_fd = uprobes_stack_event_open(
            function.BinaryPath().c_str(), function.FileOffset(), -1, cpu);
        PerfEventRingBuffer uprobes_ring_buffer{uprobes_fd,
                                                BIG_RING_BUFFER_SIZE_KB};
        if (uprobes_ring_buffer.IsOpen()) {
          tracing_fds_.push_back(uprobes_fd);
          ring_buffers_.push_back(std::move(uprobes_ring_buffer));
          uprobes_fds_to_function_.emplace(uprobes_fd, &function);
        }

        int uretprobes_fd = uretprobes_event_open(
            function.BinaryPath().c_str(), function.FileOffset(), -1, cpu);
        tracing_fds_.push_back(uretprobes_fd);

        // Redirect uretprobes to the uprobes ring buffer to reduce number of
        // ring buffers and to coalesce closely related events.
        perf_event_redirect(uretprobes_fd, uprobes_fd);
      }
    }
  }

  for (int32_t cpu : cpuset_cpus) {
    int mmap_task_fd = mmap_task_event_open(-1, cpu);
    PerfEventRingBuffer mmap_task_ring_buffer{mmap_task_fd,
                                              BIG_RING_BUFFER_SIZE_KB};
    if (mmap_task_ring_buffer.IsOpen()) {
      tracing_fds_.push_back(mmap_task_fd);
      ring_buffers_.push_back(std::move(mmap_task_ring_buffer));
    }
  }

  if (trace_callstacks_) {
    for (int32_t cpu : cpuset_cpus) {
      int sampling_fd = sample_event_open(sampling_period_ns_, -1, cpu);
      PerfEventRingBuffer sampling_ring_buffer{sampling_fd,
                                               BIG_RING_BUFFER_SIZE_KB};
      if (sampling_ring_buffer.IsOpen()) {
        tracing_fds_.push_back(sampling_fd);
        ring_buffers_.push_back(std::move(sampling_ring_buffer));
      }
    }
  }

  // Start recording events.
  for (int fd : tracing_fds_) {
    perf_event_enable(fd);
  }

  for (pid_t tid : ListThreads(pid_)) {
    // Keep threads in sync.
    listener_->OnTid(tid);
  }

  stats_.Reset();

  bool last_iteration_saw_events = false;
  std::thread deferred_events_thread(&TracerThread::ProcessDeferredEvents,
                                     this);

  while (!(*exit_requested)) {
    // Sleep if there was no new event in the last iteration so that we are not
    // constantly polling. Don't sleep so long that ring buffers overflow.
    // TODO: Refine this sleeping pattern, possibly using exponential backoff.
    if (!last_iteration_saw_events) {
      usleep(IDLE_TIME_ON_EMPTY_RING_BUFFERS_US);
    }

    last_iteration_saw_events = false;

    // Read and process events from all ring buffers. In order to ensure that no
    // buffer is read constantly while others overflow, we schedule the reading
    // using round-robin like scheduling.
    for (PerfEventRingBuffer& ring_buffer : ring_buffers_) {
      if (*exit_requested) {
        break;
      }

      // Read up to ROUND_ROBIN_POLLING_BATCH_SIZE (5) new events.
      // TODO: Some event types (e.g., stack samples) have a much longer
      //  processing time but are less frequent than others (e.g., context
      //  switches). Take this into account in our scheduling algorithm.
      for (int32_t read_from_this_buffer = 0;
           read_from_this_buffer < ROUND_ROBIN_POLLING_BATCH_SIZE;
           ++read_from_this_buffer) {
        if (*exit_requested) {
          break;
        }
        if (!ring_buffer.HasNewData()) {
          break;
        }

        last_iteration_saw_events = true;
        perf_event_header header;
        ring_buffer.ReadHeader(&header);

        // perf_event_header::type contains the type of record, e.g.,
        // PERF_RECORD_SAMPLE, PERF_RECORD_MMAP, etc., defined in enum
        // perf_event_type in linux/perf_event.h.
        switch (header.type) {
          case PERF_RECORD_SWITCH:
            // Note: as we are recording context switches on CPUs and not on
            // threads, we don't expect this type of record.
            ERROR(
                "Unexpected PERF_RECORD_SWITCH (only "
                "PERF_RECORD_SWITCH_CPU_WIDE are expected)");
            ProcessContextSwitchEvent(header, &ring_buffer);
            break;
          case PERF_RECORD_SWITCH_CPU_WIDE:
            ProcessContextSwitchCpuWideEvent(header, &ring_buffer);
            break;
          case PERF_RECORD_FORK:
            ProcessForkEvent(header, &ring_buffer);
            break;
          case PERF_RECORD_EXIT:
            ProcessExitEvent(header, &ring_buffer);
            break;
          case PERF_RECORD_MMAP:
            ProcessMmapEvent(header, &ring_buffer);
            break;
          case PERF_RECORD_SAMPLE:
            ProcessSampleEvent(header, &ring_buffer);
            break;
          case PERF_RECORD_LOST:
            ProcessLostEvent(header, &ring_buffer);
            break;
          default:
            ERROR("Unexpected perf_event_header::type: %u", header.type);
            ring_buffer.SkipRecord(header);
            break;
        }

        // Periodically print event statistics.
        PrintStatsIfTimerElapsed();
      }
    }
  }

  // Finish processing all deferred events.
  stop_deferred_thread_ = true;
  deferred_events_thread.join();
  uprobes_event_processor_->ProcessAllEvents();

  // Stop recording.
  for (int fd : tracing_fds_) {
    perf_event_disable(fd);
  }

  // Close the ring buffers.
  ring_buffers_.clear();

  // Close the file descriptors.
  for (int fd : tracing_fds_) {
    close(fd);
  }
}

void TracerThread::ProcessContextSwitchEvent(const perf_event_header& header,
                                             PerfEventRingBuffer* ring_buffer) {
  ContextSwitchPerfEvent event;
  ring_buffer->ConsumeRecord(header, &event.ring_buffer_record);
  pid_t tid = event.GetTid();
  uint16_t cpu = static_cast<uint16_t>(event.GetCpu());
  uint64_t time = event.GetTimestamp();

  if (event.IsSwitchOut()) {
    listener_->OnContextSwitchOut(ContextSwitchOut(tid, cpu, time));
  } else {
    listener_->OnContextSwitchIn(ContextSwitchIn(tid, cpu, time));
  }

  ++stats_.sched_switch_count;
}

void TracerThread::ProcessContextSwitchCpuWideEvent(
    const perf_event_header& header, PerfEventRingBuffer* ring_buffer) {
  SystemWideContextSwitchPerfEvent event;
  ring_buffer->ConsumeRecord(header, &event.ring_buffer_record);
  uint16_t cpu = static_cast<uint16_t>(event.GetCpu());
  uint64_t time = event.GetTimestamp();

  if (event.GetPrevTid() != 0) {
    ContextSwitchOut context_switch_out{event.GetPrevTid(), cpu, time};
    listener_->OnContextSwitchOut(context_switch_out);
  }
  if (event.GetNextTid() != 0) {
    ContextSwitchIn context_switch_in{event.GetNextTid(), cpu, time};
    listener_->OnContextSwitchIn(context_switch_in);
  }

  ++stats_.sched_switch_count;
}

void TracerThread::ProcessForkEvent(const perf_event_header& header,
                                    PerfEventRingBuffer* ring_buffer) {
  ForkPerfEvent event;
  ring_buffer->ConsumeRecord(header, &event.ring_buffer_record);

  if (event.GetPid() != pid_) {
    return;
  }

  // A new thread of the sampled process was spawned.
  listener_->OnTid(event.GetTid());
}

void TracerThread::ProcessExitEvent(const perf_event_header& header,
                                    PerfEventRingBuffer* ring_buffer) {
  ExitPerfEvent event;
  ring_buffer->ConsumeRecord(header, &event.ring_buffer_record);

  if (event.GetPid() != pid_) {
    return;
  }

  // Nothing to do.
}

void TracerThread::ProcessMmapEvent(const perf_event_header& header,
                                    PerfEventRingBuffer* ring_buffer) {
  pid_t pid = ReadMmapRecordPid(ring_buffer);
  ring_buffer->SkipRecord(header);

  if (pid != pid_) {
    return;
  }

  // There was a call to mmap with PROT_EXEC, hence refresh the maps.
  // This should happen rarely.
  auto event =
      std::make_unique<MapsPerfEvent>(MonotonicTimestampNs(), ReadMaps(pid_));
  event->SetOriginFileDescriptor(ring_buffer->GetFileDescriptor());
  DeferEvent(std::move(event));
}

void TracerThread::ProcessSampleEvent(const perf_event_header& header,
                                      PerfEventRingBuffer* ring_buffer) {
  int fd = ring_buffer->GetFileDescriptor();
  bool is_probe = uprobes_fds_to_function_.count(fd) > 0;
  constexpr size_t size_of_uretprobe = sizeof(perf_event_empty_sample);
  bool is_uretprobe = is_probe && (header.size == size_of_uretprobe);
  bool is_uprobe = is_probe && !is_uretprobe;

  pid_t pid;
  if (is_uretprobe) {
    pid = ReadUretprobesRecordPid(ring_buffer);
  } else {
    pid = ReadSampleRecordPid(ring_buffer);
  }

  if (pid != pid_) {
    ring_buffer->SkipRecord(header);
    return;
  }

  if (is_uprobe) {
    auto event =
        ConsumeSamplePerfEvent<UprobesWithStackPerfEvent>(ring_buffer, header);
    event->SetFunction(uprobes_fds_to_function_.at(fd));
    event->SetOriginFileDescriptor(fd);
    DeferEvent(std::move(event));
    ++stats_.uprobes_count;

  } else if (is_uretprobe) {
    auto event = make_unique_for_overwrite<UretprobesPerfEvent>();
    ring_buffer->ConsumeRecord(header, &event->ring_buffer_record);
    event->SetFunction(uprobes_fds_to_function_.at(fd));
    event->SetOriginFileDescriptor(fd);
    DeferEvent(std::move(event));
    ++stats_.uprobes_count;

  } else {
    auto event =
        ConsumeSamplePerfEvent<StackSamplePerfEvent>(ring_buffer, header);
    event->SetOriginFileDescriptor(fd);
    DeferEvent(std::move(event));
    ++stats_.sample_count;
  }
}

void TracerThread::ProcessLostEvent(const perf_event_header& header,
                                    PerfEventRingBuffer* ring_buffer) {
  LostPerfEvent event;
  ring_buffer->ConsumeRecord(header, &event.ring_buffer_record);
  LOG("Lost %lu events", event.GetNumLost());
}

void TracerThread::DeferEvent(std::unique_ptr<PerfEvent> event) {
  std::lock_guard<std::mutex> lock(deferred_events_mutex_);
  deferred_events_.emplace_back(std::move(event));
}

std::vector<std::unique_ptr<PerfEvent>> TracerThread::ConsumeDeferredEvents() {
  std::lock_guard<std::mutex> lock(deferred_events_mutex_);
  std::vector<std::unique_ptr<PerfEvent>> events(std::move(deferred_events_));
  deferred_events_.clear();
  return events;
}

void TracerThread::ProcessDeferredEvents() {
  bool should_exit = false;
  while (!should_exit) {
    // When "should_exit" becomes true, we know that we have stopped generating
    // deferred events. The last iteration will consume all remaining events.
    should_exit = stop_deferred_thread_;
    std::vector<std::unique_ptr<PerfEvent>> events = ConsumeDeferredEvents();
    if (events.empty()) {
      // TODO: use a wait/notify mechanism instead of check/sleep.
      usleep(IDLE_TIME_ON_EMPTY_DEFERRED_EVENTS_US);
    } else {
      for (auto& event : events) {
        int fd = event->GetOriginFileDescriptor();
        uprobes_event_processor_->AddEvent(fd, std::move(event));
      }

      uprobes_event_processor_->ProcessOldEvents();
    }
  }
}

void TracerThread::Reset() {
  tracing_fds_.clear();
  ring_buffers_.clear();
  uprobes_fds_to_function_.clear();
  deferred_events_.clear();
  stop_deferred_thread_ = false;
}

void TracerThread::PrintStatsIfTimerElapsed() {
  constexpr uint64_t EVENT_COUNT_WINDOW_S = 5;

  if (stats_.event_count_begin_ns + EVENT_COUNT_WINDOW_S * 1'000'000'000 <
      MonotonicTimestampNs()) {
    LOG("Events per second (last %lu s): "
        "sched switches: %lu; "
        "samples: %lu; "
        "u(ret)probes: %lu",
        EVENT_COUNT_WINDOW_S, stats_.sched_switch_count / EVENT_COUNT_WINDOW_S,
        stats_.sample_count / EVENT_COUNT_WINDOW_S,
        stats_.uprobes_count / EVENT_COUNT_WINDOW_S);
    stats_.Reset();
  }
}

}  // namespace LinuxTracing
<|endoftext|>


<commit_before>//===- lib/Clang/MappedStmt.cpp -------------------------------------------===//
//
//                                    SeeC
//
// This file is distributed under The MIT License (MIT). See LICENSE.TXT for
// details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
//===----------------------------------------------------------------------===//

#include "MappedLLVMValue.hpp"

#include "seec/Clang/MappedAST.hpp"
#include "seec/Clang/MappedModule.hpp"
#include "seec/Clang/MappedStmt.hpp"
#include "seec/Util/ModuleIndex.hpp"

#include "llvm/IR/Constants.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Value.h"

namespace seec {

namespace seec_clang {

seec::Maybe<MappedStmt::Type>
getTypeFromMDString(llvm::MDString const *MDStr)
{
  if (!MDStr)
    return seec::Maybe<MappedStmt::Type>();

  auto Str = MDStr->getString();

  if (Str.equals("lvalsimple"))
    return MappedStmt::Type::LValSimple;
  else if (Str.equals("rvalscalar"))
    return MappedStmt::Type::RValScalar;
  else if (Str.equals("rvalaggregate"))
    return MappedStmt::Type::RValAggregate;

  return seec::Maybe<MappedStmt::Type>();
}

std::unique_ptr<MappedStmt>
MappedStmt::fromMetadata(llvm::MDNode *RootMD,
                         MappedModule const &Module)
{
  if (RootMD->getNumOperands() != 4u)
    return nullptr;

  // Get the type of the mapping.
  auto TypeMD = llvm::dyn_cast_or_null<llvm::MDString>(RootMD->getOperand(0u));
  auto Type = getTypeFromMDString(TypeMD);
  if (!Type.assigned())
    return nullptr;

  // Find the clang::Stmt.
  auto StmtIdentMD = llvm::dyn_cast_or_null<llvm::MDNode>
                                           (RootMD->getOperand(1u));
  if (!StmtIdentMD)
    return nullptr;

  auto ASTAndStmt = Module.getASTAndStmt(StmtIdentMD);
  assert(ASTAndStmt.first && ASTAndStmt.second);

  // Find the values.
  auto MapVal1MD = llvm::dyn_cast_or_null<llvm::MDNode>(RootMD->getOperand(2u));
  auto MapVal2MD = llvm::dyn_cast_or_null<llvm::MDNode>(RootMD->getOperand(3u));

  auto Val1 = seec::cm::getMappedValueFromMD(MapVal1MD,
                                             Module.getModuleIndex());
  auto Val2 = seec::cm::getMappedValueFromMD(MapVal2MD,
                                             Module.getModuleIndex());
  if (!Val1)
    return nullptr;

  return std::unique_ptr<MappedStmt>(new MappedStmt(Type.get<0>(),
                                                    ASTAndStmt.first,
                                                    ASTAndStmt.second,
                                                    Val1,
                                                    Val2));
}

} // namespace seec_clang (in seec)

} // namespace seec
<commit_msg>Print debugging information to errs if MappedStmt::fromMetadata() fails.<commit_after>//===- lib/Clang/MappedStmt.cpp -------------------------------------------===//
//
//                                    SeeC
//
// This file is distributed under The MIT License (MIT). See LICENSE.TXT for
// details.
//
//===----------------------------------------------------------------------===//
///
/// \file
///
//===----------------------------------------------------------------------===//

#include "MappedLLVMValue.hpp"

#include "seec/Clang/MappedAST.hpp"
#include "seec/Clang/MappedModule.hpp"
#include "seec/Clang/MappedStmt.hpp"
#include "seec/Util/ModuleIndex.hpp"

#include "llvm/IR/Constants.h"
#include "llvm/IR/Metadata.h"
#include "llvm/IR/Value.h"

namespace seec {

namespace seec_clang {

seec::Maybe<MappedStmt::Type>
getTypeFromMDString(llvm::MDString const *MDStr)
{
  if (!MDStr)
    return seec::Maybe<MappedStmt::Type>();

  auto Str = MDStr->getString();

  if (Str.equals("lvalsimple"))
    return MappedStmt::Type::LValSimple;
  else if (Str.equals("rvalscalar"))
    return MappedStmt::Type::RValScalar;
  else if (Str.equals("rvalaggregate"))
    return MappedStmt::Type::RValAggregate;

  return seec::Maybe<MappedStmt::Type>();
}

std::unique_ptr<MappedStmt>
MappedStmt::fromMetadata(llvm::MDNode *RootMD,
                         MappedModule const &Module)
{
  if (RootMD->getNumOperands() != 4u) {
    llvm::errs() << "MappedStmt::fromMetadata(): "
                 << "invalid number of operands.\n";

    return nullptr;
  }

  // Get the type of the mapping.
  auto TypeMD = llvm::dyn_cast_or_null<llvm::MDString>(RootMD->getOperand(0u));
  auto Type = getTypeFromMDString(TypeMD);
  if (!Type.assigned()) {
    llvm::errs() << "MappedStmt::fromMetadata(): "
                 << "failed to get type.\n";

    return nullptr;
  }

  // Find the clang::Stmt.
  auto StmtIdentMD = llvm::dyn_cast_or_null<llvm::MDNode>
                                           (RootMD->getOperand(1u));
  if (!StmtIdentMD) {
    llvm::errs() << "MappedStmt::fromMetadata(): "
                 << "Stmt identifier is not an MDNode.\n";

    return nullptr;
  }

  auto ASTAndStmt = Module.getASTAndStmt(StmtIdentMD);
  assert(ASTAndStmt.first && ASTAndStmt.second);

  // Find the values.
  auto MapVal1MD = llvm::dyn_cast_or_null<llvm::MDNode>(RootMD->getOperand(2u));
  auto MapVal2MD = llvm::dyn_cast_or_null<llvm::MDNode>(RootMD->getOperand(3u));

  auto Val1 = seec::cm::getMappedValueFromMD(MapVal1MD,
                                             Module.getModuleIndex());
  auto Val2 = seec::cm::getMappedValueFromMD(MapVal2MD,
                                             Module.getModuleIndex());
  if (!Val1) {
    llvm::errs() << "MappedStmt::fromMetadata(): "
                 << "llvm::Value not found.\n";

    return nullptr;
  }

  return std::unique_ptr<MappedStmt>(new MappedStmt(Type.get<0>(),
                                                    ASTAndStmt.first,
                                                    ASTAndStmt.second,
                                                    Val1,
                                                    Val2));
}

} // namespace seec_clang (in seec)

} // namespace seec
<|endoftext|>


<commit_before>//===--- CGBlocks.cpp - Emit LLVM Code for declarations -------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This contains code to emit blocks.
//
//===----------------------------------------------------------------------===//

#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "llvm/Module.h"
#include "llvm/Target/TargetData.h"

#include <algorithm>

using namespace clang;
using namespace CodeGen;

enum {
  BLOCK_NEEDS_FREE =        (1 << 24),
  BLOCK_HAS_COPY_DISPOSE =  (1 << 25),
  BLOCK_HAS_CXX_OBJ =       (1 << 26),
  BLOCK_IS_GC =             (1 << 27),
  BLOCK_IS_GLOBAL =         (1 << 28),
  BLOCK_HAS_DESCRIPTOR =    (1 << 29)
};

llvm::Constant *CodeGenFunction::BuildDescriptorBlockDecl() {
  // FIXME: Push up.
  bool BlockHasCopyDispose = false;

  const llvm::PointerType *PtrToInt8Ty
    = llvm::PointerType::getUnqual(llvm::Type::Int8Ty);
  const llvm::Type *UnsignedLongTy
    = CGM.getTypes().ConvertType(getContext().UnsignedLongTy);
  llvm::Constant *C;
  std::vector<llvm::Constant*> Elts;

  // reserved
  C = llvm::ConstantInt::get(UnsignedLongTy, 0);
  Elts.push_back(C);

  // Size
  int sz = CGM.getTargetData()
    .getTypeStoreSizeInBits(CGM.getGenericBlockLiteralType()) / 8;
  C = llvm::ConstantInt::get(UnsignedLongTy, sz);
  Elts.push_back(C);

  if (BlockHasCopyDispose) {
    // copy_func_helper_decl
    C = llvm::ConstantInt::get(UnsignedLongTy, 0);
    C = llvm::ConstantExpr::getBitCast(C, PtrToInt8Ty);
    Elts.push_back(C);

    // destroy_func_decl
    C = llvm::ConstantInt::get(UnsignedLongTy, 0);
    C = llvm::ConstantExpr::getBitCast(C, PtrToInt8Ty);
    Elts.push_back(C);
  }

  C = llvm::ConstantStruct::get(Elts);

  C = new llvm::GlobalVariable(C->getType(), true,
                               llvm::GlobalValue::InternalLinkage,
                               C, "__block_descriptor_tmp", &CGM.getModule());
  return C;
}

llvm::Constant *CodeGenModule::getNSConcreteGlobalBlock() {
  if (NSConcreteGlobalBlock)
    return NSConcreteGlobalBlock;

  const llvm::PointerType *PtrToInt8Ty
    = llvm::PointerType::getUnqual(llvm::Type::Int8Ty);
  // FIXME: We should have a CodeGenModule::AddRuntimeVariable that does the
  // same thing as CreateRuntimeFunction if there's already a variable with
  // the same name.
  NSConcreteGlobalBlock
    = new llvm::GlobalVariable(PtrToInt8Ty, false,
                               llvm::GlobalValue::ExternalLinkage,
                               0, "_NSConcreteGlobalBlock",
                               &getModule());

  return NSConcreteGlobalBlock;
}

llvm::Constant *CodeGenModule::getNSConcreteStackBlock() {
  if (NSConcreteStackBlock)
    return NSConcreteStackBlock;

  const llvm::PointerType *PtrToInt8Ty
    = llvm::PointerType::getUnqual(llvm::Type::Int8Ty);
  // FIXME: We should have a CodeGenModule::AddRuntimeVariable that does the
  // same thing as CreateRuntimeFunction if there's already a variable with
  // the same name.
  NSConcreteStackBlock
    = new llvm::GlobalVariable(PtrToInt8Ty, false,
                               llvm::GlobalValue::ExternalLinkage,
                               0, "_NSConcreteStackBlock",
                               &getModule());

  return NSConcreteStackBlock;
}

llvm::Constant *CodeGenFunction::BuildBlockLiteralTmp(const BlockExpr *BE) {
  // FIXME: Push up
  bool BlockHasCopyDispose = false;
  bool insideFunction = false;
  bool BlockRefDeclList = false;
  bool BlockByrefDeclList = false;

  std::vector<llvm::Constant*> Elts;
  llvm::Constant *C;

  {
    // C = BuildBlockStructInitlist();
    unsigned int flags = BLOCK_HAS_DESCRIPTOR;

    if (BlockHasCopyDispose)
      flags |= BLOCK_HAS_COPY_DISPOSE;

    // __isa
    C = CGM.getNSConcreteStackBlock();
    if (!insideFunction ||
        (!BlockRefDeclList && !BlockByrefDeclList)) {
      C = CGM.getNSConcreteGlobalBlock();
      flags |= BLOCK_IS_GLOBAL;
    }
    const llvm::PointerType *PtrToInt8Ty
      = llvm::PointerType::getUnqual(llvm::Type::Int8Ty);
    C = llvm::ConstantExpr::getBitCast(C, PtrToInt8Ty);
    Elts.push_back(C);

    // __flags
    const llvm::IntegerType *IntTy = cast<llvm::IntegerType>(
      CGM.getTypes().ConvertType(CGM.getContext().IntTy));
    C = llvm::ConstantInt::get(IntTy, flags);
    Elts.push_back(C);

    // __reserved
    C = llvm::ConstantInt::get(IntTy, 0);
    Elts.push_back(C);

    // __FuncPtr
    const char *Name;
    if (const NamedDecl *ND = dyn_cast<NamedDecl>(CurFuncDecl))
      Name = ND->getNameAsCString();
    BlockInfo Info(0, Name);
    llvm::Function *Fn = CodeGenFunction(*this).GenerateBlockFunction(BE, Info);
    Elts.push_back(Fn);

    // __descriptor
    Elts.push_back(BuildDescriptorBlockDecl());

    // FIXME: Add block_original_ref_decl_list and block_byref_decl_list.
  }

  C = llvm::ConstantStruct::get(Elts);

  char Name[32];
  sprintf(Name, "__block_holder_tmp_%d", CGM.getGlobalUniqueCount());
  C = new llvm::GlobalVariable(C->getType(), true,
                               llvm::GlobalValue::InternalLinkage,
                               C, Name, &CGM.getModule());
  return C;
}

const llvm::Type *CodeGenModule::getBlockDescriptorType() {
  if (BlockDescriptorType)
    return BlockDescriptorType;

  const llvm::Type *UnsignedLongTy =
    getTypes().ConvertType(getContext().UnsignedLongTy);

  // struct __block_descriptor {
  //   unsigned long reserved;
  //   unsigned long block_size;
  // };
  BlockDescriptorType = llvm::StructType::get(UnsignedLongTy,
                                              UnsignedLongTy,
                                              NULL);

  getModule().addTypeName("struct.__block_descriptor",
                          BlockDescriptorType);

  return BlockDescriptorType;
}

const llvm::Type *
CodeGenModule::getGenericBlockLiteralType() {
  if (GenericBlockLiteralType)
    return GenericBlockLiteralType;

  const llvm::Type *Int8PtrTy =
    llvm::PointerType::getUnqual(llvm::Type::Int8Ty);

  const llvm::Type *BlockDescPtrTy =
    llvm::PointerType::getUnqual(getBlockDescriptorType());

  const llvm::IntegerType *IntTy = cast<llvm::IntegerType>(
    getTypes().ConvertType(getContext().IntTy));

  // struct __block_literal_generic {
  //   void *isa;
  //   int flags;
  //   int reserved;
  //   void (*invoke)(void *);
  //   struct __block_descriptor *descriptor;
  // };
  GenericBlockLiteralType = llvm::StructType::get(Int8PtrTy,
                                                  IntTy,
                                                  IntTy,
                                                  Int8PtrTy,
                                                  BlockDescPtrTy,
                                                  NULL);

  getModule().addTypeName("struct.__block_literal_generic",
                          GenericBlockLiteralType);

  return GenericBlockLiteralType;
}

/// getBlockFunctionType - Given a BlockPointerType, will return the
/// function type for the block, including the first block literal argument.
static QualType getBlockFunctionType(ASTContext &Ctx,
                                     const BlockPointerType *BPT) {
  const FunctionTypeProto *FTy = cast<FunctionTypeProto>(BPT->getPointeeType());

  llvm::SmallVector<QualType, 8> Types;
  Types.push_back(Ctx.getPointerType(Ctx.VoidTy));

  for (FunctionTypeProto::arg_type_iterator i = FTy->arg_type_begin(),
       e = FTy->arg_type_end(); i != e; ++i)
    Types.push_back(*i);

  return Ctx.getFunctionType(FTy->getResultType(),
                             &Types[0], Types.size(),
                             FTy->isVariadic(), 0);
}

RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr* E) {
  const BlockPointerType *BPT =
    E->getCallee()->getType()->getAsBlockPointerType();

  llvm::Value *Callee = EmitScalarExpr(E->getCallee());

  // Get a pointer to the generic block literal.
  const llvm::Type *BlockLiteralTy =
    llvm::PointerType::getUnqual(CGM.getGenericBlockLiteralType());

  // Bitcast the callee to a block literal.
  llvm::Value *BlockLiteral =
    Builder.CreateBitCast(Callee, BlockLiteralTy, "block.literal");

  // Get the function pointer from the literal.
  llvm::Value *FuncPtr = Builder.CreateStructGEP(BlockLiteral, 3, "tmp");
  llvm::Value *Func = Builder.CreateLoad(FuncPtr, FuncPtr, "tmp");

  // Cast the function pointer to the right type.
  const llvm::Type *BlockFTy =
    ConvertType(getBlockFunctionType(getContext(), BPT));
  const llvm::Type *BlockFTyPtr = llvm::PointerType::getUnqual(BlockFTy);
  Func = Builder.CreateBitCast(Func, BlockFTyPtr);

  BlockLiteral =
    Builder.CreateBitCast(BlockLiteral,
                          llvm::PointerType::getUnqual(llvm::Type::Int8Ty),
                          "tmp");

  // Add the block literal.
  QualType VoidPtrTy = getContext().getPointerType(getContext().VoidTy);
  CallArgList Args;
  Args.push_back(std::make_pair(RValue::get(BlockLiteral), VoidPtrTy));

  // And the rest of the arguments.
  for (CallExpr::const_arg_iterator i = E->arg_begin(), e = E->arg_end();
       i != e; ++i)
    Args.push_back(std::make_pair(EmitAnyExprToTemp(*i),
                                  i->getType()));

  // And call the block.
  return EmitCall(CGM.getTypes().getFunctionInfo(E->getType(), Args),
                  Func, Args);
}

llvm::Constant *
CodeGenModule::GetAddrOfGlobalBlock(const BlockExpr *BE, const char * n) {
  // Generate the block descriptor.
  const llvm::Type *UnsignedLongTy = Types.ConvertType(Context.UnsignedLongTy);
  const llvm::IntegerType *IntTy = cast<llvm::IntegerType>(
    getTypes().ConvertType(getContext().IntTy));

  llvm::Constant *DescriptorFields[2];

  // Reserved
  DescriptorFields[0] = llvm::Constant::getNullValue(UnsignedLongTy);

  // Block literal size. For global blocks we just use the size of the generic
  // block literal struct.
  uint64_t BlockLiteralSize =
    TheTargetData.getTypeStoreSizeInBits(getGenericBlockLiteralType()) / 8;
  DescriptorFields[1] = llvm::ConstantInt::get(UnsignedLongTy,BlockLiteralSize);

  llvm::Constant *DescriptorStruct =
    llvm::ConstantStruct::get(&DescriptorFields[0], 2);

  llvm::GlobalVariable *Descriptor =
    new llvm::GlobalVariable(DescriptorStruct->getType(), true,
                             llvm::GlobalVariable::InternalLinkage,
                             DescriptorStruct, "__block_descriptor_global",
                             &getModule());

  // Generate the constants for the block literal.
  llvm::Constant *LiteralFields[5];

  CodeGenFunction::BlockInfo Info(0, n);
  llvm::Function *Fn = CodeGenFunction(*this).GenerateBlockFunction(BE, Info);

  // isa
  LiteralFields[0] = getNSConcreteGlobalBlock();

  // Flags
  LiteralFields[1] = llvm::ConstantInt::get(IntTy, BLOCK_IS_GLOBAL);

  // Reserved
  LiteralFields[2] = llvm::Constant::getNullValue(IntTy);

  // Function
  LiteralFields[3] = Fn;

  // Descriptor
  LiteralFields[4] = Descriptor;

  llvm::Constant *BlockLiteralStruct =
    llvm::ConstantStruct::get(&LiteralFields[0], 5);

  llvm::GlobalVariable *BlockLiteral =
    new llvm::GlobalVariable(BlockLiteralStruct->getType(), true,
                             llvm::GlobalVariable::InternalLinkage,
                             BlockLiteralStruct, "__block_literal_global",
                             &getModule());

  return BlockLiteral;
}

llvm::Function *CodeGenFunction::GenerateBlockFunction(const BlockExpr *Expr,
                                                       const BlockInfo& Info)
{
  const FunctionTypeProto *FTy =
    cast<FunctionTypeProto>(Expr->getFunctionType());

  FunctionArgList Args;

  const BlockDecl *BD = Expr->getBlockDecl();

  // FIXME: This leaks
  ImplicitParamDecl *SelfDecl =
    ImplicitParamDecl::Create(getContext(), 0,
                              SourceLocation(), 0,
                              getContext().getPointerType(getContext().VoidTy));

  Args.push_back(std::make_pair(SelfDecl, SelfDecl->getType()));

  for (BlockDecl::param_iterator i = BD->param_begin(),
       e = BD->param_end(); i != e; ++i)
    Args.push_back(std::make_pair(*e, (*e)->getType()));

  const CGFunctionInfo &FI =
    CGM.getTypes().getFunctionInfo(FTy->getResultType(), Args);

  std::string Name = std::string("__") + Info.Name + "_block_invoke_";
  CodeGenTypes &Types = CGM.getTypes();
  const llvm::FunctionType *LTy = Types.GetFunctionType(FI, FTy->isVariadic());

  llvm::Function *Fn =
    llvm::Function::Create(LTy, llvm::GlobalValue::InternalLinkage,
                           Name,
                           &CGM.getModule());

  StartFunction(BD, FTy->getResultType(), Fn, Args,
                Expr->getBody()->getLocEnd());
  EmitStmt(Expr->getBody());
  FinishFunction(cast<CompoundStmt>(Expr->getBody())->getRBracLoc());

  return Fn;
}
<commit_msg>Be sure to initialize Name.<commit_after>//===--- CGBlocks.cpp - Emit LLVM Code for declarations -------------------===//
//
//                     The LLVM Compiler Infrastructure
//
// This file is distributed under the University of Illinois Open Source
// License. See LICENSE.TXT for details.
//
//===----------------------------------------------------------------------===//
//
// This contains code to emit blocks.
//
//===----------------------------------------------------------------------===//

#include "CodeGenFunction.h"
#include "CodeGenModule.h"
#include "llvm/Module.h"
#include "llvm/Target/TargetData.h"

#include <algorithm>

using namespace clang;
using namespace CodeGen;

enum {
  BLOCK_NEEDS_FREE =        (1 << 24),
  BLOCK_HAS_COPY_DISPOSE =  (1 << 25),
  BLOCK_HAS_CXX_OBJ =       (1 << 26),
  BLOCK_IS_GC =             (1 << 27),
  BLOCK_IS_GLOBAL =         (1 << 28),
  BLOCK_HAS_DESCRIPTOR =    (1 << 29)
};

llvm::Constant *CodeGenFunction::BuildDescriptorBlockDecl() {
  // FIXME: Push up.
  bool BlockHasCopyDispose = false;

  const llvm::PointerType *PtrToInt8Ty
    = llvm::PointerType::getUnqual(llvm::Type::Int8Ty);
  const llvm::Type *UnsignedLongTy
    = CGM.getTypes().ConvertType(getContext().UnsignedLongTy);
  llvm::Constant *C;
  std::vector<llvm::Constant*> Elts;

  // reserved
  C = llvm::ConstantInt::get(UnsignedLongTy, 0);
  Elts.push_back(C);

  // Size
  int sz = CGM.getTargetData()
    .getTypeStoreSizeInBits(CGM.getGenericBlockLiteralType()) / 8;
  C = llvm::ConstantInt::get(UnsignedLongTy, sz);
  Elts.push_back(C);

  if (BlockHasCopyDispose) {
    // copy_func_helper_decl
    C = llvm::ConstantInt::get(UnsignedLongTy, 0);
    C = llvm::ConstantExpr::getBitCast(C, PtrToInt8Ty);
    Elts.push_back(C);

    // destroy_func_decl
    C = llvm::ConstantInt::get(UnsignedLongTy, 0);
    C = llvm::ConstantExpr::getBitCast(C, PtrToInt8Ty);
    Elts.push_back(C);
  }

  C = llvm::ConstantStruct::get(Elts);

  C = new llvm::GlobalVariable(C->getType(), true,
                               llvm::GlobalValue::InternalLinkage,
                               C, "__block_descriptor_tmp", &CGM.getModule());
  return C;
}

llvm::Constant *CodeGenModule::getNSConcreteGlobalBlock() {
  if (NSConcreteGlobalBlock)
    return NSConcreteGlobalBlock;

  const llvm::PointerType *PtrToInt8Ty
    = llvm::PointerType::getUnqual(llvm::Type::Int8Ty);
  // FIXME: We should have a CodeGenModule::AddRuntimeVariable that does the
  // same thing as CreateRuntimeFunction if there's already a variable with
  // the same name.
  NSConcreteGlobalBlock
    = new llvm::GlobalVariable(PtrToInt8Ty, false,
                               llvm::GlobalValue::ExternalLinkage,
                               0, "_NSConcreteGlobalBlock",
                               &getModule());

  return NSConcreteGlobalBlock;
}

llvm::Constant *CodeGenModule::getNSConcreteStackBlock() {
  if (NSConcreteStackBlock)
    return NSConcreteStackBlock;

  const llvm::PointerType *PtrToInt8Ty
    = llvm::PointerType::getUnqual(llvm::Type::Int8Ty);
  // FIXME: We should have a CodeGenModule::AddRuntimeVariable that does the
  // same thing as CreateRuntimeFunction if there's already a variable with
  // the same name.
  NSConcreteStackBlock
    = new llvm::GlobalVariable(PtrToInt8Ty, false,
                               llvm::GlobalValue::ExternalLinkage,
                               0, "_NSConcreteStackBlock",
                               &getModule());

  return NSConcreteStackBlock;
}

llvm::Constant *CodeGenFunction::BuildBlockLiteralTmp(const BlockExpr *BE) {
  // FIXME: Push up
  bool BlockHasCopyDispose = false;
  bool insideFunction = false;
  bool BlockRefDeclList = false;
  bool BlockByrefDeclList = false;

  std::vector<llvm::Constant*> Elts;
  llvm::Constant *C;

  {
    // C = BuildBlockStructInitlist();
    unsigned int flags = BLOCK_HAS_DESCRIPTOR;

    if (BlockHasCopyDispose)
      flags |= BLOCK_HAS_COPY_DISPOSE;

    // __isa
    C = CGM.getNSConcreteStackBlock();
    if (!insideFunction ||
        (!BlockRefDeclList && !BlockByrefDeclList)) {
      C = CGM.getNSConcreteGlobalBlock();
      flags |= BLOCK_IS_GLOBAL;
    }
    const llvm::PointerType *PtrToInt8Ty
      = llvm::PointerType::getUnqual(llvm::Type::Int8Ty);
    C = llvm::ConstantExpr::getBitCast(C, PtrToInt8Ty);
    Elts.push_back(C);

    // __flags
    const llvm::IntegerType *IntTy = cast<llvm::IntegerType>(
      CGM.getTypes().ConvertType(CGM.getContext().IntTy));
    C = llvm::ConstantInt::get(IntTy, flags);
    Elts.push_back(C);

    // __reserved
    C = llvm::ConstantInt::get(IntTy, 0);
    Elts.push_back(C);

    // __FuncPtr
    const char *Name = "";
    if (const NamedDecl *ND = dyn_cast<NamedDecl>(CurFuncDecl))
      Name = ND->getNameAsCString();
    BlockInfo Info(0, Name);
    llvm::Function *Fn = CodeGenFunction(*this).GenerateBlockFunction(BE, Info);
    Elts.push_back(Fn);

    // __descriptor
    Elts.push_back(BuildDescriptorBlockDecl());

    // FIXME: Add block_original_ref_decl_list and block_byref_decl_list.
  }

  C = llvm::ConstantStruct::get(Elts);

  char Name[32];
  sprintf(Name, "__block_holder_tmp_%d", CGM.getGlobalUniqueCount());
  C = new llvm::GlobalVariable(C->getType(), true,
                               llvm::GlobalValue::InternalLinkage,
                               C, Name, &CGM.getModule());
  return C;
}

const llvm::Type *CodeGenModule::getBlockDescriptorType() {
  if (BlockDescriptorType)
    return BlockDescriptorType;

  const llvm::Type *UnsignedLongTy =
    getTypes().ConvertType(getContext().UnsignedLongTy);

  // struct __block_descriptor {
  //   unsigned long reserved;
  //   unsigned long block_size;
  // };
  BlockDescriptorType = llvm::StructType::get(UnsignedLongTy,
                                              UnsignedLongTy,
                                              NULL);

  getModule().addTypeName("struct.__block_descriptor",
                          BlockDescriptorType);

  return BlockDescriptorType;
}

const llvm::Type *
CodeGenModule::getGenericBlockLiteralType() {
  if (GenericBlockLiteralType)
    return GenericBlockLiteralType;

  const llvm::Type *Int8PtrTy =
    llvm::PointerType::getUnqual(llvm::Type::Int8Ty);

  const llvm::Type *BlockDescPtrTy =
    llvm::PointerType::getUnqual(getBlockDescriptorType());

  const llvm::IntegerType *IntTy = cast<llvm::IntegerType>(
    getTypes().ConvertType(getContext().IntTy));

  // struct __block_literal_generic {
  //   void *isa;
  //   int flags;
  //   int reserved;
  //   void (*invoke)(void *);
  //   struct __block_descriptor *descriptor;
  // };
  GenericBlockLiteralType = llvm::StructType::get(Int8PtrTy,
                                                  IntTy,
                                                  IntTy,
                                                  Int8PtrTy,
                                                  BlockDescPtrTy,
                                                  NULL);

  getModule().addTypeName("struct.__block_literal_generic",
                          GenericBlockLiteralType);

  return GenericBlockLiteralType;
}

/// getBlockFunctionType - Given a BlockPointerType, will return the
/// function type for the block, including the first block literal argument.
static QualType getBlockFunctionType(ASTContext &Ctx,
                                     const BlockPointerType *BPT) {
  const FunctionTypeProto *FTy = cast<FunctionTypeProto>(BPT->getPointeeType());

  llvm::SmallVector<QualType, 8> Types;
  Types.push_back(Ctx.getPointerType(Ctx.VoidTy));

  for (FunctionTypeProto::arg_type_iterator i = FTy->arg_type_begin(),
       e = FTy->arg_type_end(); i != e; ++i)
    Types.push_back(*i);

  return Ctx.getFunctionType(FTy->getResultType(),
                             &Types[0], Types.size(),
                             FTy->isVariadic(), 0);
}

RValue CodeGenFunction::EmitBlockCallExpr(const CallExpr* E) {
  const BlockPointerType *BPT =
    E->getCallee()->getType()->getAsBlockPointerType();

  llvm::Value *Callee = EmitScalarExpr(E->getCallee());

  // Get a pointer to the generic block literal.
  const llvm::Type *BlockLiteralTy =
    llvm::PointerType::getUnqual(CGM.getGenericBlockLiteralType());

  // Bitcast the callee to a block literal.
  llvm::Value *BlockLiteral =
    Builder.CreateBitCast(Callee, BlockLiteralTy, "block.literal");

  // Get the function pointer from the literal.
  llvm::Value *FuncPtr = Builder.CreateStructGEP(BlockLiteral, 3, "tmp");
  llvm::Value *Func = Builder.CreateLoad(FuncPtr, FuncPtr, "tmp");

  // Cast the function pointer to the right type.
  const llvm::Type *BlockFTy =
    ConvertType(getBlockFunctionType(getContext(), BPT));
  const llvm::Type *BlockFTyPtr = llvm::PointerType::getUnqual(BlockFTy);
  Func = Builder.CreateBitCast(Func, BlockFTyPtr);

  BlockLiteral =
    Builder.CreateBitCast(BlockLiteral,
                          llvm::PointerType::getUnqual(llvm::Type::Int8Ty),
                          "tmp");

  // Add the block literal.
  QualType VoidPtrTy = getContext().getPointerType(getContext().VoidTy);
  CallArgList Args;
  Args.push_back(std::make_pair(RValue::get(BlockLiteral), VoidPtrTy));

  // And the rest of the arguments.
  for (CallExpr::const_arg_iterator i = E->arg_begin(), e = E->arg_end();
       i != e; ++i)
    Args.push_back(std::make_pair(EmitAnyExprToTemp(*i),
                                  i->getType()));

  // And call the block.
  return EmitCall(CGM.getTypes().getFunctionInfo(E->getType(), Args),
                  Func, Args);
}

llvm::Constant *
CodeGenModule::GetAddrOfGlobalBlock(const BlockExpr *BE, const char * n) {
  // Generate the block descriptor.
  const llvm::Type *UnsignedLongTy = Types.ConvertType(Context.UnsignedLongTy);
  const llvm::IntegerType *IntTy = cast<llvm::IntegerType>(
    getTypes().ConvertType(getContext().IntTy));

  llvm::Constant *DescriptorFields[2];

  // Reserved
  DescriptorFields[0] = llvm::Constant::getNullValue(UnsignedLongTy);

  // Block literal size. For global blocks we just use the size of the generic
  // block literal struct.
  uint64_t BlockLiteralSize =
    TheTargetData.getTypeStoreSizeInBits(getGenericBlockLiteralType()) / 8;
  DescriptorFields[1] = llvm::ConstantInt::get(UnsignedLongTy,BlockLiteralSize);

  llvm::Constant *DescriptorStruct =
    llvm::ConstantStruct::get(&DescriptorFields[0], 2);

  llvm::GlobalVariable *Descriptor =
    new llvm::GlobalVariable(DescriptorStruct->getType(), true,
                             llvm::GlobalVariable::InternalLinkage,
                             DescriptorStruct, "__block_descriptor_global",
                             &getModule());

  // Generate the constants for the block literal.
  llvm::Constant *LiteralFields[5];

  CodeGenFunction::BlockInfo Info(0, n);
  llvm::Function *Fn = CodeGenFunction(*this).GenerateBlockFunction(BE, Info);

  // isa
  LiteralFields[0] = getNSConcreteGlobalBlock();

  // Flags
  LiteralFields[1] = llvm::ConstantInt::get(IntTy, BLOCK_IS_GLOBAL);

  // Reserved
  LiteralFields[2] = llvm::Constant::getNullValue(IntTy);

  // Function
  LiteralFields[3] = Fn;

  // Descriptor
  LiteralFields[4] = Descriptor;

  llvm::Constant *BlockLiteralStruct =
    llvm::ConstantStruct::get(&LiteralFields[0], 5);

  llvm::GlobalVariable *BlockLiteral =
    new llvm::GlobalVariable(BlockLiteralStruct->getType(), true,
                             llvm::GlobalVariable::InternalLinkage,
                             BlockLiteralStruct, "__block_literal_global",
                             &getModule());

  return BlockLiteral;
}

llvm::Function *CodeGenFunction::GenerateBlockFunction(const BlockExpr *Expr,
                                                       const BlockInfo& Info)
{
  const FunctionTypeProto *FTy =
    cast<FunctionTypeProto>(Expr->getFunctionType());

  FunctionArgList Args;

  const BlockDecl *BD = Expr->getBlockDecl();

  // FIXME: This leaks
  ImplicitParamDecl *SelfDecl =
    ImplicitParamDecl::Create(getContext(), 0,
                              SourceLocation(), 0,
                              getContext().getPointerType(getContext().VoidTy));

  Args.push_back(std::make_pair(SelfDecl, SelfDecl->getType()));

  for (BlockDecl::param_iterator i = BD->param_begin(),
       e = BD->param_end(); i != e; ++i)
    Args.push_back(std::make_pair(*e, (*e)->getType()));

  const CGFunctionInfo &FI =
    CGM.getTypes().getFunctionInfo(FTy->getResultType(), Args);

  std::string Name = std::string("__") + Info.Name + "_block_invoke_";
  CodeGenTypes &Types = CGM.getTypes();
  const llvm::FunctionType *LTy = Types.GetFunctionType(FI, FTy->isVariadic());

  llvm::Function *Fn =
    llvm::Function::Create(LTy, llvm::GlobalValue::InternalLinkage,
                           Name,
                           &CGM.getModule());

  StartFunction(BD, FTy->getResultType(), Fn, Args,
                Expr->getBody()->getLocEnd());
  EmitStmt(Expr->getBody());
  FinishFunction(cast<CompoundStmt>(Expr->getBody())->getRBracLoc());

  return Fn;
}
<|endoftext|>