* Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de
* Copyright 2006, Stephan Aßmus, superstippi@gmx.de
* Distributed under the terms of the MIT License.
*/
#include <debug.h>
#include <ctype.h>
#include <setjmp.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <KernelExport.h>
#include <debug_heap.h>
#include "debug_commands.h"
#include "debug_variables.h"
Grammar:
commandLine := ( commandPipe [ ";" commandLine ] ) | assignment
expression := term | assignment
assignment := lhs ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" )
expression
lhs := variable | dereference
term := sum
sum := product ( ( "+" | "-" ) product )*
product := unary ( ( "*" | "/" | "%" ) unary )*
unary := atom | ( "-" unary ) | dereference
dereference := "*" [ "{" expression "}" ] unary
atom := variable | ( "(" expression ")" ) | ( "[" command "]" )
variable := identifier
identifier := ( "$" | "@" | "_" | "a" - "z" | "A" - "Z" )
( "_" | "a" - "z" | "A" - "Z" | "0" - "9" )*
commandPipe := command ( "|" command )*
command := identifier argument*
argument := ( "(" expression ")" ) | ( "[" commandLine "]" )
| unquotedString | quotedString
*/
static const int kMaxTokenLength = 128;
static const int kJumpBufferCount = 10;
static const int kMaxArgumentCount = 64;
static jmp_buf sJumpBuffers[kJumpBufferCount];
static int sNextJumpBufferIndex = 0;
static char sExceptionMessage[128];
static int sExceptionPosition;
static char sTempBuffer[128];
enum {
TOKEN_ASSIGN_FLAG = 0x100,
TOKEN_FLAGS = TOKEN_ASSIGN_FLAG,
TOKEN_IDENTIFIER = 'a',
TOKEN_CONSTANT = '0',
TOKEN_PLUS = '+',
TOKEN_MINUS = '-',
TOKEN_STAR = '*',
TOKEN_SLASH = '/',
TOKEN_MODULO = '%',
TOKEN_ASSIGN = '=' | TOKEN_ASSIGN_FLAG,
TOKEN_PLUS_ASSIGN = TOKEN_PLUS | TOKEN_ASSIGN_FLAG,
TOKEN_MINUS_ASSIGN = TOKEN_MINUS | TOKEN_ASSIGN_FLAG,
TOKEN_STAR_ASSIGN = TOKEN_STAR | TOKEN_ASSIGN_FLAG,
TOKEN_SLASH_ASSIGN = TOKEN_SLASH | TOKEN_ASSIGN_FLAG,
TOKEN_MODULO_ASSIGN = TOKEN_MODULO | TOKEN_ASSIGN_FLAG,
TOKEN_OPENING_PARENTHESIS = '(',
TOKEN_CLOSING_PARENTHESIS = ')',
TOKEN_OPENING_BRACKET = '[',
TOKEN_CLOSING_BRACKET = ']',
TOKEN_OPENING_BRACE = '{',
TOKEN_CLOSING_BRACE = '}',
TOKEN_PIPE = '|',
TOKEN_SEMICOLON = ';',
TOKEN_STRING = '"',
TOKEN_UNKNOWN = '?',
TOKEN_NONE = ' ',
TOKEN_END_OF_LINE = '\n',
};
struct Token {
char string[kMaxTokenLength];
uint64 value;
int32 type;
int32 position;
void SetTo(const char* string, int32 length, int32 position, int32 type)
{
length = min_c((size_t)length, (sizeof(this->string) - 1));
strlcpy(this->string, string, length + 1);
this->type = type;
this->value = 0;
this->position = position;
}
void Unset()
{
string[0] = '\0';
value = 0;
type = TOKEN_NONE;
position = 0;
}
};
static void
parse_exception(const char* message, int32 position)
{
if (sNextJumpBufferIndex == 0) {
kprintf_unfiltered("parse_exception(): No jump buffer!\n");
kprintf_unfiltered("exception: \"%s\", position: %" B_PRId32 "\n",
message, position);
return;
}
strlcpy(sExceptionMessage, message, sizeof(sExceptionMessage));
sExceptionPosition = position;
longjmp(sJumpBuffers[sNextJumpBufferIndex - 1], 1);
}
static void*
checked_malloc(size_t size)
{
void* address = debug_malloc(size);
if (address == NULL) {
parse_exception("out of memory for command execution", -1);
return NULL;
}
return address;
}
class Tokenizer {
public:
Tokenizer(const char* string)
: fCommandMode(false)
{
SetTo(string);
}
void SetTo(const char* string)
{
fString = fCurrentChar = string;
fCurrentToken.Unset();
fReuseToken = false;
}
void SetPosition(int32 position)
{
fCurrentChar = fString + position;
fCurrentToken.Unset();
fReuseToken = false;
}
void SetCommandMode(bool commandMode)
{
if (fCommandMode == commandMode)
return;
fCommandMode = commandMode;
if (fReuseToken) {
SetPosition(fCurrentToken.position);
}
}
const char* String() const
{
return fString;
}
const Token& NextToken()
{
if (fCurrentToken.type == TOKEN_END_OF_LINE)
return fCurrentToken;
if (fReuseToken) {
fReuseToken = false;
return fCurrentToken;
}
while (*fCurrentChar != 0 && isspace(*fCurrentChar))
fCurrentChar++;
if (*fCurrentChar == 0) {
fCurrentToken.SetTo("", 0, _CurrentPos(), TOKEN_END_OF_LINE);
return fCurrentToken;
}
return (fCommandMode ? _NextTokenCommand() : _NextTokenExpression());
}
const Token& CurrentToken() const
{
return fCurrentToken;
}
void RewindToken()
{
fReuseToken = true;
}
private:
const Token& _NextTokenExpression()
{
if (isdigit(*fCurrentChar)) {
const char* begin = fCurrentChar++;
if (*fCurrentChar == 'x') {
fCurrentChar++;
while (*fCurrentChar != 0
&& (isdigit(*fCurrentChar)
|| strchr("abcdefABCDEF", *fCurrentChar))) {
fCurrentChar++;
}
if (fCurrentChar - begin == 2)
parse_exception("invalid hex number", begin - fString);
} else {
while (*fCurrentChar != 0 && isdigit(*fCurrentChar))
fCurrentChar++;
}
int32 length = fCurrentChar - begin;
fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
TOKEN_CONSTANT);
fCurrentToken.value = strtoull(fCurrentToken.string, NULL, 0);
} else if (isalpha(*fCurrentChar) || *fCurrentChar == '_'
|| *fCurrentChar == '$' || *fCurrentChar == '@') {
const char* begin = fCurrentChar;
fCurrentChar++;
while (*fCurrentChar != 0
&& (isalpha(*fCurrentChar) || *fCurrentChar == '_'
|| isdigit(*fCurrentChar))) {
fCurrentChar++;
}
int32 length = fCurrentChar - begin;
fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
TOKEN_IDENTIFIER);
} else {
const char* begin = fCurrentChar;
char c = *fCurrentChar;
fCurrentChar++;
int32 flags = 0;
switch (c) {
case '=':
fCurrentChar--;
case '+':
case '-':
case '*':
case '/':
case '%':
if (*fCurrentChar == '=') {
fCurrentChar++;
flags = TOKEN_ASSIGN_FLAG;
}
case '(':
case ')':
case '[':
case ']':
case '{':
case '}':
case ';':
{
int32 length = fCurrentChar - begin;
fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
c | flags);
break;
}
case '"':
{
fCurrentChar--;
_QuotedString();
break;
}
default:
{
fCurrentChar--;
_UnquotedString();
break;
}
}
}
return fCurrentToken;
}
const Token& _NextTokenCommand()
{
switch (*fCurrentChar) {
case '(':
case ')':
case '[':
case ']':
case '|':
case ';':
fCurrentToken.SetTo(fCurrentChar, 1, _CurrentPos(),
*fCurrentChar);
fCurrentChar++;
return fCurrentToken;
case '"':
return _QuotedString();
default:
return _UnquotedString();
}
}
const Token& _QuotedString()
{
const char* begin = fCurrentChar++;
int32 length = 0;
while (*fCurrentChar != '\0' && *fCurrentChar != '"') {
char c = *fCurrentChar;
fCurrentChar++;
if (c == '\\') {
c = *fCurrentChar;
fCurrentChar++;
if (c == '\0')
break;
}
if ((size_t)length
>= sizeof(fCurrentToken.string) - 1) {
parse_exception("quoted string too long", begin - fString);
}
fCurrentToken.string[length++] = c;
}
if (*fCurrentChar == '\0') {
parse_exception("unexpected end of line while "
"parsing quoted string", begin - fString);
}
fCurrentChar++;
fCurrentToken.string[length] = '\0';
fCurrentToken.value = 0;
fCurrentToken.type = TOKEN_STRING;
fCurrentToken.position = begin - fString;
return fCurrentToken;
}
const Token& _UnquotedString()
{
const char* begin = fCurrentChar;
while (*fCurrentChar != 0 && !_IsUnquotedDelimitingChar(*fCurrentChar))
fCurrentChar++;
int32 length = fCurrentChar - begin;
fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
TOKEN_UNKNOWN);
return fCurrentToken;
}
bool _IsUnquotedDelimitingChar(char c)
{
if (isspace(c))
return true;
switch (c) {
case '(':
case ')':
case '[':
case ']':
case '"':
return true;
case '|':
case ';':
return fCommandMode;
case '{':
case '}':
case '=':
case '+':
case '-':
case '*':
case '/':
case '%':
return !fCommandMode;
default:
return false;
}
}
int32 _CurrentPos() const
{
return fCurrentChar - fString;
}
private:
const char* fString;
const char* fCurrentChar;
Token fCurrentToken;
bool fReuseToken;
bool fCommandMode;
};
class ExpressionParser {
public:
ExpressionParser();
~ExpressionParser();
uint64 EvaluateExpression(
const char* expressionString);
uint64 EvaluateCommand(
const char* expressionString,
int& returnCode);
status_t ParseNextCommandArgument(
const char** expressionString, char* buffer,
size_t bufferSize);
private:
uint64 _ParseExpression(bool expectAssignment = false);
uint64 _ParseCommandPipe(int& returnCode);
void _ParseCommand(
debugger_command_pipe_segment& segment);
bool _ParseArgument(int& argc, char** argv);
void _GetUnparsedArgument(int& argc, char** argv);
void _AddArgument(int& argc, char** argv,
const char* argument, int32 length = -1);
uint64 _ParseSum(bool useValue, uint64 value);
uint64 _ParseProduct();
uint64 _ParsePower();
uint64 _ParseUnary();
uint64 _ParseDereference(void** _address,
uint32* _size);
uint64 _ParseAtom();
const Token& _EatToken(int32 type);
Tokenizer fTokenizer;
};
ExpressionParser::ExpressionParser()
: fTokenizer("")
{
}
ExpressionParser::~ExpressionParser()
{
}
uint64
ExpressionParser::EvaluateExpression(const char* expressionString)
{
fTokenizer.SetTo(expressionString);
uint64 value = _ParseExpression();
const Token& token = fTokenizer.NextToken();
if (token.type != TOKEN_END_OF_LINE)
parse_exception("parse error", token.position);
return value;
}
uint64
ExpressionParser::EvaluateCommand(const char* expressionString, int& returnCode)
{
fTokenizer.SetTo(expressionString);
const Token& token = fTokenizer.NextToken();
uint64 value = 0;
while (true) {
int32 startPosition = token.position;
if (token.type == TOKEN_IDENTIFIER) {
fTokenizer.NextToken();
if (token.type & TOKEN_ASSIGN_FLAG) {
fTokenizer.SetPosition(startPosition);
value = _ParseExpression(true);
returnCode = 0;
} else {
fTokenizer.SetPosition(startPosition);
fTokenizer.SetCommandMode(true);
value = _ParseCommandPipe(returnCode);
}
} else if (token.type == TOKEN_STAR) {
fTokenizer.SetPosition(startPosition);
value = _ParseExpression(true);
returnCode = 0;
} else
parse_exception("expected command or assignment", token.position);
if (fTokenizer.NextToken().type != TOKEN_SEMICOLON)
break;
fTokenizer.SetCommandMode(false);
fTokenizer.NextToken();
}
if (token.type != TOKEN_END_OF_LINE)
parse_exception("parse error", token.position);
return value;
}
status_t
ExpressionParser::ParseNextCommandArgument(const char** expressionString,
char* buffer, size_t bufferSize)
{
fTokenizer.SetTo(*expressionString);
fTokenizer.SetCommandMode(true);
if (fTokenizer.NextToken().type == TOKEN_END_OF_LINE)
return B_ENTRY_NOT_FOUND;
fTokenizer.RewindToken();
char* argv[2];
int argc = 0;
if (!_ParseArgument(argc, argv))
return B_BAD_VALUE;
strlcpy(buffer, argv[0], bufferSize);
const Token& token = fTokenizer.NextToken();
if (token.type == TOKEN_END_OF_LINE)
*expressionString = NULL;
else
*expressionString += token.position;
return B_OK;
}
uint64
ExpressionParser::_ParseExpression(bool expectAssignment)
{
const Token& token = fTokenizer.NextToken();
int32 position = token.position;
if (token.type == TOKEN_IDENTIFIER) {
char variable[MAX_DEBUG_VARIABLE_NAME_LEN];
strlcpy(variable, token.string, sizeof(variable));
int32 assignmentType = fTokenizer.NextToken().type;
if (assignmentType & TOKEN_ASSIGN_FLAG) {
uint64 rhs = _ParseExpression();
if (assignmentType == TOKEN_ASSIGN) {
if (!set_debug_variable(variable, rhs)) {
snprintf(sTempBuffer, sizeof(sTempBuffer),
"failed to set value for variable \"%s\"",
variable);
parse_exception(sTempBuffer, position);
}
return rhs;
}
if (!is_debug_variable_defined(variable)) {
snprintf(sTempBuffer, sizeof(sTempBuffer),
"variable \"%s\" not defined in modifying assignment",
variable);
parse_exception(sTempBuffer, position);
}
uint64 variableValue = get_debug_variable(variable, 0);
if ((assignmentType == TOKEN_SLASH_ASSIGN
|| assignmentType == TOKEN_MODULO_ASSIGN)
&& rhs == 0) {
parse_exception("division by zero", position);
}
switch (assignmentType) {
case TOKEN_PLUS_ASSIGN:
variableValue += rhs;
break;
case TOKEN_MINUS_ASSIGN:
variableValue -= rhs;
break;
case TOKEN_STAR_ASSIGN:
variableValue *= rhs;
break;
case TOKEN_SLASH_ASSIGN:
variableValue /= rhs;
break;
case TOKEN_MODULO_ASSIGN:
variableValue %= rhs;
break;
default:
parse_exception("internal error: unknown assignment token",
position);
break;
}
set_debug_variable(variable, 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) {
uint64 rhs = _ParseExpression();
if ((assignmentType == TOKEN_SLASH_ASSIGN
|| assignmentType == TOKEN_MODULO_ASSIGN)
&& rhs == 0) {
parse_exception("division by zero", position);
}
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;
}
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 (debug_memcpy(B_CURRENT_TEAM, 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);
}
fTokenizer.SetPosition(position);
return _ParseSum(false, 0);
}
uint64
ExpressionParser::_ParseCommandPipe(int& returnCode)
{
debugger_command_pipe* pipe = (debugger_command_pipe*)checked_malloc(
sizeof(debugger_command_pipe));
pipe->segment_count = 0;
pipe->broken = false;
do {
if (pipe->segment_count >= MAX_DEBUGGER_COMMAND_PIPE_LENGTH)
parse_exception("Pipe too long", fTokenizer.NextToken().position);
debugger_command_pipe_segment& segment
= pipe->segments[pipe->segment_count];
segment.index = pipe->segment_count++;
_ParseCommand(segment);
} while (fTokenizer.NextToken().type == TOKEN_PIPE);
fTokenizer.RewindToken();
returnCode = invoke_debugger_command_pipe(pipe);
debug_free(pipe);
return get_debug_variable("_", 0);
}
void
ExpressionParser::_ParseCommand(debugger_command_pipe_segment& segment)
{
fTokenizer.SetCommandMode(false);
const Token& token = _EatToken(TOKEN_IDENTIFIER);
fTokenizer.SetCommandMode(true);
bool ambiguous;
debugger_command* command = find_debugger_command(token.string, true,
ambiguous);
if (command == NULL) {
if (ambiguous) {
snprintf(sTempBuffer, sizeof(sTempBuffer),
"Ambiguous command \"%s\". Use tab completion or enter "
"\"help %s\" get a list of matching commands.\n", token.string,
token.string);
} else {
snprintf(sTempBuffer, sizeof(sTempBuffer),
"Unknown command \"%s\". Enter \"help\" to get a list of "
"all supported commands.\n", token.string);
}
parse_exception(sTempBuffer, -1);
}
char** argv = (char**)checked_malloc(kMaxArgumentCount * sizeof(char*));
int argc = 0;
argv[argc++] = (char*)command->name;
if ((command->flags & B_KDEBUG_DONT_PARSE_ARGUMENTS) != 0) {
_GetUnparsedArgument(argc, argv);
} else {
while (fTokenizer.NextToken().type != TOKEN_END_OF_LINE) {
fTokenizer.RewindToken();
if (!_ParseArgument(argc, argv))
break;
}
}
if (segment.index > 0) {
if (argc >= kMaxArgumentCount)
parse_exception("too many arguments for command", 0);
else
argc++;
}
segment.command = command;
segment.argc = argc;
segment.argv = argv;
segment.invocations = 0;
}
bool
ExpressionParser::_ParseArgument(int& argc, char** argv)
{
const Token& token = fTokenizer.NextToken();
switch (token.type) {
case TOKEN_OPENING_PARENTHESIS:
{
fTokenizer.SetCommandMode(false);
uint64 value = _ParseExpression();
fTokenizer.SetCommandMode(true);
_EatToken(TOKEN_CLOSING_PARENTHESIS);
snprintf(sTempBuffer, sizeof(sTempBuffer), "%" B_PRIu64, value);
_AddArgument(argc, argv, sTempBuffer);
return true;
}
case TOKEN_OPENING_BRACKET:
{
int returnValue;
uint64 value = _ParseCommandPipe(returnValue);
_EatToken(TOKEN_CLOSING_BRACKET);
snprintf(sTempBuffer, sizeof(sTempBuffer), "%" B_PRIu64, value);
_AddArgument(argc, argv, sTempBuffer);
return true;
}
case TOKEN_STRING:
case TOKEN_UNKNOWN:
_AddArgument(argc, argv, token.string);
return true;
case TOKEN_CLOSING_PARENTHESIS:
case TOKEN_CLOSING_BRACKET:
case TOKEN_PIPE:
case TOKEN_SEMICOLON:
fTokenizer.RewindToken();
return false;
default:
{
snprintf(sTempBuffer, sizeof(sTempBuffer), "unexpected token "
"\"%s\"", token.string);
parse_exception(sTempBuffer, token.position);
return false;
}
}
}
void
ExpressionParser::_GetUnparsedArgument(int& argc, char** argv)
{
int32 startPosition = fTokenizer.NextToken().position;
fTokenizer.RewindToken();
int32 parentheses = 0;
int32 brackets = 0;
bool done = false;
while (!done) {
const Token& token = fTokenizer.NextToken();
switch (token.type) {
case TOKEN_OPENING_PARENTHESIS:
parentheses++;
break;
case TOKEN_OPENING_BRACKET:
brackets++;
break;
case TOKEN_CLOSING_PARENTHESIS:
if (parentheses > 0)
parentheses--;
else
done = true;
break;
case TOKEN_CLOSING_BRACKET:
if (brackets > 0)
brackets--;
else
done = true;
break;
case TOKEN_PIPE:
case TOKEN_SEMICOLON:
if (parentheses == 0 && brackets == 0)
done = true;
break;
case TOKEN_END_OF_LINE:
done = true;
break;
}
}
int32 endPosition = fTokenizer.CurrentToken().position;
fTokenizer.RewindToken();
const char* arg = fTokenizer.String() + startPosition;
int32 argLen = endPosition - startPosition;
bool allSpaces = true;
for (int32 i = 0; allSpaces && i < argLen; i++)
allSpaces = isspace(arg[i]);
if (!allSpaces)
_AddArgument(argc, argv, arg, argLen);
}
void
ExpressionParser::_AddArgument(int& argc, char** argv, const char* argument,
int32 length)
{
if (argc == kMaxArgumentCount)
parse_exception("too many arguments for command", 0);
if (length < 0)
length = strlen(argument);
length++;
char* buffer = (char*)checked_malloc(length);
strlcpy(buffer, argument, length);
argv[argc++] = buffer;
}
uint64
ExpressionParser::_ParseSum(bool useValue, uint64 value)
{
if (!useValue)
value = _ParseProduct();
while (true) {
const Token& token = fTokenizer.NextToken();
switch (token.type) {
case TOKEN_PLUS:
value = value + _ParseProduct();
break;
case TOKEN_MINUS:
value = value - _ParseProduct();
break;
default:
fTokenizer.RewindToken();
return value;
}
}
}
uint64
ExpressionParser::_ParseProduct()
{
uint64 value = _ParseUnary();
while (true) {
Token token = fTokenizer.NextToken();
switch (token.type) {
case TOKEN_STAR:
value = value * _ParseUnary();
break;
case TOKEN_SLASH: {
uint64 rhs = _ParseUnary();
if (rhs == 0)
parse_exception("division by zero", token.position);
value = value / rhs;
break;
}
case TOKEN_MODULO: {
uint64 rhs = _ParseUnary();
if (rhs == 0)
parse_exception("modulo by zero", token.position);
value = value % rhs;
break;
}
default:
fTokenizer.RewindToken();
return value;
}
}
}
uint64
ExpressionParser::_ParseUnary()
{
switch (fTokenizer.NextToken().type) {
case TOKEN_MINUS:
return -_ParseUnary();
case TOKEN_STAR:
return _ParseDereference(NULL, NULL);
default:
fTokenizer.RewindToken();
return _ParseAtom();
}
return 0;
}
uint64
ExpressionParser::_ParseDereference(void** _address, uint32* _size)
{
int32 starPosition = fTokenizer.CurrentToken().position;
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 (%" B_PRIu64 ") for unary * operator", size);
parse_exception(sTempBuffer, position);
}
_EatToken(TOKEN_CLOSING_BRACE);
} else
fTokenizer.RewindToken();
const void* address = (const void*)(addr_t)_ParseUnary();
uint64 buffer;
if (debug_memcpy(B_CURRENT_TEAM, &buffer, address, size) != B_OK) {
snprintf(sTempBuffer, sizeof(sTempBuffer),
"failed to dereference address %p", address);
parse_exception(sTempBuffer, starPosition);
}
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
ExpressionParser::_ParseAtom()
{
const Token& token = fTokenizer.NextToken();
if (token.type == TOKEN_END_OF_LINE)
parse_exception("unexpected end of expression", token.position);
if (token.type == TOKEN_CONSTANT)
return token.value;
if (token.type == TOKEN_IDENTIFIER) {
if (!is_debug_variable_defined(token.string)) {
snprintf(sTempBuffer, sizeof(sTempBuffer),
"variable '%s' undefined", token.string);
parse_exception(sTempBuffer, token.position);
}
return get_debug_variable(token.string, 0);
}
if (token.type == TOKEN_OPENING_PARENTHESIS) {
uint64 value = _ParseExpression();
_EatToken(TOKEN_CLOSING_PARENTHESIS);
return value;
}
fTokenizer.RewindToken();
_EatToken(TOKEN_OPENING_BRACKET);
fTokenizer.SetCommandMode(true);
int returnValue;
uint64 value = _ParseCommandPipe(returnValue);
fTokenizer.SetCommandMode(false);
_EatToken(TOKEN_CLOSING_BRACKET);
return value;
}
const Token&
ExpressionParser::_EatToken(int32 type)
{
const Token& token = fTokenizer.NextToken();
if (token.type != type) {
snprintf(sTempBuffer, sizeof(sTempBuffer), "expected token type '%c', "
"got token '%s'", char(type & ~TOKEN_FLAGS), token.string);
parse_exception(sTempBuffer, token.position);
}
return token;
}
bool
evaluate_debug_expression(const char* expression, uint64* _result, bool silent)
{
if (sNextJumpBufferIndex >= kJumpBufferCount) {
kprintf_unfiltered("evaluate_debug_expression(): Out of jump buffers "
"for exception handling\n");
return 0;
}
bool success;
uint64 result;
DebugAllocPoolScope allocPoolScope;
if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) {
result = ExpressionParser().EvaluateExpression(expression);
success = true;
} else {
result = 0;
success = false;
if (!silent) {
if (sExceptionPosition >= 0) {
kprintf_unfiltered("%s, at position: %d, in expression: %s\n",
sExceptionMessage, sExceptionPosition, expression);
} else
kprintf_unfiltered("%s\n", sExceptionMessage);
}
}
sNextJumpBufferIndex--;
if (success && _result != NULL)
*_result = result;
return success;
}
int
evaluate_debug_command(const char* commandLine)
{
if (sNextJumpBufferIndex >= kJumpBufferCount) {
kprintf_unfiltered("evaluate_debug_command(): Out of jump buffers for "
"exception handling\n");
return 0;
}
int returnCode = 0;
DebugAllocPoolScope allocPoolScope;
if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) {
ExpressionParser().EvaluateCommand(commandLine, returnCode);
} else {
if (sExceptionPosition >= 0) {
kprintf_unfiltered("%s, at position: %d, in command line: %s\n",
sExceptionMessage, sExceptionPosition, commandLine);
} else
kprintf_unfiltered("%s\n", sExceptionMessage);
}
sNextJumpBufferIndex--;
return returnCode;
}
status_t
parse_next_debug_command_argument(const char** expressionString, char* buffer,
size_t bufferSize)
{
if (sNextJumpBufferIndex >= kJumpBufferCount) {
kprintf_unfiltered("parse_next_debug_command_argument(): Out of jump "
"buffers for exception handling\n");
return B_ERROR;
}
status_t error;
DebugAllocPoolScope allocPoolScope;
if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) {
error = ExpressionParser().ParseNextCommandArgument(expressionString,
buffer, bufferSize);
} else {
if (sExceptionPosition >= 0) {
kprintf_unfiltered("%s, at position: %d, in command line: %s\n",
sExceptionMessage, sExceptionPosition, *expressionString);
} else
kprintf_unfiltered("%s\n", sExceptionMessage);
error = B_BAD_VALUE;
}
sNextJumpBufferIndex--;
return error;
}