dfhack/plugins/devel/check-structures-sanity/main.cpp

151 lines
4.4 KiB
C++

#include "check-structures-sanity.h"
#include "LuaTools.h"
#include "LuaWrapper.h"
DFHACK_PLUGIN("check-structures-sanity");
static command_result command(color_ostream &, std::vector<std::string> &);
DFhackCExport command_result plugin_init(color_ostream &, std::vector<PluginCommand> & commands)
{
commands.push_back(PluginCommand(
"check-structures-sanity",
"performs a sanity check on df-structures",
command,
false,
"check-structures-sanity [-enums] [-sizes] [-lowmem] [-maxerrors n] [-failfast] [starting_point]\n"
"\n"
"-enums: report unexpected or unnamed enum or bitfield values.\n"
"-sizes: report struct and class sizes that don't match structures. (requires sizecheck)\n"
"-unnamed: report unnamed enum/bitfield values, not just undefined ones.\n"
"-maxerrors n: set the maximum number of errors before bailing out.\n"
"-failfast: crash if any error is encountered. useful only for debugging.\n"
"-maybepointer: report integers that might actually be pointers.\n"
"starting_point: a lua expression or a word like 'screen', 'item', or 'building'. (defaults to df.global)\n"
"\n"
"by default, check-structures-sanity reports invalid pointers, vectors, strings, and vtables."
));
known_types_by_size.clear();
build_size_table();
return CR_OK;
}
bool check_malloc_perturb()
{
struct T_test {
int32_t data[1024];
};
auto test = new T_test;
bool ret = (test->data[0] == 0xd2d2d2d2);
delete test;
return ret;
}
static command_result command(color_ostream & out, std::vector<std::string> & parameters)
{
if (!check_malloc_perturb())
{
out.printerr("check-structures-sanity: MALLOC_PERTURB_ not set, cannot continue\n");
return CR_FAILURE;
}
CoreSuspender suspend;
Checker checker(out);
// check parameters with values first
#define VAL_PARAM(name, expr_using_value) \
auto name ## _idx = std::find(parameters.begin(), parameters.end(), "-" #name); \
if (name ## _idx != parameters.end()) \
{ \
if (name ## _idx + 1 == parameters.end()) \
{ \
return CR_WRONG_USAGE; \
} \
try \
{ \
auto value = std::move(*(name ## _idx + 1)); \
parameters.erase((name ## _idx + 1)); \
parameters.erase(name ## _idx); \
checker.name = (expr_using_value); \
} \
catch (std::exception & ex) \
{ \
out.printerr("check-structures-sanity: argument to -%s: %s\n", #name, ex.what()); \
return CR_WRONG_USAGE; \
} \
}
VAL_PARAM(maxerrors, std::stoul(value));
#undef VAL_PARAM
#define BOOL_PARAM(name) \
auto name ## _idx = std::find(parameters.begin(), parameters.end(), "-" #name); \
if (name ## _idx != parameters.end()) \
{ \
checker.name = true; \
parameters.erase(name ## _idx); \
}
BOOL_PARAM(enums);
BOOL_PARAM(sizes);
BOOL_PARAM(unnamed);
BOOL_PARAM(failfast);
BOOL_PARAM(noprogress);
BOOL_PARAM(maybepointer);
#undef BOOL_PARAM
if (parameters.size() > 1)
{
return CR_WRONG_USAGE;
}
if (parameters.empty())
{
checker.queue_globals();
}
else
{
using namespace DFHack::Lua;
using namespace DFHack::Lua::Core;
using namespace DFHack::LuaWrapper;
StackUnwinder unwinder(State);
PushModulePublic(out, "utils", "df_expr_to_ref");
Push(parameters.at(0));
if (!SafeCall(out, 1, 1))
{
return CR_FAILURE;
}
if (!lua_touserdata(State, -1))
{
return CR_WRONG_USAGE;
}
QueueItem item(parameters.at(0), get_object_ref(State, -1));
lua_getfield(State, -1, "_type");
lua_getfield(State, -1, "_identity");
auto identity = reinterpret_cast<type_identity *>(lua_touserdata(State, -1));
if (!identity)
{
out.printerr("could not determine type identity\n");
return CR_FAILURE;
}
checker.queue_item(item, CheckedStructure(identity));
}
while (checker.process_queue())
{
if (!checker.noprogress)
{
out << "checked " << checker.checked_count << " fields\r" << std::flush;
}
}
out << "checked " << checker.checked_count << " fields" << std::endl;
return checker.error_count ? CR_FAILURE : CR_OK;
}