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:
@@ -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()
|
||||||
{
|
{
|
||||||
|
|||||||
Reference in New Issue
Block a user