vfs: check userland buffers before reading them.
[haiku.git] / src / system / kernel / debug / debug_parser.cpp
blob2a0398ce578375c72a39e470bdb33703f04e8ed1
1 /*
2 * Copyright 2008-2010, Ingo Weinhold, ingo_weinhold@gmx.de
3 * Copyright 2006, Stephan Aßmus, superstippi@gmx.de
4 * Distributed under the terms of the MIT License.
5 */
8 #include <debug.h>
10 #include <ctype.h>
11 #include <setjmp.h>
12 #include <stdio.h>
13 #include <stdlib.h>
14 #include <string.h>
16 #include <KernelExport.h>
18 #include <debug_heap.h>
20 #include "debug_commands.h"
21 #include "debug_variables.h"
25 Grammar:
27 commandLine := ( commandPipe [ ";" commandLine ] ) | assignment
28 expression := term | assignment
29 assignment := lhs ( "=" | "+=" | "-=" | "*=" | "/=" | "%=" )
30 expression
31 lhs := variable | dereference
32 term := sum
33 sum := product ( ( "+" | "-" ) product )*
34 product := unary ( ( "*" | "/" | "%" ) unary )*
35 unary := atom | ( "-" unary ) | dereference
36 dereference := "*" [ "{" expression "}" ] unary
37 atom := variable | ( "(" expression ")" ) | ( "[" command "]" )
38 variable := identifier
39 identifier := ( "$" | "@" | "_" | "a" - "z" | "A" - "Z" )
40 ( "_" | "a" - "z" | "A" - "Z" | "0" - "9" )*
41 commandPipe := command ( "|" command )*
42 command := identifier argument*
43 argument := ( "(" expression ")" ) | ( "[" commandLine "]" )
44 | unquotedString | quotedString
48 static const int kMaxTokenLength = 128;
49 static const int kJumpBufferCount = 10;
51 static const int kMaxArgumentCount = 64;
53 static jmp_buf sJumpBuffers[kJumpBufferCount];
54 static int sNextJumpBufferIndex = 0;
56 static char sExceptionMessage[128];
57 static int sExceptionPosition;
59 static char sTempBuffer[128];
60 // for composing debug output etc.
62 enum {
63 TOKEN_ASSIGN_FLAG = 0x100,
64 TOKEN_FLAGS = TOKEN_ASSIGN_FLAG,
66 TOKEN_IDENTIFIER = 'a',
68 TOKEN_CONSTANT = '0',
70 TOKEN_PLUS = '+',
71 TOKEN_MINUS = '-',
73 TOKEN_STAR = '*',
74 TOKEN_SLASH = '/',
75 TOKEN_MODULO = '%',
77 TOKEN_ASSIGN = '=' | TOKEN_ASSIGN_FLAG,
78 TOKEN_PLUS_ASSIGN = TOKEN_PLUS | TOKEN_ASSIGN_FLAG,
79 TOKEN_MINUS_ASSIGN = TOKEN_MINUS | TOKEN_ASSIGN_FLAG,
80 TOKEN_STAR_ASSIGN = TOKEN_STAR | TOKEN_ASSIGN_FLAG,
81 TOKEN_SLASH_ASSIGN = TOKEN_SLASH | TOKEN_ASSIGN_FLAG,
82 TOKEN_MODULO_ASSIGN = TOKEN_MODULO | TOKEN_ASSIGN_FLAG,
84 TOKEN_OPENING_PARENTHESIS = '(',
85 TOKEN_CLOSING_PARENTHESIS = ')',
86 TOKEN_OPENING_BRACKET = '[',
87 TOKEN_CLOSING_BRACKET = ']',
88 TOKEN_OPENING_BRACE = '{',
89 TOKEN_CLOSING_BRACE = '}',
91 TOKEN_PIPE = '|',
92 TOKEN_SEMICOLON = ';',
94 TOKEN_STRING = '"',
95 TOKEN_UNKNOWN = '?',
96 TOKEN_NONE = ' ',
97 TOKEN_END_OF_LINE = '\n',
100 struct Token {
101 char string[kMaxTokenLength];
102 uint64 value;
103 int32 type;
104 int32 position;
106 void SetTo(const char* string, int32 length, int32 position, int32 type)
108 length = min_c((size_t)length, (sizeof(this->string) - 1));
109 strlcpy(this->string, string, length + 1);
110 this->type = type;
111 this->value = 0;
112 this->position = position;
115 void Unset()
117 string[0] = '\0';
118 value = 0;
119 type = TOKEN_NONE;
120 position = 0;
125 // #pragma mark - exceptions
128 static void
129 parse_exception(const char* message, int32 position)
131 if (sNextJumpBufferIndex == 0) {
132 kprintf_unfiltered("parse_exception(): No jump buffer!\n");
133 kprintf_unfiltered("exception: \"%s\", position: %" B_PRId32 "\n",
134 message, position);
135 return;
138 strlcpy(sExceptionMessage, message, sizeof(sExceptionMessage));
139 sExceptionPosition = position;
141 longjmp(sJumpBuffers[sNextJumpBufferIndex - 1], 1);
145 static void*
146 checked_malloc(size_t size)
148 void* address = debug_malloc(size);
149 if (address == NULL) {
150 parse_exception("out of memory for command execution", -1);
151 return NULL;
154 return address;
158 // #pragma mark - Tokenizer
161 class Tokenizer {
162 public:
163 Tokenizer(const char* string)
164 : fCommandMode(false)
166 SetTo(string);
169 void SetTo(const char* string)
171 fString = fCurrentChar = string;
172 fCurrentToken.Unset();
173 fReuseToken = false;
176 void SetPosition(int32 position)
178 fCurrentChar = fString + position;
179 fCurrentToken.Unset();
180 fReuseToken = false;
183 void SetCommandMode(bool commandMode)
185 if (fCommandMode == commandMode)
186 return;
188 fCommandMode = commandMode;
190 if (fReuseToken) {
191 // We can't reuse the token, since the parsing mode changed.
192 SetPosition(fCurrentToken.position);
196 const char* String() const
198 return fString;
201 const Token& NextToken()
203 if (fCurrentToken.type == TOKEN_END_OF_LINE)
204 return fCurrentToken;
206 if (fReuseToken) {
207 fReuseToken = false;
208 return fCurrentToken;
211 while (*fCurrentChar != 0 && isspace(*fCurrentChar))
212 fCurrentChar++;
214 if (*fCurrentChar == 0) {
215 fCurrentToken.SetTo("", 0, _CurrentPos(), TOKEN_END_OF_LINE);
216 return fCurrentToken;
219 return (fCommandMode ? _NextTokenCommand() : _NextTokenExpression());
222 const Token& CurrentToken() const
224 return fCurrentToken;
227 void RewindToken()
229 fReuseToken = true;
232 private:
233 const Token& _NextTokenExpression()
235 if (isdigit(*fCurrentChar)) {
236 // number
237 const char* begin = fCurrentChar++;
239 if (*fCurrentChar == 'x') {
240 // hex number
241 fCurrentChar++;
242 while (*fCurrentChar != 0
243 && (isdigit(*fCurrentChar)
244 || strchr("abcdefABCDEF", *fCurrentChar))) {
245 fCurrentChar++;
248 if (fCurrentChar - begin == 2)
249 parse_exception("invalid hex number", begin - fString);
251 } else {
252 // decimal number
253 while (*fCurrentChar != 0 && isdigit(*fCurrentChar))
254 fCurrentChar++;
257 int32 length = fCurrentChar - begin;
258 fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
259 TOKEN_CONSTANT);
260 fCurrentToken.value = strtoull(fCurrentToken.string, NULL, 0);
262 } else if (isalpha(*fCurrentChar) || *fCurrentChar == '_'
263 || *fCurrentChar == '$' || *fCurrentChar == '@') {
264 // identifier
265 const char* begin = fCurrentChar;
266 fCurrentChar++;
267 while (*fCurrentChar != 0
268 && (isalpha(*fCurrentChar) || *fCurrentChar == '_'
269 || isdigit(*fCurrentChar))) {
270 fCurrentChar++;
273 int32 length = fCurrentChar - begin;
274 fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
275 TOKEN_IDENTIFIER);
277 } else {
278 const char* begin = fCurrentChar;
279 char c = *fCurrentChar;
280 fCurrentChar++;
281 int32 flags = 0;
283 switch (c) {
284 case '=':
285 fCurrentChar--;
286 case '+':
287 case '-':
288 case '*':
289 case '/':
290 case '%':
291 if (*fCurrentChar == '=') {
292 fCurrentChar++;
293 flags = TOKEN_ASSIGN_FLAG;
296 case '(':
297 case ')':
298 case '[':
299 case ']':
300 case '{':
301 case '}':
302 case ';':
304 int32 length = fCurrentChar - begin;
305 fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
306 c | flags);
307 break;
310 case '"':
312 fCurrentChar--;
313 _QuotedString();
314 break;
317 default:
319 fCurrentChar--;
320 _UnquotedString();
321 break;
326 return fCurrentToken;
329 const Token& _NextTokenCommand()
331 switch (*fCurrentChar) {
332 case '(':
333 case ')':
334 case '[':
335 case ']':
336 case '|':
337 case ';':
338 fCurrentToken.SetTo(fCurrentChar, 1, _CurrentPos(),
339 *fCurrentChar);
340 fCurrentChar++;
341 return fCurrentToken;
342 case '"':
343 return _QuotedString();
345 default:
346 return _UnquotedString();
350 const Token& _QuotedString()
352 const char* begin = fCurrentChar++;
353 int32 length = 0;
355 while (*fCurrentChar != '\0' && *fCurrentChar != '"') {
356 char c = *fCurrentChar;
357 fCurrentChar++;
359 if (c == '\\') {
360 // an escaped char
361 c = *fCurrentChar;
362 fCurrentChar++;
364 if (c == '\0')
365 break;
368 if ((size_t)length
369 >= sizeof(fCurrentToken.string) - 1) {
370 parse_exception("quoted string too long", begin - fString);
373 fCurrentToken.string[length++] = c;
376 if (*fCurrentChar == '\0') {
377 parse_exception("unexpected end of line while "
378 "parsing quoted string", begin - fString);
381 fCurrentChar++;
383 fCurrentToken.string[length] = '\0';
384 fCurrentToken.value = 0;
385 fCurrentToken.type = TOKEN_STRING;
386 fCurrentToken.position = begin - fString;
388 return fCurrentToken;
391 const Token& _UnquotedString()
393 const char* begin = fCurrentChar;
395 while (*fCurrentChar != 0 && !_IsUnquotedDelimitingChar(*fCurrentChar))
396 fCurrentChar++;
398 int32 length = fCurrentChar - begin;
399 fCurrentToken.SetTo(begin, length, _CurrentPos() - length,
400 TOKEN_UNKNOWN);
402 return fCurrentToken;
405 bool _IsUnquotedDelimitingChar(char c)
407 if (isspace(c))
408 return true;
410 switch (c) {
411 case '(':
412 case ')':
413 case '[':
414 case ']':
415 case '"':
416 return true;
418 case '|': // TODO: Move when we support & and | in expressions.
419 case ';':
420 return fCommandMode;
422 case '{':
423 case '}':
424 case '=':
425 case '+':
426 case '-':
427 case '*':
428 case '/':
429 case '%':
430 return !fCommandMode;
432 default:
433 return false;
437 int32 _CurrentPos() const
439 return fCurrentChar - fString;
442 private:
443 const char* fString;
444 const char* fCurrentChar;
445 Token fCurrentToken;
446 bool fReuseToken;
447 bool fCommandMode;
451 // #pragma mark - ExpressionParser
454 class ExpressionParser {
455 public:
456 ExpressionParser();
457 ~ExpressionParser();
459 uint64 EvaluateExpression(
460 const char* expressionString);
461 uint64 EvaluateCommand(
462 const char* expressionString,
463 int& returnCode);
464 status_t ParseNextCommandArgument(
465 const char** expressionString, char* buffer,
466 size_t bufferSize);
468 private:
469 uint64 _ParseExpression(bool expectAssignment = false);
470 uint64 _ParseCommandPipe(int& returnCode);
471 void _ParseCommand(
472 debugger_command_pipe_segment& segment);
473 bool _ParseArgument(int& argc, char** argv);
474 void _GetUnparsedArgument(int& argc, char** argv);
475 void _AddArgument(int& argc, char** argv,
476 const char* argument, int32 length = -1);
477 uint64 _ParseSum(bool useValue, uint64 value);
478 uint64 _ParseProduct();
479 uint64 _ParsePower();
480 uint64 _ParseUnary();
481 uint64 _ParseDereference(void** _address,
482 uint32* _size);
483 uint64 _ParseAtom();
485 const Token& _EatToken(int32 type);
487 Tokenizer fTokenizer;
491 ExpressionParser::ExpressionParser()
492 : fTokenizer("")
497 ExpressionParser::~ExpressionParser()
502 uint64
503 ExpressionParser::EvaluateExpression(const char* expressionString)
505 fTokenizer.SetTo(expressionString);
507 uint64 value = _ParseExpression();
508 const Token& token = fTokenizer.NextToken();
509 if (token.type != TOKEN_END_OF_LINE)
510 parse_exception("parse error", token.position);
512 return value;
516 uint64
517 ExpressionParser::EvaluateCommand(const char* expressionString, int& returnCode)
519 fTokenizer.SetTo(expressionString);
521 // Allowed are command or assignment. A command always starts with an
522 // identifier, an assignment either with an identifier (variable name) or
523 // a dereferenced address.
524 const Token& token = fTokenizer.NextToken();
525 uint64 value = 0;
527 while (true) {
528 int32 startPosition = token.position;
530 if (token.type == TOKEN_IDENTIFIER) {
531 fTokenizer.NextToken();
533 if (token.type & TOKEN_ASSIGN_FLAG) {
534 // an assignment
535 fTokenizer.SetPosition(startPosition);
536 value = _ParseExpression(true);
537 returnCode = 0;
538 } else {
539 // no assignment, so let's assume it's a command
540 fTokenizer.SetPosition(startPosition);
541 fTokenizer.SetCommandMode(true);
542 value = _ParseCommandPipe(returnCode);
544 } else if (token.type == TOKEN_STAR) {
545 // dereferenced address -- assignment
546 fTokenizer.SetPosition(startPosition);
547 value = _ParseExpression(true);
548 returnCode = 0;
549 } else
550 parse_exception("expected command or assignment", token.position);
552 // might be chained with ";"
553 if (fTokenizer.NextToken().type != TOKEN_SEMICOLON)
554 break;
556 fTokenizer.SetCommandMode(false);
557 fTokenizer.NextToken();
560 if (token.type != TOKEN_END_OF_LINE)
561 parse_exception("parse error", token.position);
563 return value;
567 status_t
568 ExpressionParser::ParseNextCommandArgument(const char** expressionString,
569 char* buffer, size_t bufferSize)
571 fTokenizer.SetTo(*expressionString);
572 fTokenizer.SetCommandMode(true);
574 if (fTokenizer.NextToken().type == TOKEN_END_OF_LINE)
575 return B_ENTRY_NOT_FOUND;
577 fTokenizer.RewindToken();
579 char* argv[2];
580 int argc = 0;
581 if (!_ParseArgument(argc, argv))
582 return B_BAD_VALUE;
584 strlcpy(buffer, argv[0], bufferSize);
586 const Token& token = fTokenizer.NextToken();
587 if (token.type == TOKEN_END_OF_LINE)
588 *expressionString = NULL;
589 else
590 *expressionString += token.position;
592 return B_OK;
596 uint64
597 ExpressionParser::_ParseExpression(bool expectAssignment)
599 const Token& token = fTokenizer.NextToken();
600 int32 position = token.position;
601 if (token.type == TOKEN_IDENTIFIER) {
602 char variable[MAX_DEBUG_VARIABLE_NAME_LEN];
603 strlcpy(variable, token.string, sizeof(variable));
605 int32 assignmentType = fTokenizer.NextToken().type;
606 if (assignmentType & TOKEN_ASSIGN_FLAG) {
607 // an assignment
608 uint64 rhs = _ParseExpression();
610 // handle the standard assignment separately -- the other kinds
611 // need the variable to be defined
612 if (assignmentType == TOKEN_ASSIGN) {
613 if (!set_debug_variable(variable, rhs)) {
614 snprintf(sTempBuffer, sizeof(sTempBuffer),
615 "failed to set value for variable \"%s\"",
616 variable);
617 parse_exception(sTempBuffer, position);
620 return rhs;
623 // variable must be defined
624 if (!is_debug_variable_defined(variable)) {
625 snprintf(sTempBuffer, sizeof(sTempBuffer),
626 "variable \"%s\" not defined in modifying assignment",
627 variable);
628 parse_exception(sTempBuffer, position);
631 uint64 variableValue = get_debug_variable(variable, 0);
633 // check for division by zero for the respective assignment types
634 if ((assignmentType == TOKEN_SLASH_ASSIGN
635 || assignmentType == TOKEN_MODULO_ASSIGN)
636 && rhs == 0) {
637 parse_exception("division by zero", position);
640 // compute the new variable value
641 switch (assignmentType) {
642 case TOKEN_PLUS_ASSIGN:
643 variableValue += rhs;
644 break;
645 case TOKEN_MINUS_ASSIGN:
646 variableValue -= rhs;
647 break;
648 case TOKEN_STAR_ASSIGN:
649 variableValue *= rhs;
650 break;
651 case TOKEN_SLASH_ASSIGN:
652 variableValue /= rhs;
653 break;
654 case TOKEN_MODULO_ASSIGN:
655 variableValue %= rhs;
656 break;
657 default:
658 parse_exception("internal error: unknown assignment token",
659 position);
660 break;
663 set_debug_variable(variable, variableValue);
664 return variableValue;
666 } else if (token.type == TOKEN_STAR) {
667 void* address;
668 uint32 size;
669 uint64 value = _ParseDereference(&address, &size);
671 int32 assignmentType = fTokenizer.NextToken().type;
672 if (assignmentType & TOKEN_ASSIGN_FLAG) {
673 // an assignment
674 uint64 rhs = _ParseExpression();
676 // check for division by zero for the respective assignment types
677 if ((assignmentType == TOKEN_SLASH_ASSIGN
678 || assignmentType == TOKEN_MODULO_ASSIGN)
679 && rhs == 0) {
680 parse_exception("division by zero", position);
683 // compute the new value
684 switch (assignmentType) {
685 case TOKEN_ASSIGN:
686 value = rhs;
687 break;
688 case TOKEN_PLUS_ASSIGN:
689 value += rhs;
690 break;
691 case TOKEN_MINUS_ASSIGN:
692 value -= rhs;
693 break;
694 case TOKEN_STAR_ASSIGN:
695 value *= rhs;
696 break;
697 case TOKEN_SLASH_ASSIGN:
698 value /= rhs;
699 break;
700 case TOKEN_MODULO_ASSIGN:
701 value %= rhs;
702 break;
703 default:
704 parse_exception("internal error: unknown assignment token",
705 position);
706 break;
709 // convert the value for writing to the address
710 uint64 buffer = 0;
711 switch (size) {
712 case 1:
713 *(uint8*)&buffer = value;
714 break;
715 case 2:
716 *(uint16*)&buffer = value;
717 break;
718 case 4:
719 *(uint32*)&buffer = value;
720 break;
721 case 8:
722 value = buffer;
723 break;
726 if (debug_memcpy(B_CURRENT_TEAM, address, &buffer, size) != B_OK) {
727 snprintf(sTempBuffer, sizeof(sTempBuffer),
728 "failed to write to address %p", address);
729 parse_exception(sTempBuffer, position);
732 return value;
736 if (expectAssignment) {
737 parse_exception("expected assignment",
738 fTokenizer.CurrentToken().position);
741 // no assignment -- reset to the identifier position and parse a sum
742 fTokenizer.SetPosition(position);
743 return _ParseSum(false, 0);
747 uint64
748 ExpressionParser::_ParseCommandPipe(int& returnCode)
750 debugger_command_pipe* pipe = (debugger_command_pipe*)checked_malloc(
751 sizeof(debugger_command_pipe));
753 pipe->segment_count = 0;
754 pipe->broken = false;
756 do {
757 if (pipe->segment_count >= MAX_DEBUGGER_COMMAND_PIPE_LENGTH)
758 parse_exception("Pipe too long", fTokenizer.NextToken().position);
760 debugger_command_pipe_segment& segment
761 = pipe->segments[pipe->segment_count];
762 segment.index = pipe->segment_count++;
764 _ParseCommand(segment);
766 } while (fTokenizer.NextToken().type == TOKEN_PIPE);
768 fTokenizer.RewindToken();
770 // invoke the pipe
771 returnCode = invoke_debugger_command_pipe(pipe);
773 debug_free(pipe);
775 return get_debug_variable("_", 0);
779 void
780 ExpressionParser::_ParseCommand(debugger_command_pipe_segment& segment)
782 fTokenizer.SetCommandMode(false);
783 const Token& token = _EatToken(TOKEN_IDENTIFIER);
784 fTokenizer.SetCommandMode(true);
786 bool ambiguous;
787 debugger_command* command = find_debugger_command(token.string, true,
788 ambiguous);
790 if (command == NULL) {
791 if (ambiguous) {
792 snprintf(sTempBuffer, sizeof(sTempBuffer),
793 "Ambiguous command \"%s\". Use tab completion or enter "
794 "\"help %s\" get a list of matching commands.\n", token.string,
795 token.string);
796 } else {
797 snprintf(sTempBuffer, sizeof(sTempBuffer),
798 "Unknown command \"%s\". Enter \"help\" to get a list of "
799 "all supported commands.\n", token.string);
802 parse_exception(sTempBuffer, -1);
805 // allocate temporary buffer for the argument vector
806 char** argv = (char**)checked_malloc(kMaxArgumentCount * sizeof(char*));
807 int argc = 0;
808 argv[argc++] = (char*)command->name;
810 // get the arguments
811 if ((command->flags & B_KDEBUG_DONT_PARSE_ARGUMENTS) != 0) {
812 _GetUnparsedArgument(argc, argv);
813 } else {
814 while (fTokenizer.NextToken().type != TOKEN_END_OF_LINE) {
815 fTokenizer.RewindToken();
816 if (!_ParseArgument(argc, argv))
817 break;
821 if (segment.index > 0) {
822 if (argc >= kMaxArgumentCount)
823 parse_exception("too many arguments for command", 0);
824 else
825 argc++;
828 segment.command = command;
829 segment.argc = argc;
830 segment.argv = argv;
831 segment.invocations = 0;
835 bool
836 ExpressionParser::_ParseArgument(int& argc, char** argv)
838 const Token& token = fTokenizer.NextToken();
839 switch (token.type) {
840 case TOKEN_OPENING_PARENTHESIS:
842 // this starts an expression
843 fTokenizer.SetCommandMode(false);
844 uint64 value = _ParseExpression();
845 fTokenizer.SetCommandMode(true);
846 _EatToken(TOKEN_CLOSING_PARENTHESIS);
848 snprintf(sTempBuffer, sizeof(sTempBuffer), "%" B_PRIu64, value);
849 _AddArgument(argc, argv, sTempBuffer);
850 return true;
853 case TOKEN_OPENING_BRACKET:
855 // this starts a sub command
856 int returnValue;
857 uint64 value = _ParseCommandPipe(returnValue);
858 _EatToken(TOKEN_CLOSING_BRACKET);
860 snprintf(sTempBuffer, sizeof(sTempBuffer), "%" B_PRIu64, value);
861 _AddArgument(argc, argv, sTempBuffer);
862 return true;
865 case TOKEN_STRING:
866 case TOKEN_UNKNOWN:
867 _AddArgument(argc, argv, token.string);
868 return true;
870 case TOKEN_CLOSING_PARENTHESIS:
871 case TOKEN_CLOSING_BRACKET:
872 case TOKEN_PIPE:
873 case TOKEN_SEMICOLON:
874 // those don't belong to us
875 fTokenizer.RewindToken();
876 return false;
878 default:
880 snprintf(sTempBuffer, sizeof(sTempBuffer), "unexpected token "
881 "\"%s\"", token.string);
882 parse_exception(sTempBuffer, token.position);
883 return false;
889 void
890 ExpressionParser::_GetUnparsedArgument(int& argc, char** argv)
892 int32 startPosition = fTokenizer.NextToken().position;
893 fTokenizer.RewindToken();
895 // match parentheses and brackets, but otherwise skip all tokens
896 int32 parentheses = 0;
897 int32 brackets = 0;
898 bool done = false;
899 while (!done) {
900 const Token& token = fTokenizer.NextToken();
901 switch (token.type) {
902 case TOKEN_OPENING_PARENTHESIS:
903 parentheses++;
904 break;
905 case TOKEN_OPENING_BRACKET:
906 brackets++;
907 break;
908 case TOKEN_CLOSING_PARENTHESIS:
909 if (parentheses > 0)
910 parentheses--;
911 else
912 done = true;
913 break;
914 case TOKEN_CLOSING_BRACKET:
915 if (brackets > 0)
916 brackets--;
917 else
918 done = true;
919 break;
920 case TOKEN_PIPE:
921 case TOKEN_SEMICOLON:
922 if (parentheses == 0 && brackets == 0)
923 done = true;
924 break;
925 case TOKEN_END_OF_LINE:
926 done = true;
927 break;
931 int32 endPosition = fTokenizer.CurrentToken().position;
932 fTokenizer.RewindToken();
934 // add the argument only, if it's not just all spaces
935 const char* arg = fTokenizer.String() + startPosition;
936 int32 argLen = endPosition - startPosition;
937 bool allSpaces = true;
938 for (int32 i = 0; allSpaces && i < argLen; i++)
939 allSpaces = isspace(arg[i]);
941 if (!allSpaces)
942 _AddArgument(argc, argv, arg, argLen);
946 void
947 ExpressionParser::_AddArgument(int& argc, char** argv, const char* argument,
948 int32 length)
950 if (argc == kMaxArgumentCount)
951 parse_exception("too many arguments for command", 0);
953 if (length < 0)
954 length = strlen(argument);
955 length++;
956 char* buffer = (char*)checked_malloc(length);
957 strlcpy(buffer, argument, length);
959 argv[argc++] = buffer;
963 uint64
964 ExpressionParser::_ParseSum(bool useValue, uint64 value)
966 if (!useValue)
967 value = _ParseProduct();
969 while (true) {
970 const Token& token = fTokenizer.NextToken();
971 switch (token.type) {
972 case TOKEN_PLUS:
973 value = value + _ParseProduct();
974 break;
975 case TOKEN_MINUS:
976 value = value - _ParseProduct();
977 break;
979 default:
980 fTokenizer.RewindToken();
981 return value;
987 uint64
988 ExpressionParser::_ParseProduct()
990 uint64 value = _ParseUnary();
992 while (true) {
993 Token token = fTokenizer.NextToken();
994 switch (token.type) {
995 case TOKEN_STAR:
996 value = value * _ParseUnary();
997 break;
998 case TOKEN_SLASH: {
999 uint64 rhs = _ParseUnary();
1000 if (rhs == 0)
1001 parse_exception("division by zero", token.position);
1002 value = value / rhs;
1003 break;
1005 case TOKEN_MODULO: {
1006 uint64 rhs = _ParseUnary();
1007 if (rhs == 0)
1008 parse_exception("modulo by zero", token.position);
1009 value = value % rhs;
1010 break;
1013 default:
1014 fTokenizer.RewindToken();
1015 return value;
1021 uint64
1022 ExpressionParser::_ParseUnary()
1024 switch (fTokenizer.NextToken().type) {
1025 case TOKEN_MINUS:
1026 return -_ParseUnary();
1028 case TOKEN_STAR:
1029 return _ParseDereference(NULL, NULL);
1031 default:
1032 fTokenizer.RewindToken();
1033 return _ParseAtom();
1036 return 0;
1040 uint64
1041 ExpressionParser::_ParseDereference(void** _address, uint32* _size)
1043 int32 starPosition = fTokenizer.CurrentToken().position;
1045 // optional "{ ... }" specifying the size to read
1046 uint64 size = 4;
1047 if (fTokenizer.NextToken().type == TOKEN_OPENING_BRACE) {
1048 int32 position = fTokenizer.CurrentToken().position;
1049 size = _ParseExpression();
1051 if (size != 1 && size != 2 && size != 4 && size != 8) {
1052 snprintf(sTempBuffer, sizeof(sTempBuffer),
1053 "invalid size (%" B_PRIu64 ") for unary * operator", size);
1054 parse_exception(sTempBuffer, position);
1057 _EatToken(TOKEN_CLOSING_BRACE);
1058 } else
1059 fTokenizer.RewindToken();
1061 const void* address = (const void*)(addr_t)_ParseUnary();
1063 // read bytes from address into a tempory buffer
1064 uint64 buffer;
1065 if (debug_memcpy(B_CURRENT_TEAM, &buffer, address, size) != B_OK) {
1066 snprintf(sTempBuffer, sizeof(sTempBuffer),
1067 "failed to dereference address %p", address);
1068 parse_exception(sTempBuffer, starPosition);
1071 // convert the value to uint64
1072 uint64 value = 0;
1073 switch (size) {
1074 case 1:
1075 value = *(uint8*)&buffer;
1076 break;
1077 case 2:
1078 value = *(uint16*)&buffer;
1079 break;
1080 case 4:
1081 value = *(uint32*)&buffer;
1082 break;
1083 case 8:
1084 value = buffer;
1085 break;
1088 if (_address != NULL)
1089 *_address = (void*)address;
1090 if (_size != NULL)
1091 *_size = size;
1093 return value;
1097 uint64
1098 ExpressionParser::_ParseAtom()
1100 const Token& token = fTokenizer.NextToken();
1101 if (token.type == TOKEN_END_OF_LINE)
1102 parse_exception("unexpected end of expression", token.position);
1104 if (token.type == TOKEN_CONSTANT)
1105 return token.value;
1107 if (token.type == TOKEN_IDENTIFIER) {
1108 if (!is_debug_variable_defined(token.string)) {
1109 snprintf(sTempBuffer, sizeof(sTempBuffer),
1110 "variable '%s' undefined", token.string);
1111 parse_exception(sTempBuffer, token.position);
1114 return get_debug_variable(token.string, 0);
1117 if (token.type == TOKEN_OPENING_PARENTHESIS) {
1118 uint64 value = _ParseExpression();
1120 _EatToken(TOKEN_CLOSING_PARENTHESIS);
1122 return value;
1125 // it can only be a "[ command ]" expression now
1126 fTokenizer.RewindToken();
1128 _EatToken(TOKEN_OPENING_BRACKET);
1130 fTokenizer.SetCommandMode(true);
1131 int returnValue;
1132 uint64 value = _ParseCommandPipe(returnValue);
1133 fTokenizer.SetCommandMode(false);
1135 _EatToken(TOKEN_CLOSING_BRACKET);
1137 return value;
1141 const Token&
1142 ExpressionParser::_EatToken(int32 type)
1144 const Token& token = fTokenizer.NextToken();
1145 if (token.type != type) {
1146 snprintf(sTempBuffer, sizeof(sTempBuffer), "expected token type '%c', "
1147 "got token '%s'", char(type & ~TOKEN_FLAGS), token.string);
1148 parse_exception(sTempBuffer, token.position);
1151 return token;
1156 // #pragma mark -
1159 bool
1160 evaluate_debug_expression(const char* expression, uint64* _result, bool silent)
1162 if (sNextJumpBufferIndex >= kJumpBufferCount) {
1163 kprintf_unfiltered("evaluate_debug_expression(): Out of jump buffers "
1164 "for exception handling\n");
1165 return 0;
1168 bool success;
1169 uint64 result;
1170 DebugAllocPoolScope allocPoolScope;
1171 // Will clean up all allocations when we return.
1173 if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) {
1174 result = ExpressionParser().EvaluateExpression(expression);
1175 success = true;
1176 } else {
1177 result = 0;
1178 success = false;
1179 if (!silent) {
1180 if (sExceptionPosition >= 0) {
1181 kprintf_unfiltered("%s, at position: %d, in expression: %s\n",
1182 sExceptionMessage, sExceptionPosition, expression);
1183 } else
1184 kprintf_unfiltered("%s", sExceptionMessage);
1188 sNextJumpBufferIndex--;
1190 if (success && _result != NULL)
1191 *_result = result;
1193 return success;
1198 evaluate_debug_command(const char* commandLine)
1200 if (sNextJumpBufferIndex >= kJumpBufferCount) {
1201 kprintf_unfiltered("evaluate_debug_command(): Out of jump buffers for "
1202 "exception handling\n");
1203 return 0;
1206 int returnCode = 0;
1207 DebugAllocPoolScope allocPoolScope;
1208 // Will clean up all allocations when we return.
1210 if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) {
1211 ExpressionParser().EvaluateCommand(commandLine, returnCode);
1212 } else {
1213 if (sExceptionPosition >= 0) {
1214 kprintf_unfiltered("%s, at position: %d, in command line: %s\n",
1215 sExceptionMessage, sExceptionPosition, commandLine);
1216 } else
1217 kprintf_unfiltered("%s", sExceptionMessage);
1220 sNextJumpBufferIndex--;
1222 return returnCode;
1226 status_t
1227 parse_next_debug_command_argument(const char** expressionString, char* buffer,
1228 size_t bufferSize)
1230 if (sNextJumpBufferIndex >= kJumpBufferCount) {
1231 kprintf_unfiltered("parse_next_debug_command_argument(): Out of jump "
1232 "buffers for exception handling\n");
1233 return B_ERROR;
1236 status_t error;
1237 DebugAllocPoolScope allocPoolScope;
1238 // Will clean up all allocations when we return.
1240 if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) {
1241 error = ExpressionParser().ParseNextCommandArgument(expressionString,
1242 buffer, bufferSize);
1243 } else {
1244 if (sExceptionPosition >= 0) {
1245 kprintf_unfiltered("%s, at position: %d, in command line: %s\n",
1246 sExceptionMessage, sExceptionPosition, *expressionString);
1247 } else
1248 kprintf_unfiltered("%s", sExceptionMessage);
1249 error = B_BAD_VALUE;
1252 sNextJumpBufferIndex--;
1254 return error;