sim: Add a dumpSimcall mechanism to GuestABI.

This dumps a signature for a simcall as if it was going to be invoked,
and can be used for debugging.

Change-Id: I6262b94ad4186bac8dc5a1469e9bb3b8ae9d34e1
Reviewed-on: https://gem5-review.googlesource.com/c/public/gem5/+/23460
Reviewed-by: Jason Lowe-Power <jason@lowepower.com>
Maintainer: Jason Lowe-Power <jason@lowepower.com>
Tested-by: kokoro <noreply+kokoro@google.com>
This commit is contained in:
Gabe Black
2019-12-08 01:33:23 -08:00
parent aabe7e1f69
commit b8883f887e
2 changed files with 82 additions and 0 deletions

View File

@@ -32,6 +32,7 @@
#include <functional>
#include <memory>
#include <sstream>
#include <type_traits>
class ThreadContext;
@@ -213,6 +214,14 @@ class VarArgs
}
};
template <typename ...Types>
std::ostream &
operator << (std::ostream &os, const VarArgs<Types...> &va)
{
os << "...";
return os;
}
// The ABI independent hook which tells the GuestABI mechanism what to do with
// a VarArgs argument. It constructs the underlying implementation which knows
// about the ABI, and installs it in the VarArgs wrapper to give to the
@@ -301,6 +310,44 @@ callFrom(ThreadContext *tc, typename ABI::Position &position,
callFrom<ABI, Args...>(tc, position, partial);
}
/*
* These functions are like the ones above, except they print the arguments
* a target function would be called with instead of actually calling it.
*/
// With no arguments to print, add the closing parenthesis and return.
template <typename ABI, typename Ret>
static void
dumpArgsFrom(int count, std::ostream &os, ThreadContext *tc,
typename ABI::Position &position)
{
os << ")";
}
// Recursively gather arguments for target from tc until we get to the base
// case above, and append those arguments to the string stream being
// constructed.
template <typename ABI, typename Ret, typename NextArg, typename ...Args>
static void
dumpArgsFrom(int count, std::ostream &os, ThreadContext *tc,
typename ABI::Position &position)
{
// Either open the parenthesis or add a comma, depending on where we are
// in the argument list.
os << (count ? ", " : "(");
// Extract the next argument from the thread context.
NextArg next = Argument<ABI, NextArg>::get(tc, position);
// Add this argument to the list.
os << next;
// Recursively handle any remaining arguments.
dumpArgsFrom<ABI, Ret, Args...>(count + 1, os, tc, position);
}
} // namespace GuestABI
@@ -347,4 +394,32 @@ invokeSimcall(ThreadContext *tc, void (*target)(ThreadContext *, Args...))
tc, std::function<void(ThreadContext *, Args...)>(target));
}
// These functions also wrap a simulator level function. Instead of running the
// function, they return a string which shows what arguments the function would
// be invoked with if it were called from the given context.
template <typename ABI, typename Ret, typename ...Args>
std::string
dumpSimcall(std::string name, ThreadContext *tc,
std::function<Ret(ThreadContext *, Args...)> target=
std::function<Ret(ThreadContext *, Args...)>())
{
auto position = typename ABI::Position();
std::ostringstream ss;
ss << name;
GuestABI::dumpArgsFrom<ABI, Ret, Args...>(0, ss, tc, position);
return ss.str();
}
template <typename ABI, typename Ret, typename ...Args>
std::string
dumpSimcall(std::string name, ThreadContext *tc,
Ret (*target)(ThreadContext *, Args...))
{
return dumpSimcall<ABI>(
name, tc, std::function<Ret(ThreadContext *, Args...)>(target));
}
#endif // __SIM_GUEST_ABI_HH__

View File

@@ -275,3 +275,10 @@ TEST(GuestABI, ABI_returns)
EXPECT_EQ(tc.floatResult, DoubleRetValue + 2.0);
}
}
TEST(GuestABI, dumpSimcall)
{
ThreadContext tc;
std::string dump = dumpSimcall<TestABI_1D>("test", &tc, testIntVoid);
EXPECT_EQ(dump, "test(0, 11, 2, 13, ...)");
}