Home | History | Annotate | Download | only in timeout_dumper
      1 /*
      2  * Copyright (C) 2018 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 <dirent.h>
     18 #include <poll.h>
     19 #include <sys/prctl.h>
     20 #include <sys/ptrace.h>
     21 #include <sys/types.h>
     22 #include <sys/wait.h>
     23 #include <unistd.h>
     24 
     25 #include <csignal>
     26 #include <cstdlib>
     27 #include <cstring>
     28 #include <iostream>
     29 #include <thread>
     30 #include <memory>
     31 #include <set>
     32 #include <string>
     33 
     34 #include <android-base/file.h>
     35 #include <android-base/logging.h>
     36 #include <android-base/macros.h>
     37 #include <android-base/stringprintf.h>
     38 #include <android-base/strings.h>
     39 #include <android-base/unique_fd.h>
     40 #include <backtrace/Backtrace.h>
     41 #include <backtrace/BacktraceMap.h>
     42 
     43 namespace art {
     44 namespace {
     45 
     46 using android::base::StringPrintf;
     47 using android::base::unique_fd;
     48 
     49 constexpr bool kUseAddr2line = true;
     50 
     51 namespace timeout_signal {
     52 
     53 class SignalSet {
     54  public:
     55   SignalSet() {
     56     if (sigemptyset(&set_) == -1) {
     57       PLOG(FATAL) << "sigemptyset failed";
     58     }
     59   }
     60 
     61   void Add(int signal) {
     62     if (sigaddset(&set_, signal) == -1) {
     63       PLOG(FATAL) << "sigaddset " << signal << " failed";
     64     }
     65   }
     66 
     67   void Block() {
     68     if (pthread_sigmask(SIG_BLOCK, &set_, nullptr) != 0) {
     69       PLOG(FATAL) << "pthread_sigmask failed";
     70     }
     71   }
     72 
     73   int Wait() {
     74     // Sleep in sigwait() until a signal arrives. gdb causes EINTR failures.
     75     int signal_number;
     76     int rc = TEMP_FAILURE_RETRY(sigwait(&set_, &signal_number));
     77     if (rc != 0) {
     78       PLOG(FATAL) << "sigwait failed";
     79     }
     80     return signal_number;
     81   }
     82 
     83  private:
     84   sigset_t set_;
     85 };
     86 
     87 int GetTimeoutSignal() {
     88   return SIGRTMIN + 2;
     89 }
     90 
     91 }  // namespace timeout_signal
     92 
     93 namespace addr2line {
     94 
     95 constexpr const char* kAddr2linePath =
     96     "/prebuilts/gcc/linux-x86/host/x86_64-linux-glibc2.17-4.8/bin/x86_64-linux-addr2line";
     97 
     98 std::unique_ptr<std::string> FindAddr2line() {
     99   const char* env_value = getenv("ANDROID_BUILD_TOP");
    100   if (env_value != nullptr) {
    101     std::string path = std::string(env_value) + kAddr2linePath;
    102     if (access(path.c_str(), X_OK) == 0) {
    103       return std::make_unique<std::string>(path);
    104     }
    105   }
    106 
    107   {
    108     std::string path = std::string(".") + kAddr2linePath;
    109     if (access(path.c_str(), X_OK) == 0) {
    110       return std::make_unique<std::string>(path);
    111     }
    112   }
    113 
    114   {
    115     using android::base::Dirname;
    116 
    117     std::string exec_dir = android::base::GetExecutableDirectory();
    118     std::string derived_top = Dirname(Dirname(Dirname(Dirname(exec_dir))));
    119     std::string path = derived_top + kAddr2linePath;
    120     if (access(path.c_str(), X_OK) == 0) {
    121       return std::make_unique<std::string>(path);
    122     }
    123   }
    124 
    125   constexpr const char* kHostAddr2line = "/usr/bin/addr2line";
    126   if (access(kHostAddr2line, F_OK) == 0) {
    127     return std::make_unique<std::string>(kHostAddr2line);
    128   }
    129 
    130   return nullptr;
    131 }
    132 
    133 // The state of an open pipe to addr2line. In "server" mode, addr2line takes input on stdin
    134 // and prints the result to stdout. This struct keeps the state of the open connection.
    135 struct Addr2linePipe {
    136   Addr2linePipe(int in_fd, int out_fd, const std::string& file_name, pid_t pid)
    137       : in(in_fd), out(out_fd), file(file_name), child_pid(pid), odd(true) {}
    138 
    139   ~Addr2linePipe() {
    140     kill(child_pid, SIGKILL);
    141   }
    142 
    143   unique_fd in;      // The file descriptor that is connected to the output of addr2line.
    144   unique_fd out;     // The file descriptor that is connected to the input of addr2line.
    145 
    146   const std::string file;     // The file addr2line is working on, so that we know when to close
    147                               // and restart.
    148   const pid_t child_pid;      // The pid of the child, which we should kill when we're done.
    149   bool odd;                   // Print state for indentation of lines.
    150 };
    151 
    152 std::unique_ptr<Addr2linePipe> Connect(const std::string& name, const char* args[]) {
    153   int caller_to_addr2line[2];
    154   int addr2line_to_caller[2];
    155 
    156   if (pipe(caller_to_addr2line) == -1) {
    157     return nullptr;
    158   }
    159   if (pipe(addr2line_to_caller) == -1) {
    160     close(caller_to_addr2line[0]);
    161     close(caller_to_addr2line[1]);
    162     return nullptr;
    163   }
    164 
    165   pid_t pid = fork();
    166   if (pid == -1) {
    167     close(caller_to_addr2line[0]);
    168     close(caller_to_addr2line[1]);
    169     close(addr2line_to_caller[0]);
    170     close(addr2line_to_caller[1]);
    171     return nullptr;
    172   }
    173 
    174   if (pid == 0) {
    175     dup2(caller_to_addr2line[0], STDIN_FILENO);
    176     dup2(addr2line_to_caller[1], STDOUT_FILENO);
    177 
    178     close(caller_to_addr2line[0]);
    179     close(caller_to_addr2line[1]);
    180     close(addr2line_to_caller[0]);
    181     close(addr2line_to_caller[1]);
    182 
    183     execv(args[0], const_cast<char* const*>(args));
    184     exit(1);
    185   } else {
    186     close(caller_to_addr2line[0]);
    187     close(addr2line_to_caller[1]);
    188     return std::make_unique<Addr2linePipe>(addr2line_to_caller[0],
    189                                            caller_to_addr2line[1],
    190                                            name,
    191                                            pid);
    192   }
    193 }
    194 
    195 void WritePrefix(std::ostream& os, const char* prefix, bool odd) {
    196   if (prefix != nullptr) {
    197     os << prefix;
    198   }
    199   os << "  ";
    200   if (!odd) {
    201     os << " ";
    202   }
    203 }
    204 
    205 void Drain(size_t expected,
    206            const char* prefix,
    207            std::unique_ptr<Addr2linePipe>* pipe /* inout */,
    208            std::ostream& os) {
    209   DCHECK(pipe != nullptr);
    210   DCHECK(pipe->get() != nullptr);
    211   int in = pipe->get()->in.get();
    212   DCHECK_GE(in, 0);
    213 
    214   bool prefix_written = false;
    215 
    216   for (;;) {
    217     constexpr uint32_t kWaitTimeExpectedMilli = 500;
    218     constexpr uint32_t kWaitTimeUnexpectedMilli = 50;
    219 
    220     int timeout = expected > 0 ? kWaitTimeExpectedMilli : kWaitTimeUnexpectedMilli;
    221     struct pollfd read_fd{in, POLLIN, 0};
    222     int retval = TEMP_FAILURE_RETRY(poll(&read_fd, 1, timeout));
    223     if (retval == -1) {
    224       // An error occurred.
    225       pipe->reset();
    226       return;
    227     }
    228 
    229     if (retval == 0) {
    230       // Timeout.
    231       return;
    232     }
    233 
    234     if (!(read_fd.revents & POLLIN)) {
    235       // addr2line call exited.
    236       pipe->reset();
    237       return;
    238     }
    239 
    240     constexpr size_t kMaxBuffer = 128;  // Relatively small buffer. Should be OK as we're on an
    241     // alt stack, but just to be sure...
    242     char buffer[kMaxBuffer];
    243     memset(buffer, 0, kMaxBuffer);
    244     int bytes_read = TEMP_FAILURE_RETRY(read(in, buffer, kMaxBuffer - 1));
    245     if (bytes_read <= 0) {
    246       // This should not really happen...
    247       pipe->reset();
    248       return;
    249     }
    250     buffer[bytes_read] = '\0';
    251 
    252     char* tmp = buffer;
    253     while (*tmp != 0) {
    254       if (!prefix_written) {
    255         WritePrefix(os, prefix, (*pipe)->odd);
    256         prefix_written = true;
    257       }
    258       char* new_line = strchr(tmp, '\n');
    259       if (new_line == nullptr) {
    260         os << tmp;
    261 
    262         break;
    263       } else {
    264         os << std::string(tmp, new_line - tmp + 1);
    265 
    266         tmp = new_line + 1;
    267         prefix_written = false;
    268         (*pipe)->odd = !(*pipe)->odd;
    269 
    270         if (expected > 0) {
    271           expected--;
    272         }
    273       }
    274     }
    275   }
    276 }
    277 
    278 void Addr2line(const std::string& addr2line,
    279                const std::string& map_src,
    280                uintptr_t offset,
    281                std::ostream& os,
    282                const char* prefix,
    283                std::unique_ptr<Addr2linePipe>* pipe /* inout */) {
    284   DCHECK(pipe != nullptr);
    285 
    286   if (map_src == "[vdso]" || android::base::EndsWith(map_src, ".vdex")) {
    287     // addr2line will not work on the vdso.
    288     // vdex files are special frames injected for the interpreter
    289     // so they don't have any line number information available.
    290     return;
    291   }
    292 
    293   if (*pipe == nullptr || (*pipe)->file != map_src) {
    294     if (*pipe != nullptr) {
    295       Drain(0, prefix, pipe, os);
    296     }
    297     pipe->reset();  // Close early.
    298 
    299     const char* args[] = {
    300         addr2line.c_str(),
    301         "--functions",
    302         "--inlines",
    303         "--demangle",
    304         "-e",
    305         map_src.c_str(),
    306         nullptr
    307     };
    308     *pipe = Connect(map_src, args);
    309   }
    310 
    311   Addr2linePipe* pipe_ptr = pipe->get();
    312   if (pipe_ptr == nullptr) {
    313     // Failed...
    314     return;
    315   }
    316 
    317   // Send the offset.
    318   const std::string hex_offset = StringPrintf("%zx\n", offset);
    319 
    320   if (!android::base::WriteFully(pipe_ptr->out.get(), hex_offset.data(), hex_offset.length())) {
    321     // Error. :-(
    322     pipe->reset();
    323     return;
    324   }
    325 
    326   // Now drain (expecting two lines).
    327   Drain(2U, prefix, pipe, os);
    328 }
    329 
    330 }  // namespace addr2line
    331 
    332 namespace ptrace {
    333 
    334 std::set<pid_t> PtraceSiblings(pid_t pid) {
    335   std::set<pid_t> ret;
    336   std::string task_path = android::base::StringPrintf("/proc/%d/task", pid);
    337 
    338   std::unique_ptr<DIR, int (*)(DIR*)> d(opendir(task_path.c_str()), closedir);
    339 
    340   // Bail early if the task directory cannot be opened.
    341   if (d == nullptr) {
    342     PLOG(ERROR) << "Failed to scan task folder";
    343     return ret;
    344   }
    345 
    346   struct dirent* de;
    347   while ((de = readdir(d.get())) != nullptr) {
    348     // Ignore "." and "..".
    349     if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) {
    350       continue;
    351     }
    352 
    353     char* end;
    354     pid_t tid = strtoul(de->d_name, &end, 10);
    355     if (*end) {
    356       continue;
    357     }
    358 
    359     if (tid == pid) {
    360       continue;
    361     }
    362 
    363     if (::ptrace(PTRACE_ATTACH, tid, 0, 0) != 0) {
    364       PLOG(ERROR) << "Failed to attach to tid " << tid;
    365       continue;
    366     }
    367 
    368     ret.insert(tid);
    369   }
    370   return ret;
    371 }
    372 
    373 void DumpABI(pid_t forked_pid) {
    374   enum class ABI { kArm, kArm64, kMips, kMips64, kX86, kX86_64 };
    375 #if defined(__arm__)
    376   constexpr ABI kDumperABI = ABI::kArm;
    377 #elif defined(__aarch64__)
    378   constexpr ABI kDumperABI = ABI::kArm64;
    379 #elif defined(__mips__) && !defined(__LP64__)
    380   constexpr ABI kDumperABI = ABI::kMips;
    381 #elif defined(__mips__) && defined(__LP64__)
    382   constexpr ABI kDumperABI = ABI::kMips64;
    383 #elif defined(__i386__)
    384   constexpr ABI kDumperABI = ABI::kX86;
    385 #elif defined(__x86_64__)
    386   constexpr ABI kDumperABI = ABI::kX86_64;
    387 #else
    388 #error Unsupported architecture
    389 #endif
    390 
    391   char data[1024];  // Should be more than enough.
    392   struct iovec io_vec;
    393   io_vec.iov_base = &data;
    394   io_vec.iov_len = 1024;
    395   ABI to_print;
    396   if (0 != ::ptrace(PTRACE_GETREGSET, forked_pid, /* NT_PRSTATUS */ 1, &io_vec)) {
    397     LOG(ERROR) << "Could not get registers to determine abi.";
    398     // Use 64-bit as default.
    399     switch (kDumperABI) {
    400       case ABI::kArm:
    401       case ABI::kArm64:
    402         to_print = ABI::kArm64;
    403         break;
    404       case ABI::kMips:
    405       case ABI::kMips64:
    406         to_print = ABI::kMips64;
    407         break;
    408       case ABI::kX86:
    409       case ABI::kX86_64:
    410         to_print = ABI::kX86_64;
    411         break;
    412       default:
    413         __builtin_unreachable();
    414     }
    415   } else {
    416     // Check the length of the data. Assume that it's the same arch as the tool.
    417     switch (kDumperABI) {
    418       case ABI::kArm:
    419       case ABI::kArm64:
    420         to_print = io_vec.iov_len == 18 * sizeof(uint32_t) ? ABI::kArm : ABI::kArm64;
    421         break;
    422       case ABI::kMips:
    423       case ABI::kMips64:
    424         to_print = ABI::kMips64;  // TODO Figure out how this should work.
    425         break;
    426       case ABI::kX86:
    427       case ABI::kX86_64:
    428         to_print = io_vec.iov_len == 17 * sizeof(uint32_t) ? ABI::kX86 : ABI::kX86_64;
    429         break;
    430       default:
    431         __builtin_unreachable();
    432     }
    433   }
    434   std::string abi_str;
    435   switch (to_print) {
    436     case ABI::kArm:
    437       abi_str = "arm";
    438       break;
    439     case ABI::kArm64:
    440       abi_str = "arm64";
    441       break;
    442     case ABI::kMips:
    443       abi_str = "mips";
    444       break;
    445     case ABI::kMips64:
    446       abi_str = "mips64";
    447       break;
    448     case ABI::kX86:
    449       abi_str = "x86";
    450       break;
    451     case ABI::kX86_64:
    452       abi_str = "x86_64";
    453       break;
    454   }
    455   std::cerr << "ABI: '" << abi_str << "'" << std::endl;
    456 }
    457 
    458 }  // namespace ptrace
    459 
    460 template <typename T>
    461 bool WaitLoop(uint32_t max_wait_micros, const T& handler) {
    462   constexpr uint32_t kWaitMicros = 10;
    463   const size_t kMaxLoopCount = max_wait_micros / kWaitMicros;
    464 
    465   for (size_t loop_count = 1; loop_count <= kMaxLoopCount; ++loop_count) {
    466     bool ret;
    467     if (handler(&ret)) {
    468       return ret;
    469     }
    470     usleep(kWaitMicros);
    471   }
    472   return false;
    473 }
    474 
    475 bool WaitForMainSigStop(const std::atomic<bool>& saw_wif_stopped_for_main) {
    476   auto handler = [&](bool* res) {
    477     if (saw_wif_stopped_for_main) {
    478       *res = true;
    479       return true;
    480     }
    481     return false;
    482   };
    483   constexpr uint32_t kMaxWaitMicros = 30 * 1000 * 1000;  // 30s wait.
    484   return WaitLoop(kMaxWaitMicros, handler);
    485 }
    486 
    487 bool WaitForSigStopped(pid_t pid, uint32_t max_wait_micros) {
    488   auto handler = [&](bool* res) {
    489     int status;
    490     pid_t rc = TEMP_FAILURE_RETRY(waitpid(pid, &status, WNOHANG));
    491     if (rc == -1) {
    492       PLOG(ERROR) << "Failed to waitpid for " << pid;
    493       *res = false;
    494       return true;
    495     }
    496     if (rc == pid) {
    497       if (!(WIFSTOPPED(status))) {
    498         LOG(ERROR) << "Did not get expected stopped signal for " << pid;
    499         *res = false;
    500       } else {
    501         *res = true;
    502       }
    503       return true;
    504     }
    505     return false;
    506   };
    507   return WaitLoop(max_wait_micros, handler);
    508 }
    509 
    510 #ifdef __LP64__
    511 constexpr bool kIs64Bit = true;
    512 #else
    513 constexpr bool kIs64Bit = false;
    514 #endif
    515 
    516 void DumpThread(pid_t pid,
    517                 pid_t tid,
    518                 const std::string* addr2line_path,
    519                 const char* prefix,
    520                 BacktraceMap* map) {
    521   // Use std::cerr to avoid the LOG prefix.
    522   std::cerr << std::endl << "=== pid: " << pid << " tid: " << tid << " ===" << std::endl;
    523 
    524   constexpr uint32_t kMaxWaitMicros = 1000 * 1000;  // 1s.
    525   if (pid != tid && !WaitForSigStopped(tid, kMaxWaitMicros)) {
    526     LOG(ERROR) << "Failed to wait for sigstop on " << tid;
    527   }
    528 
    529   std::unique_ptr<Backtrace> backtrace(Backtrace::Create(pid, tid, map));
    530   if (backtrace == nullptr) {
    531     LOG(ERROR) << prefix << "(failed to create Backtrace for thread " << tid << ")";
    532     return;
    533   }
    534   backtrace->SetSkipFrames(false);
    535   if (!backtrace->Unwind(0, nullptr)) {
    536     LOG(ERROR) << prefix << "(backtrace::Unwind failed for thread " << tid
    537                << ": " <<  backtrace->GetErrorString(backtrace->GetError()) << ")";
    538     return;
    539   }
    540   if (backtrace->NumFrames() == 0) {
    541     LOG(ERROR) << prefix << "(no native stack frames for thread " << tid << ")";
    542     return;
    543   }
    544 
    545   std::unique_ptr<addr2line::Addr2linePipe> addr2line_state;
    546 
    547   for (Backtrace::const_iterator it = backtrace->begin();
    548       it != backtrace->end(); ++it) {
    549     std::ostringstream oss;
    550     oss << prefix << StringPrintf("#%02zu pc ", it->num);
    551     bool try_addr2line = false;
    552     if (!BacktraceMap::IsValid(it->map)) {
    553       oss << StringPrintf(kIs64Bit ? "%016" PRIx64 "  ???" : "%08" PRIx64 "  ???", it->pc);
    554     } else {
    555       oss << StringPrintf(kIs64Bit ? "%016" PRIx64 "  " : "%08" PRIx64 "  ", it->rel_pc);
    556       if (it->map.name.empty()) {
    557         oss << StringPrintf("<anonymous:%" PRIx64 ">", it->map.start);
    558       } else {
    559         oss << it->map.name;
    560       }
    561       if (it->map.offset != 0) {
    562         oss << StringPrintf(" (offset %" PRIx64 ")", it->map.offset);
    563       }
    564       oss << " (";
    565       if (!it->func_name.empty()) {
    566         oss << it->func_name;
    567         if (it->func_offset != 0) {
    568           oss << "+" << it->func_offset;
    569         }
    570         // Functions found using the gdb jit interface will be in an empty
    571         // map that cannot be found using addr2line.
    572         if (!it->map.name.empty()) {
    573           try_addr2line = true;
    574         }
    575       } else {
    576         oss << "???";
    577       }
    578       oss << ")";
    579     }
    580     std::cerr << oss.str() << std::endl;
    581     if (try_addr2line && addr2line_path != nullptr) {
    582       addr2line::Addr2line(*addr2line_path,
    583                            it->map.name,
    584                            it->rel_pc,
    585                            std::cerr,
    586                            prefix,
    587                            &addr2line_state);
    588     }
    589   }
    590 
    591   if (addr2line_state != nullptr) {
    592     addr2line::Drain(0, prefix, &addr2line_state, std::cerr);
    593   }
    594 }
    595 
    596 void DumpProcess(pid_t forked_pid, const std::atomic<bool>& saw_wif_stopped_for_main) {
    597   LOG(ERROR) << "Timeout for process " << forked_pid;
    598 
    599   CHECK_EQ(0, ::ptrace(PTRACE_ATTACH, forked_pid, 0, 0));
    600   std::set<pid_t> tids = ptrace::PtraceSiblings(forked_pid);
    601   tids.insert(forked_pid);
    602 
    603   ptrace::DumpABI(forked_pid);
    604 
    605   // Check whether we have and should use addr2line.
    606   std::unique_ptr<std::string> addr2line_path;
    607   if (kUseAddr2line) {
    608     addr2line_path = addr2line::FindAddr2line();
    609     if (addr2line_path == nullptr) {
    610       LOG(ERROR) << "Did not find usable addr2line";
    611     }
    612   }
    613 
    614   if (!WaitForMainSigStop(saw_wif_stopped_for_main)) {
    615     LOG(ERROR) << "Did not receive SIGSTOP for pid " << forked_pid;
    616   }
    617 
    618   std::unique_ptr<BacktraceMap> backtrace_map(BacktraceMap::Create(forked_pid));
    619   if (backtrace_map == nullptr) {
    620     LOG(ERROR) << "Could not create BacktraceMap";
    621     return;
    622   }
    623 
    624   for (pid_t tid : tids) {
    625     DumpThread(forked_pid, tid, addr2line_path.get(), "  ", backtrace_map.get());
    626   }
    627 }
    628 
    629 [[noreturn]]
    630 void WaitMainLoop(pid_t forked_pid, std::atomic<bool>* saw_wif_stopped_for_main) {
    631   for (;;) {
    632     // Consider switching to waitid to not get woken up for WIFSTOPPED.
    633     int status;
    634     pid_t res = TEMP_FAILURE_RETRY(waitpid(forked_pid, &status, 0));
    635     if (res == -1) {
    636       PLOG(FATAL) << "Failure during waitpid";
    637       __builtin_unreachable();
    638     }
    639 
    640     if (WIFEXITED(status)) {
    641       _exit(WEXITSTATUS(status));
    642       __builtin_unreachable();
    643     }
    644     if (WIFSIGNALED(status)) {
    645       _exit(1);
    646       __builtin_unreachable();
    647     }
    648     if (WIFSTOPPED(status)) {
    649       *saw_wif_stopped_for_main = true;
    650       continue;
    651     }
    652     if (WIFCONTINUED(status)) {
    653       continue;
    654     }
    655 
    656     LOG(FATAL) << "Unknown status " << std::hex << status;
    657   }
    658 }
    659 
    660 [[noreturn]]
    661 void SetupAndWait(pid_t forked_pid) {
    662   timeout_signal::SignalSet signals;
    663   signals.Add(timeout_signal::GetTimeoutSignal());
    664   signals.Block();
    665 
    666   std::atomic<bool> saw_wif_stopped_for_main(false);
    667 
    668   std::thread signal_catcher([&]() {
    669     signals.Block();
    670     int sig = signals.Wait();
    671     CHECK_EQ(sig, timeout_signal::GetTimeoutSignal());
    672 
    673     DumpProcess(forked_pid, saw_wif_stopped_for_main);
    674 
    675     // Don't clean up. Just kill the child and exit.
    676     kill(forked_pid, SIGKILL);
    677     _exit(1);
    678   });
    679 
    680   WaitMainLoop(forked_pid, &saw_wif_stopped_for_main);
    681 }
    682 
    683 }  // namespace
    684 }  // namespace art
    685 
    686 int main(int argc ATTRIBUTE_UNUSED, char** argv) {
    687   pid_t orig_ppid = getpid();
    688 
    689   pid_t pid = fork();
    690   if (pid == 0) {
    691     if (prctl(PR_SET_PDEATHSIG, SIGTERM) == -1) {
    692       _exit(1);
    693     }
    694 
    695     if (getppid() != orig_ppid) {
    696       _exit(2);
    697     }
    698 
    699     execvp(argv[1], &argv[1]);
    700 
    701     _exit(3);
    702     __builtin_unreachable();
    703   }
    704 
    705   art::SetupAndWait(pid);
    706   __builtin_unreachable();
    707 }
    708