arch-power: Add multi-mode debugging support

This adds multi-mode support for remote debugging via GDB
with the addition of the XML target description files for
both 32-bit and 64-bit variants of the Power architecture.
Proper byte order conversions have also been added.

MSR has now been modeled to some extent but it is still
not exposed by getRegs() since its a privileged register
that cannot be modified from userspace. Similarly, the
target descriptions require FPSCR to also be part of the
payload and hence, it has been added too.

Change-Id: I156fdccb791f161959dbb2c3dd8ab1e510d9cd4b
Signed-off-by: Sandipan Das <sandipan@linux.ibm.com>
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/40946
Reviewed-by: Boris Shingarov <shingarov@labware.com>
Maintainer: Boris Shingarov <shingarov@labware.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Sandipan Das
2021-02-06 17:50:49 +05:30
committed by Boris Shingarov
parent 6b37a7e02c
commit 065362ddfc
9 changed files with 319 additions and 115 deletions

View File

@@ -0,0 +1,49 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2007-2020 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.power.core">
<reg name="r0" bitsize="32" type="uint32"/>
<reg name="r1" bitsize="32" type="uint32"/>
<reg name="r2" bitsize="32" type="uint32"/>
<reg name="r3" bitsize="32" type="uint32"/>
<reg name="r4" bitsize="32" type="uint32"/>
<reg name="r5" bitsize="32" type="uint32"/>
<reg name="r6" bitsize="32" type="uint32"/>
<reg name="r7" bitsize="32" type="uint32"/>
<reg name="r8" bitsize="32" type="uint32"/>
<reg name="r9" bitsize="32" type="uint32"/>
<reg name="r10" bitsize="32" type="uint32"/>
<reg name="r11" bitsize="32" type="uint32"/>
<reg name="r12" bitsize="32" type="uint32"/>
<reg name="r13" bitsize="32" type="uint32"/>
<reg name="r14" bitsize="32" type="uint32"/>
<reg name="r15" bitsize="32" type="uint32"/>
<reg name="r16" bitsize="32" type="uint32"/>
<reg name="r17" bitsize="32" type="uint32"/>
<reg name="r18" bitsize="32" type="uint32"/>
<reg name="r19" bitsize="32" type="uint32"/>
<reg name="r20" bitsize="32" type="uint32"/>
<reg name="r21" bitsize="32" type="uint32"/>
<reg name="r22" bitsize="32" type="uint32"/>
<reg name="r23" bitsize="32" type="uint32"/>
<reg name="r24" bitsize="32" type="uint32"/>
<reg name="r25" bitsize="32" type="uint32"/>
<reg name="r26" bitsize="32" type="uint32"/>
<reg name="r27" bitsize="32" type="uint32"/>
<reg name="r28" bitsize="32" type="uint32"/>
<reg name="r29" bitsize="32" type="uint32"/>
<reg name="r30" bitsize="32" type="uint32"/>
<reg name="r31" bitsize="32" type="uint32"/>
<reg name="pc" bitsize="32" type="code_ptr" regnum="64"/>
<reg name="msr" bitsize="32" type="uint32"/>
<reg name="cr" bitsize="32" type="uint32"/>
<reg name="lr" bitsize="32" type="code_ptr"/>
<reg name="ctr" bitsize="32" type="uint32"/>
<reg name="xer" bitsize="32" type="uint32"/>
</feature>

44
ext/gdb-xml/power-fpu.xml Normal file
View File

@@ -0,0 +1,44 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2007-2020 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.power.fpu">
<reg name="f0" bitsize="64" type="ieee_double" regnum="32"/>
<reg name="f1" bitsize="64" type="ieee_double"/>
<reg name="f2" bitsize="64" type="ieee_double"/>
<reg name="f3" bitsize="64" type="ieee_double"/>
<reg name="f4" bitsize="64" type="ieee_double"/>
<reg name="f5" bitsize="64" type="ieee_double"/>
<reg name="f6" bitsize="64" type="ieee_double"/>
<reg name="f7" bitsize="64" type="ieee_double"/>
<reg name="f8" bitsize="64" type="ieee_double"/>
<reg name="f9" bitsize="64" type="ieee_double"/>
<reg name="f10" bitsize="64" type="ieee_double"/>
<reg name="f11" bitsize="64" type="ieee_double"/>
<reg name="f12" bitsize="64" type="ieee_double"/>
<reg name="f13" bitsize="64" type="ieee_double"/>
<reg name="f14" bitsize="64" type="ieee_double"/>
<reg name="f15" bitsize="64" type="ieee_double"/>
<reg name="f16" bitsize="64" type="ieee_double"/>
<reg name="f17" bitsize="64" type="ieee_double"/>
<reg name="f18" bitsize="64" type="ieee_double"/>
<reg name="f19" bitsize="64" type="ieee_double"/>
<reg name="f20" bitsize="64" type="ieee_double"/>
<reg name="f21" bitsize="64" type="ieee_double"/>
<reg name="f22" bitsize="64" type="ieee_double"/>
<reg name="f23" bitsize="64" type="ieee_double"/>
<reg name="f24" bitsize="64" type="ieee_double"/>
<reg name="f25" bitsize="64" type="ieee_double"/>
<reg name="f26" bitsize="64" type="ieee_double"/>
<reg name="f27" bitsize="64" type="ieee_double"/>
<reg name="f28" bitsize="64" type="ieee_double"/>
<reg name="f29" bitsize="64" type="ieee_double"/>
<reg name="f30" bitsize="64" type="ieee_double"/>
<reg name="f31" bitsize="64" type="ieee_double"/>
<reg name="fpscr" bitsize="32" group="float" regnum="70"/>
</feature>

View File

@@ -1,92 +0,0 @@
<?xml version="1.0"?>
<!--
GDB feature descriptor defining the structure of the G packet,
i.e., the representation of register contents on the wire.
This file does not model any real variant of PowerPC in particular
(such as, RS/6000 or e500): it simply reflects BaseGdbRegCache's
fields in power/remote_gdb.hh.
As such, this description is something of an oversimplification
relative to the XML files in the GDB source, in that it does not
take into account possible variations in features resulting in
non-sequential numbering of registers.
-->
<target>
<architecture>powerpc</architecture>
<feature name="org.gnu.gdb.power">
<reg name="r0" bitsize="32"/>
<reg name="r1" bitsize="32"/>
<reg name="r2" bitsize="32"/>
<reg name="r3" bitsize="32"/>
<reg name="r4" bitsize="32"/>
<reg name="r5" bitsize="32"/>
<reg name="r6" bitsize="32"/>
<reg name="r7" bitsize="32"/>
<reg name="r8" bitsize="32"/>
<reg name="r9" bitsize="32"/>
<reg name="r10" bitsize="32"/>
<reg name="r11" bitsize="32"/>
<reg name="r12" bitsize="32"/>
<reg name="r13" bitsize="32"/>
<reg name="r14" bitsize="32"/>
<reg name="r15" bitsize="32"/>
<reg name="r16" bitsize="32"/>
<reg name="r17" bitsize="32"/>
<reg name="r18" bitsize="32"/>
<reg name="r19" bitsize="32"/>
<reg name="r20" bitsize="32"/>
<reg name="r21" bitsize="32"/>
<reg name="r22" bitsize="32"/>
<reg name="r23" bitsize="32"/>
<reg name="r24" bitsize="32"/>
<reg name="r25" bitsize="32"/>
<reg name="r26" bitsize="32"/>
<reg name="r27" bitsize="32"/>
<reg name="r28" bitsize="32"/>
<reg name="r29" bitsize="32"/>
<reg name="r30" bitsize="32"/>
<reg name="r31" bitsize="32"/>
<reg name="f0" bitsize="64" type="ieee_double" regnum="32"/>
<reg name="f1" bitsize="64" type="ieee_double"/>
<reg name="f2" bitsize="64" type="ieee_double"/>
<reg name="f3" bitsize="64" type="ieee_double"/>
<reg name="f4" bitsize="64" type="ieee_double"/>
<reg name="f5" bitsize="64" type="ieee_double"/>
<reg name="f6" bitsize="64" type="ieee_double"/>
<reg name="f7" bitsize="64" type="ieee_double"/>
<reg name="f8" bitsize="64" type="ieee_double"/>
<reg name="f9" bitsize="64" type="ieee_double"/>
<reg name="f10" bitsize="64" type="ieee_double"/>
<reg name="f11" bitsize="64" type="ieee_double"/>
<reg name="f12" bitsize="64" type="ieee_double"/>
<reg name="f13" bitsize="64" type="ieee_double"/>
<reg name="f14" bitsize="64" type="ieee_double"/>
<reg name="f15" bitsize="64" type="ieee_double"/>
<reg name="f16" bitsize="64" type="ieee_double"/>
<reg name="f17" bitsize="64" type="ieee_double"/>
<reg name="f18" bitsize="64" type="ieee_double"/>
<reg name="f19" bitsize="64" type="ieee_double"/>
<reg name="f20" bitsize="64" type="ieee_double"/>
<reg name="f21" bitsize="64" type="ieee_double"/>
<reg name="f22" bitsize="64" type="ieee_double"/>
<reg name="f23" bitsize="64" type="ieee_double"/>
<reg name="f24" bitsize="64" type="ieee_double"/>
<reg name="f25" bitsize="64" type="ieee_double"/>
<reg name="f26" bitsize="64" type="ieee_double"/>
<reg name="f27" bitsize="64" type="ieee_double"/>
<reg name="f28" bitsize="64" type="ieee_double"/>
<reg name="f29" bitsize="64" type="ieee_double"/>
<reg name="f30" bitsize="64" type="ieee_double"/>
<reg name="f31" bitsize="64" type="ieee_double"/>
<reg name="pc" bitsize="32" type="code_ptr" regnum="64"/>
<reg name="msr" bitsize="32"/>
<reg name="cr" bitsize="32"/>
<reg name="lr" bitsize="32" type="code_ptr"/>
<reg name="ctr" bitsize="32"/>
<reg name="xer" bitsize="32"/>
</feature>
</target>

View File

@@ -0,0 +1,49 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2007-2020 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!DOCTYPE feature SYSTEM "gdb-target.dtd">
<feature name="org.gnu.gdb.power.core">
<reg name="r0" bitsize="64" type="uint64"/>
<reg name="r1" bitsize="64" type="uint64"/>
<reg name="r2" bitsize="64" type="uint64"/>
<reg name="r3" bitsize="64" type="uint64"/>
<reg name="r4" bitsize="64" type="uint64"/>
<reg name="r5" bitsize="64" type="uint64"/>
<reg name="r6" bitsize="64" type="uint64"/>
<reg name="r7" bitsize="64" type="uint64"/>
<reg name="r8" bitsize="64" type="uint64"/>
<reg name="r9" bitsize="64" type="uint64"/>
<reg name="r10" bitsize="64" type="uint64"/>
<reg name="r11" bitsize="64" type="uint64"/>
<reg name="r12" bitsize="64" type="uint64"/>
<reg name="r13" bitsize="64" type="uint64"/>
<reg name="r14" bitsize="64" type="uint64"/>
<reg name="r15" bitsize="64" type="uint64"/>
<reg name="r16" bitsize="64" type="uint64"/>
<reg name="r17" bitsize="64" type="uint64"/>
<reg name="r18" bitsize="64" type="uint64"/>
<reg name="r19" bitsize="64" type="uint64"/>
<reg name="r20" bitsize="64" type="uint64"/>
<reg name="r21" bitsize="64" type="uint64"/>
<reg name="r22" bitsize="64" type="uint64"/>
<reg name="r23" bitsize="64" type="uint64"/>
<reg name="r24" bitsize="64" type="uint64"/>
<reg name="r25" bitsize="64" type="uint64"/>
<reg name="r26" bitsize="64" type="uint64"/>
<reg name="r27" bitsize="64" type="uint64"/>
<reg name="r28" bitsize="64" type="uint64"/>
<reg name="r29" bitsize="64" type="uint64"/>
<reg name="r30" bitsize="64" type="uint64"/>
<reg name="r31" bitsize="64" type="uint64"/>
<reg name="pc" bitsize="64" type="code_ptr" regnum="64"/>
<reg name="msr" bitsize="64" type="uint64"/>
<reg name="cr" bitsize="32" type="uint32"/>
<reg name="lr" bitsize="64" type="code_ptr"/>
<reg name="ctr" bitsize="64" type="uint64"/>
<reg name="xer" bitsize="32" type="uint32"/>
</feature>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2007-2020 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!-- PowerPC UISA - a PPC processor as viewed by user-level code. A UISA-only
view of the PowerPC. -->
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target>
<architecture>powerpc:common</architecture>
<xi:include href="power-core.xml"/>
<xi:include href="power-fpu.xml"/>
</target>

View File

@@ -0,0 +1,16 @@
<?xml version="1.0"?>
<!-- Copyright (C) 2007-2020 Free Software Foundation, Inc.
Copying and distribution of this file, with or without modification,
are permitted in any medium without royalty provided the copyright
notice and this notice are preserved. -->
<!-- PowerPC UISA - a PPC processor as viewed by user-level code. A UISA-only
view of the PowerPC. -->
<!DOCTYPE target SYSTEM "gdb-target.dtd">
<target>
<architecture>powerpc:common64</architecture>
<xi:include href="power64-core.xml"/>
<xi:include href="power-fpu.xml"/>
</target>

View File

@@ -59,4 +59,8 @@ if env['TARGET_ISA'] == 'power':
ISADesc('isa/main.isa')
GdbXml('power.xml', 'gdb_xml_power')
GdbXml('power-core.xml', 'gdb_xml_power_core')
GdbXml('power64-core.xml', 'gdb_xml_power64_core')
GdbXml('power-fpu.xml', 'gdb_xml_power_fpu')
GdbXml('powerpc-32.xml', 'gdb_xml_powerpc_32')
GdbXml('powerpc-64.xml', 'gdb_xml_powerpc_64')

View File

@@ -136,7 +136,12 @@
#include <string>
#include "blobs/gdb_xml_power.hh"
#include "arch/power/regs/misc.hh"
#include "blobs/gdb_xml_power64_core.hh"
#include "blobs/gdb_xml_power_core.hh"
#include "blobs/gdb_xml_power_fpu.hh"
#include "blobs/gdb_xml_powerpc_32.hh"
#include "blobs/gdb_xml_powerpc_64.hh"
#include "cpu/thread_state.hh"
#include "debug/GDBAcc.hh"
#include "debug/GDBMisc.hh"
@@ -149,7 +154,7 @@ namespace gem5
using namespace PowerISA;
RemoteGDB::RemoteGDB(System *_system, int _port)
: BaseRemoteGDB(_system, _port), regCache(this)
: BaseRemoteGDB(_system, _port), regCache32(this), regCache64(this)
{
}
@@ -173,22 +178,26 @@ RemoteGDB::PowerGdbRegCache::getRegs(ThreadContext *context)
{
DPRINTF(GDBAcc, "getRegs in remotegdb \n");
Msr msr = context->readIntReg(INTREG_MSR);
ByteOrder order = (msr.le ? ByteOrder::little : ByteOrder::big);
// Default order on 32-bit PowerPC:
// R0-R31 (32-bit each), F0-F31 (64-bit IEEE754 double),
// PC, MSR, CR, LR, CTR, XER (32-bit each)
// PC, MSR, CR, LR, CTR, XER, FPSCR (32-bit each)
for (int i = 0; i < NumIntArchRegs; i++)
r.gpr[i] = htobe((uint32_t)context->readIntReg(i));
r.gpr[i] = htog((uint32_t)context->readIntReg(i), order);
for (int i = 0; i < NumFloatArchRegs; i++)
r.fpr[i] = context->readFloatReg(i);
r.pc = htobe((uint32_t)context->pcState().pc());
r.msr = 0; // Is MSR modeled?
r.cr = htobe((uint32_t)context->readIntReg(INTREG_CR));
r.lr = htobe((uint32_t)context->readIntReg(INTREG_LR));
r.ctr = htobe((uint32_t)context->readIntReg(INTREG_CTR));
r.xer = htobe((uint32_t)context->readIntReg(INTREG_XER));
r.pc = htog((uint32_t)context->pcState().pc(), order);
r.msr = 0; // MSR is privileged, hence not exposed here
r.cr = htog((uint32_t)context->readIntReg(INTREG_CR), order);
r.lr = htog((uint32_t)context->readIntReg(INTREG_LR), order);
r.ctr = htog((uint32_t)context->readIntReg(INTREG_CTR), order);
r.xer = htog((uint32_t)context->readIntReg(INTREG_XER), order);
r.fpscr = htog((uint32_t)context->readIntReg(INTREG_FPSCR), order);
}
void
@@ -196,24 +205,89 @@ RemoteGDB::PowerGdbRegCache::setRegs(ThreadContext *context) const
{
DPRINTF(GDBAcc, "setRegs in remotegdb \n");
Msr msr = context->readIntReg(INTREG_MSR);
ByteOrder order = (msr.le ? ByteOrder::little : ByteOrder::big);
for (int i = 0; i < NumIntArchRegs; i++)
context->setIntReg(i, betoh(r.gpr[i]));
context->setIntReg(i, gtoh(r.gpr[i], order));
for (int i = 0; i < NumFloatArchRegs; i++)
context->setFloatReg(i, r.fpr[i]);
context->pcState(betoh(r.pc));
// Is MSR modeled?
context->setIntReg(INTREG_CR, betoh(r.cr));
context->setIntReg(INTREG_LR, betoh(r.lr));
context->setIntReg(INTREG_CTR, betoh(r.ctr));
context->setIntReg(INTREG_XER, betoh(r.xer));
auto pc = context->pcState();
pc.byteOrder(order);
pc.set(gtoh(r.pc, order));
context->pcState(pc);
// MSR is privileged, hence not modified here
context->setIntReg(INTREG_CR, gtoh(r.cr, order));
context->setIntReg(INTREG_LR, gtoh(r.lr, order));
context->setIntReg(INTREG_CTR, gtoh(r.ctr, order));
context->setIntReg(INTREG_XER, gtoh(r.xer, order));
context->setIntReg(INTREG_FPSCR, gtoh(r.fpscr, order));
}
void
RemoteGDB::Power64GdbRegCache::getRegs(ThreadContext *context)
{
DPRINTF(GDBAcc, "getRegs in remotegdb \n");
Msr msr = context->readIntReg(INTREG_MSR);
ByteOrder order = (msr.le ? ByteOrder::little : ByteOrder::big);
// Default order on 64-bit PowerPC:
// GPRR0-GPRR31 (64-bit each), FPR0-FPR31 (64-bit IEEE754 double),
// CIA, MSR, CR, LR, CTR, XER, FPSCR (only CR, XER, FPSCR are 32-bit
// each and the rest are 64-bit)
for (int i = 0; i < NumIntArchRegs; i++)
r.gpr[i] = htog(context->readIntReg(i), order);
for (int i = 0; i < NumFloatArchRegs; i++)
r.fpr[i] = context->readFloatReg(i);
r.pc = htog(context->pcState().pc(), order);
r.msr = 0; // MSR is privileged, hence not exposed here
r.cr = htog((uint32_t)context->readIntReg(INTREG_CR), order);
r.lr = htog(context->readIntReg(INTREG_LR), order);
r.ctr = htog(context->readIntReg(INTREG_CTR), order);
r.xer = htog((uint32_t)context->readIntReg(INTREG_XER), order);
r.fpscr = htog((uint32_t)context->readIntReg(INTREG_FPSCR), order);
}
void
RemoteGDB::Power64GdbRegCache::setRegs(ThreadContext *context) const
{
DPRINTF(GDBAcc, "setRegs in remotegdb \n");
Msr msr = context->readIntReg(INTREG_MSR);
ByteOrder order = (msr.le ? ByteOrder::little : ByteOrder::big);
for (int i = 0; i < NumIntArchRegs; i++)
context->setIntReg(i, gtoh(r.gpr[i], order));
for (int i = 0; i < NumFloatArchRegs; i++)
context->setFloatReg(i, r.fpr[i]);
auto pc = context->pcState();
pc.byteOrder(order);
pc.set(gtoh(r.pc, order));
context->pcState(pc);
// MSR is privileged, hence not modified here
context->setIntReg(INTREG_CR, gtoh(r.cr, order));
context->setIntReg(INTREG_LR, gtoh(r.lr, order));
context->setIntReg(INTREG_CTR, gtoh(r.ctr, order));
context->setIntReg(INTREG_XER, gtoh(r.xer, order));
context->setIntReg(INTREG_FPSCR, gtoh(r.fpscr, order));
}
BaseGdbRegCache*
RemoteGDB::gdbRegs()
{
return &regCache;
Msr msr = context()->readIntReg(INTREG_MSR);
if (msr.sf)
return &regCache64;
else
return &regCache32;
}
bool
@@ -222,10 +296,20 @@ RemoteGDB::getXferFeaturesRead(const std::string &annex, std::string &output)
#define GDB_XML(x, s) \
{ x, std::string(reinterpret_cast<const char *>(Blobs::s), \
Blobs::s ## _len) }
static const std::map<std::string, std::string> annexMap {
GDB_XML("target.xml", gdb_xml_power),
static const std::map<std::string, std::string> annexMap32{
GDB_XML("target.xml", gdb_xml_powerpc_32),
GDB_XML("power-core.xml", gdb_xml_power_core),
GDB_XML("power-fpu.xml", gdb_xml_power_fpu)
};
static const std::map<std::string, std::string> annexMap64{
GDB_XML("target.xml", gdb_xml_powerpc_64),
GDB_XML("power64-core.xml", gdb_xml_power64_core),
GDB_XML("power-fpu.xml", gdb_xml_power_fpu)
};
#undef GDB_XML
Msr msr = context()->readIntReg(INTREG_MSR);
auto& annexMap = msr.sf ? annexMap64 : annexMap32;
auto it = annexMap.find(annex);
if (it == annexMap.end())
return false;

View File

@@ -53,7 +53,7 @@ class RemoteGDB : public BaseRemoteGDB
{
using BaseGdbRegCache::BaseGdbRegCache;
private:
struct
struct GEM5_PACKED
{
uint32_t gpr[NumIntArchRegs];
uint64_t fpr[NumFloatArchRegs];
@@ -63,7 +63,9 @@ class RemoteGDB : public BaseRemoteGDB
uint32_t lr;
uint32_t ctr;
uint32_t xer;
uint32_t fpscr;
} r;
public:
char *data() const { return (char *)&r; }
size_t size() const { return sizeof(r); }
@@ -76,16 +78,48 @@ class RemoteGDB : public BaseRemoteGDB
}
};
PowerGdbRegCache regCache;
class Power64GdbRegCache : public BaseGdbRegCache
{
using BaseGdbRegCache::BaseGdbRegCache;
private:
struct GEM5_PACKED
{
uint64_t gpr[NumIntArchRegs];
uint64_t fpr[NumFloatArchRegs];
uint64_t pc;
uint64_t msr;
uint32_t cr;
uint64_t lr;
uint64_t ctr;
uint32_t xer;
uint32_t fpscr;
} r;
public:
char *data() const { return (char *)&r; }
size_t size() const { return sizeof(r); }
void getRegs(ThreadContext*);
void setRegs(ThreadContext*) const;
const std::string
name() const
{
return gdb->name() + ".Power64GdbRegCache";
}
};
PowerGdbRegCache regCache32;
Power64GdbRegCache regCache64;
public:
RemoteGDB(System *_system, int _port);
BaseGdbRegCache *gdbRegs();
std::vector<std::string>
availableFeatures() const
{
return {"qXfer:features:read+"};
};
bool getXferFeaturesRead(const std::string &annex, std::string &output);
};