LCOV - code coverage report
Current view: directory - src/trusted/debug_stub - target.cc (source / functions) Found Hit Coverage
Test: coverage.lcov Lines: 628 496 79.0 %
Date: 2014-06-18 Functions: 0 0 -

       1                 : /*
       2                 :  * Copyright (c) 2012 The Native Client Authors. All rights reserved.
       3                 :  * Use of this source code is governed by a BSD-style license that can be
       4                 :  * found in the LICENSE file.
       5                 :  */
       6                 : 
       7                 : #include <string.h>
       8                 : #include <stdlib.h>
       9                 : #include <stdio.h>
      10                 : 
      11                 : #include <algorithm>
      12                 : 
      13                 : #include "native_client/src/include/nacl_scoped_ptr.h"
      14                 : #include "native_client/src/shared/platform/nacl_check.h"
      15                 : #include "native_client/src/shared/platform/nacl_exit.h"
      16                 : #include "native_client/src/shared/platform/nacl_log.h"
      17                 : #include "native_client/src/trusted/debug_stub/abi.h"
      18                 : #include "native_client/src/trusted/debug_stub/packet.h"
      19                 : #include "native_client/src/trusted/debug_stub/platform.h"
      20                 : #include "native_client/src/trusted/debug_stub/session.h"
      21                 : #include "native_client/src/trusted/debug_stub/target.h"
      22                 : #include "native_client/src/trusted/debug_stub/thread.h"
      23                 : #include "native_client/src/trusted/debug_stub/util.h"
      24                 : #include "native_client/src/trusted/service_runtime/nacl_app_thread.h"
      25                 : #include "native_client/src/trusted/service_runtime/sel_ldr.h"
      26                 : #include "native_client/src/trusted/service_runtime/thread_suspension.h"
      27                 : 
      28                 : #if NACL_WINDOWS
      29                 : #define snprintf sprintf_s
      30                 : #endif
      31                 : 
      32                 : using std::string;
      33                 : 
      34                 : using port::IPlatform;
      35                 : using port::IThread;
      36                 : using port::MutexLock;
      37                 : 
      38                 : namespace gdb_rsp {
      39                 : 
      40                 : 
      41                 : // Arbitrary descriptor to return when the main nexe is opened.
      42                 : // This can be shared as the file connections are stateless.
      43                 : static const char kMainNexeFilename[] = "nexe";
      44                 : static const char kIrtNexeFilename[] = "irt";
      45                 : static const uint64_t kMainNexeFd = 123;
      46                 : static const uint64_t kIrtNexeFd = 234;
      47                 : 
      48                 : // The GDB debug protocol specifies particular values for return values,
      49                 : // errno values, and mode flags. Explicitly defining the subset used herein.
      50                 : static const uint64_t kGdbErrorResult = static_cast<uint64_t>(-1);
      51                 : static const uint64_t kGdbO_RDONLY = 0;
      52                 : static const uint64_t kGdbEPERM = 1;
      53                 : static const uint64_t kGdbENOENT = 2;
      54                 : static const uint64_t kGdbEBADF = 9;
      55                 : 
      56                 : // Assume a buffer size that matches GDB's actual current request size.
      57                 : static const size_t kGdbPreadChunkSize = 4096;
      58                 : 
      59                 : 
      60              34 : Target::Target(struct NaClApp *nap, const Abi* abi)
      61                 :   : nap_(nap),
      62                 :     abi_(abi),
      63                 :     session_(NULL),
      64                 :     initial_breakpoint_addr_(0),
      65                 :     ctx_(NULL),
      66                 :     cur_signal_(0),
      67                 :     sig_thread_(0),
      68                 :     reg_thread_(0),
      69                 :     step_over_breakpoint_thread_(0),
      70                 :     all_threads_suspended_(false),
      71                 :     detaching_(false),
      72              68 :     should_exit_(false) {
      73              51 :   if (NULL == abi_) abi_ = Abi::Get();
      74              34 : }
      75                 : 
      76               0 : Target::~Target() {
      77               0 :   Destroy();
      78               0 : }
      79                 : 
      80                 : bool Target::Init() {
      81              34 :   string targ_xml = "l<target><architecture>";
      82                 : 
      83              34 :   targ_xml += abi_->GetName();
      84              17 :   targ_xml += "</architecture><osabi>NaCl</osabi>";
      85              34 :   targ_xml += abi_->GetTargetXml();
      86              17 :   targ_xml += "</target>";
      87                 : 
      88                 :   // Set a more specific result which won't change.
      89              85 :   properties_["target.xml"] = targ_xml;
      90              85 :   properties_["Supported"] =
      91                 :     "PacketSize=1000;qXfer:features:read+";
      92                 : 
      93              17 :   NaClXMutexCtor(&mutex_);
      94              51 :   ctx_ = new uint8_t[abi_->GetContextSize()];
      95                 : 
      96              17 :   initial_breakpoint_addr_ = (uint32_t) nap_->initial_entry_pt;
      97              34 :   if (!AddBreakpoint(initial_breakpoint_addr_))
      98               0 :     return false;
      99              34 :   return true;
     100              17 : }
     101                 : 
     102                 : void Target::Destroy() {
     103               0 :   NaClMutexDtor(&mutex_);
     104                 : 
     105               0 :   delete[] ctx_;
     106               0 : }
     107                 : 
     108              69 : bool Target::AddBreakpoint(uint32_t user_address) {
     109              69 :   const Abi::BPDef *bp = abi_->GetBreakpointDef();
     110                 : 
     111                 :   // If we already have a breakpoint here then don't add it
     112              69 :   if (breakpoint_map_.find(user_address) != breakpoint_map_.end())
     113               0 :     return false;
     114                 : 
     115              69 :   uintptr_t sysaddr = NaClUserToSysAddrRange(nap_, user_address, bp->size_);
     116              69 :   if (sysaddr == kNaClBadAddress)
     117               0 :     return false;
     118                 :   // We allow setting breakpoints in the code area but not the data area.
     119              69 :   if (user_address + bp->size_ > nap_->dynamic_text_end)
     120               0 :     return false;
     121                 : 
     122                 :   // We add the breakpoint by overwriting the start of an instruction
     123                 :   // with a breakpoint instruction.  (At least, we assume that we have
     124                 :   // been given the address of the start of an instruction.)  In order
     125                 :   // to be able to remove the breakpoint later, we save a copy of the
     126                 :   // locations we are overwriting into breakpoint_map_.
     127              69 :   uint8_t *data = new uint8_t[bp->size_];
     128                 : 
     129                 :   // Copy the old code from here
     130              69 :   if (!IPlatform::GetMemory(sysaddr, bp->size_, data)) {
     131               2 :     delete[] data;
     132               1 :     return false;
     133                 :   }
     134              68 :   if (!IPlatform::SetMemory(nap_, sysaddr, bp->size_, bp->code_)) {
     135               0 :     delete[] data;
     136               0 :     return false;
     137                 :   }
     138                 : 
     139              68 :   breakpoint_map_[user_address] = data;
     140              68 :   return true;
     141              69 : }
     142                 : 
     143              68 : bool Target::RemoveBreakpoint(uint32_t user_address) {
     144              68 :   const Abi::BPDef *bp_def = abi_->GetBreakpointDef();
     145                 : 
     146              68 :   BreakpointMap_t::iterator iter = breakpoint_map_.find(user_address);
     147              68 :   if (iter == breakpoint_map_.end())
     148               0 :     return false;
     149                 : 
     150              68 :   uintptr_t sysaddr = NaClUserToSys(nap_, user_address);
     151              68 :   uint8_t *data = iter->second;
     152                 :   // Copy back the old code, and free the data
     153              68 :   if (!IPlatform::SetMemory(nap_, sysaddr, bp_def->size_, data)) {
     154               0 :     NaClLog(LOG_ERROR, "Failed to undo breakpoint.\n");
     155               0 :     return false;
     156                 :   }
     157             136 :   delete[] data;
     158              68 :   breakpoint_map_.erase(iter);
     159              68 :   return true;
     160              68 : }
     161                 : 
     162              92 : void Target::CopyFaultSignalFromAppThread(IThread *thread) {
     163             182 :   if (thread->GetFaultSignal() == 0 && thread->HasThreadFaulted()) {
     164              84 :     int signal =
     165              84 :         IThread::ExceptionToSignal(thread->GetAppThread()->fault_signal);
     166                 :     // If a thread hits a breakpoint, we want to ensure that it is
     167                 :     // reported as SIGTRAP rather than SIGSEGV.  This is necessary
     168                 :     // because we use HLT (which produces SIGSEGV) rather than the
     169                 :     // more usual INT3 (which produces SIGTRAP) on x86, in order to
     170                 :     // work around a Mac OS X bug.  Similarly, on ARM we use an
     171                 :     // illegal instruction (which produces SIGILL) rather than the
     172                 :     // more usual BKPT (which produces SIGTRAP).
     173                 :     //
     174                 :     // We need to check each thread to see whether it hit a
     175                 :     // breakpoint.  We record this on the thread object because:
     176                 :     //  * We need to check the threads before accepting any commands
     177                 :     //    from GDB which might remove breakpoints from
     178                 :     //    breakpoint_map_, which would remove our ability to tell
     179                 :     //    whether a thread hit a breakpoint.
     180                 :     //  * Although we deliver fault events to GDB one by one, we might
     181                 :     //    have multiple threads that have hit breakpoints.
     182              84 :     if ((NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 &&
     183                 :          signal == NACL_ABI_SIGSEGV) ||
     184                 :         (NACL_ARCH(NACL_BUILD_ARCH) == NACL_arm &&
     185                 :          signal == NACL_ABI_SIGILL)) {
     186                 :       // Casting to uint32_t is necessary to drop the top 32 bits of
     187                 :       // %rip on x86-64.
     188              48 :       uint32_t prog_ctr = (uint32_t) thread->GetContext()->prog_ctr;
     189              48 :       if (breakpoint_map_.find(prog_ctr) != breakpoint_map_.end()) {
     190              48 :         signal = NACL_ABI_SIGTRAP;
     191              48 :       }
     192              48 :     }
     193              84 :     thread->SetFaultSignal(signal);
     194              84 :   }
     195              92 : }
     196                 : 
     197                 : void Target::RemoveInitialBreakpoint() {
     198              84 :   if (initial_breakpoint_addr_ != 0) {
     199              17 :     if (!RemoveBreakpoint(initial_breakpoint_addr_)) {
     200               0 :       NaClLog(LOG_FATAL,
     201                 :               "RemoveInitialBreakpoint: Failed to remove breakpoint\n");
     202               0 :     }
     203              17 :     initial_breakpoint_addr_ = 0;
     204              17 :   }
     205              84 : }
     206                 : 
     207                 : // When the debugger reads memory, we want to report the original
     208                 : // memory contents without the modifications we made to add
     209                 : // breakpoints.  This function undoes the modifications from a copy of
     210                 : // memory.
     211             462 : void Target::EraseBreakpointsFromCopyOfMemory(uint32_t user_address,
     212             462 :                                               uint8_t *data, uint32_t size) {
     213             462 :   uint32_t user_end = user_address + size;
     214             462 :   const Abi::BPDef *bp = abi_->GetBreakpointDef();
     215             462 :   for (BreakpointMap_t::iterator iter = breakpoint_map_.begin();
     216             654 :        iter != breakpoint_map_.end();
     217             192 :        ++iter) {
     218             192 :     uint32_t breakpoint_address = iter->first;
     219             192 :     uint32_t breakpoint_end = breakpoint_address + bp->size_;
     220             192 :     uint8_t *original_data = iter->second;
     221                 : 
     222             192 :     uint32_t overlap_start = std::max(user_address, breakpoint_address);
     223             192 :     uint32_t overlap_end = std::min(user_end, breakpoint_end);
     224             192 :     if (overlap_start < overlap_end) {
     225              82 :       uint8_t *dest = data + (overlap_start - user_address);
     226              82 :       uint8_t *src = original_data + (overlap_start - breakpoint_address);
     227              82 :       size_t copy_size = overlap_end - overlap_start;
     228                 :       // Sanity check: do some bounds checks.
     229             328 :       CHECK(data <= dest && dest + copy_size <= data + size);
     230             328 :       CHECK(original_data <= src
     231                 :             && src + copy_size <= original_data + bp->size_);
     232              82 :       memcpy(dest, src, copy_size);
     233              82 :     }
     234             192 :   }
     235             462 : }
     236                 : 
     237              29 : void Target::Run(Session *ses) {
     238              29 :   NaClXMutexLock(&mutex_);
     239              29 :   session_ = ses;
     240              29 :   NaClXMutexUnlock(&mutex_);
     241                 : 
     242              29 :   do {
     243             160 :     WaitForDebugEvent();
     244                 : 
     245                 :     // Lock to prevent anyone else from modifying threads
     246                 :     // or updating the signal information.
     247             160 :     MutexLock lock(&mutex_);
     248                 : 
     249             160 :     ProcessDebugEvent();
     250             145 :     ProcessCommands();
     251             290 :   } while (session_->Connected());
     252                 : 
     253              14 :   NaClXMutexLock(&mutex_);
     254              14 :   session_ = NULL;
     255              14 :   NaClXMutexUnlock(&mutex_);
     256              14 : }
     257                 : 
     258                 : bool Target::IsInitialBreakpointActive() {
     259             160 :   return initial_breakpoint_addr_ != 0;
     260                 : }
     261                 : 
     262                 : void Target::WaitForDebugEvent() {
     263             160 :   if (all_threads_suspended_) {
     264                 :     // If all threads are suspended (which may be left over from a previous
     265                 :     // connection), we are already ready to handle commands from GDB.
     266              12 :     return;
     267                 :   }
     268                 :   // Wait for either:
     269                 :   //   * an untrusted thread to fault (or single-step)
     270                 :   //   * an interrupt from GDB
     271             148 :   bool ignore_input_from_gdb = step_over_breakpoint_thread_ != 0 ||
     272             222 :     IsInitialBreakpointActive();
     273             148 :   session_->WaitForDebugStubEvent(nap_, ignore_input_from_gdb);
     274             308 : }
     275                 : 
     276                 : void Target::ProcessDebugEvent() {
     277             160 :   if (all_threads_suspended_) {
     278                 :     // We are already in a suspended state.
     279              12 :     return;
     280             148 :   } else if (step_over_breakpoint_thread_ != 0) {
     281                 :     // We are waiting for a specific thread to fault while all other
     282                 :     // threads are suspended.  Note that faulted_thread_count might
     283                 :     // be >1, because multiple threads can fault simultaneously
     284                 :     // before the debug stub gets a chance to suspend all threads.
     285                 :     // This is why we must check the status of a specific thread --
     286                 :     // we cannot call UnqueueAnyFaultedThread() and expect it to
     287                 :     // return step_over_breakpoint_thread_.
     288              74 :     IThread *thread = threads_[step_over_breakpoint_thread_];
     289              74 :     if (!thread->HasThreadFaulted()) {
     290                 :       // The thread has not faulted.  Nothing to do, so try again.
     291                 :       // Note that we do not respond to input from GDB while in this
     292                 :       // state.
     293                 :       // TODO(mseaborn): We should allow GDB to interrupt execution.
     294              63 :       return;
     295                 :     }
     296                 :     // All threads but one are already suspended.  We only need to
     297                 :     // suspend the single thread that we allowed to run.
     298              11 :     thread->SuspendThread();
     299              11 :     CopyFaultSignalFromAppThread(thread);
     300              11 :     cur_signal_ = thread->GetFaultSignal();
     301              11 :     thread->UnqueueFaultedThread();
     302              11 :     sig_thread_ = step_over_breakpoint_thread_;
     303              11 :     reg_thread_ = step_over_breakpoint_thread_;
     304              11 :     step_over_breakpoint_thread_ = 0;
     305              85 :   } else if (nap_->faulted_thread_count != 0) {
     306                 :     // At least one untrusted thread has got an exception.  First we
     307                 :     // need to ensure that all threads are suspended.  Then we can
     308                 :     // retrieve a thread from the set of faulted threads.
     309              73 :     SuspendAllThreads();
     310              73 :     UnqueueAnyFaultedThread(&sig_thread_, &cur_signal_);
     311              73 :     reg_thread_ = sig_thread_;
     312              73 :   } else {
     313                 :     // Otherwise look for messages from GDB.  To fix a potential
     314                 :     // race condition, we don't do this on the first run, because in
     315                 :     // that case we are waiting for the initial breakpoint to be
     316                 :     // reached.  We don't want GDB to observe states where the
     317                 :     // (internal) initial breakpoint is still registered or where
     318                 :     // the initial thread is suspended in NaClStartThreadInApp()
     319                 :     // before executing its first untrusted instruction.
     320               2 :     if (IsInitialBreakpointActive() || !session_->IsDataAvailable()) {
     321                 :       // No input from GDB.  Nothing to do, so try again.
     322               0 :       return;
     323                 :     }
     324                 :     // GDB should have tried to interrupt the target.
     325                 :     // See http://sourceware.org/gdb/current/onlinedocs/gdb/Interrupts.html
     326                 :     // TODO(eaeltsin): should we verify the interrupt sequence?
     327                 : 
     328                 :     // Indicate we have no current thread.
     329                 :     // TODO(eaeltsin): or pick any thread? Add a test.
     330                 :     // See http://code.google.com/p/nativeclient/issues/detail?id=2743
     331               1 :     sig_thread_ = 0;
     332               1 :     SuspendAllThreads();
     333                 :   }
     334                 : 
     335              85 :   bool initial_breakpoint_was_active = IsInitialBreakpointActive();
     336                 : 
     337              85 :   if (sig_thread_ != 0) {
     338                 :     // Reset single stepping.
     339              84 :     threads_[sig_thread_]->SetStep(false);
     340              84 :     RemoveInitialBreakpoint();
     341              84 :   }
     342                 : 
     343                 :   // Next update the current thread info
     344              85 :   char tmp[16];
     345              85 :   snprintf(tmp, sizeof(tmp), "QC%x", sig_thread_);
     346             425 :   properties_["C"] = tmp;
     347                 : 
     348              85 :   if (!initial_breakpoint_was_active) {
     349                 :     // First time on a connection, we don't send the signal.
     350                 :     // All other times, send the signal that triggered us.
     351              68 :     Packet pktOut;
     352              68 :     SetStopReply(&pktOut);
     353              68 :     session_->SendPacketOnly(&pktOut);
     354              68 :   }
     355                 : 
     356              85 :   all_threads_suspended_ = true;
     357             245 : }
     358                 : 
     359                 : void Target::ProcessCommands() {
     360             160 :   if (!all_threads_suspended_) {
     361                 :     // Don't process commands if we haven't stopped all threads.
     362              63 :     return;
     363                 :   }
     364                 : 
     365                 :   // Now we are ready to process commands.
     366                 :   // Loop through packets until we process a continue packet or a detach.
     367              97 :   Packet recv, reply;
     368              97 :   do {
     369            2374 :     if (!session_->GetPacket(&recv))
     370              13 :       continue;
     371            1174 :     reply.Clear();
     372            2348 :     if (ProcessPacket(&recv, &reply)) {
     373                 :       // If this is a continue type command, break out of this loop.
     374              68 :       break;
     375                 :     }
     376                 :     // Otherwise send the response.
     377            1106 :     session_->SendPacket(&reply);
     378                 : 
     379            1106 :     if (detaching_) {
     380               1 :       detaching_ = false;
     381               1 :       session_->Disconnect();
     382               1 :       Resume();
     383               1 :       return;
     384                 :     }
     385                 : 
     386            1105 :     if (should_exit_) {
     387               0 :       NaClExit(-9);
     388               0 :     }
     389            3296 :   } while (session_->Connected());
     390                 : 
     391             162 :   if (session_->Connected()) {
     392                 :     // Continue if we're still connected.
     393              68 :     Resume();
     394              68 :   }
     395             453 : }
     396                 : 
     397                 : void Target::Resume() {
     398                 :   // Reset the signal value
     399              69 :   cur_signal_ = 0;
     400                 : 
     401                 :   // TODO(eaeltsin): it might make sense to resume signaled thread before
     402                 :   // others, though it is not required by GDB docs.
     403              69 :   if (step_over_breakpoint_thread_ == 0) {
     404              58 :     ResumeAllThreads();
     405              58 :   } else {
     406                 :     // Resume one thread while leaving all others suspended.
     407              11 :     threads_[step_over_breakpoint_thread_]->ResumeThread();
     408                 :   }
     409                 : 
     410              69 :   all_threads_suspended_ = false;
     411              69 : }
     412                 : 
     413              97 : void Target::SetStopReply(Packet *pktOut) const {
     414              97 :   pktOut->AddRawChar('T');
     415              97 :   pktOut->AddWord8(cur_signal_);
     416                 : 
     417                 :   // gdbserver handles GDB interrupt by sending SIGINT to the debuggee, thus
     418                 :   // GDB interrupt is also a case of a signalled thread.
     419                 :   // At the moment we handle GDB interrupt differently, without using a signal,
     420                 :   // so in this case sig_thread_ is 0.
     421                 :   // This might seem weird to GDB, so at least avoid reporting tid 0.
     422                 :   // TODO(eaeltsin): http://code.google.com/p/nativeclient/issues/detail?id=2743
     423              97 :   if (sig_thread_ != 0) {
     424                 :     // Add 'thread:<tid>;' pair. Note terminating ';' is required.
     425              96 :     pktOut->AddString("thread:");
     426              96 :     pktOut->AddNumberSep(sig_thread_, ';');
     427              96 :   }
     428              97 : }
     429                 : 
     430                 : 
     431               1 : bool Target::GetFirstThreadId(uint32_t *id) {
     432               1 :   threadItr_ = threads_.begin();
     433               1 :   return GetNextThreadId(id);
     434                 : }
     435                 : 
     436               3 : bool Target::GetNextThreadId(uint32_t *id) {
     437               4 :   if (threadItr_ == threads_.end()) return false;
     438                 : 
     439               2 :   *id = (*threadItr_).first;
     440               2 :   threadItr_++;
     441                 : 
     442               2 :   return true;
     443               3 : }
     444                 : 
     445                 : 
     446             469 : uint64_t Target::AdjustUserAddr(uint64_t addr) {
     447                 :   // On x86-64, GDB sometimes uses memory addresses with the %r15
     448                 :   // sandbox base included, so we must accept these addresses.
     449                 :   // TODO(eaeltsin): Fix GDB to not use addresses with %r15 added.
     450                 :   if (NACL_ARCH(NACL_BUILD_ARCH) == NACL_x86 && NACL_BUILD_SUBARCH == 64 &&
     451             469 :       NaClIsUserAddr(nap_, (uintptr_t) addr)) {
     452              17 :     return NaClSysToUser(nap_, (uintptr_t) addr);
     453                 :   }
     454                 :   // Otherwise, we expect an untrusted address.
     455             452 :   return addr;
     456             469 : }
     457                 : 
     458               0 : void Target::EmitFileError(Packet *pktOut, int code) {
     459               0 :   pktOut->AddString("F");
     460               0 :   pktOut->AddNumberSep(kGdbErrorResult, ',');
     461               0 :   pktOut->AddNumberSep(code, 0);
     462               0 : }
     463                 : 
     464             101 : void Target::ProcessFilePacket(Packet *pktIn, Packet *pktOut, ErrDef *err) {
     465             101 :   std::string cmd;
     466             202 :   if (!pktIn->GetStringSep(&cmd, ':')) {
     467               0 :     *err = BAD_FORMAT;
     468               0 :     return;
     469                 :   }
     470             505 :   CHECK(cmd == "File");
     471             101 :   std::string subcmd;
     472             202 :   if (!pktIn->GetStringSep(&subcmd, ':')) {
     473               0 :     *err = BAD_FORMAT;
     474               0 :     return;
     475                 :   }
     476             202 :   if (subcmd == "open") {
     477               1 :     std::string filename;
     478               1 :     char sep;
     479               1 :     uint64_t flags;
     480               1 :     uint64_t mode;
     481               2 :     if (!pktIn->GetHexString(&filename) ||
     482               3 :         !pktIn->GetRawChar(&sep) ||
     483                 :         sep != ',' ||
     484               2 :         !pktIn->GetNumberSep(&flags, NULL) ||
     485               2 :         !pktIn->GetNumberSep(&mode, NULL)) {
     486               0 :       *err = BAD_ARGS;
     487               0 :       return;
     488                 :     }
     489               2 :     if (filename == kMainNexeFilename) {
     490               1 :       if (flags == kGdbO_RDONLY) {
     491               1 :         pktOut->AddString("F");
     492               1 :         pktOut->AddNumberSep(kMainNexeFd, 0);
     493               1 :       } else {
     494               0 :         EmitFileError(pktOut, kGdbEPERM);
     495                 :       }
     496               1 :     } else if (filename == kIrtNexeFilename) {
     497               0 :       if (flags == kGdbO_RDONLY) {
     498               0 :         pktOut->AddString("F");
     499               0 :         pktOut->AddNumberSep(kIrtNexeFd, 0);
     500               0 :       } else {
     501               0 :         EmitFileError(pktOut, kGdbEPERM);
     502                 :       }
     503               0 :     } else {
     504               0 :       EmitFileError(pktOut, kGdbENOENT);
     505                 :     }
     506               1 :     return;
     507             201 :   } else if (subcmd == "close") {
     508               1 :     uint64_t fd;
     509               2 :     if (!pktIn->GetNumberSep(&fd, NULL)) {
     510               0 :       *err = BAD_ARGS;
     511               0 :       return;
     512                 :     }
     513               1 :     if (fd == kMainNexeFd) {
     514               1 :       pktOut->AddString("F");
     515               1 :       pktOut->AddNumberSep(0, 0);
     516               1 :     } else if (fd == kIrtNexeFd) {
     517               0 :       pktOut->AddString("F");
     518               0 :       pktOut->AddNumberSep(0, 0);
     519               0 :     } else {
     520               0 :       EmitFileError(pktOut, kGdbEBADF);
     521                 :     }
     522               1 :     return;
     523             198 :   } else if (subcmd == "pread") {
     524              99 :     uint64_t fd;
     525              99 :     uint64_t count;
     526              99 :     uint64_t offset;
     527              99 :     std::string data;
     528             198 :     if (!pktIn->GetNumberSep(&fd, NULL) ||
     529             198 :         !pktIn->GetNumberSep(&count, NULL) ||
     530             198 :         !pktIn->GetNumberSep(&offset, NULL)) {
     531               0 :       *err = BAD_ARGS;
     532               0 :       return;
     533                 :     }
     534              99 :     NaClDesc *desc;
     535              99 :     if (fd == kMainNexeFd) {
     536              99 :       desc = nap_->main_nexe_desc;
     537              99 :     } else if (fd == kIrtNexeFd) {
     538               0 :       desc = nap_->irt_nexe_desc;
     539               0 :     } else {
     540               0 :       EmitFileError(pktOut, kGdbEBADF);
     541               0 :       return;
     542                 :     }
     543             396 :     CHECK(NULL != desc);
     544              99 :     if (count > kGdbPreadChunkSize) {
     545               0 :       count = kGdbPreadChunkSize;
     546               0 :     }
     547             198 :     nacl::scoped_array<char> buffer(new char[kGdbPreadChunkSize]);
     548             297 :     ssize_t result = (*NACL_VTBL(NaClDesc, desc)->PRead)(
     549              99 :         desc, buffer.get(),
     550                 :         static_cast<size_t>(count),
     551                 :         static_cast<nacl_off64_t>(offset));
     552              99 :     pktOut->AddString("F");
     553              99 :     if (result < 0) {
     554               0 :       pktOut->AddNumberSep(kGdbErrorResult, ',');
     555               0 :       pktOut->AddNumberSep(static_cast<uint64_t>(-result), 0);
     556               0 :     } else {
     557              99 :       pktOut->AddNumberSep(static_cast<uint64_t>(result), ';');
     558             198 :       pktOut->AddEscapedData(buffer.get(), static_cast<size_t>(result));
     559                 :     }
     560                 :     return;
     561             198 :   }
     562               0 :   NaClLog(LOG_ERROR, "Unknown vFile command: %s\n", pktIn->GetPayload());
     563               0 :   *err = BAD_FORMAT;
     564             303 : }
     565                 : 
     566            1174 : bool Target::ProcessPacket(Packet* pktIn, Packet* pktOut) {
     567            1174 :   char cmd;
     568            1174 :   int32_t seq = -1;
     569            1174 :   ErrDef  err = NONE;
     570                 : 
     571                 :   // Clear the outbound message
     572            1174 :   pktOut->Clear();
     573                 : 
     574                 :   // Pull out the sequence.
     575            1174 :   pktIn->GetSequence(&seq);
     576            1174 :   if (seq != -1) pktOut->SetSequence(seq);
     577                 : 
     578                 :   // Find the command
     579            1174 :   pktIn->GetRawChar(&cmd);
     580                 : 
     581            1174 :   switch (cmd) {
     582                 :     // IN : $?
     583                 :     // OUT: $Sxx
     584                 :     case '?':
     585              29 :       SetStopReply(pktOut);
     586              29 :       break;
     587                 : 
     588                 :     case 'c':
     589               0 :       return true;
     590                 : 
     591                 :     // IN : $D
     592                 :     // OUT: $OK
     593                 :     case 'D':
     594               1 :       Detach();
     595               1 :       pktOut->AddString("OK");
     596               1 :       return false;
     597                 : 
     598                 :     // IN : $k
     599                 :     // OUT: $OK
     600                 :     case 'k':
     601              15 :       Kill();
     602              15 :       pktOut->AddString("OK");
     603              15 :       return false;
     604                 : 
     605                 :     // IN : $g
     606                 :     // OUT: $xx...xx
     607                 :     case 'g': {
     608             100 :       IThread *thread = GetRegThread();
     609             100 :       if (NULL == thread) {
     610               0 :         err = BAD_ARGS;
     611               0 :         break;
     612                 :       }
     613                 : 
     614                 :       // Copy OS preserved registers to GDB payload
     615            5000 :       for (uint32_t a = 0; a < abi_->GetRegisterCount(); a++) {
     616            2400 :         const Abi::RegDef *def = abi_->GetRegisterDef(a);
     617            2400 :         thread->GetRegister(a, &ctx_[def->offset_], def->bytes_);
     618            2400 :       }
     619                 : 
     620             100 :       pktOut->AddBlock(ctx_, abi_->GetContextSize());
     621             100 :       break;
     622                 :     }
     623                 : 
     624                 :     // IN : $Gxx..xx
     625                 :     // OUT: $OK
     626                 :     case 'G': {
     627              16 :       IThread *thread = GetRegThread();
     628              16 :       if (NULL == thread) {
     629               0 :         err = BAD_ARGS;
     630               0 :         break;
     631                 :       }
     632                 : 
     633              16 :       pktIn->GetBlock(ctx_, abi_->GetContextSize());
     634                 : 
     635                 :       // GDB payload to OS registers
     636             800 :       for (uint32_t a = 0; a < abi_->GetRegisterCount(); a++) {
     637             384 :         const Abi::RegDef *def = abi_->GetRegisterDef(a);
     638             384 :         thread->SetRegister(a, &ctx_[def->offset_], def->bytes_);
     639             384 :       }
     640                 : 
     641              16 :       pktOut->AddString("OK");
     642              16 :       break;
     643                 :     }
     644                 : 
     645                 :     // IN : $H(c/g)(-1,0,xxxx)
     646                 :     // OUT: $OK
     647                 :     case 'H': {
     648              61 :         char type;
     649              61 :         uint64_t id;
     650                 : 
     651              61 :         if (!pktIn->GetRawChar(&type)) {
     652               0 :           err = BAD_FORMAT;
     653               0 :           break;
     654                 :         }
     655              61 :         if (!pktIn->GetNumberSep(&id, 0)) {
     656               0 :           err = BAD_FORMAT;
     657               0 :           break;
     658                 :         }
     659                 : 
     660              61 :         if (threads_.begin() == threads_.end()) {
     661               0 :             err = BAD_ARGS;
     662               0 :             break;
     663                 :         }
     664                 : 
     665                 :         // If we are using "any" get the first thread
     666              90 :         if (id == static_cast<uint64_t>(-1)) id = threads_.begin()->first;
     667                 : 
     668                 :         // Verify that we have the thread
     669              61 :         if (threads_.find(static_cast<uint32_t>(id)) == threads_.end()) {
     670              29 :           err = BAD_ARGS;
     671              29 :           break;
     672                 :         }
     673                 : 
     674              61 :         pktOut->AddString("OK");
     675              61 :         switch (type) {
     676                 :           case 'g':
     677               3 :             reg_thread_ = static_cast<uint32_t>(id);
     678              32 :             break;
     679                 : 
     680                 :           case 'c':
     681                 :             // 'c' is deprecated in favor of vCont.
     682                 :           default:
     683              29 :             err = BAD_ARGS;
     684              29 :             break;
     685                 :         }
     686              32 :         break;
     687                 :       }
     688                 : 
     689                 :     // IN : $maaaa,llll
     690                 :     // OUT: $xx..xx
     691                 :     case 'm': {
     692             465 :         uint64_t user_addr;
     693             465 :         uint64_t wlen;
     694             465 :         uint32_t len;
     695             465 :         if (!pktIn->GetNumberSep(&user_addr, 0)) {
     696               0 :           err = BAD_FORMAT;
     697               0 :           break;
     698                 :         }
     699             465 :         if (!pktIn->GetNumberSep(&wlen, 0)) {
     700               0 :           err = BAD_FORMAT;
     701               0 :           break;
     702                 :         }
     703             465 :         user_addr = AdjustUserAddr(user_addr);
     704             465 :         uint64_t sys_addr = NaClUserToSysAddrRange(nap_, (uintptr_t) user_addr,
     705                 :                                                    (size_t) wlen);
     706             465 :         if (sys_addr == kNaClBadAddress) {
     707               0 :           err = FAILED;
     708               0 :           break;
     709                 :         }
     710                 : 
     711             465 :         len = static_cast<uint32_t>(wlen);
     712             465 :         nacl::scoped_array<uint8_t> block(new uint8_t[len]);
     713            1395 :         if (!port::IPlatform::GetMemory(sys_addr, len, block.get())) {
     714               3 :           err = FAILED;
     715               3 :           break;
     716                 :         }
     717             924 :         EraseBreakpointsFromCopyOfMemory((uint32_t) user_addr,
     718             462 :                                          block.get(), len);
     719                 : 
     720             924 :         pktOut->AddBlock(block.get(), len);
     721             462 :         break;
     722             465 :       }
     723                 : 
     724                 :     // IN : $Maaaa,llll:xx..xx
     725                 :     // OUT: $OK
     726                 :     case 'M':  {
     727               4 :         uint64_t user_addr;
     728               4 :         uint64_t wlen;
     729               4 :         uint32_t len;
     730                 : 
     731               4 :         if (!pktIn->GetNumberSep(&user_addr, 0)) {
     732               0 :           err = BAD_FORMAT;
     733               0 :           break;
     734                 :         }
     735               4 :         if (!pktIn->GetNumberSep(&wlen, 0)) {
     736               0 :           err = BAD_FORMAT;
     737               0 :           break;
     738                 :         }
     739               4 :         user_addr = AdjustUserAddr(user_addr);
     740               4 :         uint64_t sys_addr = NaClUserToSysAddrRange(nap_, (uintptr_t) user_addr,
     741                 :                                                    (size_t) wlen);
     742               4 :         if (sys_addr == kNaClBadAddress) {
     743               0 :           err = FAILED;
     744               0 :           break;
     745                 :         }
     746               4 :         len = static_cast<uint32_t>(wlen);
     747                 :         // We disallow the debugger from modifying code.
     748               4 :         if (user_addr < nap_->dynamic_text_end) {
     749               0 :           err = FAILED;
     750               0 :           break;
     751                 :         }
     752                 : 
     753               4 :         nacl::scoped_array<uint8_t> block(new uint8_t[len]);
     754               8 :         pktIn->GetBlock(block.get(), len);
     755                 : 
     756              12 :         if (!port::IPlatform::SetMemory(nap_, sys_addr, len, block.get())) {
     757               0 :           err = FAILED;
     758               0 :           break;
     759                 :         }
     760                 : 
     761               4 :         pktOut->AddString("OK");
     762               4 :         break;
     763               4 :       }
     764                 : 
     765                 :     case 'q': {
     766             180 :       string tmp;
     767             360 :       const char *str = &pktIn->GetPayload()[1];
     768             720 :       stringvec toks = StringSplit(str, ":;");
     769             540 :       PropertyMap_t::const_iterator itr = properties_.find(toks[0]);
     770                 : 
     771                 :       // If this is a thread query
     772             718 :       if (!strcmp(str, "fThreadInfo") || !strcmp(str, "sThreadInfo")) {
     773               3 :         uint32_t curr;
     774               3 :         bool more = false;
     775               3 :         if (str[0] == 'f') {
     776               2 :           more = GetFirstThreadId(&curr);
     777               1 :         } else {
     778               4 :           more = GetNextThreadId(&curr);
     779                 :         }
     780                 : 
     781               3 :         if (!more) {
     782               1 :           pktOut->AddString("l");
     783               1 :         } else {
     784               2 :           pktOut->AddString("m");
     785               2 :           pktOut->AddNumberSep(curr, 0);
     786                 :         }
     787               3 :         break;
     788                 :       }
     789                 : 
     790                 :       // Check for architecture query
     791             177 :       tmp = "Xfer:features:read:target.xml";
     792             708 :       if (!strncmp(str, tmp.data(), tmp.length())) {
     793             145 :         stringvec args = StringSplit(&str[tmp.length()+1], ",");
     794              58 :         if (args.size() != 2) break;
     795                 : 
     796             145 :         const char *out = properties_["target.xml"].data();
     797             116 :         int offs = strtol(args[0].data(), NULL, 16);
     798             116 :         int max  = strtol(args[1].data(), NULL, 16) + offs;
     799              58 :         int len  = static_cast<int>(strlen(out));
     800                 : 
     801              58 :         if (max >= len) max = len;
     802                 : 
     803            2291 :         while (offs < max) {
     804            2233 :           pktOut->AddRawChar(out[offs]);
     805            2233 :           offs++;
     806            2233 :         }
     807              29 :         break;
     808              29 :       }
     809                 : 
     810                 :       // Check the property cache
     811             592 :       if (itr != properties_.end()) {
     812              87 :         pktOut->AddString(itr->second.data());
     813              29 :       }
     814             148 :       break;
     815             360 :     }
     816                 : 
     817                 :     case 's': {
     818               0 :       IThread *thread = GetRunThread();
     819               0 :       if (thread) thread->SetStep(true);
     820               0 :       return true;
     821                 :     }
     822                 : 
     823                 :     case 'T': {
     824               5 :       uint64_t id;
     825               5 :       if (!pktIn->GetNumberSep(&id, 0)) {
     826               0 :         err = BAD_FORMAT;
     827               0 :         break;
     828                 :       }
     829                 : 
     830               5 :       if (GetThread(static_cast<uint32_t>(id)) == NULL) {
     831               0 :         err = BAD_ARGS;
     832               0 :         break;
     833                 :       }
     834                 : 
     835               5 :       pktOut->AddString("OK");
     836               5 :       break;
     837                 :     }
     838                 : 
     839                 :     case 'v': {
     840             187 :       const char *str = pktIn->GetPayload() + 1;
     841                 : 
     842             187 :       if (strncmp(str, "Cont", 4) == 0) {
     843                 :         // vCont
     844              86 :         const char *subcommand = str + 4;
     845                 : 
     846              86 :         if (strcmp(subcommand, "?") == 0) {
     847                 :           // Report supported vCont actions. These 4 are required.
     848              18 :           pktOut->AddString("vCont;s;S;c;C");
     849              18 :           break;
     850                 :         }
     851                 : 
     852              68 :         if (strcmp(subcommand, ";c") == 0) {
     853                 :           // Continue all threads.
     854              32 :           return true;
     855                 :         }
     856                 : 
     857              36 :         if (strncmp(subcommand, ";s:", 3) == 0) {
     858                 :           // Single step one thread and optionally continue all other threads.
     859              36 :           char *end;
     860              36 :           uint32_t thread_id = static_cast<uint32_t>(
     861              36 :               strtol(subcommand + 3, &end, 16));
     862              36 :           if (end == subcommand + 3) {
     863               0 :             err = BAD_ARGS;
     864               0 :             break;
     865                 :           }
     866                 : 
     867              36 :           ThreadMap_t::iterator it = threads_.find(thread_id);
     868              36 :           if (it == threads_.end()) {
     869               0 :             err = BAD_ARGS;
     870               0 :             break;
     871                 :           }
     872                 : 
     873              36 :           if (*end == 0) {
     874                 :             // Single step one thread and keep other threads stopped.
     875                 :             // GDB uses this to continue from a breakpoint, which works by:
     876                 :             // - replacing trap instruction with the original instruction;
     877                 :             // - single-stepping through the original instruction. Other threads
     878                 :             //   must remain stopped, otherwise they might execute the code at
     879                 :             //   the same address and thus miss the breakpoint;
     880                 :             // - replacing the original instruction with trap instruction;
     881                 :             // - continuing all threads;
     882              11 :             if (thread_id != sig_thread_) {
     883               0 :               err = BAD_ARGS;
     884               0 :               break;
     885                 :             }
     886              11 :             step_over_breakpoint_thread_ = sig_thread_;
     887              36 :           } else if (strcmp(end, ";c") == 0) {
     888                 :             // Single step one thread and continue all other threads.
     889              25 :           } else {
     890                 :             // Unsupported combination of single step and other args.
     891               0 :             err = BAD_ARGS;
     892               0 :             break;
     893                 :           }
     894                 : 
     895              36 :           it->second->SetStep(true);
     896              36 :           return true;
     897                 :         }
     898                 : 
     899                 :         // Continue one thread and keep other threads stopped.
     900                 :         //
     901                 :         // GDB sends this for software single step, which is used:
     902                 :         // - on Win64 to step over rsp modification and subsequent rsp
     903                 :         //   sandboxing at once. For details, see:
     904                 :         //     http://code.google.com/p/nativeclient/issues/detail?id=2903
     905                 :         // - TODO: on ARM, which has no hardware support for single step
     906                 :         // - TODO: to step over syscalls
     907                 :         //
     908                 :         // Unfortunately, we can't make this just Win-specific. We might
     909                 :         // use Linux GDB to connect to Win debug stub, so even Linux GDB
     910                 :         // should send software single step. Vice versa, software single
     911                 :         // step-enabled Win GDB might be connected to Linux debug stub,
     912                 :         // so even Linux debug stub should accept software single step.
     913               0 :         if (strncmp(subcommand, ";c:", 3) == 0) {
     914               0 :           char *end;
     915               0 :           uint32_t thread_id = static_cast<uint32_t>(
     916               0 :               strtol(subcommand + 3, &end, 16));
     917               0 :           if (end != subcommand + 3 && *end == 0) {
     918               0 :             if (thread_id == sig_thread_) {
     919               0 :               step_over_breakpoint_thread_ = sig_thread_;
     920               0 :               return true;
     921                 :             }
     922               0 :           }
     923                 : 
     924               0 :           err = BAD_ARGS;
     925               0 :           break;
     926                 :         }
     927                 : 
     928                 :         // Unsupported form of vCont.
     929               0 :         err = BAD_FORMAT;
     930               0 :         break;
     931             101 :       } else if (strncmp(str, "File:", 5) == 0) {
     932             101 :         ProcessFilePacket(pktIn, pktOut, &err);
     933             101 :         break;
     934                 :       }
     935                 : 
     936               0 :       NaClLog(LOG_ERROR, "Unknown command: %s\n", pktIn->GetPayload());
     937               0 :       return false;
     938                 :     }
     939                 : 
     940                 :     case 'Z': {
     941              52 :       uint64_t breakpoint_type;
     942              52 :       uint64_t breakpoint_address;
     943              52 :       uint64_t breakpoint_kind;
     944             104 :       if (!pktIn->GetNumberSep(&breakpoint_type, 0) ||
     945                 :           breakpoint_type != 0 ||
     946              52 :           !pktIn->GetNumberSep(&breakpoint_address, 0) ||
     947              52 :           !pktIn->GetNumberSep(&breakpoint_kind, 0)) {
     948               0 :         err = BAD_FORMAT;
     949               0 :         break;
     950                 :       }
     951              52 :       if (breakpoint_address != (uint32_t) breakpoint_address ||
     952              52 :           !AddBreakpoint((uint32_t) breakpoint_address)) {
     953               1 :         err = FAILED;
     954               1 :         break;
     955                 :       }
     956              51 :       pktOut->AddString("OK");
     957              51 :       break;
     958                 :     }
     959                 : 
     960                 :     case 'z': {
     961              51 :       uint64_t breakpoint_type;
     962              51 :       uint64_t breakpoint_address;
     963              51 :       uint64_t breakpoint_kind;
     964             102 :       if (!pktIn->GetNumberSep(&breakpoint_type, 0) ||
     965                 :           breakpoint_type != 0 ||
     966              51 :           !pktIn->GetNumberSep(&breakpoint_address, 0) ||
     967              51 :           !pktIn->GetNumberSep(&breakpoint_kind, 0)) {
     968               0 :         err = BAD_FORMAT;
     969               0 :         break;
     970                 :       }
     971              51 :       if (breakpoint_address != (uint32_t) breakpoint_address ||
     972              51 :           !RemoveBreakpoint((uint32_t) breakpoint_address)) {
     973               0 :         err = FAILED;
     974               0 :         break;
     975                 :       }
     976              51 :       pktOut->AddString("OK");
     977              51 :       break;
     978                 :     }
     979                 : 
     980                 :     default: {
     981                 :       // If the command is not recognzied, ignore it by sending an
     982                 :       // empty reply.
     983               8 :       string str;
     984               8 :       pktIn->GetString(&str);
     985              16 :       NaClLog(LOG_ERROR, "Unknown command: %s\n", pktIn->GetPayload());
     986               8 :       return false;
     987               8 :     }
     988                 :   }
     989                 : 
     990                 :   // If there is an error, return the error code instead of a payload
     991            1082 :   if (err) {
     992              62 :     pktOut->Clear();
     993              62 :     pktOut->AddRawChar('E');
     994              62 :     pktOut->AddWord8(err);
     995              62 :   }
     996            1082 :   return false;
     997            1174 : }
     998                 : 
     999                 : 
    1000              20 : void Target::TrackThread(struct NaClAppThread *natp) {
    1001                 :   // natp->thread_num values are 0-based indexes, but we treat 0 as
    1002                 :   // "not a thread ID", so we add 1.
    1003              20 :   uint32_t id = natp->thread_num + 1;
    1004              20 :   MutexLock lock(&mutex_);
    1005             100 :   CHECK(threads_[id] == 0);
    1006              60 :   threads_[id] = IThread::Create(id, natp);
    1007              20 : }
    1008                 : 
    1009               3 : void Target::IgnoreThread(struct NaClAppThread *natp) {
    1010               3 :   uint32_t id = natp->thread_num + 1;
    1011               3 :   MutexLock lock(&mutex_);
    1012               6 :   ThreadMap_t::iterator iter = threads_.find(id);
    1013              18 :   CHECK(iter != threads_.end());
    1014              12 :   delete iter->second;
    1015               3 :   threads_.erase(iter);
    1016               3 : }
    1017                 : 
    1018                 : void Target::Exit() {
    1019               2 :   MutexLock lock(&mutex_);
    1020               2 :   if (session_ != NULL) {
    1021               1 :     Packet exit_packet;
    1022               1 :     if (NACL_ABI_WIFSIGNALED(nap_->exit_status)) {
    1023               0 :       exit_packet.AddRawChar('X');
    1024               0 :       exit_packet.AddWord8(NACL_ABI_WTERMSIG(nap_->exit_status));
    1025               0 :     } else {
    1026               1 :       exit_packet.AddRawChar('W');
    1027               1 :       exit_packet.AddWord8(NACL_ABI_WEXITSTATUS(nap_->exit_status));
    1028                 :     }
    1029               1 :     session_->SendPacket(&exit_packet);
    1030               2 :   }
    1031               2 : }
    1032                 : 
    1033                 : void Target::Detach() {
    1034               1 :   NaClLog(LOG_INFO, "Requested Detach.\n");
    1035               1 :   detaching_ = true;
    1036               1 : }
    1037                 : 
    1038                 : void Target::Kill() {
    1039              15 :   NaClLog(LOG_INFO, "Requested Kill.\n");
    1040              15 :   should_exit_ = true;
    1041              15 : }
    1042                 : 
    1043                 : IThread* Target::GetRegThread() {
    1044             116 :   ThreadMap_t::const_iterator itr;
    1045                 : 
    1046             116 :   switch (reg_thread_) {
    1047                 :     // If we want "any" then try the signal'd thread first
    1048                 :     case 0:
    1049                 :     case 0xFFFFFFFF:
    1050               0 :       itr = threads_.begin();
    1051               0 :       break;
    1052                 : 
    1053                 :     default:
    1054             116 :       itr = threads_.find(reg_thread_);
    1055             116 :       break;
    1056                 :   }
    1057                 : 
    1058             116 :   if (itr == threads_.end()) return 0;
    1059                 : 
    1060             116 :   return itr->second;
    1061             116 : }
    1062                 : 
    1063                 : IThread* Target::GetRunThread() {
    1064                 :   // This is used to select a thread for "s" (step) command only.
    1065                 :   // For multi-threaded targets, "s" is deprecated in favor of "vCont", which
    1066                 :   // always specifies the thread explicitly when needed. However, we want
    1067                 :   // to keep backward compatibility here, as using "s" when debugging
    1068                 :   // a single-threaded program might be a popular use case.
    1069               0 :   if (threads_.size() == 1) {
    1070               0 :     return threads_.begin()->second;
    1071                 :   }
    1072               0 :   return NULL;
    1073               0 : }
    1074                 : 
    1075               5 : IThread* Target::GetThread(uint32_t id) {
    1076               5 :   ThreadMap_t::const_iterator itr;
    1077               5 :   itr = threads_.find(id);
    1078              10 :   if (itr != threads_.end()) return itr->second;
    1079                 : 
    1080               0 :   return NULL;
    1081               5 : }
    1082                 : 
    1083                 : void Target::SuspendAllThreads() {
    1084              74 :   NaClUntrustedThreadsSuspendAll(nap_, /* save_registers= */ 1);
    1085              74 :   for (ThreadMap_t::const_iterator iter = threads_.begin();
    1086             155 :        iter != threads_.end();
    1087              81 :        ++iter) {
    1088              81 :     IThread *thread = iter->second;
    1089              81 :     thread->CopyRegistersFromAppThread();
    1090              81 :     CopyFaultSignalFromAppThread(thread);
    1091              81 :   }
    1092              74 : }
    1093                 : 
    1094                 : void Target::ResumeAllThreads() {
    1095              58 :   for (ThreadMap_t::const_iterator iter = threads_.begin();
    1096             122 :        iter != threads_.end();
    1097              64 :        ++iter) {
    1098              64 :     iter->second->CopyRegistersToAppThread();
    1099              64 :   }
    1100              58 :   NaClUntrustedThreadsResumeAll(nap_);
    1101              58 : }
    1102                 : 
    1103                 : // UnqueueAnyFaultedThread() picks a thread that has been blocked as a
    1104                 : // result of faulting and unblocks it.  It returns the thread's ID via
    1105                 : // |thread_id| and the type of fault via |signal|.  As a precondition,
    1106                 : // all threads must be currently suspended.
    1107              73 : void Target::UnqueueAnyFaultedThread(uint32_t *thread_id, int8_t *signal) {
    1108              73 :   for (ThreadMap_t::const_iterator iter = threads_.begin();
    1109              79 :        iter != threads_.end();
    1110               6 :        ++iter) {
    1111              79 :     IThread *thread = iter->second;
    1112              79 :     if (thread->GetFaultSignal() != 0) {
    1113              73 :       *signal = thread->GetFaultSignal();
    1114              73 :       *thread_id = thread->GetId();
    1115              73 :       thread->UnqueueFaultedThread();
    1116              73 :       return;
    1117                 :     }
    1118               6 :   }
    1119               0 :   NaClLog(LOG_FATAL, "UnqueueAnyFaultedThread: No threads queued\n");
    1120              73 : }
    1121                 : 
    1122                 : }  // namespace gdb_rsp

Generated by: LCOV version 1.7