mirror of
https://github.com/ultimatepp/ultimatepp.git
synced 2026-05-15 14:16:07 -06:00
654 lines
12 KiB
C++
654 lines
12 KiB
C++
#include "Debuggers.h"
|
|
|
|
#ifdef PLATFORM_WIN32
|
|
|
|
#define LLOG(x) // DLOG(x)
|
|
|
|
#ifdef _DEBUG
|
|
String Pdb::Val::ToString() const
|
|
{
|
|
String r;
|
|
r << "Pdb::Val type: " << type << ", ref: " << ref << ", array: " << array
|
|
<< ", rvalue: " << rvalue;
|
|
return r;
|
|
}
|
|
#endif
|
|
|
|
void Pdb::ThrowError(const char *s)
|
|
{
|
|
throw CParser::Error(s);
|
|
}
|
|
|
|
int Pdb::SizeOfType(int ti)
|
|
{
|
|
if(ti >= 0)
|
|
return GetType(ti).size;
|
|
switch(ti) {
|
|
case BOOL1:
|
|
case SINT1:
|
|
case UINT1:
|
|
return 1;
|
|
case SINT2:
|
|
case UINT2:
|
|
return 2;
|
|
case SINT4:
|
|
case UINT4:
|
|
case FLT:
|
|
return 4;
|
|
case SINT8:
|
|
case UINT8:
|
|
case DBL:
|
|
return 8;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
int Pdb::SizeOfType(const String& name)
|
|
{
|
|
TypeInfo f = GetTypeInfo(name);
|
|
if(f.ref)
|
|
return win64 ? 8 : 4;
|
|
return SizeOfType(f.type);
|
|
}
|
|
|
|
#define READINT0(type) { \
|
|
type x; \
|
|
if(v.address < 10000) \
|
|
x = (type)GetCpuRegister(*v.context, (int)v.address); \
|
|
else \
|
|
if(!Copy(v.address, &x, sizeof(x))) \
|
|
ThrowError("??"); \
|
|
v.ival = x; \
|
|
break; \
|
|
} \
|
|
|
|
#define READINT(q, type) \
|
|
case q: READINT0(type)
|
|
|
|
#define READFLT(q, type) \
|
|
case q: { \
|
|
type x; \
|
|
if(!Copy(v.address, &x, sizeof(x))) \
|
|
ThrowError("??"); \
|
|
v.fval = x; \
|
|
break; \
|
|
} \
|
|
|
|
adr_t Pdb::PeekPtr(adr_t address)
|
|
{
|
|
adr_t r = 0;
|
|
if(!Copy(address, &r, win64 ? 8 : 4))
|
|
ThrowError("??");
|
|
return r;
|
|
}
|
|
|
|
byte Pdb::PeekByte(adr_t address)
|
|
{
|
|
byte b;
|
|
if(!Copy(address, &b, 1))
|
|
ThrowError("??");
|
|
return b;
|
|
}
|
|
|
|
word Pdb::PeekWord(adr_t address)
|
|
{
|
|
word w;
|
|
if(!Copy(address, &w, 2))
|
|
ThrowError("??");
|
|
return w;
|
|
}
|
|
|
|
dword Pdb::PeekDword(adr_t address)
|
|
{
|
|
dword w;
|
|
if(!Copy(address, &w, 4))
|
|
ThrowError("??");
|
|
return w;
|
|
}
|
|
|
|
Pdb::Val Pdb::GetRVal(Pdb::Val v)
|
|
{ // read data from debugee
|
|
if(v.rvalue)
|
|
return v;
|
|
v.rvalue = true;
|
|
if(v.array) {
|
|
v.array = false;
|
|
v.ref++;
|
|
}
|
|
else
|
|
if(v.ref) { // Fetch pointer from the debugee memory
|
|
v.address = PeekPtr(v.address);
|
|
}
|
|
else
|
|
if(v.bitcnt) { // Fetch bitfield from debugee memory
|
|
dword w;
|
|
if(!Copy(v.address, &w, win64 ? 8 : 4))
|
|
ThrowError("??");
|
|
w = (w >> v.bitpos) & (0xffffffff >> (32 - v.bitcnt));
|
|
if(v.type == SINT1 || v.type == SINT2 || v.type == SINT4 || v.type == SINT8) {
|
|
if(w & (1 << (v.bitcnt - 1)))
|
|
w |= (0xffffffff << v.bitcnt);
|
|
v.ival = (int32)w;
|
|
}
|
|
else
|
|
v.ival = w;
|
|
}
|
|
else {
|
|
if(v.address < 10000 && !v.context)
|
|
ThrowError("??"); // Register value is missing context
|
|
switch(v.type) { // resolve primitive value
|
|
READINT(BOOL1, bool)
|
|
READINT(UINT1, byte);
|
|
READINT(SINT1, int8);
|
|
READINT(UINT2, uint16);
|
|
READINT(SINT2, int16);
|
|
READINT(UINT4, uint32);
|
|
READINT(SINT4, int32);
|
|
READINT(UINT8, uint64);
|
|
READINT(SINT8, int64);
|
|
READFLT(FLT, float);
|
|
READFLT(DBL, double);
|
|
case PFUNC:
|
|
if(win64) {
|
|
READINT0(uint64);
|
|
}
|
|
else {
|
|
READINT0(uint32)
|
|
}
|
|
break;
|
|
case UNKNOWN:
|
|
break;
|
|
default:
|
|
ThrowError("Invalid operand");
|
|
}
|
|
}
|
|
return v;
|
|
}
|
|
|
|
#define GETINT(q, type) case q: return (type)v.ival;
|
|
|
|
int64 Pdb::GetInt64(Pdb::Val v)
|
|
{
|
|
v = GetRVal(v);
|
|
switch(v.type) {
|
|
GETINT(BOOL1, bool)
|
|
GETINT(UINT1, byte);
|
|
GETINT(SINT1, int8);
|
|
GETINT(UINT2, uint16);
|
|
GETINT(SINT2, int16);
|
|
GETINT(UINT4, uint32);
|
|
GETINT(SINT4, int32);
|
|
GETINT(UINT8, uint64);
|
|
GETINT(SINT8, int64);
|
|
case FLT:
|
|
case DBL:
|
|
if(FitsInInt64(v.fval))
|
|
return (int64)v.fval;
|
|
}
|
|
ThrowError("Invalid operand");
|
|
return 0;
|
|
}
|
|
|
|
int64 Pdb::GetInt64Attr(Pdb::Val v, const char *a)
|
|
{
|
|
return GetInt64(GetAttr(v, a));
|
|
}
|
|
|
|
#define GETINT(q, type) case q: return (type)v.ival;
|
|
|
|
double Pdb::GetFlt(Pdb::Val v)
|
|
{
|
|
v = GetRVal(v);
|
|
if(v.type == DBL || v.type == FLT)
|
|
return v.fval;
|
|
return (double)GetInt64(v);
|
|
}
|
|
|
|
void Pdb::ZeroDiv(double x)
|
|
{
|
|
if(x == 0)
|
|
ThrowError("Divide by zero");
|
|
}
|
|
|
|
Pdb::Val Pdb::Compute(Pdb::Val v1, Pdb::Val v2, int oper)
|
|
{
|
|
LLOG("Compute " << char(oper));
|
|
LLOG("v1: " << v1);
|
|
LLOG("v2: " << v2);
|
|
if(v1.ref || v1.array) {
|
|
int q = (int)GetInt64(v2) * (v1.ref > 1 ? 4 : SizeOfType(v1.type));
|
|
v1 = GetRVal(v1);
|
|
switch(oper) {
|
|
case '+': v1.address += q; break;
|
|
case '-': v1.address -= q; break;
|
|
default: ThrowError("Invalid pointer arithmetics");
|
|
}
|
|
return v1;
|
|
}
|
|
if(v2.ref || v2.array) {
|
|
int q = (int)GetInt64(v1) * (v2.ref ? 4 : SizeOfType(v2.type));
|
|
v2 = GetRVal(v2);
|
|
if(oper == '+')
|
|
v2.address += q;
|
|
else
|
|
ThrowError("Invalid pointer arithmetics");
|
|
return v2;
|
|
}
|
|
Pdb::Val res;
|
|
res.type = max(v1.type, v2.type);
|
|
res.rvalue = true;
|
|
if(res.type == DBL || res.type == FLT) {
|
|
double a = GetFlt(v1);
|
|
double b = GetFlt(v2);
|
|
switch(oper) {
|
|
case '+': res.fval = a + b; break;
|
|
case '-': res.fval = a - b; break;
|
|
case '*': res.fval = a * b; break;
|
|
case '/': ZeroDiv(b); res.fval = a / b; break;
|
|
default: ThrowError("Invalid operands for operation");
|
|
}
|
|
}
|
|
else {
|
|
int64 a = GetInt64(v1);
|
|
int64 b = GetInt64(v2);
|
|
switch(oper) {
|
|
case '+': res.ival = a + b; break;
|
|
case '-': res.ival = a - b; break;
|
|
case '*': res.ival = a * b; break;
|
|
case '/': ZeroDiv((double)b); res.ival = a / b; break;
|
|
case '%': ZeroDiv((double)b); res.ival = a % b; break;
|
|
default: ThrowError("Invalid operands for operation");
|
|
}
|
|
}
|
|
return res;
|
|
}
|
|
|
|
Pdb::Val Pdb::RValue(int64 i)
|
|
{
|
|
Val v;
|
|
v.rvalue = true;
|
|
v.ival = i;
|
|
v.type = i >= -128 && i <= 127 ? SINT1 :
|
|
i >= -32768 && i <= 32767 ? SINT2 :
|
|
i >= INT_MIN && i <= INT_MAX ? SINT4 :
|
|
SINT8;
|
|
return v;
|
|
}
|
|
|
|
Pdb::Val Pdb::DeRef(Pdb::Val v)
|
|
{
|
|
if(v.ref <= 0)
|
|
ThrowError("Only pointer can be dereferenced");
|
|
v = GetRVal(v);
|
|
v.ref--;
|
|
v.rvalue = false;
|
|
return v;
|
|
}
|
|
|
|
Pdb::Val Pdb::Ref(Pdb::Val v)
|
|
{
|
|
if(v.rvalue)
|
|
ThrowError("R-value cannot be referenced");
|
|
if(v.reference)
|
|
v.reference = false;
|
|
else {
|
|
v.rvalue = true;
|
|
v.ref++;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
Pdb::Val Pdb::Field0(Pdb::Val v, const String& field)
|
|
{
|
|
Val v1;
|
|
v1.type = Null;
|
|
if(v.rvalue || v.ref || v.array || v.type < 0)
|
|
return v1;
|
|
const Type& t = GetType(v.type);
|
|
int q = t.member.Find(field);
|
|
if(q >= 0) {
|
|
Val r = t.member[q];
|
|
r.address += v.address;
|
|
return r;
|
|
}
|
|
q = t.static_member.Find(field);
|
|
if(q >= 0)
|
|
return t.static_member[q];
|
|
for(int i = 0; i < t.base.GetCount(); i++) {
|
|
v1 = t.base[i];
|
|
v1.address += v.address;
|
|
if(v1.type >= 0) {
|
|
if(GetType(v1.type).name == field)
|
|
return v1;
|
|
v1 = Field0(v1, field);
|
|
if(!IsNull(v1.type))
|
|
return v1;
|
|
}
|
|
}
|
|
return v1;
|
|
}
|
|
|
|
Pdb::Val Pdb::Field(Pdb::Val v, const String& field)
|
|
{
|
|
if(v.type < 0)
|
|
ThrowError("Must be struct, class or union");
|
|
v = Field0(v, field);
|
|
if(IsNull(v.type))
|
|
ThrowError("Unknown member \'" + field + "\'");
|
|
return v;
|
|
}
|
|
|
|
Pdb::Val Pdb::Term(CParser& p)
|
|
{
|
|
if(p.Char2('0', 'x') || p.Char2('0', 'X'))
|
|
return RValue(p.ReadNumber64(16));
|
|
if(p.IsChar2('0', '.')) {
|
|
Val v;
|
|
v.type = DBL;
|
|
v.rvalue = true;
|
|
v.fval = p.ReadDouble();
|
|
return v;
|
|
}
|
|
if(p.Char('0'))
|
|
return RValue(p.IsNumber() ? p.ReadNumber64(8) : 0);
|
|
if(p.IsNumber()) {
|
|
double d = p.ReadDouble();
|
|
if(d >= INT_MIN && d <= INT_MAX && (int)d == d) {
|
|
return RValue((int)d);
|
|
}
|
|
else {
|
|
Val v;
|
|
v.type = DBL;
|
|
v.rvalue = true;
|
|
v.fval = d;
|
|
return v;
|
|
}
|
|
}
|
|
if(p.IsChar('\'')) {
|
|
String s = p.ReadString('\'');
|
|
if(s.GetLength() != 1)
|
|
ThrowError("Invalid character literal");
|
|
return RValue(s[0]);
|
|
}
|
|
if(p.Char('(')) {
|
|
Val v = Exp0(p);
|
|
p.PassChar(')');
|
|
return v;
|
|
}
|
|
if(!p.IsId())
|
|
ThrowError("Missing id");
|
|
String id = p.ReadId();
|
|
while(p.Char2(':', ':') && p.IsId())
|
|
id << "::" << p.ReadId();
|
|
if(current_frame) {
|
|
Frame& f = *current_frame;
|
|
int q = f.local.Find(id);
|
|
if(q >= 0)
|
|
return f.local[q];
|
|
q = f.param.Find(id);
|
|
if(q >= 0)
|
|
return f.param[q];
|
|
q = f.local.Find("this");
|
|
if(q >= 0) {
|
|
Val v = Field0(DeRef(f.local[q]), id);
|
|
if(!IsNull(v.type))
|
|
return v;
|
|
}
|
|
String scope = f.fn.name;
|
|
do {
|
|
String n = id;
|
|
int q = scope.ReverseFind("::");
|
|
if(q >= 0) {
|
|
scope.Trim(q);
|
|
n = scope + "::" + id;
|
|
}
|
|
else
|
|
scope.Clear();
|
|
Val v = GetGlobal(n);
|
|
if(v.address)
|
|
return v;
|
|
}
|
|
while(scope.GetCount());
|
|
}
|
|
ThrowError("\'" + id + "\' undefined");
|
|
return Val();
|
|
}
|
|
|
|
String ReadType(CParser& p)
|
|
{
|
|
String t = p.ReadId();
|
|
if(p.Char('<')) {
|
|
t << '<';
|
|
int l = 1;
|
|
while(l > 0) {
|
|
if(p.Char('<')) {
|
|
t << '<';
|
|
l++;
|
|
}
|
|
else
|
|
if(p.Char('>')) {
|
|
t << '>';
|
|
l--;
|
|
}
|
|
else
|
|
if(p.IsNumber())
|
|
t << p.ReadInt();
|
|
else
|
|
if(p.Char(','))
|
|
t << ',';
|
|
else
|
|
t << p.ReadId();
|
|
}
|
|
}
|
|
return t;
|
|
}
|
|
|
|
Pdb::Val Pdb::Post(CParser& p)
|
|
{
|
|
Val v = Term(p);
|
|
LLOG("Post: " << v);
|
|
for(;;) {
|
|
auto DoIndex = [&](bool key) {
|
|
int ech = key ? ')' : ']';
|
|
int64 i = Null;
|
|
if(!p.Char(ech))
|
|
i = (int)GetInt64(Exp0(p));
|
|
p.Char(ech);
|
|
Pretty p;
|
|
if(PrettyVal(v, 0, 0, p) && p.kind != SINGLE_VALUE && p.data_type.GetCount() &&
|
|
(IsNull(i) && p.data_count || abs(i) < p.data_count)) {
|
|
if(IsNull(i))
|
|
i = p.data_count - 1;
|
|
else
|
|
if(i < 0)
|
|
i = p.data_count + i;
|
|
Pretty p;
|
|
PrettyVal(v, i, 1, p);
|
|
Val item;
|
|
int q = 0;
|
|
if(p.data_type.GetCount() > 1) {
|
|
if(!key)
|
|
q = 1;
|
|
}
|
|
(TypeInfo &)item = GetTypeInfo(p.data_type[q]);
|
|
item.context = v.context;
|
|
item.address = q < p.data_ptr.GetCount() ? p.data_ptr[q] : 0;
|
|
v = item;
|
|
}
|
|
else
|
|
v = DeRef(Compute(v, RValue(Nvl(i)), '+'));
|
|
};
|
|
|
|
if(p.Char(':'))
|
|
v = Field(v.ref ? DeRef(v) : v, ReadType(p));
|
|
else
|
|
if(p.Char('.') || p.Char2(':', ':') || p.Char2('-', '>'))
|
|
v = Field(v.ref ? DeRef(v) : v, p.ReadId());
|
|
else
|
|
if(p.Char('['))
|
|
DoIndex(false);
|
|
else
|
|
if(p.Char('('))
|
|
DoIndex(true);
|
|
else
|
|
break;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
Pdb::Val Pdb::Unary(CParser& p)
|
|
{
|
|
if(p.Char('-'))
|
|
return Compute(RValue(0), Unary(p), '-');
|
|
if(p.Char('+'))
|
|
return GetRVal(Unary(p));
|
|
if(p.Char('#')) {
|
|
Pretty pp;
|
|
if(PrettyVal(Unary(p), 0, 0, pp))
|
|
return RValue((int)pp.data_count);
|
|
ThrowError("Value not recognized as high-level type");
|
|
}
|
|
if(p.Char('*'))
|
|
return DeRef(Unary(p));
|
|
if(p.Char('&'))
|
|
return Ref(Unary(p));
|
|
if(p.Char('!')) {
|
|
Val v = GetRVal(Unary(p));
|
|
if(v.type == FLT || v.type == DBL)
|
|
return RValue(!GetFlt(v));
|
|
else
|
|
return RValue(!GetInt64(v));
|
|
}
|
|
return Post(p);
|
|
}
|
|
|
|
Pdb::Val Pdb::Multiplicative(CParser& p)
|
|
{
|
|
Val v = Unary(p);
|
|
for(;;) {
|
|
if(p.Char('*'))
|
|
v = Compute(v, Unary(p), '*');
|
|
else
|
|
if(p.Char('/'))
|
|
v = Compute(v, Unary(p), '/');
|
|
else
|
|
if(p.Char('%'))
|
|
v = Compute(v, Unary(p), '%');
|
|
else
|
|
break;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
Pdb::Val Pdb::Additive(CParser& p)
|
|
{
|
|
Val v = Multiplicative(p);
|
|
for(;;) {
|
|
if(p.Char('+'))
|
|
v = Compute(v, Multiplicative(p), '+');
|
|
else
|
|
if(p.Char('-'))
|
|
v = Compute(v, Multiplicative(p), '-');
|
|
else
|
|
break;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
Pdb::Val Pdb::Compare(Val v1, CParser& p, int r1, int r2)
|
|
{
|
|
int q;
|
|
Val v2 = Additive(p);
|
|
if(findarg(max(v1.type, v2.type), DBL, FLT) >= 0)
|
|
q = SgnCompare(GetFlt(v1), GetFlt(v2));
|
|
else
|
|
q = SgnCompare(GetInt64(v1), GetInt64(v2));
|
|
q = sgn(q);
|
|
return RValue(q == r1 || q == r2);
|
|
}
|
|
|
|
Pdb::Val Pdb::Comparison(CParser& p)
|
|
{
|
|
Val v = Additive(p);
|
|
for(;;) {
|
|
if(p.Char2('=', '='))
|
|
v = Compare(v, p, 0, 0);
|
|
else
|
|
if(p.Char2('!', '='))
|
|
v = Compare(v, p, -1, 1);
|
|
else
|
|
if(p.Char2('<', '='))
|
|
v = Compare(v, p, -1, 0);
|
|
else
|
|
if(p.Char2('>', '='))
|
|
v = Compare(v, p, 1, 0);
|
|
else
|
|
if(p.Char('<'))
|
|
v = Compare(v, p, -1, -1);
|
|
else
|
|
if(p.Char('>'))
|
|
v = Compare(v, p, 1, 1);
|
|
else
|
|
break;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
void Pdb::GetBools(Val v1, Val v2, bool& a, bool& b)
|
|
{
|
|
if(findarg(max(v1.type, v2.type), DBL, FLT) >= 0) {
|
|
a = (bool)GetFlt(v1);
|
|
b = (bool)GetFlt(v2);
|
|
}
|
|
else {
|
|
a = (bool)GetInt64(v1);
|
|
b = (bool)GetInt64(v2);
|
|
}
|
|
}
|
|
|
|
Pdb::Val Pdb::LogAnd(CParser& p)
|
|
{
|
|
Val v = Comparison(p);
|
|
for(;;) {
|
|
if(p.Char2('&', '&')) {
|
|
bool a, b;
|
|
GetBools(v, Comparison(p), a, b);
|
|
v = RValue(a && b);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
Pdb::Val Pdb::LogOr(CParser& p)
|
|
{
|
|
Val v = LogAnd(p);
|
|
for(;;) {
|
|
if(p.Char2('|', '|')) {
|
|
bool a, b;
|
|
GetBools(v, LogAnd(p), a, b);
|
|
v = RValue(a || b);
|
|
}
|
|
else
|
|
break;
|
|
}
|
|
return v;
|
|
}
|
|
|
|
Pdb::Val Pdb::Exp0(CParser& p)
|
|
{
|
|
DR_LOG("Evaluating Expression: " << p.GetPtr());
|
|
LLOG("Evaluating Expression: " << p.GetPtr());
|
|
return LogOr(p);
|
|
}
|
|
|
|
Pdb::Val Pdb::Exp(CParser& p)
|
|
{
|
|
Pdb::Val v = Exp0(p);
|
|
if(!p.IsEof())
|
|
ThrowError("Invalid expression");
|
|
return v;
|
|
}
|
|
|
|
#endif
|