session.cc 3.5 KB
Newer Older
1 2 3 4 5
// 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/session.h"
6
#include "src/debug/wasm/gdb-server/packet.h"
7 8 9 10 11 12 13
#include "src/debug/wasm/gdb-server/transport.h"

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

14 15
Session::Session(TransportBase* transport)
    : io_(transport), connected_(true), ack_enabled_(true) {}
16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38

void Session::WaitForDebugStubEvent() { io_->WaitForDebugStubEvent(); }

bool Session::SignalThreadEvent() { return io_->SignalThreadEvent(); }

bool Session::IsDataAvailable() const { return io_->IsDataAvailable(); }

bool Session::IsConnected() const { return connected_; }

void Session::Disconnect() {
  io_->Disconnect();
  connected_ = false;
}

bool Session::GetChar(char* ch) {
  if (!io_->Read(ch, 1)) {
    Disconnect();
    return false;
  }

  return true;
}

39
bool Session::SendPacket(Packet* pkt, bool expect_ack) {
40
  char ch;
41 42 43
  do {
    std::string data = pkt->GetPacketData();

44 45 46
    TRACE_GDB_REMOTE("TX %s\n", data.size() < 160
                                    ? data.c_str()
                                    : (data.substr(0, 160) + "...").c_str());
47 48 49 50 51
    if (!io_->Write(data.data(), static_cast<int32_t>(data.length()))) {
      return false;
    }

    // If ACKs are off, we are done.
52
    if (!expect_ack || !ack_enabled_) {
53 54 55 56 57 58 59 60 61 62
      break;
    }

    // Otherwise, poll for '+'
    if (!GetChar(&ch)) {
      return false;
    }

    // Retry if we didn't get a '+'
  } while (ch != '+');
63 64 65 66

  return true;
}

67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 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
bool Session::GetPayload(Packet* pkt, uint8_t* checksum) {
  pkt->Clear();
  *checksum = 0;

  // Stream in the characters
  char ch;
  while (GetChar(&ch)) {
    if (ch == '#') {
      // If we see a '#' we must be done with the data.
      return true;
    } else if (ch == '$') {
      // If we see a '$' we must have missed the last cmd, let's retry.
      TRACE_GDB_REMOTE("RX Missing $, retry.\n");
      *checksum = 0;
      pkt->Clear();
    } else {
      // Keep a running XSUM.
      *checksum += ch;
      pkt->AddRawChar(ch);
    }
  }
  return false;
}

bool Session::GetPacket(Packet* pkt) {
  while (true) {
    // Toss characters until we see a start of command
    char ch;
    do {
      if (!GetChar(&ch)) {
        return false;
      }
    } while (ch != '$');

    uint8_t running_checksum = 0;
    if (!GetPayload(pkt, &running_checksum)) {
      return false;
    }

    // Get two nibble checksum
    uint8_t trailing_checksum = 0;
    char chars[2];
    if (!GetChar(&chars[0]) || !GetChar(&chars[1]) ||
        !HexToUInt8(chars, &trailing_checksum)) {
      return false;
    }

    TRACE_GDB_REMOTE("RX $%s#%c%c\n", pkt->GetPayload(), chars[0], chars[1]);

    pkt->ParseSequence();

    // If ACKs are off, we are done.
    if (!ack_enabled_) {
      return true;
    }

    // If the XSUMs don't match, signal bad packet
    if (trailing_checksum == running_checksum) {
      char out[3] = {'+', 0, 0};

      // If we have a sequence number
      int32_t seq;
      if (pkt->GetSequence(&seq)) {
        // Respond with sequence number
        UInt8ToHex(seq, &out[1]);
        return io_->Write(out, 3);
      } else {
        return io_->Write(out, 1);
      }
    } else {
      // Resend a bad XSUM and look for retransmit
      TRACE_GDB_REMOTE("RX Bad XSUM, retry\n");
      io_->Write("-", 1);
      // retry...
    }
  }
}

145 146 147 148
}  // namespace gdb_server
}  // namespace wasm
}  // namespace internal
}  // namespace v8