Extended debugger language: Dereferenced addresses are now supported as

left hand side of assignments. IOW it is possible to modify memory, now.


git-svn-id: file:///srv/svn/repos/haiku/haiku/trunk@27193 a95241bf-73f2-0310-859d-f6bbb57e9c96
This commit is contained in:
Ingo Weinhold
2008-08-25 00:24:06 +00:00
parent 18f2a9c17d
commit b9f431f1df
+172 -75
View File
@@ -23,15 +23,17 @@
commandLine := commandPipe | ( "(" expression ")" ) commandLine := commandPipe | ( "(" expression ")" )
expression := term | assignment expression := term | assignment
assignment := variable ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" ) assignment := lhs ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" )
expression expression
lhs := variable | dereference
term := sum term := sum
sum := product ( ( "+" | "-" ) product )* sum := product ( ( "+" | "-" ) product )*
product := unary ( ( "*" | "/" | "%" ) unary )* product := unary ( ( "*" | "/" | "%" ) unary )*
unary := atom | ( ( "-" | "*" [ "{" expression "}" ] ) unary ) unary := atom | ( "-" unary ) | dereference
dereference := "*" [ "{" expression "}" ] unary
atom := variable | ( "(" expression ")" ) | ( "[" command "]" ) atom := variable | ( "(" expression ")" ) | ( "[" command "]" )
variable := identifier variable := identifier
identifier := ( "_" | "a" - "z" | "A" - "Z" ) identifier := ( "$" | "_" | "a" - "z" | "A" - "Z" )
( "_" | "a" - "z" | "A" - "Z" | "0" - "9" )* ( "_" | "a" - "z" | "A" - "Z" | "0" - "9" )*
commandPipe := command ( "|" command )* commandPipe := command ( "|" command )*
command := identifier argument* command := identifier argument*
@@ -479,7 +481,7 @@ class ExpressionParser {
size_t bufferSize); size_t bufferSize);
private: private:
uint64 _ParseExpression(); uint64 _ParseExpression(bool expectAssignment = false);
uint64 _ParseCommandPipe(int& returnCode); uint64 _ParseCommandPipe(int& returnCode);
void _ParseCommand( void _ParseCommand(
debugger_command_pipe_segment& segment); debugger_command_pipe_segment& segment);
@@ -487,10 +489,12 @@ class ExpressionParser {
void _GetUnparsedArgument(int& argc, char** argv); void _GetUnparsedArgument(int& argc, char** argv);
void _AddArgument(int& argc, char** argv, void _AddArgument(int& argc, char** argv,
const char* argument, int32 length = -1); const char* argument, int32 length = -1);
uint64 _ParseSum(); uint64 _ParseSum(bool useValue, uint64 value);
uint64 _ParseProduct(); uint64 _ParseProduct();
uint64 _ParsePower(); uint64 _ParsePower();
uint64 _ParseUnary(); uint64 _ParseUnary();
uint64 _ParseDereference(void** _address,
uint32* _size);
uint64 _ParseAtom(); uint64 _ParseAtom();
const Token& _EatToken(int32 type); const Token& _EatToken(int32 type);
@@ -525,28 +529,36 @@ ExpressionParser::EvaluateExpression(const char* expressionString)
uint64 uint64
ExpressionParser::EvaluateCommand(const char* expressionString, ExpressionParser::EvaluateCommand(const char* expressionString, int& returnCode)
int& returnCode)
{ {
fTokenizer.SetTo(expressionString); fTokenizer.SetTo(expressionString);
// Allowed is not only a command, but also an assignment. Either starts with // Allowed are command or assignment. A command always starts with an
// an identifier. // identifier, an assignment either with an identifier (variable name) or
_EatToken(TOKEN_IDENTIFIER); // a dereferenced address.
uint64 value;
const Token& token = fTokenizer.NextToken(); const Token& token = fTokenizer.NextToken();
if (token.type & TOKEN_ASSIGN_FLAG) { uint64 value = 0;
// an assignment
if (token.type == TOKEN_IDENTIFIER) {
fTokenizer.NextToken();
if (token.type & TOKEN_ASSIGN_FLAG) {
// an assignment
fTokenizer.SetTo(expressionString);
value = _ParseExpression(true);
returnCode = 0;
} else {
// no assignment, so let's assume it's a command
fTokenizer.SetTo(expressionString);
fTokenizer.SetCommandMode(true);
value = _ParseCommandPipe(returnCode);
}
} else if (token.type == TOKEN_STAR) {
// dereferenced address -- assignment
fTokenizer.SetTo(expressionString); fTokenizer.SetTo(expressionString);
value = _ParseExpression(); value = _ParseExpression(true);
returnCode = 0; returnCode = 0;
} else { } else
// no assignment, so let's assume it's a command parse_exception("expected command or assignment", 0);
fTokenizer.SetTo(expressionString);
fTokenizer.SetCommandMode(true);
value = _ParseCommandPipe(returnCode);
}
if (token.type != TOKEN_END_OF_LINE) if (token.type != TOKEN_END_OF_LINE)
parse_exception("parse error", token.position); parse_exception("parse error", token.position);
@@ -585,7 +597,7 @@ ExpressionParser::ParseNextCommandArgument(const char** expressionString,
uint64 uint64
ExpressionParser::_ParseExpression() ExpressionParser::_ParseExpression(bool expectAssignment)
{ {
const Token& token = fTokenizer.NextToken(); const Token& token = fTokenizer.NextToken();
int32 position = token.position; int32 position = token.position;
@@ -646,18 +658,92 @@ ExpressionParser::_ParseExpression()
variableValue %= rhs; variableValue %= rhs;
break; break;
default: default:
fTokenizer.SetPosition(position); parse_exception("internal error: unknown assignment token",
return _ParseSum(); position);
break;
} }
set_debug_variable(variable, variableValue); set_debug_variable(variable, variableValue);
return variableValue; return variableValue;
} }
} else if (token.type == TOKEN_STAR) {
void* address;
uint32 size;
uint64 value = _ParseDereference(&address, &size);
int32 assignmentType = fTokenizer.NextToken().type;
if (assignmentType & TOKEN_ASSIGN_FLAG) {
// an assignment
uint64 rhs = _ParseExpression();
// check for division by zero for the respective assignment types
if ((assignmentType == TOKEN_SLASH_ASSIGN
|| assignmentType == TOKEN_MODULO_ASSIGN)
&& rhs == 0) {
parse_exception("division by zero", position);
}
// compute the new value
switch (assignmentType) {
case TOKEN_ASSIGN:
value = rhs;
break;
case TOKEN_PLUS_ASSIGN:
value += rhs;
break;
case TOKEN_MINUS_ASSIGN:
value -= rhs;
break;
case TOKEN_STAR_ASSIGN:
value *= rhs;
break;
case TOKEN_SLASH_ASSIGN:
value /= rhs;
break;
case TOKEN_MODULO_ASSIGN:
value %= rhs;
break;
default:
parse_exception("internal error: unknown assignment token",
position);
break;
}
// convert the value for writing to the address
uint64 buffer = 0;
switch (size) {
case 1:
*(uint8*)&buffer = value;
break;
case 2:
*(uint16*)&buffer = value;
break;
case 4:
*(uint32*)&buffer = value;
break;
case 8:
value = buffer;
break;
}
if (user_memcpy(address, &buffer, size) != B_OK) {
snprintf(sTempBuffer, sizeof(sTempBuffer),
"failed to write to address %p", address);
parse_exception(sTempBuffer, position);
}
return value;
}
}
if (expectAssignment) {
parse_exception("expected assignment",
fTokenizer.CurrentToken().position);
} }
// no assignment -- reset to the identifier position and parse a sum // no assignment -- reset to the identifier position and parse a sum
fTokenizer.SetPosition(position); fTokenizer.SetPosition(position);
return _ParseSum(); return _ParseSum(false, 0);
} }
@@ -877,9 +963,10 @@ ExpressionParser::_AddArgument(int& argc, char** argv, const char* argument,
uint64 uint64
ExpressionParser::_ParseSum() ExpressionParser::_ParseSum(bool useValue, uint64 value)
{ {
uint64 value = _ParseProduct(); if (!useValue)
value = _ParseProduct();
while (true) { while (true) {
const Token& token = fTokenizer.NextToken(); const Token& token = fTokenizer.NextToken();
@@ -941,54 +1028,7 @@ ExpressionParser::_ParseUnary()
return -_ParseUnary(); return -_ParseUnary();
case TOKEN_STAR: case TOKEN_STAR:
{ return _ParseDereference(NULL, NULL);
int32 starPosition = fTokenizer.CurrentToken().position;
// optional "{ ... }" specifying the size to read
uint64 size = 4;
if (fTokenizer.NextToken().type == TOKEN_OPENING_BRACE) {
int32 position = fTokenizer.CurrentToken().position;
size = _ParseExpression();
if (size != 1 && size != 2 && size != 4 && size != 8) {
snprintf(sTempBuffer, sizeof(sTempBuffer),
"invalid size (%llu) for unary * operator", size);
parse_exception(sTempBuffer, position);
}
_EatToken(TOKEN_CLOSING_BRACE);
} else
fTokenizer.RewindToken();
const void* address = (const void*)(addr_t)_ParseUnary();
// read bytes from address into a tempory buffer
uint64 buffer;
if (user_memcpy(&buffer, address, size) != B_OK) {
snprintf(sTempBuffer, sizeof(sTempBuffer),
"failed to dereference address %p", address);
parse_exception(sTempBuffer, starPosition);
}
// convert the value to uint64
uint64 value = 0;
switch (size) {
case 1:
value = *(uint8*)&buffer;
break;
case 2:
value = *(uint16*)&buffer;
break;
case 4:
value = *(uint32*)&buffer;
break;
case 8:
value = buffer;
break;
}
return value;
}
default: default:
fTokenizer.RewindToken(); fTokenizer.RewindToken();
@@ -999,6 +1039,63 @@ ExpressionParser::_ParseUnary()
} }
uint64
ExpressionParser::_ParseDereference(void** _address, uint32* _size)
{
int32 starPosition = fTokenizer.CurrentToken().position;
// optional "{ ... }" specifying the size to read
uint64 size = 4;
if (fTokenizer.NextToken().type == TOKEN_OPENING_BRACE) {
int32 position = fTokenizer.CurrentToken().position;
size = _ParseExpression();
if (size != 1 && size != 2 && size != 4 && size != 8) {
snprintf(sTempBuffer, sizeof(sTempBuffer),
"invalid size (%llu) for unary * operator", size);
parse_exception(sTempBuffer, position);
}
_EatToken(TOKEN_CLOSING_BRACE);
} else
fTokenizer.RewindToken();
const void* address = (const void*)(addr_t)_ParseUnary();
// read bytes from address into a tempory buffer
uint64 buffer;
if (user_memcpy(&buffer, address, size) != B_OK) {
snprintf(sTempBuffer, sizeof(sTempBuffer),
"failed to dereference address %p", address);
parse_exception(sTempBuffer, starPosition);
}
// convert the value to uint64
uint64 value = 0;
switch (size) {
case 1:
value = *(uint8*)&buffer;
break;
case 2:
value = *(uint16*)&buffer;
break;
case 4:
value = *(uint32*)&buffer;
break;
case 8:
value = buffer;
break;
}
if (_address != NULL)
*_address = (void*)address;
if (_size != NULL)
*_size = size;
return value;
}
uint64 uint64
ExpressionParser::_ParseAtom() ExpressionParser::_ParseAtom()
{ {