target.cc 20.4 KB
Newer Older
1 2 3 4 5 6
// Copyright 2020 the V8 project authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.

#include "src/debug/wasm/gdb-server/target.h"

7
#include <inttypes.h>
8
#include "src/base/platform/time.h"
9
#include "src/debug/wasm/gdb-server/gdb-remote-util.h"
10
#include "src/debug/wasm/gdb-server/gdb-server.h"
11
#include "src/debug/wasm/gdb-server/packet.h"
12 13 14 15 16 17 18 19
#include "src/debug/wasm/gdb-server/session.h"
#include "src/debug/wasm/gdb-server/transport.h"

namespace v8 {
namespace internal {
namespace wasm {
namespace gdb_server {

20 21
static const int kThreadId = 1;

22 23 24 25
// Signals.
static const int kSigTrace = 5;
static const int kSigSegv = 11;

26
Target::Target(GdbServer* gdb_server)
27 28 29
    : gdb_server_(gdb_server),
      status_(Status::Running),
      cur_signal_(0),
30 31 32 33 34 35
      session_(nullptr),
      debugger_initial_suspension_(true),
      semaphore_(0),
      current_isolate_(nullptr) {
  InitQueryPropertyMap();
}
36 37 38 39

void Target::InitQueryPropertyMap() {
  // Request LLDB to send packets up to 4000 bytes for bulk transfers.
  query_properties_["Supported"] =
40
      "PacketSize=1000;vContSupported-;qXfer:libraries:read+;wasm+;";
41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60

  query_properties_["Attached"] = "1";

  // There is only one register, named 'pc', in this architecture
  query_properties_["RegisterInfo0"] =
      "name:pc;alt-name:pc;bitsize:64;offset:0;encoding:uint;format:hex;set:"
      "General Purpose Registers;gcc:16;dwarf:16;generic:pc;";
  query_properties_["RegisterInfo1"] = "E45";

  // ProcessInfo for wasm32
  query_properties_["ProcessInfo"] =
      "pid:1;ppid:1;uid:1;gid:1;euid:1;egid:1;name:6c6c6462;triple:" +
      Mem2Hex("wasm32-unknown-unknown-wasm") + ";ptrsize:4;";
  query_properties_["Symbol"] = "OK";

  // Current thread info
  char buff[16];
  snprintf(buff, sizeof(buff), "QC%x", kThreadId);
  query_properties_["C"] = buff;
}
61 62

void Target::Terminate() {
63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88
  // Executed in the Isolate thread, when the process shuts down.
  SetStatus(Status::Terminated);
}

void Target::OnProgramBreak(Isolate* isolate,
                            const std::vector<wasm_addr_t>& call_frames) {
  OnSuspended(isolate, kSigTrace, call_frames);
}
void Target::OnException(Isolate* isolate,
                         const std::vector<wasm_addr_t>& call_frames) {
  OnSuspended(isolate, kSigSegv, call_frames);
}
void Target::OnSuspended(Isolate* isolate, int signal,
                         const std::vector<wasm_addr_t>& call_frames) {
  // This function will be called in the isolate thread, when the wasm
  // interpreter gets suspended.

  bool isWaitingForSuspension = (status_ == Status::WaitingForSuspension);
  SetStatus(Status::Suspended, signal, call_frames, isolate);
  if (isWaitingForSuspension) {
    // Wake the GdbServer thread that was blocked waiting for the Target
    // to suspend.
    semaphore_.Signal();
  } else if (session_) {
    session_->SignalThreadEvent();
  }
89 90 91 92 93 94 95
}

void Target::Run(Session* session) {
  // Executed in the GdbServer thread.
  session_ = session;
  do {
    WaitForDebugEvent();
96
    ProcessDebugEvent();
97 98 99 100 101 102 103 104
    ProcessCommands();
  } while (!IsTerminated() && session_->IsConnected());
  session_ = nullptr;
}

void Target::WaitForDebugEvent() {
  // Executed in the GdbServer thread.

105
  if (status_ == Status::Running) {
106 107 108 109 110 111 112
    // Wait for either:
    //   * the thread to fault (or single-step)
    //   * an interrupt from LLDB
    session_->WaitForDebugStubEvent();
  }
}

113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151
void Target::ProcessDebugEvent() {
  // Executed in the GdbServer thread

  if (status_ == Status::Running) {
    // Blocks, waiting for the engine to suspend.
    Suspend();
  }

  // Here, the wasm interpreter has suspended and we have updated the current
  // thread info.

  if (debugger_initial_suspension_) {
    // First time on a connection, we don't send the signal.
    // All other times, send the signal that triggered us.
    debugger_initial_suspension_ = false;
  } else {
    Packet pktOut;
    SetStopReply(&pktOut);
    session_->SendPacket(&pktOut, false);
  }
}

void Target::Suspend() {
  // Executed in the GdbServer thread
  if (status_ == Status::Running) {
    // TODO(paolosev) - this only suspends the wasm interpreter.
    gdb_server_->Suspend();

    status_ = Status::WaitingForSuspension;
  }

  while (status_ == Status::WaitingForSuspension) {
    if (semaphore_.WaitFor(base::TimeDelta::FromMilliseconds(500))) {
      // Here the wasm interpreter is suspended.
      return;
    }
  }
}

152 153 154 155 156
void Target::ProcessCommands() {
  // GDB-remote messages are processed in the GDBServer thread.

  if (IsTerminated()) {
    return;
157 158 159
  } else if (status_ != Status::Suspended) {
    // Don't process commands if we haven't stopped.
    return;
160 161
  }

162 163 164
  // Now we are ready to process commands.
  // Loop through packets until we process a continue packet or a detach.
  Packet recv, reply;
165 166 167 168 169
  while (session_->IsConnected()) {
    if (!session_->GetPacket(&recv)) {
      continue;
    }

170
    reply.Clear();
171 172 173 174 175 176 177
    ProcessPacketResult result = ProcessPacket(&recv, &reply);
    switch (result) {
      case ProcessPacketResult::Paused:
        session_->SendPacket(&reply);
        break;

      case ProcessPacketResult::Continue:
178
        DCHECK_EQ(status_, Status::Running);
179
        // If this is a continue type command, break out of this loop.
180
        gdb_server_->QuitMessageLoopOnPause();
181 182 183
        return;

      case ProcessPacketResult::Detach:
184
        SetStatus(Status::Running);
185 186
        session_->SendPacket(&reply);
        session_->Disconnect();
187
        gdb_server_->QuitMessageLoopOnPause();
188 189 190 191 192 193 194 195
        return;

      case ProcessPacketResult::Kill:
        session_->SendPacket(&reply);
        exit(-9);

      default:
        UNREACHABLE();
196
    }
197
  }
198 199 200 201

  if (!session_->IsConnected()) {
    debugger_initial_suspension_ = true;
  }
202 203
}

204 205 206 207 208 209 210 211
Target::ProcessPacketResult Target::ProcessPacket(Packet* pkt_in,
                                                  Packet* pkt_out) {
  ErrorCode err = ErrorCode::None;

  // Clear the outbound message.
  pkt_out->Clear();

  // Set the sequence number, if present.
212
  int32_t seq = -1;
213 214
  if (pkt_in->GetSequence(&seq)) {
    pkt_out->SetSequence(seq);
215 216
  }

217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237
  // A GDB-remote packet begins with an upper- or lower-case letter, which
  // generally represents a single command.
  // The letters 'q' and 'Q' introduce a "General query packets" and are used
  // to extend the protocol with custom commands.
  // The format of GDB-remote commands is documented here:
  // https://sourceware.org/gdb/onlinedocs/gdb/Overview.html#Overview.
  char cmd;
  pkt_in->GetRawChar(&cmd);

  switch (cmd) {
    // Queries the reason the target halted.
    // IN : $?
    // OUT: A Stop-reply packet
    case '?':
      SetStopReply(pkt_out);
      break;

    // Resumes execution
    // IN : $c
    // OUT: A Stop-reply packet is sent later, when the execution halts.
    case 'c':
238 239
      SetStatus(Status::Running);
      return ProcessPacketResult::Continue;
240 241 242 243 244 245 246 247

    // Detaches the debugger from this target
    // IN : $D
    // OUT: $OK
    case 'D':
      TRACE_GDB_REMOTE("Requested Detach.\n");
      pkt_out->AddString("OK");
      return ProcessPacketResult::Detach;
248

249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360
    // Read general registers (We only support register 'pc' that contains
    // the current instruction pointer).
    // IN : $g
    // OUT: $xx...xx
    case 'g': {
      uint64_t pc = GetCurrentPc();
      pkt_out->AddBlock(&pc, sizeof(pc));
      break;
    }

    // Write general registers - NOT SUPPORTED
    // IN : $Gxx..xx
    // OUT: $ (empty string)
    case 'G': {
      break;
    }

    // Set thread for subsequent operations. For Wasm targets, we currently
    // assume that there is only one thread with id = kThreadId (= 1).
    // IN : $H(c/g)(-1,0,xxxx)
    // OUT: $OK
    case 'H': {
      // Type of the operation (‘m’, ‘M’, ‘g’, ‘G’, ...)
      char operation;
      if (!pkt_in->GetRawChar(&operation)) {
        err = ErrorCode::BadFormat;
        break;
      }

      uint64_t thread_id;
      if (!pkt_in->GetNumberSep(&thread_id, 0)) {
        err = ErrorCode::BadFormat;
        break;
      }

      // Ignore, only one thread supported for now.
      pkt_out->AddString("OK");
      break;
    }

    // Kills the debuggee.
    // IN : $k
    // OUT: $OK
    case 'k':
      TRACE_GDB_REMOTE("Requested Kill.\n");
      pkt_out->AddString("OK");
      return ProcessPacketResult::Kill;

    // Reads {llll} addressable memory units starting at address {aaaa}.
    // IN : $maaaa,llll
    // OUT: $xx..xx
    case 'm': {
      uint64_t address;
      if (!pkt_in->GetNumberSep(&address, 0)) {
        err = ErrorCode::BadFormat;
        break;
      }
      wasm_addr_t wasm_addr(address);

      uint64_t len;
      if (!pkt_in->GetNumberSep(&len, 0)) {
        err = ErrorCode::BadFormat;
        break;
      }

      if (len > Transport::kBufSize / 2) {
        err = ErrorCode::BadArgs;
        break;
      }

      uint32_t length = static_cast<uint32_t>(len);
      uint8_t buff[Transport::kBufSize];
      if (wasm_addr.ModuleId() > 0) {
        uint32_t read =
            gdb_server_->GetWasmModuleBytes(wasm_addr, buff, length);
        if (read > 0) {
          pkt_out->AddBlock(buff, read);
        } else {
          err = ErrorCode::Failed;
        }
      } else {
        err = ErrorCode::BadArgs;
      }
      break;
    }

    // Writes {llll} addressable memory units starting at address {aaaa}.
    // IN : $Maaaa,llll:xx..xx
    // OUT: $OK
    case 'M': {
      // Writing to memory not supported for Wasm.
      err = ErrorCode::Failed;
      break;
    }

    // pN: Reads the value of register N.
    // IN : $pxx
    // OUT: $xx..xx
    case 'p': {
      uint64_t pc = GetCurrentPc();
      pkt_out->AddBlock(&pc, sizeof(pc));
    } break;

    case 'q': {
      err = ProcessQueryPacket(pkt_in, pkt_out);
      break;
    }

    // Single step
    // IN : $s
    // OUT: A Stop-reply packet is sent later, when the execution halts.
    case 's': {
361 362 363 364 365
      if (status_ == Status::Suspended) {
        gdb_server_->PrepareStep();
        SetStatus(Status::Running);
      }
      return ProcessPacketResult::Continue;
366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385
    }

    // Find out if the thread 'id' is alive.
    // IN : $T
    // OUT: $OK if alive, $Enn if thread is dead.
    case 'T': {
      uint64_t id;
      if (!pkt_in->GetNumberSep(&id, 0)) {
        err = ErrorCode::BadFormat;
        break;
      }
      if (id != kThreadId) {
        err = ErrorCode::BadArgs;
        break;
      }
      pkt_out->AddString("OK");
      break;
    }

    // Z: Adds a breakpoint
386 387
    // IN : $Z<type>,<addr>,<kind>
    //      <type>: 0: sw breakpoint, 1: hw breakpoint, 2: watchpoint
388 389
    // OUT: $OK (success) or $Enn (error)
    case 'Z': {
390
      uint64_t breakpoint_type;
391
      uint64_t breakpoint_address;
392 393
      uint64_t breakpoint_kind;
      // Only software breakpoints are supported.
394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412
      if (!pkt_in->GetNumberSep(&breakpoint_type, 0) || breakpoint_type != 0 ||
          !pkt_in->GetNumberSep(&breakpoint_address, 0) ||
          !pkt_in->GetNumberSep(&breakpoint_kind, 0)) {
        err = ErrorCode::BadFormat;
        break;
      }

      wasm_addr_t wasm_breakpoint_addr(breakpoint_address);
      if (!gdb_server_->AddBreakpoint(wasm_breakpoint_addr.ModuleId(),
                                      wasm_breakpoint_addr.Offset())) {
        err = ErrorCode::Failed;
        break;
      }

      pkt_out->AddString("OK");
      break;
    }

    // z: Removes a breakpoint
413 414
    // IN : $z<type>,<addr>,<kind>
    //      <type>: 0: sw breakpoint, 1: hw breakpoint, 2: watchpoint
415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450
    // OUT: $OK (success) or $Enn (error)
    case 'z': {
      uint64_t breakpoint_type;
      uint64_t breakpoint_address;
      uint64_t breakpoint_kind;
      if (!pkt_in->GetNumberSep(&breakpoint_type, 0) || breakpoint_type != 0 ||
          !pkt_in->GetNumberSep(&breakpoint_address, 0) ||
          !pkt_in->GetNumberSep(&breakpoint_kind, 0)) {
        err = ErrorCode::BadFormat;
        break;
      }

      wasm_addr_t wasm_breakpoint_addr(breakpoint_address);
      if (!gdb_server_->RemoveBreakpoint(wasm_breakpoint_addr.ModuleId(),
                                         wasm_breakpoint_addr.Offset())) {
        err = ErrorCode::Failed;
        break;
      }

      pkt_out->AddString("OK");
      break;
    }

    // If the command is not recognized, ignore it by sending an empty reply.
    default: {
      TRACE_GDB_REMOTE("Unknown command: %s\n", pkt_in->GetPayload());
    }
  }

  // If there is an error, return the error code instead of a payload
  if (err != ErrorCode::None) {
    pkt_out->Clear();
    pkt_out->AddRawChar('E');
    pkt_out->AddWord8(static_cast<uint8_t>(err));
  }
  return ProcessPacketResult::Paused;
451 452
}

453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 486 487 488
Target::ErrorCode Target::ProcessQueryPacket(const Packet* pkt_in,
                                             Packet* pkt_out) {
  const char* str = &pkt_in->GetPayload()[1];

  // Get first thread query
  // IN : $qfThreadInfo
  // OUT: $m<tid>
  //
  // Get next thread query
  // IN : $qsThreadInfo
  // OUT: $m<tid> or l to denote end of list.
  if (!strcmp(str, "fThreadInfo") || !strcmp(str, "sThreadInfo")) {
    if (str[0] == 'f') {
      pkt_out->AddString("m");
      pkt_out->AddNumberSep(kThreadId, 0);
    } else {
      pkt_out->AddString("l");
    }
    return ErrorCode::None;
  }

  // Get a list of loaded libraries
  // IN : $qXfer:libraries:read
  // OUT: an XML document which lists loaded libraries, with this format:
  // <library-list>
  //   <library name="foo.wasm">
  //     <section address="0x100000000"/>
  //   </library>
  //   <library name="bar.wasm">
  //     <section address="0x200000000"/>
  //   </library>
  // </library-list>
  // Note that LLDB must be compiled with libxml2 support to handle this packet.
  std::string tmp = "Xfer:libraries:read";
  if (!strncmp(str, tmp.data(), tmp.length())) {
    std::vector<GdbServer::WasmModuleInfo> modules =
489
        gdb_server_->GetLoadedModules(true);
490 491 492 493 494 495 496 497 498 499 500 501 502 503 504 505 506 507 508 509 510 511 512
    std::string result("l<library-list>");
    for (const auto& module : modules) {
      wasm_addr_t address(module.module_id, 0);
      char address_string[32];
      snprintf(address_string, sizeof(address_string), "%" PRIu64,
               static_cast<uint64_t>(address));
      result += "<library name=\"";
      result += module.module_name;
      result += "\"><section address=\"";
      result += address_string;
      result += "\"/></library>";
    }
    result += "</library-list>";
    pkt_out->AddString(result.c_str());
    return ErrorCode::None;
  }

  // Get the current call stack.
  // IN : $qWasmCallStack
  // OUT: $xx..xxyy..yyzz..zz (A sequence of uint64_t values represented as
  //                           consecutive 8-bytes blocks).
  std::vector<std::string> toks = StringSplit(str, ":;");
  if (toks[0] == "WasmCallStack") {
513 514 515 516 517 518 519
    std::vector<wasm_addr_t> call_stack_pcs = gdb_server_->GetWasmCallStack();
    std::vector<uint64_t> buffer;
    for (wasm_addr_t pc : call_stack_pcs) {
      buffer.push_back(pc);
    }
    pkt_out->AddBlock(buffer.data(),
                      static_cast<uint32_t>(sizeof(uint64_t) * buffer.size()));
520 521 522 523
    return ErrorCode::None;
  }

  // Get a Wasm global value in the Wasm module specified.
524
  // IN : $qWasmGlobal:frame_index;index
525 526 527
  // OUT: $xx..xx
  if (toks[0] == "WasmGlobal") {
    if (toks.size() == 3) {
528
      uint32_t frame_index =
529 530 531
          static_cast<uint32_t>(strtol(toks[1].data(), nullptr, 10));
      uint32_t index =
          static_cast<uint32_t>(strtol(toks[2].data(), nullptr, 10));
532 533 534 535
      uint8_t buff[16];
      uint32_t size = 0;
      if (gdb_server_->GetWasmGlobal(frame_index, index, buff, 16, &size)) {
        pkt_out->AddBlock(buff, size);
536 537 538 539 540 541 542 543 544
        return ErrorCode::None;
      } else {
        return ErrorCode::Failed;
      }
    }
    return ErrorCode::BadFormat;
  }

  // Get a Wasm local value in the stack frame specified.
545
  // IN : $qWasmLocal:frame_index;index
546 547
  // OUT: $xx..xx
  if (toks[0] == "WasmLocal") {
548
    if (toks.size() == 3) {
549
      uint32_t frame_index =
550 551
          static_cast<uint32_t>(strtol(toks[1].data(), nullptr, 10));
      uint32_t index =
552
          static_cast<uint32_t>(strtol(toks[2].data(), nullptr, 10));
553 554 555 556 557 558 559 560 561 562 563 564 565 566 567 568 569 570 571
      uint8_t buff[16];
      uint32_t size = 0;
      if (gdb_server_->GetWasmLocal(frame_index, index, buff, 16, &size)) {
        pkt_out->AddBlock(buff, size);
        return ErrorCode::None;
      } else {
        return ErrorCode::Failed;
      }
    }
    return ErrorCode::BadFormat;
  }

  // Get a Wasm local from the operand stack at the index specified.
  // IN : qWasmStackValue:frame_index;index
  // OUT: $xx..xx
  if (toks[0] == "WasmStackValue") {
    if (toks.size() == 3) {
      uint32_t frame_index =
          static_cast<uint32_t>(strtol(toks[1].data(), nullptr, 10));
572
      uint32_t index =
573 574 575 576 577
          static_cast<uint32_t>(strtol(toks[2].data(), nullptr, 10));
      uint8_t buff[16];
      uint32_t size = 0;
      if (gdb_server_->GetWasmStackValue(frame_index, index, buff, 16, &size)) {
        pkt_out->AddBlock(buff, size);
578 579 580 581 582 583 584 585
        return ErrorCode::None;
      } else {
        return ErrorCode::Failed;
      }
    }
    return ErrorCode::BadFormat;
  }

586 587
  // Read Wasm Memory.
  // IN : $qWasmMem:module_id;addr;len
588 589 590
  // OUT: $xx..xx
  if (toks[0] == "WasmMem") {
    if (toks.size() == 4) {
591 592 593 594 595 596 597 598 599 600 601 602 603 604 605 606 607 608 609 610 611 612 613 614 615 616 617
      uint32_t module_id = strtoul(toks[1].data(), nullptr, 10);
      uint32_t address = strtoul(toks[2].data(), nullptr, 16);
      uint32_t length = strtoul(toks[3].data(), nullptr, 16);
      if (length > Transport::kBufSize / 2) {
        return ErrorCode::BadArgs;
      }
      uint8_t buff[Transport::kBufSize];
      uint32_t read =
          gdb_server_->GetWasmMemory(module_id, address, buff, length);
      if (read > 0) {
        pkt_out->AddBlock(buff, read);
        return ErrorCode::None;
      } else {
        return ErrorCode::Failed;
      }
    }
    return ErrorCode::BadFormat;
  }

  // Read Wasm Data.
  // IN : $qWasmData:module_id;addr;len
  // OUT: $xx..xx
  if (toks[0] == "WasmData") {
    if (toks.size() == 4) {
      uint32_t module_id = strtoul(toks[1].data(), nullptr, 10);
      uint32_t address = strtoul(toks[2].data(), nullptr, 16);
      uint32_t length = strtoul(toks[3].data(), nullptr, 16);
618 619 620 621 622
      if (length > Transport::kBufSize / 2) {
        return ErrorCode::BadArgs;
      }
      uint8_t buff[Transport::kBufSize];
      uint32_t read =
623
          gdb_server_->GetWasmData(module_id, address, buff, length);
624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663
      if (read > 0) {
        pkt_out->AddBlock(buff, read);
        return ErrorCode::None;
      } else {
        return ErrorCode::Failed;
      }
    }
    return ErrorCode::BadFormat;
  }

  // No match so far, check the property cache.
  QueryPropertyMap::const_iterator it = query_properties_.find(toks[0]);
  if (it != query_properties_.end()) {
    pkt_out->AddString(it->second.data());
  }
  // If not found, just send an empty response.
  return ErrorCode::None;
}

// A Stop-reply packet has the format:
//   Sxx
// or:
//   Txx<name1>:<value1>;...;<nameN>:<valueN>
// where 'xx' is a two-digit hex number that represents the stop signal
// and the <name>:<value> pairs are used to report additional information,
// like the thread id.
void Target::SetStopReply(Packet* pkt_out) const {
  pkt_out->AddRawChar('T');
  pkt_out->AddWord8(cur_signal_);

  // Adds 'thread-pcs:<pc1>,...,<pcN>;' A list of pc values for all threads that
  // currently exist in the process.
  char buff[64];
  snprintf(buff, sizeof(buff), "thread-pcs:%" PRIx64 ";",
           static_cast<uint64_t>(GetCurrentPc()));
  pkt_out->AddString(buff);

  // Adds 'thread:<tid>;' pair. Note that a terminating ';' is required.
  pkt_out->AddString("thread:");
  pkt_out->AddNumberSep(kThreadId, ';');
664 665 666 667

  // If the loaded modules have changed since the last stop packet, signals
  // that.
  if (gdb_server_->HasModuleListChanged()) pkt_out->AddString("library:;");
668 669
}

670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699
void Target::SetStatus(Status status, int8_t signal,
                       std::vector<wasm_addr_t> call_frames, Isolate* isolate) {
  v8::base::MutexGuard guard(&mutex_);

  DCHECK((status == Status::Suspended && signal != 0 &&
          call_frames.size() > 0 && isolate != nullptr) ||
         (status != Status::Suspended && signal == 0 &&
          call_frames.size() == 0 && isolate == nullptr));

  current_isolate_ = isolate;
  status_ = status;
  cur_signal_ = signal;
  call_frames_ = call_frames;
}

const std::vector<wasm_addr_t> Target::GetCallStack() const {
  v8::base::MutexGuard guard(&mutex_);

  return call_frames_;
}

wasm_addr_t Target::GetCurrentPc() const {
  v8::base::MutexGuard guard(&mutex_);

  wasm_addr_t pc{0};
  if (call_frames_.size() > 0) {
    pc = call_frames_[0];
  }
  return pc;
}
700

701 702 703 704
}  // namespace gdb_server
}  // namespace wasm
}  // namespace internal
}  // namespace v8