Home | History | Annotate | Download | only in simpleperf
      1 /*
      2  * Copyright (C) 2017 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 "inplace_sampler_lib.h"
     18 
     19 #include <inttypes.h>
     20 #include <pthread.h>
     21 #include <signal.h>
     22 #include <stdio.h>
     23 #include <stdlib.h>
     24 #include <string.h>
     25 #include <sys/syscall.h>
     26 #include <sys/ucontext.h>
     27 #include <unistd.h>
     28 
     29 #include <map>
     30 #include <memory>
     31 #include <queue>
     32 #include <set>
     33 #include <string>
     34 #include <unordered_map>
     35 
     36 #include <android-base/logging.h>
     37 #include <android-base/macros.h>
     38 #include <backtrace/Backtrace.h>
     39 #define LOG_TAG "InplaceSampler"
     40 #include <log/log.h>
     41 
     42 #include "environment.h"
     43 #include "UnixSocket.h"
     44 #include "utils.h"
     45 
     46 #define DEFAULT_SIGNO  SIGRTMAX
     47 static constexpr int DEFAULT_SAMPLE_FREQ = 4000;
     48 static constexpr int CHECK_THREADS_INTERVAL_IN_MS = 200;
     49 
     50 namespace {
     51 
     52 struct ThreadInfo {
     53   std::string name;
     54 };
     55 
     56 // SampleManager controls the whole sampling process:
     57 //   Read commands from simpleperf
     58 //   Set up timers to send signals for each profiled thread regularly.
     59 //   Send thread info and map info to simpleperf.
     60 class SampleManager {
     61  public:
     62   SampleManager(std::unique_ptr<UnixSocketConnection> conn) : conn_(std::move(conn)),
     63       tid_(gettid()), signo_(DEFAULT_SIGNO), sample_freq_(DEFAULT_SAMPLE_FREQ),
     64       sample_period_in_ns_(0), dump_callchain_(false), monitor_all_threads_(true) {
     65   }
     66   void Run();
     67 
     68  private:
     69   bool HandleMessage(const UnixSocketMessage& msg);
     70   bool ParseStartProfilingMessage(const UnixSocketMessage& msg);
     71   bool SendStartProfilingReplyMessage(bool ok);
     72   bool StartProfiling();
     73   bool InstallSignalHandler();
     74   bool CheckThreads();
     75   bool CheckThreadNameChange(uint64_t timestamp);
     76   bool CheckMapChange(uint64_t timestamp);
     77   void SendThreadMapInfo();
     78   void SendFakeSampleRecord();
     79 
     80   std::unique_ptr<UnixSocketConnection> conn_;
     81 
     82   int tid_;
     83   int signo_;
     84   uint32_t sample_freq_;
     85   uint32_t sample_period_in_ns_;
     86   bool dump_callchain_;
     87   bool monitor_all_threads_;
     88   std::set<int> monitor_tid_filter_;
     89   std::map<int, ThreadInfo> threads_;
     90   std::map<uint64_t, ThreadMmap> maps_;
     91   std::queue<std::unique_ptr<char[]>> thread_map_info_q_;
     92 
     93   IOEventLoop loop_;
     94 };
     95 
     96 void SampleManager::Run() {
     97   auto read_callback = [&](const UnixSocketMessage& msg) {
     98     return HandleMessage(msg);
     99   };
    100   auto close_callback = [&]() {
    101     return loop_.ExitLoop();
    102   };
    103   if (!conn_->PrepareForIO(loop_, read_callback, close_callback)) {
    104     return;
    105   }
    106   loop_.RunLoop();
    107 }
    108 
    109 bool SampleManager::HandleMessage(const UnixSocketMessage& msg) {
    110   if (msg.type == START_PROFILING) {
    111     if (!ParseStartProfilingMessage(msg)) {
    112       if (!SendStartProfilingReplyMessage(false)) {
    113         return false;
    114       }
    115       return conn_->NoMoreMessage();
    116     }
    117     if (!SendStartProfilingReplyMessage(true)) {
    118       return false;
    119     }
    120     return StartProfiling();
    121   }
    122   if (msg.type == END_PROFILING) {
    123     // Close connection after clearing send buffer.
    124     return conn_->NoMoreMessage();
    125   }
    126   LOG(ERROR) << "Unexpected msg type: " << msg.type;
    127   return false;
    128 }
    129 
    130 bool SampleManager::ParseStartProfilingMessage(const UnixSocketMessage& msg) {
    131   char* option = const_cast<char*>(msg.data);
    132   while (option != nullptr && *option != '\0') {
    133     char* next_option = strchr(option, ' ');
    134     if (next_option != nullptr) {
    135       *next_option++ = '\0';
    136     }
    137     char* equal_op = strchr(option, '=');
    138     if (equal_op != nullptr) {
    139       char* key = option;
    140       *equal_op = '\0';
    141       char* value = equal_op + 1;
    142       if (strcmp(key, "freq") == 0) {
    143         sample_freq_ = atoi(value);
    144       } else if (strcmp(key, "signal") == 0) {
    145         signo_ = atoi(value);
    146       } else if (strcmp(key, "tids") == 0) {
    147         monitor_all_threads_ = false;
    148         while (*value != '\0') {
    149           int tid = static_cast<int>(strtol(value, &value, 10));
    150           monitor_tid_filter_.insert(tid);
    151           if (*value == ',') {
    152             ++value;
    153           }
    154         }
    155       } else if (strcmp(key, "dump_callchain") == 0) {
    156         dump_callchain_ = (strcmp(value, "1") == 0);
    157       }
    158     }
    159     option = next_option;
    160   }
    161   if (sample_freq_ == 0 || sample_freq_ > 1000000000) {
    162     LOG(ERROR) << "Unexpected sample_freq: " << sample_freq_;
    163     return false;
    164   }
    165   if (sample_freq_ == 1) {
    166     sample_period_in_ns_ = 999999999;
    167   } else {
    168     sample_period_in_ns_ = 1000000000 / sample_freq_;
    169   }
    170   return true;
    171 }
    172 
    173 bool SampleManager::SendStartProfilingReplyMessage(bool ok) {
    174   const char* s = ok ? "ok" : "error";
    175   size_t size = sizeof(UnixSocketMessage) + strlen(s) + 1;
    176   std::unique_ptr<char[]> data(new char[size]);
    177   UnixSocketMessage* msg = reinterpret_cast<UnixSocketMessage*>(data.get());
    178   msg->len = size;
    179   msg->type = START_PROFILING_REPLY;
    180   strcpy(msg->data, s);
    181   return conn_->SendMessage(*msg, true);
    182 }
    183 
    184 bool SampleManager::StartProfiling() {
    185   if (!InstallSignalHandler()) {
    186     return false;
    187   }
    188   if (!CheckThreads()) {
    189     return false;
    190   }
    191   timeval tv;
    192   tv.tv_sec = CHECK_THREADS_INTERVAL_IN_MS / 1000;
    193   tv.tv_usec = CHECK_THREADS_INTERVAL_IN_MS % 1000 * 1000;
    194   return loop_.AddPeriodicEvent(tv, [&]() {
    195     return CheckThreads();
    196   });
    197 }
    198 
    199 bool SampleManager::InstallSignalHandler() {
    200   return true;
    201 }
    202 
    203 bool SampleManager::CheckThreads() {
    204   uint64_t timestamp = GetSystemClock();
    205   if (!CheckMapChange(timestamp)) {
    206     return false;
    207   }
    208   if (!CheckThreadNameChange(timestamp)) {
    209     return false;
    210   }
    211   SendThreadMapInfo();
    212   // For testing.
    213   SendFakeSampleRecord();
    214   return true;
    215 }
    216 
    217 bool SampleManager::CheckThreadNameChange(uint64_t timestamp) {
    218   std::vector<pid_t> tids = GetThreadsInProcess(getpid());
    219   std::map<pid_t, std::string> current;
    220   for (auto& tid : tids) {
    221     if (tid == tid_) {
    222       // Skip sample thread.
    223       continue;
    224     }
    225     if (monitor_all_threads_ || monitor_tid_filter_.find(tid) != monitor_tid_filter_.end()) {
    226       std::string name;
    227       if (GetThreadName(tid, &name)) {
    228         current[tid] = name;
    229       }
    230     }
    231   }
    232   // Check new threads or threads with new names.
    233   for (auto& pair : current) {
    234     pid_t tid = pair.first;
    235     auto it = threads_.find(tid);
    236     if (it == threads_.end() || it->second.name != pair.second) {
    237       threads_[tid].name = pair.second;
    238       size_t size = sizeof(UnixSocketMessage) + sizeof(uint64_t) + sizeof(uint32_t) +
    239           pair.second.size() + 1;
    240       std::unique_ptr<char[]> data(new char[size]);
    241       UnixSocketMessage* msg = reinterpret_cast<UnixSocketMessage*>(data.get());
    242       msg->len = size;
    243       msg->type = THREAD_INFO;
    244       char* p = msg->data;
    245       MoveToBinaryFormat(timestamp, p);
    246       MoveToBinaryFormat(static_cast<uint32_t>(tid), p);
    247       MoveToBinaryFormat(pair.second.c_str(), pair.second.size() + 1, p);
    248       thread_map_info_q_.push(std::move(data));
    249     }
    250   }
    251   // Check deleted threads.
    252   for (auto it = threads_.begin(); it != threads_.end();) {
    253     int tid = it->first;
    254     if (current.find(tid) == current.end()) {
    255       it = threads_.erase(it);
    256     } else {
    257       ++it;
    258     }
    259   }
    260   return true;
    261 }
    262 
    263 bool SampleManager::CheckMapChange(uint64_t timestamp) {
    264   std::vector<ThreadMmap> maps;
    265   if (!GetThreadMmapsInProcess(getpid(), &maps)) {
    266     return false;
    267   }
    268   // Check new maps or changed maps.
    269   for (auto& map : maps) {
    270     if (!map.executable) {
    271       continue;
    272     }
    273     auto it = maps_.find(map.start_addr);
    274     if (it == maps_.end() || it->second.len != map.len || it->second.pgoff != map.pgoff ||
    275         it->second.name != map.name) {
    276       maps_[map.start_addr] = map;
    277       size_t size = sizeof(UnixSocketMessage) + sizeof(uint64_t) * 4 + map.name.size() + 1;
    278       std::unique_ptr<char[]> data(new char[size]);
    279       UnixSocketMessage* msg = reinterpret_cast<UnixSocketMessage*>(data.get());
    280       msg->len = size;
    281       msg->type = MAP_INFO;
    282       char* p = msg->data;
    283       MoveToBinaryFormat(timestamp, p);
    284       MoveToBinaryFormat(map.start_addr, p);
    285       MoveToBinaryFormat(map.len, p);
    286       MoveToBinaryFormat(map.pgoff, p);
    287       MoveToBinaryFormat(map.name.c_str(), map.name.size() + 1, p);
    288       thread_map_info_q_.push(std::move(data));
    289     }
    290   }
    291   return true;
    292 }
    293 
    294 void SampleManager::SendThreadMapInfo() {
    295   while (!thread_map_info_q_.empty()) {
    296     auto& data = thread_map_info_q_.front();
    297     UnixSocketMessage* msg = reinterpret_cast<UnixSocketMessage*>(data.get());
    298     if (!conn_->SendMessage(*msg, false)) {
    299       break;
    300     }
    301     thread_map_info_q_.pop();
    302   }
    303 }
    304 
    305 static void FakeFunction() {
    306 }
    307 
    308 void SampleManager::SendFakeSampleRecord() {
    309   size_t size = sizeof(UnixSocketMessage) + sizeof(uint64_t) * 2 + sizeof(uint32_t) *  3;
    310   std::unique_ptr<char[]> data(new char[size]);
    311   UnixSocketMessage* msg = reinterpret_cast<UnixSocketMessage*>(data.get());
    312   uint64_t ip = static_cast<uint64_t>(reinterpret_cast<uintptr_t>(&FakeFunction));
    313   msg->len = size;
    314   msg->type = SAMPLE_INFO;
    315   char* p = msg->data;
    316   MoveToBinaryFormat(GetSystemClock(), p);
    317   MoveToBinaryFormat(static_cast<uint32_t>(tid_), p);
    318   MoveToBinaryFormat(1u, p);
    319   MoveToBinaryFormat(1u, p);
    320   MoveToBinaryFormat(ip, p);
    321   conn_->SendMessage(*msg, false);
    322 }
    323 
    324 static void* CommunicationThread(void*) {
    325   pthread_setname_np(pthread_self(), "inplace_sampler");
    326   std::string server_path = "inplace_sampler_server_" + std::to_string(getpid());
    327   std::unique_ptr<UnixSocketServer> server = UnixSocketServer::Create(server_path, true);
    328   if (server == nullptr) {
    329     LOG(ERROR) << "failed to create server at path " << server_path;
    330     return nullptr;
    331   }
    332   LOG(INFO) << "Create inplace_sampler_server at " << server_path;
    333   while (true) {
    334     std::unique_ptr<UnixSocketConnection> conn = server->AcceptConnection();
    335     if (conn == nullptr) {
    336       break;
    337     }
    338     SampleManager manager(std::move(conn));
    339     manager.Run();
    340   }
    341   return nullptr;
    342 }
    343 
    344 __attribute__((constructor)) void InitSampler() {
    345   pthread_attr_t attr;
    346   if (pthread_attr_init(&attr) != 0) {
    347     LOG(ERROR) << "pthread_attr_init failed";
    348     return;
    349   }
    350   if (pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED) != 0) {
    351     LOG(ERROR) << "pthread_attr_setdetachstate failed";
    352     return;
    353   }
    354   pthread_t thread;
    355   if (pthread_create(&thread, &attr, CommunicationThread, nullptr) != 0) {
    356     LOG(ERROR) << "pthread_create failed";
    357     return;
    358   }
    359   pthread_attr_destroy(&attr);
    360 }
    361 
    362 }  // namespace
    363