genode/repos/os/include/monitor/gdb_packet.h
Norman Feske 6a57683e52 New debug monitor
The new monitor component at os/src/monitor is the designated successor
of the gdb_monitor. This initial version, however, implements only the
subset needed to inspect the memory of the monitored component(s).

In contrast to the gdb_monitor, the new component supports the monitoring
of multiple components, leveraging the sandbox API. It can therefore be
used as a drop-in replacement for the init component. Like the gdb_monitor,
the new monitor speaks the GDB protocol over Genode's terminal session.
But the protocol implementation does not re-use any gdbserver code,
sidestepping the complexities of POSIX.

There exist two run scripts illustrating the new component. The
os/run/monitor.run script exercises memory inspection via the 'm' command
by letting a test program monitor itself. The os/run/monitor_gdb.run
script allows for the interactive use of GDB to interact with monitored
components.

Issue #4917
2023-06-16 11:24:26 +02:00

118 lines
2.6 KiB
C++

/*
* \brief GDB packet parser
* \author Norman Feske
* \date 2023-05-11
*/
/*
* Copyright (C) 2023 Genode Labs GmbH
*
* This file is part of the Genode OS framework, which is distributed
* under the terms of the GNU Affero General Public License version 3.
*/
#ifndef _MONITOR__GDB_PACKET_H_
#define _MONITOR__GDB_PACKET_H_
#include <util/string.h>
namespace Genode { template <size_t> struct Gdb_packet; }
template <Genode::size_t MAX_SIZE>
struct Genode::Gdb_packet
{
enum class State {
IDLE, INCOMPLETE,
EXPECT_CHECKSUM1, EXPECT_CHECKSUM2,
COMPLETE, CORRUPT
};
State state { State::IDLE };
unsigned cursor = 0;
struct Checksum
{
uint8_t accumulated;
uint8_t expected;
bool matches() const { return (accumulated == expected); }
};
Checksum checksum { };
char buf[MAX_SIZE] { };
void reset()
{
state = State::IDLE;
cursor = 0;
checksum = { };
}
enum class Append_result { OK, COMPLETE, OVERFLOW, CORRUPT };
Append_result append(char const c)
{
if (cursor >= sizeof(buf))
return Append_result::OVERFLOW;
auto interpret = [&]
{
auto is_hex_digit = [] (char c) { return is_digit(c, true); };
auto hex_digit = [] (char c) { return digit (c, true); };
if (state == State::EXPECT_CHECKSUM1 || state == State::EXPECT_CHECKSUM2)
if (!is_hex_digit(c))
return State::CORRUPT;
switch (state) {
case State::IDLE:
return (c == '$') ? State::INCOMPLETE
: State::IDLE;
case State::INCOMPLETE:
return (c == '#') ? State::EXPECT_CHECKSUM1
: State::INCOMPLETE;
case State::EXPECT_CHECKSUM1:
checksum.expected = uint8_t(hex_digit(c) << 4u);
return State::EXPECT_CHECKSUM2;
case State::EXPECT_CHECKSUM2:
checksum.expected |= uint8_t(hex_digit(c));
return checksum.matches() ? State::COMPLETE
: State::CORRUPT;
case State::COMPLETE:
case State::CORRUPT:
break; /* expect call of 'reset' */
};
return state;
};
State const orig_state = state;
state = interpret();
/* capture only the command payload between '$' and '#' */
if ((orig_state == State::INCOMPLETE) && (state == State::INCOMPLETE)) {
buf[cursor++] = c;
checksum.accumulated = uint8_t(checksum.accumulated + c);
}
return (state == State::COMPLETE) ? Append_result::COMPLETE :
(state == State::CORRUPT) ? Append_result::CORRUPT :
Append_result::OK;
}
bool complete() const { return state == State::COMPLETE; }
void with_command(auto const &fn) const
{
if (complete())
fn(Const_byte_range_ptr { buf, cursor });
}
};
#endif /* _MONITOR__GDB_PACKET_H_ */