2023-09-10 22:27:10 +01:00
|
|
|
#include "VContRangeStep.hpp"
|
|
|
|
|
|
|
|
|
|
#include <string>
|
|
|
|
|
|
2024-07-23 21:14:22 +01:00
|
|
|
#include "src/Targets/Microchip/AVR8/OpcodeDecoder/Decoder.hpp"
|
2023-09-10 22:27:10 +01:00
|
|
|
#include "src/Services/Avr8InstructionService.hpp"
|
|
|
|
|
#include "src/Services/StringService.hpp"
|
|
|
|
|
#include "src/Services/PathService.hpp"
|
|
|
|
|
|
|
|
|
|
#include "src/DebugServer/Gdb/ResponsePackets/ErrorResponsePacket.hpp"
|
|
|
|
|
#include "src/Exceptions/Exception.hpp"
|
|
|
|
|
#include "src/Logger/Logger.hpp"
|
|
|
|
|
|
|
|
|
|
namespace DebugServer::Gdb::AvrGdb::CommandPackets
|
|
|
|
|
{
|
|
|
|
|
using Services::TargetControllerService;
|
|
|
|
|
using ResponsePackets::ErrorResponsePacket;
|
|
|
|
|
using ::Exceptions::Exception;
|
|
|
|
|
|
2024-10-25 22:22:25 +01:00
|
|
|
VContRangeStep::VContRangeStep(const RawPacket& rawPacket)
|
2024-10-26 16:19:05 +01:00
|
|
|
: Gdb::CommandPackets::CommandPacket(rawPacket)
|
2023-09-10 22:27:10 +01:00
|
|
|
{
|
2024-07-23 21:14:22 +01:00
|
|
|
using Services::StringService;
|
|
|
|
|
|
2023-09-10 22:27:10 +01:00
|
|
|
if (this->data.size() < 10) {
|
2024-07-23 21:14:22 +01:00
|
|
|
throw Exception{"Unexpected VContRangeStep packet size"};
|
2023-09-10 22:27:10 +01:00
|
|
|
}
|
|
|
|
|
|
2024-07-23 21:14:22 +01:00
|
|
|
const auto command = std::string{this->data.begin() + 7, this->data.end()};
|
2023-09-10 22:27:10 +01:00
|
|
|
|
2024-07-23 21:14:22 +01:00
|
|
|
const auto delimiterPos = command.find(',');
|
|
|
|
|
const auto threadIdDelimiterPos = command.find(':');
|
|
|
|
|
if (delimiterPos == std::string::npos || delimiterPos >= (command.size() - 1)) {
|
|
|
|
|
throw Exception{"Invalid VContRangeStep packet"};
|
2023-09-10 22:27:10 +01:00
|
|
|
}
|
|
|
|
|
|
2024-07-23 21:14:22 +01:00
|
|
|
this->startAddress = StringService::toUint32(command.substr(0, delimiterPos), 16);
|
|
|
|
|
this->endAddress = StringService::toUint32(
|
|
|
|
|
command.substr(delimiterPos + 1, threadIdDelimiterPos - (delimiterPos + 1)),
|
|
|
|
|
16
|
2023-09-10 22:27:10 +01:00
|
|
|
);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 21:14:22 +01:00
|
|
|
void VContRangeStep::handle(
|
2024-10-25 22:22:25 +01:00
|
|
|
DebugSession& debugSession,
|
|
|
|
|
const AvrGdbTargetDescriptor& gdbTargetDescriptor,
|
2024-07-23 21:14:22 +01:00
|
|
|
const Targets::TargetDescriptor& targetDescriptor,
|
2024-12-14 16:17:02 +00:00
|
|
|
const Targets::TargetState& targetState,
|
2024-07-23 21:14:22 +01:00
|
|
|
TargetControllerService& targetControllerService
|
|
|
|
|
) {
|
|
|
|
|
using Targets::Microchip::Avr8::OpcodeDecoder::Decoder;
|
2023-09-10 22:27:10 +01:00
|
|
|
using Services::Avr8InstructionService;
|
|
|
|
|
using Services::StringService;
|
|
|
|
|
|
|
|
|
|
Logger::info("Handling VContRangeStep packet");
|
|
|
|
|
|
|
|
|
|
Logger::debug("Requested stepping range start address: 0x" + StringService::toHex(this->startAddress));
|
|
|
|
|
Logger::debug("Requested stepping range end address (exclusive): 0x" + StringService::toHex(this->endAddress));
|
|
|
|
|
|
|
|
|
|
try {
|
2024-07-23 21:14:22 +01:00
|
|
|
const auto stepAddressRange = Targets::TargetMemoryAddressRange{this->startAddress, this->endAddress};
|
|
|
|
|
const auto stepByteSize = stepAddressRange.size() - 1; // -1 because the end address is exclusive
|
2024-10-25 22:22:25 +01:00
|
|
|
const auto& programMemoryAddressRange = gdbTargetDescriptor.programMemorySegmentDescriptor.addressRange;
|
2023-09-10 22:27:10 +01:00
|
|
|
|
|
|
|
|
if (
|
2024-07-23 21:14:22 +01:00
|
|
|
stepAddressRange.startAddress > stepAddressRange.endAddress
|
|
|
|
|
|| (stepAddressRange.startAddress % 2) != 0
|
|
|
|
|
|| (stepAddressRange.endAddress % 2) != 0
|
|
|
|
|
|| !programMemoryAddressRange.contains(stepAddressRange)
|
2023-09-10 22:27:10 +01:00
|
|
|
) {
|
2024-07-23 21:14:22 +01:00
|
|
|
throw Exception{"Invalid address range in VContRangeStep"};
|
2023-09-10 22:27:10 +01:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (debugSession.activeRangeSteppingSession.has_value()) {
|
|
|
|
|
Logger::warning(
|
|
|
|
|
"Attempted to start new range stepping session with one already active - terminating active session"
|
|
|
|
|
);
|
|
|
|
|
debugSession.terminateRangeSteppingSession(targetControllerService);
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 21:14:22 +01:00
|
|
|
if (stepByteSize <= 2) {
|
2023-09-10 22:27:10 +01:00
|
|
|
// Single step requested. No need for a range step here.
|
2024-07-23 21:14:22 +01:00
|
|
|
targetControllerService.stepTargetExecution();
|
2023-09-10 22:27:10 +01:00
|
|
|
debugSession.waitingForBreak = true;
|
|
|
|
|
return;
|
|
|
|
|
}
|
|
|
|
|
|
2024-12-05 23:09:01 +00:00
|
|
|
auto rangeSteppingSession = RangeSteppingSession{
|
|
|
|
|
gdbTargetDescriptor.programAddressSpaceDescriptor,
|
|
|
|
|
gdbTargetDescriptor.programMemorySegmentDescriptor,
|
|
|
|
|
stepAddressRange,
|
|
|
|
|
{}
|
|
|
|
|
};
|
2023-09-10 22:27:10 +01:00
|
|
|
|
2024-07-23 21:14:22 +01:00
|
|
|
const auto instructionsByAddress = Decoder::decode(
|
|
|
|
|
stepAddressRange.startAddress,
|
|
|
|
|
targetControllerService.readMemory(
|
2024-10-25 22:22:25 +01:00
|
|
|
gdbTargetDescriptor.programAddressSpaceDescriptor,
|
|
|
|
|
gdbTargetDescriptor.programMemorySegmentDescriptor,
|
2024-07-23 21:14:22 +01:00
|
|
|
stepAddressRange.startAddress,
|
|
|
|
|
stepByteSize
|
|
|
|
|
)
|
2023-09-10 22:27:10 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
Logger::debug(
|
2024-07-23 21:14:22 +01:00
|
|
|
"Inspecting " + std::to_string(instructionsByAddress.size()) + " instruction(s) within stepping range "
|
|
|
|
|
"(byte addresses) 0x" + StringService::toHex(stepAddressRange.startAddress) + " -> 0x"
|
|
|
|
|
+ StringService::toHex(stepAddressRange.endAddress) + ", in preparation for new range stepping "
|
|
|
|
|
"session"
|
2023-09-10 22:27:10 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
for (const auto& [instructionAddress, instruction] : instructionsByAddress) {
|
|
|
|
|
if (!instruction.has_value()) {
|
|
|
|
|
/*
|
|
|
|
|
* We weren't able to decode the opcode at this address. We have no idea what this instruction
|
|
|
|
|
* will do.
|
|
|
|
|
*/
|
|
|
|
|
Logger::error(
|
|
|
|
|
"Failed to decode AVR8 opcode at byte address 0x" + StringService::toHex(instructionAddress)
|
2023-09-20 18:56:44 +01:00
|
|
|
+ " - the instruction will have to be intercepted. Please enable debug logging, reproduce "
|
|
|
|
|
"this message and report as an issue via " + Services::PathService::homeDomainName()
|
|
|
|
|
+ "/report-issue"
|
2023-09-10 22:27:10 +01:00
|
|
|
);
|
|
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* We have no choice but to intercept it. When we reach it, we'll perform a single step and see
|
|
|
|
|
* what happens.
|
|
|
|
|
*/
|
|
|
|
|
rangeSteppingSession.interceptedAddresses.insert(instructionAddress);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (instruction->canChangeProgramFlow) {
|
|
|
|
|
const auto destinationAddress = Avr8InstructionService::resolveProgramDestinationAddress(
|
|
|
|
|
*instruction,
|
|
|
|
|
instructionAddress,
|
|
|
|
|
instructionsByAddress
|
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
if (!destinationAddress.has_value()) {
|
|
|
|
|
/*
|
|
|
|
|
* We don't know where this instruction may jump to, so we'll have to intercept it and perform
|
|
|
|
|
* a single step when we reach it.
|
|
|
|
|
*/
|
|
|
|
|
Logger::debug(
|
|
|
|
|
"Intercepting CCPF instruction (\"" + instruction->name + "\") at byte address 0x"
|
2023-09-20 18:56:44 +01:00
|
|
|
+ StringService::toHex(instructionAddress)
|
2023-09-10 22:27:10 +01:00
|
|
|
);
|
|
|
|
|
rangeSteppingSession.interceptedAddresses.insert(instructionAddress);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 21:14:22 +01:00
|
|
|
if (!programMemoryAddressRange.contains(*destinationAddress)) {
|
2023-09-10 22:27:10 +01:00
|
|
|
/*
|
|
|
|
|
* This instruction may jump to an invalid address. Someone screwed up here - could be
|
|
|
|
|
* something wrong in Bloom (opcode decoding bug, incorrect program memory address range in
|
|
|
|
|
* the target descriptor, etc.), or the user has an invalid instruction in their program code.
|
|
|
|
|
*
|
|
|
|
|
* We have no choice but to intercept the instruction. When we reach it, we'll perform a single
|
|
|
|
|
* step and see what happens.
|
|
|
|
|
*/
|
|
|
|
|
Logger::debug(
|
|
|
|
|
"Intercepting CCPF instruction (\"" + instruction->name + "\") with invalid destination "
|
2024-07-23 21:14:22 +01:00
|
|
|
"byte address (0x" + StringService::toHex(*destinationAddress) + "), at byte address 0x"
|
2023-09-20 18:56:44 +01:00
|
|
|
+ StringService::toHex(instructionAddress)
|
2023-09-10 22:27:10 +01:00
|
|
|
);
|
|
|
|
|
rangeSteppingSession.interceptedAddresses.insert(instructionAddress);
|
|
|
|
|
continue;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (
|
2024-07-23 21:14:22 +01:00
|
|
|
*destinationAddress < stepAddressRange.startAddress
|
|
|
|
|
|| *destinationAddress >= stepAddressRange.endAddress
|
2023-09-10 22:27:10 +01:00
|
|
|
) {
|
|
|
|
|
/*
|
|
|
|
|
* This instruction may jump to an address outside the requested stepping range.
|
|
|
|
|
*
|
|
|
|
|
* Because we know exactly where it will jump to (if it jumps), we only need to intercept the
|
|
|
|
|
* destination address.
|
|
|
|
|
*/
|
|
|
|
|
Logger::debug(
|
2023-09-20 18:56:44 +01:00
|
|
|
"Intercepting destination byte address 0x" + StringService::toHex(*destinationAddress)
|
|
|
|
|
+ " of CCPF instruction (\"" + instruction->name + "\") at byte address 0x"
|
|
|
|
|
+ StringService::toHex(instructionAddress)
|
2023-09-10 22:27:10 +01:00
|
|
|
);
|
|
|
|
|
rangeSteppingSession.interceptedAddresses.insert(*destinationAddress);
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
2024-07-23 21:14:22 +01:00
|
|
|
/*
|
|
|
|
|
* Finally, ensure that we intercept the first instruction outside the range (which is the end address
|
|
|
|
|
* of the range, because it's exclusive).
|
|
|
|
|
*/
|
|
|
|
|
rangeSteppingSession.interceptedAddresses.insert(stepAddressRange.endAddress);
|
|
|
|
|
|
2023-09-10 22:27:10 +01:00
|
|
|
debugSession.startRangeSteppingSession(std::move(rangeSteppingSession), targetControllerService);
|
2023-09-11 16:56:37 +01:00
|
|
|
|
|
|
|
|
/*
|
|
|
|
|
* GDB expects us to start the range stepping session with a single step, and then only continue if the
|
|
|
|
|
* single step didn't immediately take us out of the stepping range.
|
|
|
|
|
*
|
|
|
|
|
* So we kick off a single step here, then let AvrGdbRsp::handleTargetStoppedGdbResponse() determine if
|
|
|
|
|
* we should continue. See that member function for more.
|
|
|
|
|
*/
|
|
|
|
|
debugSession.activeRangeSteppingSession->singleStepping = true;
|
2024-07-23 21:14:22 +01:00
|
|
|
targetControllerService.stepTargetExecution();
|
2023-09-10 22:27:10 +01:00
|
|
|
debugSession.waitingForBreak = true;
|
|
|
|
|
|
|
|
|
|
} catch (const Exception& exception) {
|
|
|
|
|
Logger::error("Failed to start new range stepping session - " + exception.getMessage());
|
2024-07-23 21:14:22 +01:00
|
|
|
debugSession.connection.writePacket(ErrorResponsePacket{});
|
2023-09-10 22:27:10 +01:00
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|