#include #include #include #include #include #include #include #include #include "debugnet.h" #include "debugmsg.h" #include "debugger.h" #include "system.h" #include "processor.h" #include "log.h" #include "modules/coreinit/coreinit_thread.h" DebugNet gDebugNet; struct DebugSymbolInfo { std::string name; uint32_t address; uint32_t type; template void serialize(Archive &ar) { ar(name, address, type); } }; struct DebugModuleInfo { std::string name; uint32_t entryPoint; std::vector symbols; template void serialize(Archive &ar) { ar(name, entryPoint, symbols); } }; struct DebugThreadInfo { std::string name; int32_t curCoreId; uint32_t attribs; uint32_t state; uint32_t cia; uint32_t gpr[32]; uint32_t crf; template void serialize(Archive &ar) { ar(name, curCoreId, attribs, state); ar(cia, gpr, crf); } }; struct DebugPauseInfo { std::vector modules; uint32_t userModuleIdx; std::vector threads; template void serialize(Archive &ar) { ar(modules); ar(userModuleIdx); ar(threads); } }; void populateDebugPauseInfo(DebugPauseInfo& info) { auto &loadedModules = gLoader.getLoadedModules(); int moduleIdx = 0; int userModuleIdx = -1; auto userModule = gSystem.getUserModule(); for (auto &i : loadedModules) { auto &moduleName = i.first; auto &loadedModule = i.second; DebugModuleInfo tmod; tmod.name = moduleName; tmod.entryPoint = loadedModule->entryPoint(); info.modules.push_back(tmod); if (loadedModule == userModule) { userModuleIdx = moduleIdx; } moduleIdx++; } info.userModuleIdx = static_cast(userModuleIdx); auto &coreList = gProcessor.getCoreList(); auto &fiberList = gProcessor.getFiberList(); std::map threads; for (auto &fiber : fiberList) { auto &thread = fiber->thread; auto titer = threads.find(thread); assert(titer == threads.end()); threads.emplace(thread, fiber); } for (auto &i : threads) { auto &thread = i.first; auto &fiber = i.second; DebugThreadInfo tinfo; tinfo.name = thread->name; tinfo.curCoreId = -1; for (auto &core : coreList) { if (core->currentFiber) { if (thread == core->currentFiber->thread) { tinfo.curCoreId = core->id; } } } tinfo.attribs = thread->attr; tinfo.state = thread->state; tinfo.cia = fiber->state.cia; tinfo.crf = fiber->state.cr.value; memcpy(tinfo.gpr, fiber->state.gpr, sizeof(uint32_t) * 32); info.threads.push_back(tinfo); } } enum class DebugPacketType : uint16_t { Invalid = 0, PreLaunch = 1, BpHit = 2, Pause = 3, Resume = 4, AddBreakpoint = 5, RemoveBreakpoint = 6, }; #pragma pack(push, 1) struct DebugNetHeader { uint32_t size; DebugPacketType command; uint16_t reqId; }; #pragma pack(pop) class DebugPacket : public MessageClass { }; template using DebugPacketBase = MessageClassBase; class DebugPacketBpHit : public DebugPacketBase { public: uint32_t coreId; uint32_t userData; DebugPauseInfo info; template void serialize(Archive &ar) { ar(coreId, userData, info); } }; class DebugPacketPreLaunch : public DebugPacketBase { public: DebugPauseInfo info; template void serialize(Archive &ar) { ar(info); } }; class DebugPacketPause : public DebugPacketBase { public: template void serialize(Archive &ar) { } }; class DebugPacketResume : public DebugPacketBase { public: template void serialize(Archive &ar) { } }; class DebugPacketBpAdd : public DebugPacketBase { public: uint32_t address; uint32_t userData; template void serialize(Archive &ar) { ar(address, userData); } }; class DebugPacketBpRemove : public DebugPacketBase { public: uint32_t address; template void serialize(Archive &ar) { ar(address); } }; template int serializePacket2(std::vector &data, DebugPacket *packet) { std::ostringstream str; cereal::BinaryOutputArchive archive(str); T &typedPacket = *(T*)packet; archive(typedPacket); std::string& payload = str.str(); data.resize(sizeof(DebugNetHeader) + payload.size()); auto &header = *reinterpret_cast(data.data()); header.size = (uint16_t)data.size(); header.command = packet->type(); std::copy(payload.begin(), payload.end(), data.begin() + sizeof(DebugNetHeader)); return 0; } int serializePacket(std::vector &data, DebugPacket *packet) { if (packet->isA(DebugPacketType::BpHit)) { return serializePacket2(data, packet); } else if (packet->isA(DebugPacketType::PreLaunch)) { return serializePacket2(data, packet); } else if (packet->isA(DebugPacketType::Pause)) { return serializePacket2(data, packet); } else if (packet->isA(DebugPacketType::Resume)) { return serializePacket2(data, packet); } else { return -1; } } struct vectorwrapbuf : public std::basic_streambuf { vectorwrapbuf(const uint8_t *start, const uint8_t *end) { this->setg((char*)start, (char*)start, (char*)end); } }; template int deserializePacket2(const std::vector &data, DebugPacket *&packet) { vectorwrapbuf databuf(data.data() + sizeof(DebugNetHeader), data.data() + data.size()); std::istream stream(&databuf); cereal::BinaryInputArchive archive(stream); packet = new T(); T &packetRef = *static_cast(packet); archive(packetRef); return 0; } int deserializePacket(const std::vector &data, DebugPacket *&packet) { auto &header = *reinterpret_cast(data.data()); assert(data.size() == header.size); if (header.command == DebugPacketType::BpHit) { return deserializePacket2(data, packet); } else if (header.command == DebugPacketType::PreLaunch) { return deserializePacket2(data, packet); } else if (header.command == DebugPacketType::Pause) { return deserializePacket2(data, packet); } else if (header.command == DebugPacketType::Resume) { return deserializePacket2(data, packet); } else { return -1; } } DebugNet::DebugNet() { mConnected = false; } bool DebugNet::connect(const std::string& address, uint16_t port) { mConnected = false; WSADATA wsaData; WSAStartup(MAKEWORD(2, 2), &wsaData); SOCKET sock = socket(AF_INET, SOCK_STREAM, 0); struct sockaddr_in server; server.sin_addr.s_addr = inet_addr(address.c_str()); server.sin_family = AF_INET; server.sin_port = htons(port); int conres = ::connect(sock, (struct sockaddr *)&server, sizeof(server)); if (conres < 0) { // Connect failure return false; } mConnected = true; mSocket = (void*)sock; mNetworkThread = std::thread(&DebugNet::networkThread, this); return true; } void DebugNet::networkThread() { SOCKET sock = (SOCKET)mSocket; std::vector buffer; while (true) { buffer.resize(sizeof(DebugNetHeader)); int recvd = recv(sock, (char*)&buffer[0], sizeof(DebugNetHeader), 0); if (recvd <= 0) { // Disconnected handleDisconnect(); break; } assert(recvd == sizeof(DebugNetHeader)); DebugNetHeader *header = (DebugNetHeader*)buffer.data(); buffer.resize(header->size); recvd = recv(sock, (char*)&buffer[sizeof(DebugNetHeader)], (int)buffer.size() - sizeof(DebugNetHeader), 0); if (recvd <= 0) { handleDisconnect(); break; } assert(recvd == buffer.size() - sizeof(DebugNetHeader)); DebugPacket *packet = nullptr; deserializePacket(buffer, packet); handlePacket(packet); } } void DebugNet::handleDisconnect() { mConnected = false; gDebugger.notify(new DebugMessageDebuggerDc()); } void DebugNet::handlePacket(DebugPacket *pak) { gLog->debug("Handling packet {}", (int)pak->type()); switch (pak->type()) { case DebugPacketType::AddBreakpoint: { auto *bpPak = static_cast(pak); gDebugger.addBreakpoint(bpPak->address, bpPak->userData); break; } case DebugPacketType::RemoveBreakpoint: { auto *bpPak = static_cast(pak); gDebugger.removeBreakpoint(bpPak->address); break; } case DebugPacketType::Pause: { gDebugger.pause(); break; } case DebugPacketType::Resume: { gDebugger.resume(); break; } } } void DebugNet::writePacket(DebugPacket *pak) { gLog->debug("Writing packet {}", (int)pak->type()); std::vector buffer; serializePacket(buffer, pak); SOCKET sock = (SOCKET)mSocket; send(sock, (char*)&buffer[0], (int)buffer.size(), 0); } void DebugNet::writePrelaunch() { auto pak = new DebugPacketPreLaunch(); populateDebugPauseInfo(pak->info); writePacket(pak); } void DebugNet::writeBreakpointHit(uint32_t coreId, uint32_t userData) { auto pak = new DebugPacketBpHit(); pak->coreId = coreId; pak->userData = userData; populateDebugPauseInfo(pak->info); writePacket(pak); }