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