#include "check-structures-sanity.h" #include "LuaTools.h" #include "LuaWrapper.h" DFHACK_PLUGIN("check-structures-sanity"); static command_result command(color_ostream &, std::vector &); DFhackCExport command_result plugin_init(color_ostream &, std::vector & 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 { uint32_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 & 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(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; }