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 */ 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 [ ";" 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 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 TOKEN_SEMICOLON = ';', 93 94 TOKEN_STRING = '"', 95 TOKEN_UNKNOWN = '?', 96 TOKEN_NONE = ' ', 97 TOKEN_END_OF_LINE = '\n', 98 }; 99 100 struct Token { 101 char string[kMaxTokenLength]; 102 uint64 value; 103 int32 type; 104 int32 position; 105 106 void SetTo(const char* string, int32 length, int32 position, int32 type) 107 { 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; 113 } 114 115 void Unset() 116 { 117 string[0] = '\0'; 118 value = 0; 119 type = TOKEN_NONE; 120 position = 0; 121 } 122 }; 123 124 125 // #pragma mark - exceptions 126 127 128 static void 129 parse_exception(const char* message, int32 position) 130 { 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; 136 } 137 138 strlcpy(sExceptionMessage, message, sizeof(sExceptionMessage)); 139 sExceptionPosition = position; 140 141 longjmp(sJumpBuffers[sNextJumpBufferIndex - 1], 1); 142 } 143 144 145 static void* 146 checked_malloc(size_t size) 147 { 148 void* address = debug_malloc(size); 149 if (address == NULL) { 150 parse_exception("out of memory for command execution", -1); 151 return NULL; 152 } 153 154 return address; 155 } 156 157 158 // #pragma mark - Tokenizer 159 160 161 class Tokenizer { 162 public: 163 Tokenizer(const char* string) 164 : fCommandMode(false) 165 { 166 SetTo(string); 167 } 168 169 void SetTo(const char* string) 170 { 171 fString = fCurrentChar = string; 172 fCurrentToken.Unset(); 173 fReuseToken = false; 174 } 175 176 void SetPosition(int32 position) 177 { 178 fCurrentChar = fString + position; 179 fCurrentToken.Unset(); 180 fReuseToken = false; 181 } 182 183 void SetCommandMode(bool commandMode) 184 { 185 if (fCommandMode == commandMode) 186 return; 187 188 fCommandMode = commandMode; 189 190 if (fReuseToken) { 191 // We can't reuse the token, since the parsing mode changed. 192 SetPosition(fCurrentToken.position); 193 } 194 } 195 196 const char* String() const 197 { 198 return fString; 199 } 200 201 const Token& NextToken() 202 { 203 if (fCurrentToken.type == TOKEN_END_OF_LINE) 204 return fCurrentToken; 205 206 if (fReuseToken) { 207 fReuseToken = false; 208 return fCurrentToken; 209 } 210 211 while (*fCurrentChar != 0 && isspace(*fCurrentChar)) 212 fCurrentChar++; 213 214 if (*fCurrentChar == 0) { 215 fCurrentToken.SetTo("", 0, _CurrentPos(), TOKEN_END_OF_LINE); 216 return fCurrentToken; 217 } 218 219 return (fCommandMode ? _NextTokenCommand() : _NextTokenExpression()); 220 } 221 222 const Token& CurrentToken() const 223 { 224 return fCurrentToken; 225 } 226 227 void RewindToken() 228 { 229 fReuseToken = true; 230 } 231 232 private: 233 const Token& _NextTokenExpression() 234 { 235 if (isdigit(*fCurrentChar)) { 236 // number 237 const char* begin = fCurrentChar++; 238 239 if (*fCurrentChar == 'x') { 240 // hex number 241 fCurrentChar++; 242 while (*fCurrentChar != 0 243 && (isdigit(*fCurrentChar) 244 || strchr("abcdefABCDEF", *fCurrentChar))) { 245 fCurrentChar++; 246 } 247 248 if (fCurrentChar - begin == 2) 249 parse_exception("invalid hex number", begin - fString); 250 251 } else { 252 // decimal number 253 while (*fCurrentChar != 0 && isdigit(*fCurrentChar)) 254 fCurrentChar++; 255 } 256 257 int32 length = fCurrentChar - begin; 258 fCurrentToken.SetTo(begin, length, _CurrentPos() - length, 259 TOKEN_CONSTANT); 260 fCurrentToken.value = strtoull(fCurrentToken.string, NULL, 0); 261 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++; 271 } 272 273 int32 length = fCurrentChar - begin; 274 fCurrentToken.SetTo(begin, length, _CurrentPos() - length, 275 TOKEN_IDENTIFIER); 276 277 } else { 278 const char* begin = fCurrentChar; 279 char c = *fCurrentChar; 280 fCurrentChar++; 281 int32 flags = 0; 282 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; 294 } 295 296 case '(': 297 case ')': 298 case '[': 299 case ']': 300 case '{': 301 case '}': 302 case ';': 303 { 304 int32 length = fCurrentChar - begin; 305 fCurrentToken.SetTo(begin, length, _CurrentPos() - length, 306 c | flags); 307 break; 308 } 309 310 case '"': 311 { 312 fCurrentChar--; 313 _QuotedString(); 314 break; 315 } 316 317 default: 318 { 319 fCurrentChar--; 320 _UnquotedString(); 321 break; 322 } 323 } 324 } 325 326 return fCurrentToken; 327 } 328 329 const Token& _NextTokenCommand() 330 { 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(); 344 345 default: 346 return _UnquotedString(); 347 } 348 } 349 350 const Token& _QuotedString() 351 { 352 const char* begin = fCurrentChar++; 353 int32 length = 0; 354 355 while (*fCurrentChar != '\0' && *fCurrentChar != '"') { 356 char c = *fCurrentChar; 357 fCurrentChar++; 358 359 if (c == '\\') { 360 // an escaped char 361 c = *fCurrentChar; 362 fCurrentChar++; 363 364 if (c == '\0') 365 break; 366 } 367 368 if ((size_t)length 369 >= sizeof(fCurrentToken.string) - 1) { 370 parse_exception("quoted string too long", begin - fString); 371 } 372 373 fCurrentToken.string[length++] = c; 374 } 375 376 if (*fCurrentChar == '\0') { 377 parse_exception("unexpected end of line while " 378 "parsing quoted string", begin - fString); 379 } 380 381 fCurrentChar++; 382 383 fCurrentToken.string[length] = '\0'; 384 fCurrentToken.value = 0; 385 fCurrentToken.type = TOKEN_STRING; 386 fCurrentToken.position = begin - fString; 387 388 return fCurrentToken; 389 } 390 391 const Token& _UnquotedString() 392 { 393 const char* begin = fCurrentChar; 394 395 while (*fCurrentChar != 0 && !_IsUnquotedDelimitingChar(*fCurrentChar)) 396 fCurrentChar++; 397 398 int32 length = fCurrentChar - begin; 399 fCurrentToken.SetTo(begin, length, _CurrentPos() - length, 400 TOKEN_UNKNOWN); 401 402 return fCurrentToken; 403 } 404 405 bool _IsUnquotedDelimitingChar(char c) 406 { 407 if (isspace(c)) 408 return true; 409 410 switch (c) { 411 case '(': 412 case ')': 413 case '[': 414 case ']': 415 case '"': 416 return true; 417 418 case '|': // TODO: Move when we support & and | in expressions. 419 case ';': 420 return fCommandMode; 421 422 case '{': 423 case '}': 424 case '=': 425 case '+': 426 case '-': 427 case '*': 428 case '/': 429 case '%': 430 return !fCommandMode; 431 432 default: 433 return false; 434 } 435 } 436 437 int32 _CurrentPos() const 438 { 439 return fCurrentChar - fString; 440 } 441 442 private: 443 const char* fString; 444 const char* fCurrentChar; 445 Token fCurrentToken; 446 bool fReuseToken; 447 bool fCommandMode; 448 }; 449 450 451 // #pragma mark - ExpressionParser 452 453 454 class ExpressionParser { 455 public: 456 ExpressionParser(); 457 ~ExpressionParser(); 458 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); 467 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(); 484 485 const Token& _EatToken(int32 type); 486 487 Tokenizer fTokenizer; 488 }; 489 490 491 ExpressionParser::ExpressionParser() 492 : fTokenizer("") 493 { 494 } 495 496 497 ExpressionParser::~ExpressionParser() 498 { 499 } 500 501 502 uint64 503 ExpressionParser::EvaluateExpression(const char* expressionString) 504 { 505 fTokenizer.SetTo(expressionString); 506 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); 511 512 return value; 513 } 514 515 516 uint64 517 ExpressionParser::EvaluateCommand(const char* expressionString, int& returnCode) 518 { 519 fTokenizer.SetTo(expressionString); 520 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; 526 527 while (true) { 528 int32 startPosition = token.position; 529 530 if (token.type == TOKEN_IDENTIFIER) { 531 fTokenizer.NextToken(); 532 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); 543 } 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); 551 552 // might be chained with ";" 553 if (fTokenizer.NextToken().type != TOKEN_SEMICOLON) 554 break; 555 556 fTokenizer.SetCommandMode(false); 557 fTokenizer.NextToken(); 558 } 559 560 if (token.type != TOKEN_END_OF_LINE) 561 parse_exception("parse error", token.position); 562 563 return value; 564 } 565 566 567 status_t 568 ExpressionParser::ParseNextCommandArgument(const char** expressionString, 569 char* buffer, size_t bufferSize) 570 { 571 fTokenizer.SetTo(*expressionString); 572 fTokenizer.SetCommandMode(true); 573 574 if (fTokenizer.NextToken().type == TOKEN_END_OF_LINE) 575 return B_ENTRY_NOT_FOUND; 576 577 fTokenizer.RewindToken(); 578 579 char* argv[2]; 580 int argc = 0; 581 if (!_ParseArgument(argc, argv)) 582 return B_BAD_VALUE; 583 584 strlcpy(buffer, argv[0], bufferSize); 585 586 const Token& token = fTokenizer.NextToken(); 587 if (token.type == TOKEN_END_OF_LINE) 588 *expressionString = NULL; 589 else 590 *expressionString += token.position; 591 592 return B_OK; 593 } 594 595 596 uint64 597 ExpressionParser::_ParseExpression(bool expectAssignment) 598 { 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)); 604 605 int32 assignmentType = fTokenizer.NextToken().type; 606 if (assignmentType & TOKEN_ASSIGN_FLAG) { 607 // an assignment 608 uint64 rhs = _ParseExpression(); 609 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); 618 } 619 620 return rhs; 621 } 622 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); 629 } 630 631 uint64 variableValue = get_debug_variable(variable, 0); 632 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); 638 } 639 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; 661 } 662 663 set_debug_variable(variable, variableValue); 664 return variableValue; 665 } 666 } else if (token.type == TOKEN_STAR) { 667 void* address; 668 uint32 size; 669 uint64 value = _ParseDereference(&address, &size); 670 671 int32 assignmentType = fTokenizer.NextToken().type; 672 if (assignmentType & TOKEN_ASSIGN_FLAG) { 673 // an assignment 674 uint64 rhs = _ParseExpression(); 675 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); 681 } 682 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; 707 } 708 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; 724 } 725 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); 730 } 731 732 return value; 733 } 734 } 735 736 if (expectAssignment) { 737 parse_exception("expected assignment", 738 fTokenizer.CurrentToken().position); 739 } 740 741 // no assignment -- reset to the identifier position and parse a sum 742 fTokenizer.SetPosition(position); 743 return _ParseSum(false, 0); 744 } 745 746 747 uint64 748 ExpressionParser::_ParseCommandPipe(int& returnCode) 749 { 750 debugger_command_pipe* pipe = (debugger_command_pipe*)checked_malloc( 751 sizeof(debugger_command_pipe)); 752 753 pipe->segment_count = 0; 754 pipe->broken = false; 755 756 do { 757 if (pipe->segment_count >= MAX_DEBUGGER_COMMAND_PIPE_LENGTH) 758 parse_exception("Pipe too long", fTokenizer.NextToken().position); 759 760 debugger_command_pipe_segment& segment 761 = pipe->segments[pipe->segment_count]; 762 segment.index = pipe->segment_count++; 763 764 _ParseCommand(segment); 765 766 } while (fTokenizer.NextToken().type == TOKEN_PIPE); 767 768 fTokenizer.RewindToken(); 769 770 // invoke the pipe 771 returnCode = invoke_debugger_command_pipe(pipe); 772 773 debug_free(pipe); 774 775 return get_debug_variable("_", 0); 776 } 777 778 779 void 780 ExpressionParser::_ParseCommand(debugger_command_pipe_segment& segment) 781 { 782 fTokenizer.SetCommandMode(false); 783 const Token& token = _EatToken(TOKEN_IDENTIFIER); 784 fTokenizer.SetCommandMode(true); 785 786 bool ambiguous; 787 debugger_command* command = find_debugger_command(token.string, true, 788 ambiguous); 789 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); 800 } 801 802 parse_exception(sTempBuffer, -1); 803 } 804 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; 809 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; 818 } 819 } 820 821 if (segment.index > 0) { 822 if (argc >= kMaxArgumentCount) 823 parse_exception("too many arguments for command", 0); 824 else 825 argc++; 826 } 827 828 segment.command = command; 829 segment.argc = argc; 830 segment.argv = argv; 831 segment.invocations = 0; 832 } 833 834 835 bool 836 ExpressionParser::_ParseArgument(int& argc, char** argv) 837 { 838 const Token& token = fTokenizer.NextToken(); 839 switch (token.type) { 840 case TOKEN_OPENING_PARENTHESIS: 841 { 842 // this starts an expression 843 fTokenizer.SetCommandMode(false); 844 uint64 value = _ParseExpression(); 845 fTokenizer.SetCommandMode(true); 846 _EatToken(TOKEN_CLOSING_PARENTHESIS); 847 848 snprintf(sTempBuffer, sizeof(sTempBuffer), "%" B_PRIu64, value); 849 _AddArgument(argc, argv, sTempBuffer); 850 return true; 851 } 852 853 case TOKEN_OPENING_BRACKET: 854 { 855 // this starts a sub command 856 int returnValue; 857 uint64 value = _ParseCommandPipe(returnValue); 858 _EatToken(TOKEN_CLOSING_BRACKET); 859 860 snprintf(sTempBuffer, sizeof(sTempBuffer), "%" B_PRIu64, value); 861 _AddArgument(argc, argv, sTempBuffer); 862 return true; 863 } 864 865 case TOKEN_STRING: 866 case TOKEN_UNKNOWN: 867 _AddArgument(argc, argv, token.string); 868 return true; 869 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; 877 878 default: 879 { 880 snprintf(sTempBuffer, sizeof(sTempBuffer), "unexpected token " 881 "\"%s\"", token.string); 882 parse_exception(sTempBuffer, token.position); 883 return false; 884 } 885 } 886 } 887 888 889 void 890 ExpressionParser::_GetUnparsedArgument(int& argc, char** argv) 891 { 892 int32 startPosition = fTokenizer.NextToken().position; 893 fTokenizer.RewindToken(); 894 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; 928 } 929 } 930 931 int32 endPosition = fTokenizer.CurrentToken().position; 932 fTokenizer.RewindToken(); 933 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]); 940 941 if (!allSpaces) 942 _AddArgument(argc, argv, arg, argLen); 943 } 944 945 946 void 947 ExpressionParser::_AddArgument(int& argc, char** argv, const char* argument, 948 int32 length) 949 { 950 if (argc == kMaxArgumentCount) 951 parse_exception("too many arguments for command", 0); 952 953 if (length < 0) 954 length = strlen(argument); 955 length++; 956 char* buffer = (char*)checked_malloc(length); 957 strlcpy(buffer, argument, length); 958 959 argv[argc++] = buffer; 960 } 961 962 963 uint64 964 ExpressionParser::_ParseSum(bool useValue, uint64 value) 965 { 966 if (!useValue) 967 value = _ParseProduct(); 968 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; 978 979 default: 980 fTokenizer.RewindToken(); 981 return value; 982 } 983 } 984 } 985 986 987 uint64 988 ExpressionParser::_ParseProduct() 989 { 990 uint64 value = _ParseUnary(); 991 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; 1004 } 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; 1011 } 1012 1013 default: 1014 fTokenizer.RewindToken(); 1015 return value; 1016 } 1017 } 1018 } 1019 1020 1021 uint64 1022 ExpressionParser::_ParseUnary() 1023 { 1024 switch (fTokenizer.NextToken().type) { 1025 case TOKEN_MINUS: 1026 return -_ParseUnary(); 1027 1028 case TOKEN_STAR: 1029 return _ParseDereference(NULL, NULL); 1030 1031 default: 1032 fTokenizer.RewindToken(); 1033 return _ParseAtom(); 1034 } 1035 1036 return 0; 1037 } 1038 1039 1040 uint64 1041 ExpressionParser::_ParseDereference(void** _address, uint32* _size) 1042 { 1043 int32 starPosition = fTokenizer.CurrentToken().position; 1044 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(); 1050 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); 1055 } 1056 1057 _EatToken(TOKEN_CLOSING_BRACE); 1058 } else 1059 fTokenizer.RewindToken(); 1060 1061 const void* address = (const void*)(addr_t)_ParseUnary(); 1062 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); 1069 } 1070 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; 1086 } 1087 1088 if (_address != NULL) 1089 *_address = (void*)address; 1090 if (_size != NULL) 1091 *_size = size; 1092 1093 return value; 1094 } 1095 1096 1097 uint64 1098 ExpressionParser::_ParseAtom() 1099 { 1100 const Token& token = fTokenizer.NextToken(); 1101 if (token.type == TOKEN_END_OF_LINE) 1102 parse_exception("unexpected end of expression", token.position); 1103 1104 if (token.type == TOKEN_CONSTANT) 1105 return token.value; 1106 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); 1112 } 1113 1114 return get_debug_variable(token.string, 0); 1115 } 1116 1117 if (token.type == TOKEN_OPENING_PARENTHESIS) { 1118 uint64 value = _ParseExpression(); 1119 1120 _EatToken(TOKEN_CLOSING_PARENTHESIS); 1121 1122 return value; 1123 } 1124 1125 // it can only be a "[ command ]" expression now 1126 fTokenizer.RewindToken(); 1127 1128 _EatToken(TOKEN_OPENING_BRACKET); 1129 1130 fTokenizer.SetCommandMode(true); 1131 int returnValue; 1132 uint64 value = _ParseCommandPipe(returnValue); 1133 fTokenizer.SetCommandMode(false); 1134 1135 _EatToken(TOKEN_CLOSING_BRACKET); 1136 1137 return value; 1138 } 1139 1140 1141 const Token& 1142 ExpressionParser::_EatToken(int32 type) 1143 { 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); 1149 } 1150 1151 return token; 1152 } 1153 1154 1155 1156 // #pragma mark - 1157 1158 1159 bool 1160 evaluate_debug_expression(const char* expression, uint64* _result, bool silent) 1161 { 1162 if (sNextJumpBufferIndex >= kJumpBufferCount) { 1163 kprintf_unfiltered("evaluate_debug_expression(): Out of jump buffers " 1164 "for exception handling\n"); 1165 return 0; 1166 } 1167 1168 bool success; 1169 uint64 result; 1170 DebugAllocPoolScope allocPoolScope; 1171 // Will clean up all allocations when we return. 1172 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\n", sExceptionMessage); 1185 } 1186 } 1187 1188 sNextJumpBufferIndex--; 1189 1190 if (success && _result != NULL) 1191 *_result = result; 1192 1193 return success; 1194 } 1195 1196 1197 int 1198 evaluate_debug_command(const char* commandLine) 1199 { 1200 if (sNextJumpBufferIndex >= kJumpBufferCount) { 1201 kprintf_unfiltered("evaluate_debug_command(): Out of jump buffers for " 1202 "exception handling\n"); 1203 return 0; 1204 } 1205 1206 int returnCode = 0; 1207 DebugAllocPoolScope allocPoolScope; 1208 // Will clean up all allocations when we return. 1209 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\n", sExceptionMessage); 1218 } 1219 1220 sNextJumpBufferIndex--; 1221 1222 return returnCode; 1223 } 1224 1225 1226 status_t 1227 parse_next_debug_command_argument(const char** expressionString, char* buffer, 1228 size_t bufferSize) 1229 { 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; 1234 } 1235 1236 status_t error; 1237 DebugAllocPoolScope allocPoolScope; 1238 // Will clean up all allocations when we return. 1239 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\n", sExceptionMessage); 1249 error = B_BAD_VALUE; 1250 } 1251 1252 sNextJumpBufferIndex--; 1253 1254 return error; 1255 } 1256