dfhack/plugins/devel/check-structures-sanity/check-structures-sanity.h

161 lines
5.8 KiB
C++

#pragma once
#include "Console.h"
#include "PluginManager.h"
#include "MemAccess.h"
#include "DataDefs.h"
#include "DataIdentity.h"
#include <deque>
#include <map>
#include <string>
using namespace DFHack;
#ifdef WIN32
#define UNEXPECTED __debugbreak()
#else
#define UNEXPECTED __asm__ volatile ("int $0x03; nop")
#endif
#define PTR_ADD(ptr, offset) reinterpret_cast<const void *>(uintptr_t(ptr) + (offset))
struct QueueItem
{
QueueItem(const std::string &, const void *);
QueueItem(const QueueItem &, const std::string &, const void *);
QueueItem(const QueueItem &, size_t, const void *);
std::string path;
const void *ptr;
};
struct CheckedStructure
{
type_identity *identity;
size_t count;
size_t allocated_count;
enum_identity *eid;
bool ptr_is_array;
bool inside_structure;
CheckedStructure();
explicit CheckedStructure(type_identity *, size_t = 0);
CheckedStructure(type_identity *, size_t, enum_identity *, bool);
CheckedStructure(const struct_field_info *);
size_t full_size() const;
bool has_type_at_offset(const CheckedStructure &, size_t) const;
};
#define MIN_SIZE_FOR_SUGGEST 64
extern std::map<size_t, std::vector<std::string>> known_types_by_size;
void build_size_table();
namespace
{
template<typename T, bool is_pointer = std::is_pointer<T>::value>
struct safe_t
{
typedef T type;
};
template<typename T>
struct safe_t<T, true>
{
typedef void *type;
};
}
class Checker
{
color_ostream & out;
std::vector<t_memrange> mapped;
std::map<const void *, std::pair<std::string, CheckedStructure>> data;
std::deque<QueueItem> queue;
public:
size_t checked_count;
size_t error_count;
size_t maxerrors;
bool maxerrors_reported;
bool enums;
bool sizes;
bool unnamed;
bool failfast;
bool noprogress;
bool maybepointer;
Checker(color_ostream & out);
bool queue_item(const QueueItem & item, CheckedStructure cs);
void queue_globals();
bool process_queue();
bool is_in_global(const QueueItem & item);
bool is_valid_dereference(const QueueItem & item, const CheckedStructure & cs, size_t size, bool quiet);
inline bool is_valid_dereference(const QueueItem & item, const CheckedStructure & cs, bool quiet = false)
{
return is_valid_dereference(item, cs, cs.full_size(), quiet);
}
inline bool is_valid_dereference(const QueueItem & item, size_t size, bool quiet = false)
{
return is_valid_dereference(item, CheckedStructure(df::identity_traits<void *>::get()), size, quiet);
}
template<typename T>
const T validate_and_dereference(const QueueItem & item, bool quiet = false)
{
CheckedStructure cs;
cs.identity = df::identity_traits<typename safe_t<T>::type>::get();
if (!is_valid_dereference(item, cs, quiet))
return T();
return *reinterpret_cast<const T *>(item.ptr);
}
int64_t get_int_value(const QueueItem & item, type_identity *type, bool quiet = false);
const char *get_vtable_name(const QueueItem & item, const CheckedStructure & cs, bool quiet = false);
std::pair<const void *, CheckedStructure> validate_vector_size(const QueueItem & item, const CheckedStructure & cs, bool quiet = false);
size_t get_allocated_size(const QueueItem & item);
#ifndef WIN32
// this function doesn't make sense on windows, where std::string is not pointer-sized.
const std::string *validate_stl_string_pointer(const void *const*);
#endif
static const char *const *get_enum_item_key(enum_identity *identity, int64_t value);
static const char *const *get_enum_item_attr_or_key(enum_identity *identity, int64_t value, const char *attr_name);
private:
color_ostream & fail(int, const QueueItem &, const CheckedStructure &);
void dispatch_item(const QueueItem &, const CheckedStructure &);
void dispatch_single_item(const QueueItem &, const CheckedStructure &);
void dispatch_primitive(const QueueItem &, const CheckedStructure &);
void dispatch_pointer(const QueueItem &, const CheckedStructure &);
void dispatch_container(const QueueItem &, const CheckedStructure &);
void dispatch_ptr_container(const QueueItem &, const CheckedStructure &);
void dispatch_bit_container(const QueueItem &, const CheckedStructure &);
void dispatch_bitfield(const QueueItem &, const CheckedStructure &);
void dispatch_enum(const QueueItem &, const CheckedStructure &);
void dispatch_struct(const QueueItem &, const CheckedStructure &);
void dispatch_field(const QueueItem &, const CheckedStructure &, struct_identity *, const struct_field_info *);
void dispatch_class(const QueueItem &, const CheckedStructure &);
void dispatch_buffer(const QueueItem &, const CheckedStructure &);
void dispatch_stl_ptr_vector(const QueueItem &, const CheckedStructure &);
void dispatch_tagged_union(const QueueItem &, const QueueItem &, const CheckedStructure &, const CheckedStructure &, const char *);
void dispatch_tagged_union_vector(const QueueItem &, const QueueItem &, const CheckedStructure &, const CheckedStructure &, const char *);
void dispatch_untagged_union(const QueueItem &, const CheckedStructure &);
void check_unknown_pointer(const QueueItem &);
void check_stl_vector(const QueueItem &, type_identity *, type_identity *);
void check_stl_string(const QueueItem &);
void check_possible_pointer(const QueueItem &, const CheckedStructure &);
friend struct CheckedStructure;
static type_identity *wrap_in_pointer(type_identity *);
static type_identity *wrap_in_stl_ptr_vector(type_identity *);
};
#define FAIL(message) \
do \
{ \
auto & failstream = fail(__LINE__, item, cs); \
failstream << message; \
failstream << COLOR_RESET << std::endl; \
if (failfast) \
UNEXPECTED; \
} \
while (false)