1 /* 2 * Copyright 2008-2009, 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 */ 6 7 8 #include <debug.h> 9 10 #include <ctype.h> 11 #include <setjmp.h> 12 #include <stdio.h> 13 #include <stdlib.h> 14 #include <string.h> 15 16 #include <KernelExport.h> 17 18 #include <debug_heap.h> 19 20 #include "debug_commands.h" 21 #include "debug_variables.h" 22 23 24 /* 25 Grammar: 26 27 commandLine := commandPipe | ( "(" expression ")" ) 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 45 */ 46 47 48 static const int kMaxTokenLength = 128; 49 static const int kJumpBufferCount = 10; 50 51 static const int kMaxArgumentCount = 64; 52 53 static jmp_buf sJumpBuffers[kJumpBufferCount]; 54 static int sNextJumpBufferIndex = 0; 55 56 static char sExceptionMessage[128]; 57 static int sExceptionPosition; 58 59 static char sTempBuffer[128]; 60 // for composing debug output etc. 61 62 enum { 63 TOKEN_ASSIGN_FLAG = 0x100, 64 TOKEN_FLAGS = TOKEN_ASSIGN_FLAG, 65 66 TOKEN_IDENTIFIER = 'a', 67 68 TOKEN_CONSTANT = '0', 69 70 TOKEN_PLUS = '+', 71 TOKEN_MINUS = '-', 72 73 TOKEN_STAR = '*', 74 TOKEN_SLASH = '/', 75 TOKEN_MODULO = '%', 76 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, 83 84 TOKEN_OPENING_PARENTHESIS = '(', 85 TOKEN_CLOSING_PARENTHESIS = ')', 86 TOKEN_OPENING_BRACKET = '[', 87 TOKEN_CLOSING_BRACKET = ']', 88 TOKEN_OPENING_BRACE = '{', 89 TOKEN_CLOSING_BRACE = '}', 90 91 TOKEN_PIPE = '|', 92 93 TOKEN_STRING = '"', 94 TOKEN_UNKNOWN = '?', 95 TOKEN_NONE = ' ', 96 TOKEN_END_OF_LINE = '\n', 97 }; 98 99 struct Token { 100 char string[kMaxTokenLength]; 101 uint64 value; 102 int32 type; 103 int32 position; 104 105 void SetTo(const char* string, int32 length, int32 position, int32 type) 106 { 107 length = min_c((size_t)length, (sizeof(this->string) - 1)); 108 strlcpy(this->string, string, length + 1); 109 this->type = type; 110 this->value = 0; 111 this->position = position; 112 } 113 114 void Unset() 115 { 116 string[0] = '\0'; 117 value = 0; 118 type = TOKEN_NONE; 119 position = 0; 120 } 121 }; 122 123 124 // #pragma mark - exceptions 125 126 127 static void 128 parse_exception(const char* message, int32 position) 129 { 130 if (sNextJumpBufferIndex == 0) { 131 kprintf_unfiltered("parse_exception(): No jump buffer!\n"); 132 kprintf_unfiltered("exception: \"%s\", position: %lu\n", message, 133 position); 134 return; 135 } 136 137 strlcpy(sExceptionMessage, message, sizeof(sExceptionMessage)); 138 sExceptionPosition = position; 139 140 longjmp(sJumpBuffers[sNextJumpBufferIndex - 1], 1); 141 } 142 143 144 static void* 145 checked_malloc(size_t size) 146 { 147 void* address = debug_malloc(size); 148 if (address == NULL) { 149 parse_exception("out of memory for command execution", -1); 150 return NULL; 151 } 152 153 return address; 154 } 155 156 157 // #pragma mark - Tokenizer 158 159 160 class Tokenizer { 161 public: 162 Tokenizer(const char* string) 163 : fCommandMode(false) 164 { 165 SetTo(string); 166 } 167 168 void SetTo(const char* string) 169 { 170 fString = fCurrentChar = string; 171 fCurrentToken.Unset(); 172 fReuseToken = false; 173 } 174 175 void SetPosition(int32 position) 176 { 177 fCurrentChar = fString + position; 178 fCurrentToken.Unset(); 179 fReuseToken = false; 180 } 181 182 void SetCommandMode(bool commandMode) 183 { 184 fCommandMode = commandMode; 185 } 186 187 const char* String() const 188 { 189 return fString; 190 } 191 192 const Token& NextToken() 193 { 194 if (fCurrentToken.type == TOKEN_END_OF_LINE) 195 return fCurrentToken; 196 197 if (fReuseToken) { 198 fReuseToken = false; 199 return fCurrentToken; 200 } 201 202 while (*fCurrentChar != 0 && isspace(*fCurrentChar)) 203 fCurrentChar++; 204 205 if (*fCurrentChar == 0) { 206 fCurrentToken.SetTo("", 0, _CurrentPos(), TOKEN_END_OF_LINE); 207 return fCurrentToken; 208 } 209 210 return (fCommandMode ? _NextTokenCommand() : _NextTokenExpression()); 211 } 212 213 const Token& CurrentToken() const 214 { 215 return fCurrentToken; 216 } 217 218 void RewindToken() 219 { 220 fReuseToken = true; 221 } 222 223 private: 224 const Token& _NextTokenExpression() 225 { 226 if (isdigit(*fCurrentChar)) { 227 // number 228 const char* begin = fCurrentChar++; 229 230 if (*fCurrentChar == 'x') { 231 // hex number 232 fCurrentChar++; 233 while (*fCurrentChar != 0 234 && (isdigit(*fCurrentChar) 235 || strchr("abcdefABCDEF", *fCurrentChar))) { 236 fCurrentChar++; 237 } 238 239 if (fCurrentChar - begin == 2) 240 parse_exception("invalid hex number", begin - fString); 241 242 } else { 243 // decimal number 244 while (*fCurrentChar != 0 && isdigit(*fCurrentChar)) 245 fCurrentChar++; 246 } 247 248 int32 length = fCurrentChar - begin; 249 fCurrentToken.SetTo(begin, length, _CurrentPos() - length, 250 TOKEN_CONSTANT); 251 fCurrentToken.value = strtoull(fCurrentToken.string, NULL, 0); 252 253 } else if (isalpha(*fCurrentChar) || *fCurrentChar == '_' 254 || *fCurrentChar == '$' || *fCurrentChar == '@') { 255 // identifier 256 const char* begin = fCurrentChar; 257 fCurrentChar++; 258 while (*fCurrentChar != 0 259 && (isalpha(*fCurrentChar) || *fCurrentChar == '_' 260 || isdigit(*fCurrentChar))) { 261 fCurrentChar++; 262 } 263 264 int32 length = fCurrentChar - begin; 265 fCurrentToken.SetTo(begin, length, _CurrentPos() - length, 266 TOKEN_IDENTIFIER); 267 268 } else { 269 const char* begin = fCurrentChar; 270 char c = *fCurrentChar; 271 fCurrentChar++; 272 int32 flags = 0; 273 274 switch (c) { 275 case '=': 276 fCurrentChar--; 277 case '+': 278 case '-': 279 case '*': 280 case '/': 281 case '%': 282 if (*fCurrentChar == '=') { 283 fCurrentChar++; 284 flags = TOKEN_ASSIGN_FLAG; 285 } 286 287 case '(': 288 case ')': 289 case '[': 290 case ']': 291 case '{': 292 case '}': 293 { 294 int32 length = fCurrentChar - begin; 295 fCurrentToken.SetTo(begin, length, _CurrentPos() - length, 296 c | flags); 297 break; 298 } 299 300 case '"': 301 { 302 fCurrentChar--; 303 _QuotedString(); 304 break; 305 } 306 307 default: 308 { 309 fCurrentChar--; 310 _UnquotedString(); 311 break; 312 } 313 } 314 } 315 316 return fCurrentToken; 317 } 318 319 const Token& _NextTokenCommand() 320 { 321 switch (*fCurrentChar) { 322 case '(': 323 case ')': 324 case '[': 325 case ']': 326 case '|': 327 fCurrentToken.SetTo(fCurrentChar, 1, _CurrentPos(), 328 *fCurrentChar); 329 fCurrentChar++; 330 return fCurrentToken; 331 case '"': 332 return _QuotedString(); 333 334 default: 335 return _UnquotedString(); 336 } 337 } 338 339 const Token& _QuotedString() 340 { 341 const char* begin = fCurrentChar++; 342 int32 length = 0; 343 344 while (*fCurrentChar != '\0' && *fCurrentChar != '"') { 345 char c = *fCurrentChar; 346 fCurrentChar++; 347 348 if (c == '\\') { 349 // an escaped char 350 c = *fCurrentChar; 351 fCurrentChar++; 352 353 if (c == '\0') 354 break; 355 } 356 357 if ((size_t)length 358 >= sizeof(fCurrentToken.string) - 1) { 359 parse_exception("quoted string too long", begin - fString); 360 } 361 362 fCurrentToken.string[length++] = c; 363 } 364 365 if (*fCurrentChar == '\0') { 366 parse_exception("unexpected end of line while " 367 "parsing quoted string", begin - fString); 368 } 369 370 fCurrentChar++; 371 372 fCurrentToken.string[length] = '\0'; 373 fCurrentToken.value = 0; 374 fCurrentToken.type = TOKEN_STRING; 375 fCurrentToken.position = begin - fString; 376 377 return fCurrentToken; 378 } 379 380 const Token& _UnquotedString() 381 { 382 const char* begin = fCurrentChar; 383 384 while (*fCurrentChar != 0 && !_IsUnquotedDelimitingChar(*fCurrentChar)) 385 fCurrentChar++; 386 387 int32 length = fCurrentChar - begin; 388 fCurrentToken.SetTo(begin, length, _CurrentPos() - length, 389 TOKEN_UNKNOWN); 390 391 return fCurrentToken; 392 } 393 394 bool _IsUnquotedDelimitingChar(char c) 395 { 396 if (isspace(c)) 397 return true; 398 399 switch (c) { 400 case '(': 401 case ')': 402 case '[': 403 case ']': 404 case '"': 405 return true; 406 407 case '{': 408 case '}': 409 case '=': 410 case '+': 411 case '-': 412 case '*': 413 case '/': 414 case '%': 415 return !fCommandMode; 416 417 default: 418 return false; 419 } 420 } 421 422 int32 _CurrentPos() const 423 { 424 return fCurrentChar - fString; 425 } 426 427 private: 428 const char* fString; 429 const char* fCurrentChar; 430 Token fCurrentToken; 431 bool fReuseToken; 432 bool fCommandMode; 433 }; 434 435 436 // #pragma mark - ExpressionParser 437 438 439 class ExpressionParser { 440 public: 441 ExpressionParser(); 442 ~ExpressionParser(); 443 444 uint64 EvaluateExpression( 445 const char* expressionString); 446 uint64 EvaluateCommand( 447 const char* expressionString, 448 int& returnCode); 449 status_t ParseNextCommandArgument( 450 const char** expressionString, char* buffer, 451 size_t bufferSize); 452 453 private: 454 uint64 _ParseExpression(bool expectAssignment = false); 455 uint64 _ParseCommandPipe(int& returnCode); 456 void _ParseCommand( 457 debugger_command_pipe_segment& segment); 458 bool _ParseArgument(int& argc, char** argv); 459 void _GetUnparsedArgument(int& argc, char** argv); 460 void _AddArgument(int& argc, char** argv, 461 const char* argument, int32 length = -1); 462 uint64 _ParseSum(bool useValue, uint64 value); 463 uint64 _ParseProduct(); 464 uint64 _ParsePower(); 465 uint64 _ParseUnary(); 466 uint64 _ParseDereference(void** _address, 467 uint32* _size); 468 uint64 _ParseAtom(); 469 470 const Token& _EatToken(int32 type); 471 472 Tokenizer fTokenizer; 473 }; 474 475 476 ExpressionParser::ExpressionParser() 477 : fTokenizer("") 478 { 479 } 480 481 482 ExpressionParser::~ExpressionParser() 483 { 484 } 485 486 487 uint64 488 ExpressionParser::EvaluateExpression(const char* expressionString) 489 { 490 fTokenizer.SetTo(expressionString); 491 492 uint64 value = _ParseExpression(); 493 const Token& token = fTokenizer.NextToken(); 494 if (token.type != TOKEN_END_OF_LINE) 495 parse_exception("parse error", token.position); 496 497 return value; 498 } 499 500 501 uint64 502 ExpressionParser::EvaluateCommand(const char* expressionString, int& returnCode) 503 { 504 fTokenizer.SetTo(expressionString); 505 506 // Allowed are command or assignment. A command always starts with an 507 // identifier, an assignment either with an identifier (variable name) or 508 // a dereferenced address. 509 const Token& token = fTokenizer.NextToken(); 510 uint64 value = 0; 511 512 if (token.type == TOKEN_IDENTIFIER) { 513 fTokenizer.NextToken(); 514 if (token.type & TOKEN_ASSIGN_FLAG) { 515 // an assignment 516 fTokenizer.SetTo(expressionString); 517 value = _ParseExpression(true); 518 returnCode = 0; 519 } else { 520 // no assignment, so let's assume it's a command 521 fTokenizer.SetTo(expressionString); 522 fTokenizer.SetCommandMode(true); 523 value = _ParseCommandPipe(returnCode); 524 } 525 } else if (token.type == TOKEN_STAR) { 526 // dereferenced address -- assignment 527 fTokenizer.SetTo(expressionString); 528 value = _ParseExpression(true); 529 returnCode = 0; 530 } else 531 parse_exception("expected command or assignment", 0); 532 533 if (token.type != TOKEN_END_OF_LINE) 534 parse_exception("parse error", token.position); 535 536 return value; 537 } 538 539 540 status_t 541 ExpressionParser::ParseNextCommandArgument(const char** expressionString, 542 char* buffer, size_t bufferSize) 543 { 544 fTokenizer.SetTo(*expressionString); 545 fTokenizer.SetCommandMode(true); 546 547 if (fTokenizer.NextToken().type == TOKEN_END_OF_LINE) 548 return B_ENTRY_NOT_FOUND; 549 550 fTokenizer.RewindToken(); 551 552 char* argv[2]; 553 int argc = 0; 554 if (!_ParseArgument(argc, argv)) 555 return B_BAD_VALUE; 556 557 strlcpy(buffer, argv[0], bufferSize); 558 559 const Token& token = fTokenizer.NextToken(); 560 if (token.type == TOKEN_END_OF_LINE) 561 *expressionString = NULL; 562 else 563 *expressionString += token.position; 564 565 return B_OK; 566 } 567 568 569 uint64 570 ExpressionParser::_ParseExpression(bool expectAssignment) 571 { 572 const Token& token = fTokenizer.NextToken(); 573 int32 position = token.position; 574 if (token.type == TOKEN_IDENTIFIER) { 575 char variable[MAX_DEBUG_VARIABLE_NAME_LEN]; 576 strlcpy(variable, token.string, sizeof(variable)); 577 578 int32 assignmentType = fTokenizer.NextToken().type; 579 if (assignmentType & TOKEN_ASSIGN_FLAG) { 580 // an assignment 581 uint64 rhs = _ParseExpression(); 582 583 // handle the standard assignment separately -- the other kinds 584 // need the variable to be defined 585 if (assignmentType == TOKEN_ASSIGN) { 586 if (!set_debug_variable(variable, rhs)) { 587 snprintf(sTempBuffer, sizeof(sTempBuffer), 588 "failed to set value for variable \"%s\"", 589 variable); 590 parse_exception(sTempBuffer, position); 591 } 592 593 return rhs; 594 } 595 596 // variable must be defined 597 if (!is_debug_variable_defined(variable)) { 598 snprintf(sTempBuffer, sizeof(sTempBuffer), 599 "variable \"%s\" not defined in modifying assignment", 600 variable); 601 parse_exception(sTempBuffer, position); 602 } 603 604 uint64 variableValue = get_debug_variable(variable, 0); 605 606 // check for division by zero for the respective assignment types 607 if ((assignmentType == TOKEN_SLASH_ASSIGN 608 || assignmentType == TOKEN_MODULO_ASSIGN) 609 && rhs == 0) { 610 parse_exception("division by zero", position); 611 } 612 613 // compute the new variable value 614 switch (assignmentType) { 615 case TOKEN_PLUS_ASSIGN: 616 variableValue += rhs; 617 break; 618 case TOKEN_MINUS_ASSIGN: 619 variableValue -= rhs; 620 break; 621 case TOKEN_STAR_ASSIGN: 622 variableValue *= rhs; 623 break; 624 case TOKEN_SLASH_ASSIGN: 625 variableValue /= rhs; 626 break; 627 case TOKEN_MODULO_ASSIGN: 628 variableValue %= rhs; 629 break; 630 default: 631 parse_exception("internal error: unknown assignment token", 632 position); 633 break; 634 } 635 636 set_debug_variable(variable, variableValue); 637 return variableValue; 638 } 639 } else if (token.type == TOKEN_STAR) { 640 void* address; 641 uint32 size; 642 uint64 value = _ParseDereference(&address, &size); 643 644 int32 assignmentType = fTokenizer.NextToken().type; 645 if (assignmentType & TOKEN_ASSIGN_FLAG) { 646 // an assignment 647 uint64 rhs = _ParseExpression(); 648 649 // check for division by zero for the respective assignment types 650 if ((assignmentType == TOKEN_SLASH_ASSIGN 651 || assignmentType == TOKEN_MODULO_ASSIGN) 652 && rhs == 0) { 653 parse_exception("division by zero", position); 654 } 655 656 // compute the new value 657 switch (assignmentType) { 658 case TOKEN_ASSIGN: 659 value = rhs; 660 break; 661 case TOKEN_PLUS_ASSIGN: 662 value += rhs; 663 break; 664 case TOKEN_MINUS_ASSIGN: 665 value -= rhs; 666 break; 667 case TOKEN_STAR_ASSIGN: 668 value *= rhs; 669 break; 670 case TOKEN_SLASH_ASSIGN: 671 value /= rhs; 672 break; 673 case TOKEN_MODULO_ASSIGN: 674 value %= rhs; 675 break; 676 default: 677 parse_exception("internal error: unknown assignment token", 678 position); 679 break; 680 } 681 682 // convert the value for writing to the address 683 uint64 buffer = 0; 684 switch (size) { 685 case 1: 686 *(uint8*)&buffer = value; 687 break; 688 case 2: 689 *(uint16*)&buffer = value; 690 break; 691 case 4: 692 *(uint32*)&buffer = value; 693 break; 694 case 8: 695 value = buffer; 696 break; 697 } 698 699 if (debug_memcpy(address, &buffer, size) != B_OK) { 700 snprintf(sTempBuffer, sizeof(sTempBuffer), 701 "failed to write to address %p", address); 702 parse_exception(sTempBuffer, position); 703 } 704 705 return value; 706 } 707 } 708 709 if (expectAssignment) { 710 parse_exception("expected assignment", 711 fTokenizer.CurrentToken().position); 712 } 713 714 // no assignment -- reset to the identifier position and parse a sum 715 fTokenizer.SetPosition(position); 716 return _ParseSum(false, 0); 717 } 718 719 720 uint64 721 ExpressionParser::_ParseCommandPipe(int& returnCode) 722 { 723 debugger_command_pipe* pipe = (debugger_command_pipe*)checked_malloc( 724 sizeof(debugger_command_pipe)); 725 726 pipe->segment_count = 0; 727 pipe->broken = false; 728 729 do { 730 if (pipe->segment_count >= MAX_DEBUGGER_COMMAND_PIPE_LENGTH) 731 parse_exception("Pipe too long", fTokenizer.NextToken().position); 732 733 debugger_command_pipe_segment& segment 734 = pipe->segments[pipe->segment_count]; 735 segment.index = pipe->segment_count++; 736 737 _ParseCommand(segment); 738 739 } while (fTokenizer.NextToken().type == TOKEN_PIPE); 740 741 fTokenizer.RewindToken(); 742 743 // invoke the pipe 744 returnCode = invoke_debugger_command_pipe(pipe); 745 746 debug_free(pipe); 747 748 return get_debug_variable("_", 0); 749 } 750 751 752 void 753 ExpressionParser::_ParseCommand(debugger_command_pipe_segment& segment) 754 { 755 fTokenizer.SetCommandMode(false); 756 const Token& token = _EatToken(TOKEN_IDENTIFIER); 757 fTokenizer.SetCommandMode(true); 758 759 bool ambiguous; 760 debugger_command* command = find_debugger_command(token.string, true, 761 ambiguous); 762 763 if (command == NULL) { 764 if (ambiguous) { 765 snprintf(sTempBuffer, sizeof(sTempBuffer), 766 "Ambiguous command \"%s\". Use tab completion or enter " 767 "\"help %s\" get a list of matching commands.\n", token.string, 768 token.string); 769 } else { 770 snprintf(sTempBuffer, sizeof(sTempBuffer), 771 "Unknown command \"%s\". Enter \"help\" to get a list of " 772 "all supported commands.\n", token.string); 773 } 774 775 parse_exception(sTempBuffer, -1); 776 } 777 778 // allocate temporary buffer for the argument vector 779 char** argv = (char**)checked_malloc(kMaxArgumentCount * sizeof(char*)); 780 int argc = 0; 781 argv[argc++] = (char*)command->name; 782 783 // get the arguments 784 if ((command->flags & B_KDEBUG_DONT_PARSE_ARGUMENTS) != 0) { 785 _GetUnparsedArgument(argc, argv); 786 } else { 787 while (fTokenizer.NextToken().type != TOKEN_END_OF_LINE) { 788 fTokenizer.RewindToken(); 789 if (!_ParseArgument(argc, argv)) 790 break; 791 } 792 } 793 794 if (segment.index > 0) { 795 if (argc >= kMaxArgumentCount) 796 parse_exception("too many arguments for command", 0); 797 else 798 argc++; 799 } 800 801 segment.command = command; 802 segment.argc = argc; 803 segment.argv = argv; 804 segment.invocations = 0; 805 } 806 807 808 bool 809 ExpressionParser::_ParseArgument(int& argc, char** argv) 810 { 811 const Token& token = fTokenizer.NextToken(); 812 switch (token.type) { 813 case TOKEN_OPENING_PARENTHESIS: 814 { 815 // this starts an expression 816 fTokenizer.SetCommandMode(false); 817 uint64 value = _ParseExpression(); 818 fTokenizer.SetCommandMode(true); 819 _EatToken(TOKEN_CLOSING_PARENTHESIS); 820 821 snprintf(sTempBuffer, sizeof(sTempBuffer), "%llu", value); 822 _AddArgument(argc, argv, sTempBuffer); 823 return true; 824 } 825 826 case TOKEN_OPENING_BRACKET: 827 { 828 // this starts a sub command 829 int returnValue; 830 uint64 value = _ParseCommandPipe(returnValue); 831 _EatToken(TOKEN_CLOSING_BRACKET); 832 833 snprintf(sTempBuffer, sizeof(sTempBuffer), "%llu", value); 834 _AddArgument(argc, argv, sTempBuffer); 835 return true; 836 } 837 838 case TOKEN_STRING: 839 case TOKEN_UNKNOWN: 840 _AddArgument(argc, argv, token.string); 841 return true; 842 843 case TOKEN_CLOSING_PARENTHESIS: 844 case TOKEN_CLOSING_BRACKET: 845 case TOKEN_PIPE: 846 // those don't belong to us 847 fTokenizer.RewindToken(); 848 return false; 849 850 default: 851 { 852 snprintf(sTempBuffer, sizeof(sTempBuffer), "unexpected token " 853 "\"%s\"", token.string); 854 parse_exception(sTempBuffer, token.position); 855 return false; 856 } 857 } 858 } 859 860 861 void 862 ExpressionParser::_GetUnparsedArgument(int& argc, char** argv) 863 { 864 int32 startPosition = fTokenizer.NextToken().position; 865 fTokenizer.RewindToken(); 866 867 // match parentheses and brackets, but otherwise skip all tokens 868 int32 parentheses = 0; 869 int32 brackets = 0; 870 bool done = false; 871 while (!done) { 872 const Token& token = fTokenizer.NextToken(); 873 switch (token.type) { 874 case TOKEN_OPENING_PARENTHESIS: 875 parentheses++; 876 break; 877 case TOKEN_OPENING_BRACKET: 878 brackets++; 879 break; 880 case TOKEN_CLOSING_PARENTHESIS: 881 if (parentheses > 0) 882 parentheses--; 883 else 884 done = true; 885 break; 886 case TOKEN_CLOSING_BRACKET: 887 if (brackets > 0) 888 brackets--; 889 else 890 done = true; 891 break; 892 case TOKEN_PIPE: 893 if (parentheses == 0 && brackets == 0) 894 done = true; 895 break; 896 case TOKEN_END_OF_LINE: 897 done = true; 898 break; 899 } 900 } 901 902 int32 endPosition = fTokenizer.CurrentToken().position; 903 fTokenizer.RewindToken(); 904 905 // add the argument only, if it's not just all spaces 906 const char* arg = fTokenizer.String() + startPosition; 907 int32 argLen = endPosition - startPosition; 908 bool allSpaces = true; 909 for (int32 i = 0; allSpaces && i < argLen; i++) 910 allSpaces = isspace(arg[i]); 911 912 if (!allSpaces) 913 _AddArgument(argc, argv, arg, argLen); 914 } 915 916 917 void 918 ExpressionParser::_AddArgument(int& argc, char** argv, const char* argument, 919 int32 length) 920 { 921 if (argc == kMaxArgumentCount) 922 parse_exception("too many arguments for command", 0); 923 924 if (length < 0) 925 length = strlen(argument); 926 length++; 927 char* buffer = (char*)checked_malloc(length); 928 strlcpy(buffer, argument, length); 929 930 argv[argc++] = buffer; 931 } 932 933 934 uint64 935 ExpressionParser::_ParseSum(bool useValue, uint64 value) 936 { 937 if (!useValue) 938 value = _ParseProduct(); 939 940 while (true) { 941 const Token& token = fTokenizer.NextToken(); 942 switch (token.type) { 943 case TOKEN_PLUS: 944 value = value + _ParseProduct(); 945 break; 946 case TOKEN_MINUS: 947 value = value - _ParseProduct(); 948 break; 949 950 default: 951 fTokenizer.RewindToken(); 952 return value; 953 } 954 } 955 } 956 957 958 uint64 959 ExpressionParser::_ParseProduct() 960 { 961 uint64 value = _ParseUnary(); 962 963 while (true) { 964 Token token = fTokenizer.NextToken(); 965 switch (token.type) { 966 case TOKEN_STAR: 967 value = value * _ParseUnary(); 968 break; 969 case TOKEN_SLASH: { 970 uint64 rhs = _ParseUnary(); 971 if (rhs == 0) 972 parse_exception("division by zero", token.position); 973 value = value / rhs; 974 break; 975 } 976 case TOKEN_MODULO: { 977 uint64 rhs = _ParseUnary(); 978 if (rhs == 0) 979 parse_exception("modulo by zero", token.position); 980 value = value % rhs; 981 break; 982 } 983 984 default: 985 fTokenizer.RewindToken(); 986 return value; 987 } 988 } 989 } 990 991 992 uint64 993 ExpressionParser::_ParseUnary() 994 { 995 switch (fTokenizer.NextToken().type) { 996 case TOKEN_MINUS: 997 return -_ParseUnary(); 998 999 case TOKEN_STAR: 1000 return _ParseDereference(NULL, NULL); 1001 1002 default: 1003 fTokenizer.RewindToken(); 1004 return _ParseAtom(); 1005 } 1006 1007 return 0; 1008 } 1009 1010 1011 uint64 1012 ExpressionParser::_ParseDereference(void** _address, uint32* _size) 1013 { 1014 int32 starPosition = fTokenizer.CurrentToken().position; 1015 1016 // optional "{ ... }" specifying the size to read 1017 uint64 size = 4; 1018 if (fTokenizer.NextToken().type == TOKEN_OPENING_BRACE) { 1019 int32 position = fTokenizer.CurrentToken().position; 1020 size = _ParseExpression(); 1021 1022 if (size != 1 && size != 2 && size != 4 && size != 8) { 1023 snprintf(sTempBuffer, sizeof(sTempBuffer), 1024 "invalid size (%llu) for unary * operator", size); 1025 parse_exception(sTempBuffer, position); 1026 } 1027 1028 _EatToken(TOKEN_CLOSING_BRACE); 1029 } else 1030 fTokenizer.RewindToken(); 1031 1032 const void* address = (const void*)(addr_t)_ParseUnary(); 1033 1034 // read bytes from address into a tempory buffer 1035 uint64 buffer; 1036 if (debug_memcpy(&buffer, address, size) != B_OK) { 1037 snprintf(sTempBuffer, sizeof(sTempBuffer), 1038 "failed to dereference address %p", address); 1039 parse_exception(sTempBuffer, starPosition); 1040 } 1041 1042 // convert the value to uint64 1043 uint64 value = 0; 1044 switch (size) { 1045 case 1: 1046 value = *(uint8*)&buffer; 1047 break; 1048 case 2: 1049 value = *(uint16*)&buffer; 1050 break; 1051 case 4: 1052 value = *(uint32*)&buffer; 1053 break; 1054 case 8: 1055 value = buffer; 1056 break; 1057 } 1058 1059 if (_address != NULL) 1060 *_address = (void*)address; 1061 if (_size != NULL) 1062 *_size = size; 1063 1064 return value; 1065 } 1066 1067 1068 uint64 1069 ExpressionParser::_ParseAtom() 1070 { 1071 const Token& token = fTokenizer.NextToken(); 1072 if (token.type == TOKEN_END_OF_LINE) 1073 parse_exception("unexpected end of expression", token.position); 1074 1075 if (token.type == TOKEN_CONSTANT) 1076 return token.value; 1077 1078 if (token.type == TOKEN_IDENTIFIER) { 1079 if (!is_debug_variable_defined(token.string)) { 1080 snprintf(sTempBuffer, sizeof(sTempBuffer), 1081 "variable '%s' undefined", token.string); 1082 parse_exception(sTempBuffer, token.position); 1083 } 1084 1085 return get_debug_variable(token.string, 0); 1086 } 1087 1088 if (token.type == TOKEN_OPENING_PARENTHESIS) { 1089 uint64 value = _ParseExpression(); 1090 1091 _EatToken(TOKEN_CLOSING_PARENTHESIS); 1092 1093 return value; 1094 } 1095 1096 // it can only be a "[ command ]" expression now 1097 fTokenizer.RewindToken(); 1098 1099 _EatToken(TOKEN_OPENING_BRACKET); 1100 1101 fTokenizer.SetCommandMode(true); 1102 int returnValue; 1103 uint64 value = _ParseCommandPipe(returnValue); 1104 fTokenizer.SetCommandMode(false); 1105 1106 _EatToken(TOKEN_CLOSING_BRACKET); 1107 1108 return value; 1109 } 1110 1111 1112 const Token& 1113 ExpressionParser::_EatToken(int32 type) 1114 { 1115 const Token& token = fTokenizer.NextToken(); 1116 if (token.type != type) { 1117 snprintf(sTempBuffer, sizeof(sTempBuffer), "expected token type '%c', " 1118 "got token '%s'", char(type & ~TOKEN_FLAGS), token.string); 1119 parse_exception(sTempBuffer, token.position); 1120 } 1121 1122 return token; 1123 } 1124 1125 1126 1127 // #pragma mark - 1128 1129 1130 bool 1131 evaluate_debug_expression(const char* expression, uint64* _result, bool silent) 1132 { 1133 if (sNextJumpBufferIndex >= kJumpBufferCount) { 1134 kprintf_unfiltered("evaluate_debug_expression(): Out of jump buffers " 1135 "for exception handling\n"); 1136 return 0; 1137 } 1138 1139 bool success; 1140 uint64 result; 1141 DebugAllocPoolScope allocPoolScope; 1142 // Will clean up all allocations when we return. 1143 1144 if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) { 1145 result = ExpressionParser().EvaluateExpression(expression); 1146 success = true; 1147 } else { 1148 result = 0; 1149 success = false; 1150 if (!silent) { 1151 if (sExceptionPosition >= 0) { 1152 kprintf_unfiltered("%s, at position: %d, in expression: %s\n", 1153 sExceptionMessage, sExceptionPosition, expression); 1154 } else 1155 kprintf_unfiltered("%s", sExceptionMessage); 1156 } 1157 } 1158 1159 sNextJumpBufferIndex--; 1160 1161 if (success && _result != NULL) 1162 *_result = result; 1163 1164 return success; 1165 } 1166 1167 1168 int 1169 evaluate_debug_command(const char* commandLine) 1170 { 1171 if (sNextJumpBufferIndex >= kJumpBufferCount) { 1172 kprintf_unfiltered("evaluate_debug_command(): Out of jump buffers for " 1173 "exception handling\n"); 1174 return 0; 1175 } 1176 1177 int returnCode = 0; 1178 DebugAllocPoolScope allocPoolScope; 1179 // Will clean up all allocations when we return. 1180 1181 if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) { 1182 ExpressionParser().EvaluateCommand(commandLine, returnCode); 1183 } else { 1184 if (sExceptionPosition >= 0) { 1185 kprintf_unfiltered("%s, at position: %d, in command line: %s\n", 1186 sExceptionMessage, sExceptionPosition, commandLine); 1187 } else 1188 kprintf_unfiltered("%s", sExceptionMessage); 1189 } 1190 1191 sNextJumpBufferIndex--; 1192 1193 return returnCode; 1194 } 1195 1196 1197 status_t 1198 parse_next_debug_command_argument(const char** expressionString, char* buffer, 1199 size_t bufferSize) 1200 { 1201 if (sNextJumpBufferIndex >= kJumpBufferCount) { 1202 kprintf_unfiltered("parse_next_debug_command_argument(): Out of jump " 1203 "buffers for exception handling\n"); 1204 return B_ERROR; 1205 } 1206 1207 status_t error; 1208 DebugAllocPoolScope allocPoolScope; 1209 // Will clean up all allocations when we return. 1210 1211 if (setjmp(sJumpBuffers[sNextJumpBufferIndex++]) == 0) { 1212 error = ExpressionParser().ParseNextCommandArgument(expressionString, 1213 buffer, bufferSize); 1214 } else { 1215 if (sExceptionPosition >= 0) { 1216 kprintf_unfiltered("%s, at position: %d, in command line: %s\n", 1217 sExceptionMessage, sExceptionPosition, *expressionString); 1218 } else 1219 kprintf_unfiltered("%s", sExceptionMessage); 1220 error = B_BAD_VALUE; 1221 } 1222 1223 sNextJumpBufferIndex--; 1224 1225 return error; 1226 } 1227