check-structures-sanity: add -lowmem argument to use depth-first search instead of readth-first search

add progress indicator if called from the console
develop
Ben Lubar 2020-02-15 12:54:04 -06:00
parent 122169a559
commit e4ff184280
No known key found for this signature in database
GPG Key ID: 92939677AB59EDA4
1 changed files with 40 additions and 18 deletions

@ -14,7 +14,7 @@
#include <windows.h> #include <windows.h>
#endif #endif
#include <queue> #include <deque>
#include <set> #include <set>
#include <typeinfo> #include <typeinfo>
@ -37,13 +37,14 @@ DFhackCExport command_result plugin_init(color_ostream &, std::vector<PluginComm
"performs a sanity check on df-structures", "performs a sanity check on df-structures",
command, command,
false, false,
"check-structures-sanity [-enums] [-sizes] [starting_point]\n" "check-structures-sanity [-enums] [-sizes] [-lowmem] [starting_point]\n"
"\n" "\n"
"-enums: report unexpected or unnamed enum or bitfield values\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" "-sizes: report struct and class sizes that don't match structures. (requires sizecheck)\n"
"starting_point: a lua expression or a word like 'screen', 'item', or 'building' (defaults to df.global)\n" "-lowmem: use depth-first search instead of breadth-first search. uses less memory but may produce less sensible field names.\n"
"starting_point: a lua expression or a word like 'screen', 'item', or 'building'. (defaults to df.global)\n"
"\n" "\n"
"by default, check-structures-sanity reports invalid pointers, vectors, strings, and vtables" "by default, check-structures-sanity reports invalid pointers, vectors, strings, and vtables."
)); ));
return CR_OK; return CR_OK;
} }
@ -79,9 +80,11 @@ class Checker
std::vector<t_memrange> mapped; std::vector<t_memrange> mapped;
std::set<void *> seen_addr; std::set<void *> seen_addr;
public: public:
std::queue<ToCheck> queue; std::deque<ToCheck> queue;
size_t num_checked;
bool enums; bool enums;
bool sizes; bool sizes;
bool lowmem;
private: private:
bool ok; bool ok;
@ -125,6 +128,7 @@ static command_result command(color_ostream & out, std::vector<std::string> & pa
} }
BOOL_PARAM(enums); BOOL_PARAM(enums);
BOOL_PARAM(sizes); BOOL_PARAM(sizes);
BOOL_PARAM(lowmem);
#undef BOOL_PARAM #undef BOOL_PARAM
if (parameters.size() > 1) if (parameters.size() > 1)
@ -139,7 +143,7 @@ static command_result command(color_ostream & out, std::vector<std::string> & pa
global.ptr = nullptr; global.ptr = nullptr;
global.identity = &df::global::_identity; global.identity = &df::global::_identity;
checker.queue.push(std::move(global)); checker.queue.push_back(std::move(global));
} }
else else
{ {
@ -170,7 +174,7 @@ static command_result command(color_ostream & out, std::vector<std::string> & pa
return CR_FAILURE; return CR_FAILURE;
} }
checker.queue.push(std::move(ref)); checker.queue.push_back(std::move(ref));
} }
return checker.check() ? CR_OK : CR_FAILURE; return checker.check() ? CR_OK : CR_FAILURE;
@ -187,15 +191,33 @@ Checker::Checker(color_ostream & out) :
bool Checker::check() bool Checker::check()
{ {
seen_addr.clear(); seen_addr.clear();
num_checked = 0;
ok = true; ok = true;
while (!queue.empty()) while (!queue.empty())
{ {
ToCheck current = std::move(queue.front()); ToCheck current;
queue.pop(); if (lowmem)
{
current = std::move(queue.back());
queue.pop_back();
}
else
{
current = std::move(queue.front());
queue.pop_front();
}
check_dispatch(current); check_dispatch(current);
num_checked++;
if (out.is_console() && num_checked % 1000 == 0)
{
out << "checked " << num_checked << " fields\r" << std::flush;
} }
}
out << "checked " << num_checked << " fields" << std::endl;
return ok; return ok;
} }
@ -356,7 +378,7 @@ void Checker::queue_field(ToCheck && item, const struct_field_info *field)
UNEXPECTED; UNEXPECTED;
break; break;
case struct_field_info::PRIMITIVE: case struct_field_info::PRIMITIVE:
queue.push(std::move(item)); queue.push_back(std::move(item));
break; break;
case struct_field_info::STATIC_STRING: case struct_field_info::STATIC_STRING:
// TODO: check static strings? // TODO: check static strings?
@ -364,21 +386,21 @@ void Checker::queue_field(ToCheck && item, const struct_field_info *field)
case struct_field_info::POINTER: case struct_field_info::POINTER:
item.temp_identity = std::unique_ptr<df::pointer_identity>(new df::pointer_identity(field->type)); item.temp_identity = std::unique_ptr<df::pointer_identity>(new df::pointer_identity(field->type));
item.identity = item.temp_identity.get(); item.identity = item.temp_identity.get();
queue.push(std::move(item)); queue.push_back(std::move(item));
break; break;
case struct_field_info::STATIC_ARRAY: case struct_field_info::STATIC_ARRAY:
queue_static_array(item, item.ptr, field->type, field->count, false, field->eid); queue_static_array(item, item.ptr, field->type, field->count, false, field->eid);
break; break;
case struct_field_info::SUBSTRUCT: case struct_field_info::SUBSTRUCT:
queue.push(std::move(item)); queue.push_back(std::move(item));
break; break;
case struct_field_info::CONTAINER: case struct_field_info::CONTAINER:
queue.push(std::move(item)); queue.push_back(std::move(item));
break; break;
case struct_field_info::STL_VECTOR_PTR: case struct_field_info::STL_VECTOR_PTR:
item.temp_identity = std::unique_ptr<df::stl_ptr_vector_identity>(new df::stl_ptr_vector_identity(field->type, field->eid)); item.temp_identity = std::unique_ptr<df::stl_ptr_vector_identity>(new df::stl_ptr_vector_identity(field->type, field->eid));
item.identity = item.temp_identity.get(); item.identity = item.temp_identity.get();
queue.push(std::move(item)); queue.push_back(std::move(item));
break; break;
case struct_field_info::OBJ_METHOD: case struct_field_info::OBJ_METHOD:
case struct_field_info::CLASS_METHOD: case struct_field_info::CLASS_METHOD:
@ -429,7 +451,7 @@ void Checker::queue_static_array(const ToCheck & array, void *base, type_identit
item.temp_identity = std::unique_ptr<pointer_identity>(new pointer_identity(type)); item.temp_identity = std::unique_ptr<pointer_identity>(new pointer_identity(type));
item.identity = item.temp_identity.get(); item.identity = item.temp_identity.get();
} }
queue.push(std::move(item)); queue.push_back(std::move(item));
} }
} }
@ -621,7 +643,7 @@ void Checker::check_pointer(const ToCheck & item)
return; return;
} }
queue.push(ToCheck(item, "", *reinterpret_cast<void **>(item.ptr), static_cast<pointer_identity *>(item.identity)->getTarget())); queue.push_back(ToCheck(item, "", *reinterpret_cast<void **>(item.ptr), static_cast<pointer_identity *>(item.identity)->getTarget()));
} }
void Checker::check_bitfield(const ToCheck & item) void Checker::check_bitfield(const ToCheck & item)