xref: /haiku/src/system/kernel/debug/debug_parser.cpp (revision e1c4049fed1047bdb957b0529e1921e97ef94770)
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