VirtualBox

source: vbox/trunk/src/VBox/Debugger/DBGCEval.cpp@ 35694

Last change on this file since 35694 was 35637, checked in by vboxsync, 14 years ago

Debugger console: Made the evaluator a bit smarter wrt to the register and variable operators (e.g. don't confuse @ah with a hexadecimal number).

  • Property svn:eol-style set to native
  • Property svn:keywords set to Author Date Id Revision
File size: 38.2 KB
Line 
1/* $Id: DBGCEval.cpp 35637 2011-01-19 17:42:59Z vboxsync $ */
2/** @file
3 * DBGC - Debugger Console, command evaluator.
4 */
5
6/*
7 * Copyright (C) 2006-2011 Oracle Corporation
8 *
9 * This file is part of VirtualBox Open Source Edition (OSE), as
10 * available from http://www.virtualbox.org. This file is free software;
11 * you can redistribute it and/or modify it under the terms of the GNU
12 * General Public License (GPL) as published by the Free Software
13 * Foundation, in version 2 as it comes in the "COPYING" file of the
14 * VirtualBox OSE distribution. VirtualBox OSE is distributed in the
15 * hope that it will be useful, but WITHOUT ANY WARRANTY of any kind.
16 */
17
18/*******************************************************************************
19* Header Files *
20*******************************************************************************/
21#define LOG_GROUP LOG_GROUP_DBGC
22#include <VBox/dbg.h>
23#include <VBox/err.h>
24#include <VBox/log.h>
25
26#include <iprt/asm.h>
27#include <iprt/assert.h>
28#include <iprt/string.h>
29#include <iprt/ctype.h>
30
31#include "DBGCInternal.h"
32
33
34/*******************************************************************************
35* Global Variables *
36*******************************************************************************/
37/** Bitmap where set bits indicates the characters the may start an operator name. */
38static uint32_t g_bmOperatorChars[256 / (4*8)];
39
40
41
42/**
43 * Initializes g_bmOperatorChars.
44 */
45void dbgcEvalInit(void)
46{
47 memset(g_bmOperatorChars, 0, sizeof(g_bmOperatorChars));
48 for (unsigned iOp = 0; iOp < g_cOps; iOp++)
49 ASMBitSet(&g_bmOperatorChars[0], (uint8_t)g_aOps[iOp].szName[0]);
50}
51
52
53/**
54 * Checks whether the character may be the start of an operator.
55 *
56 * @returns true/false.
57 * @param ch The character.
58 */
59DECLINLINE(bool) dbgcIsOpChar(char ch)
60{
61 return ASMBitTest(&g_bmOperatorChars[0], (uint8_t)ch);
62}
63
64
65static int dbgcEvalSubString(PDBGC pDbgc, char *pszExpr, size_t cchExpr, PDBGCVAR pArg)
66{
67 Log2(("dbgcEvalSubString: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
68
69 /*
70 * Removing any quoting and escapings.
71 */
72 char ch = *pszExpr;
73 if (ch == '"' || ch == '\'' || ch == '`')
74 {
75 if (pszExpr[--cchExpr] != ch)
76 return VERR_PARSE_UNBALANCED_QUOTE;
77 cchExpr--;
78 pszExpr++;
79
80 /** @todo string unescaping. */
81 }
82 pszExpr[cchExpr] = '\0';
83
84 /*
85 * Make the argument.
86 */
87 pArg->pDesc = NULL;
88 pArg->pNext = NULL;
89 pArg->enmType = DBGCVAR_TYPE_STRING;
90 pArg->u.pszString = pszExpr;
91 pArg->enmRangeType = DBGCVAR_RANGE_BYTES;
92 pArg->u64Range = cchExpr;
93
94 NOREF(pDbgc);
95 return VINF_SUCCESS;
96}
97
98
99static int dbgcEvalSubNum(char *pszExpr, unsigned uBase, PDBGCVAR pArg)
100{
101 Log2(("dbgcEvalSubNum: uBase=%d pszExpr=%s\n", uBase, pszExpr));
102 /*
103 * Convert to number.
104 */
105 uint64_t u64 = 0;
106 char ch;
107 while ((ch = *pszExpr) != '\0')
108 {
109 uint64_t u64Prev = u64;
110 unsigned u = ch - '0';
111 if (u < 10 && u < uBase)
112 u64 = u64 * uBase + u;
113 else if (ch >= 'a' && (u = ch - ('a' - 10)) < uBase)
114 u64 = u64 * uBase + u;
115 else if (ch >= 'A' && (u = ch - ('A' - 10)) < uBase)
116 u64 = u64 * uBase + u;
117 else
118 return VERR_PARSE_INVALID_NUMBER;
119
120 /* check for overflow - ARG!!! How to detect overflow correctly!?!?!? */
121 if (u64Prev != u64 / uBase)
122 return VERR_PARSE_NUMBER_TOO_BIG;
123
124 /* next */
125 pszExpr++;
126 }
127
128 /*
129 * Initialize the argument.
130 */
131 pArg->pDesc = NULL;
132 pArg->pNext = NULL;
133 pArg->enmType = DBGCVAR_TYPE_NUMBER;
134 pArg->u.u64Number = u64;
135 pArg->enmRangeType = DBGCVAR_RANGE_NONE;
136 pArg->u64Range = 0;
137
138 return VINF_SUCCESS;
139}
140
141
142/**
143 * Match variable and variable descriptor, promoting the variable if necessary.
144 *
145 * @returns VBox status code.
146 * @param pDbgc Debug console instanace.
147 * @param pVar Variable.
148 * @param pVarDesc Variable descriptor.
149 */
150static int dbgcEvalSubMatchVar(PDBGC pDbgc, PDBGCVAR pVar, PCDBGCVARDESC pVarDesc)
151{
152 /*
153 * (If match or promoted to match, return, else break.)
154 */
155 switch (pVarDesc->enmCategory)
156 {
157 /*
158 * Anything goes
159 */
160 case DBGCVAR_CAT_ANY:
161 return VINF_SUCCESS;
162
163 /*
164 * Pointer with and without range.
165 * We can try resolve strings and symbols as symbols and promote
166 * numbers to flat GC pointers.
167 */
168 case DBGCVAR_CAT_POINTER_NO_RANGE:
169 case DBGCVAR_CAT_POINTER_NUMBER_NO_RANGE:
170 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
171 return VERR_PARSE_NO_RANGE_ALLOWED;
172 /* fallthru */
173 case DBGCVAR_CAT_POINTER:
174 case DBGCVAR_CAT_POINTER_NUMBER:
175 switch (pVar->enmType)
176 {
177 case DBGCVAR_TYPE_GC_FLAT:
178 case DBGCVAR_TYPE_GC_FAR:
179 case DBGCVAR_TYPE_GC_PHYS:
180 case DBGCVAR_TYPE_HC_FLAT:
181 case DBGCVAR_TYPE_HC_PHYS:
182 return VINF_SUCCESS;
183
184 case DBGCVAR_TYPE_SYMBOL:
185 case DBGCVAR_TYPE_STRING:
186 {
187 DBGCVAR Var;
188 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
189 if (RT_SUCCESS(rc))
190 {
191 /* deal with range */
192 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
193 {
194 Var.enmRangeType = pVar->enmRangeType;
195 Var.u64Range = pVar->u64Range;
196 }
197 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
198 Var.enmRangeType = DBGCVAR_RANGE_NONE;
199 *pVar = Var;
200 return rc;
201 }
202 break;
203 }
204
205 case DBGCVAR_TYPE_NUMBER:
206 if ( pVarDesc->enmCategory != DBGCVAR_CAT_POINTER_NUMBER
207 && pVarDesc->enmCategory != DBGCVAR_CAT_POINTER_NUMBER_NO_RANGE)
208 {
209 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;
210 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
211 pVar->u.GCFlat = GCPtr;
212 }
213 return VINF_SUCCESS;
214
215 default:
216 break;
217 }
218 break;
219
220 /*
221 * GC pointer with and without range.
222 * We can try resolve strings and symbols as symbols and
223 * promote numbers to flat GC pointers.
224 */
225 case DBGCVAR_CAT_GC_POINTER_NO_RANGE:
226 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
227 return VERR_PARSE_NO_RANGE_ALLOWED;
228 /* fallthru */
229 case DBGCVAR_CAT_GC_POINTER:
230 switch (pVar->enmType)
231 {
232 case DBGCVAR_TYPE_GC_FLAT:
233 case DBGCVAR_TYPE_GC_FAR:
234 case DBGCVAR_TYPE_GC_PHYS:
235 return VINF_SUCCESS;
236
237 case DBGCVAR_TYPE_HC_FLAT:
238 case DBGCVAR_TYPE_HC_PHYS:
239 return VERR_PARSE_CONVERSION_FAILED;
240
241 case DBGCVAR_TYPE_SYMBOL:
242 case DBGCVAR_TYPE_STRING:
243 {
244 DBGCVAR Var;
245 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_GC_FLAT, &Var);
246 if (RT_SUCCESS(rc))
247 {
248 /* deal with range */
249 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
250 {
251 Var.enmRangeType = pVar->enmRangeType;
252 Var.u64Range = pVar->u64Range;
253 }
254 else if (pVarDesc->enmCategory == DBGCVAR_CAT_POINTER_NO_RANGE)
255 Var.enmRangeType = DBGCVAR_RANGE_NONE;
256 *pVar = Var;
257 return rc;
258 }
259 break;
260 }
261
262 case DBGCVAR_TYPE_NUMBER:
263 {
264 RTGCPTR GCPtr = (RTGCPTR)pVar->u.u64Number;
265 pVar->enmType = DBGCVAR_TYPE_GC_FLAT;
266 pVar->u.GCFlat = GCPtr;
267 return VINF_SUCCESS;
268 }
269
270 default:
271 break;
272 }
273 break;
274
275 /*
276 * Number with or without a range.
277 * Numbers can be resolved from symbols, but we cannot demote a pointer
278 * to a number.
279 */
280 case DBGCVAR_CAT_NUMBER_NO_RANGE:
281 if (pVar->enmRangeType != DBGCVAR_RANGE_NONE)
282 return VERR_PARSE_NO_RANGE_ALLOWED;
283 /* fallthru */
284 case DBGCVAR_CAT_NUMBER:
285 switch (pVar->enmType)
286 {
287 case DBGCVAR_TYPE_NUMBER:
288 return VINF_SUCCESS;
289
290 case DBGCVAR_TYPE_SYMBOL:
291 case DBGCVAR_TYPE_STRING:
292 {
293 DBGCVAR Var;
294 int rc = dbgcSymbolGet(pDbgc, pVar->u.pszString, DBGCVAR_TYPE_NUMBER, &Var);
295 if (RT_SUCCESS(rc))
296 {
297 *pVar = Var;
298 return rc;
299 }
300 break;
301 }
302 default:
303 break;
304 }
305 break;
306
307 /*
308 * Strings can easily be made from symbols (and of course strings).
309 * We could consider reformatting the addresses and numbers into strings later...
310 */
311 case DBGCVAR_CAT_STRING:
312 switch (pVar->enmType)
313 {
314 case DBGCVAR_TYPE_SYMBOL:
315 pVar->enmType = DBGCVAR_TYPE_STRING;
316 /* fallthru */
317 case DBGCVAR_TYPE_STRING:
318 return VINF_SUCCESS;
319 default:
320 break;
321 }
322 break;
323
324 /*
325 * Symol is pretty much the same thing as a string (at least until we actually implement it).
326 */
327 case DBGCVAR_CAT_SYMBOL:
328 switch (pVar->enmType)
329 {
330 case DBGCVAR_TYPE_STRING:
331 pVar->enmType = DBGCVAR_TYPE_SYMBOL;
332 /* fallthru */
333 case DBGCVAR_TYPE_SYMBOL:
334 return VINF_SUCCESS;
335 default:
336 break;
337 }
338 break;
339
340 /*
341 * Anything else is illegal.
342 */
343 default:
344 AssertMsgFailed(("enmCategory=%d\n", pVar->enmType));
345 break;
346 }
347
348 return VERR_PARSE_NO_ARGUMENT_MATCH;
349}
350
351
352/**
353 * Matches a set of variables with a description set.
354 *
355 * This is typically used for routine arguments before a call. The effects in
356 * addition to the validation, is that some variables might be propagated to
357 * other types in order to match the description. The following transformations
358 * are supported:
359 * - String reinterpreted as a symbol and resolved to a number or pointer.
360 * - Number to a pointer.
361 * - Pointer to a number.
362 *
363 * @returns VBox status code. Modified @a paVars on success.
364 */
365static int dbgcEvalSubMatchVars(PDBGC pDbgc, unsigned cVarsMin, unsigned cVarsMax,
366 PCDBGCVARDESC paVarDescs, unsigned cVarDescs,
367 PDBGCVAR paVars, unsigned cVars)
368{
369 /*
370 * Just do basic min / max checks first.
371 */
372 if (cVars < cVarsMin)
373 return VERR_PARSE_TOO_FEW_ARGUMENTS;
374 if (cVars > cVarsMax)
375 return VERR_PARSE_TOO_MANY_ARGUMENTS;
376
377 /*
378 * Match the descriptors and actual variables.
379 */
380 PCDBGCVARDESC pPrevDesc = NULL;
381 unsigned cCurDesc = 0;
382 unsigned iVar = 0;
383 unsigned iVarDesc = 0;
384 while (iVar < cVars)
385 {
386 /* walk the descriptors */
387 if (iVarDesc >= cVarDescs)
388 return VERR_PARSE_TOO_MANY_ARGUMENTS;
389 if ( ( paVarDescs[iVarDesc].fFlags & DBGCVD_FLAGS_DEP_PREV
390 && &paVarDescs[iVarDesc - 1] != pPrevDesc)
391 || cCurDesc >= paVarDescs[iVarDesc].cTimesMax)
392 {
393 iVarDesc++;
394 if (iVarDesc >= cVarDescs)
395 return VERR_PARSE_TOO_MANY_ARGUMENTS;
396 cCurDesc = 0;
397 }
398
399 /*
400 * Skip thru optional arguments until we find something which matches
401 * or can easily be promoted to what the descriptor want.
402 */
403 for (;;)
404 {
405 int rc = dbgcEvalSubMatchVar(pDbgc, &paVars[iVar], &paVarDescs[iVarDesc]);
406 if (RT_SUCCESS(rc))
407 {
408 paVars[iVar].pDesc = &paVarDescs[iVarDesc];
409 cCurDesc++;
410 break;
411 }
412
413 /* can we advance? */
414 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
415 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;
416 if (++iVarDesc >= cVarDescs)
417 return VERR_PARSE_ARGUMENT_TYPE_MISMATCH;
418 cCurDesc = 0;
419 }
420
421 /* next var */
422 iVar++;
423 }
424
425 /*
426 * Check that the rest of the descriptors are optional.
427 */
428 while (iVarDesc < cVarDescs)
429 {
430 if (paVarDescs[iVarDesc].cTimesMin > cCurDesc)
431 return VERR_PARSE_TOO_FEW_ARGUMENTS;
432 cCurDesc = 0;
433
434 /* next */
435 iVarDesc++;
436 }
437
438 return VINF_SUCCESS;
439}
440
441
442/**
443 * Evaluates one argument with respect to unary operators.
444 *
445 * @returns VBox status code. pResult contains the result on success.
446 *
447 * @param pDbgc Debugger console instance data.
448 * @param pszExpr The expression string.
449 * @param cchExpr The length of the expression.
450 * @param enmCategory The target category for the result.
451 * @param pResult Where to store the result of the expression evaluation.
452 */
453static int dbgcEvalSubUnary(PDBGC pDbgc, char *pszExpr, size_t cchExpr, DBGCVARCAT enmCategory, PDBGCVAR pResult)
454{
455 Log2(("dbgcEvalSubUnary: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
456
457 /*
458 * The state of the expression is now such that it will start by zero or more
459 * unary operators and being followed by an expression of some kind.
460 * The expression is either plain or in parenthesis.
461 *
462 * Being in a lazy, recursive mode today, the parsing is done as simple as possible. :-)
463 * ASSUME: unary operators are all of equal precedence.
464 */
465 int rc = VINF_SUCCESS;
466 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, pszExpr, false, ' ');
467 if (pOp)
468 {
469 /* binary operators means syntax error. */
470 if (pOp->fBinary)
471 return VERR_PARSE_UNEXPECTED_OPERATOR;
472
473 /*
474 * If the next expression (the one following the unary operator) is in a
475 * parenthesis a full eval is needed. If not the unary eval will suffice.
476 */
477 /* calc and strip next expr. */
478 char *pszExpr2 = pszExpr + pOp->cchName;
479 while (RT_C_IS_BLANK(*pszExpr2))
480 pszExpr2++;
481
482 if (!*pszExpr2)
483 rc = VERR_PARSE_EMPTY_ARGUMENT;
484 else
485 {
486 DBGCVAR Arg;
487 if (*pszExpr2 == '(')
488 rc = dbgcEvalSub(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), pOp->enmCatArg1, &Arg);
489 else
490 rc = dbgcEvalSubUnary(pDbgc, pszExpr2, cchExpr - (pszExpr2 - pszExpr), pOp->enmCatArg1, &Arg);
491 if (RT_SUCCESS(rc))
492 rc = pOp->pfnHandlerUnary(pDbgc, &Arg, pResult);
493 }
494 }
495 else
496 {
497 /*
498 * Didn't find any operators, so it we have to check if this can be an
499 * function call before assuming numeric or string expression.
500 *
501 * (ASSUMPTIONS:)
502 * A function name only contains alphanumerical chars and it can not start
503 * with a numerical character.
504 * Immediately following the name is a parenthesis which must over
505 * the remaining part of the expression.
506 */
507 bool fExternal = *pszExpr == '.';
508 char *pszFun = fExternal ? pszExpr + 1 : pszExpr;
509 char *pszFunEnd = NULL;
510 if (pszExpr[cchExpr - 1] == ')' && RT_C_IS_ALPHA(*pszFun))
511 {
512 pszFunEnd = pszExpr + 1;
513 while (*pszFunEnd != '(' && RT_C_IS_ALNUM(*pszFunEnd))
514 pszFunEnd++;
515 if (*pszFunEnd != '(')
516 pszFunEnd = NULL;
517 }
518
519 if (pszFunEnd)
520 {
521 /*
522 * Ok, it's a function call.
523 */
524 if (fExternal)
525 pszExpr++, cchExpr--;
526 PCDBGCCMD pFun = dbgcRoutineLookup(pDbgc, pszExpr, pszFunEnd - pszExpr, fExternal);
527 if (!pFun)
528 return VERR_PARSE_FUNCTION_NOT_FOUND;
529 if (!pFun->pResultDesc)
530 return VERR_PARSE_NOT_A_FUNCTION;
531
532 /*
533 * Parse the expression in parenthesis.
534 */
535 cchExpr -= pszFunEnd - pszExpr;
536 pszExpr = pszFunEnd;
537 /** @todo implement multiple arguments. */
538 DBGCVAR Arg;
539 rc = dbgcEvalSub(pDbgc, pszExpr, cchExpr, enmCategory, &Arg);
540 if (!rc)
541 {
542 rc = dbgcEvalSubMatchVars(pDbgc, pFun->cArgsMin, pFun->cArgsMax, pFun->paArgDescs, pFun->cArgDescs, &Arg, 1);
543 if (!rc)
544 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, &Arg, 1, pResult);
545 }
546 else if (rc == VERR_PARSE_EMPTY_ARGUMENT && pFun->cArgsMin == 0)
547 rc = pFun->pfnHandler(pFun, &pDbgc->CmdHlp, pDbgc->pVM, NULL, 0, pResult);
548 }
549 else if ( enmCategory == DBGCVAR_CAT_STRING
550 || enmCategory == DBGCVAR_CAT_SYMBOL)
551 rc = dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
552 else
553 {
554 /*
555 * Didn't find any operators, so it must be a plain expression.
556 * This might be numeric or a string expression.
557 */
558 char ch = pszExpr[0];
559 char ch2 = pszExpr[1];
560 if (ch == '0' && (ch2 == 'x' || ch2 == 'X'))
561 rc = dbgcEvalSubNum(pszExpr + 2, 16, pResult);
562 else if (ch == '0' && (ch2 == 'i' || ch2 == 'i'))
563 rc = dbgcEvalSubNum(pszExpr + 2, 10, pResult);
564 else if (ch == '0' && (ch2 == 't' || ch2 == 'T'))
565 rc = dbgcEvalSubNum(pszExpr + 2, 8, pResult);
566 /// @todo 0b doesn't work as a binary prefix, we confuse it with 0bf8:0123 and stuff.
567 //else if (ch == '0' && (ch2 == 'b' || ch2 == 'b'))
568 // rc = dbgcEvalSubNum(pszExpr + 2, 2, pResult);
569 else
570 {
571 /*
572 * Hexadecimal number or a string?
573 */
574 char *psz = pszExpr;
575 while (RT_C_IS_XDIGIT(*psz))
576 psz++;
577 if (!*psz)
578 rc = dbgcEvalSubNum(pszExpr, 16, pResult);
579 else if ((*psz == 'h' || *psz == 'H') && !psz[1])
580 {
581 *psz = '\0';
582 rc = dbgcEvalSubNum(pszExpr, 16, pResult);
583 }
584 else
585 rc = dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
586 }
587 }
588 }
589
590 return rc;
591}
592
593
594/**
595 * Evaluates one argument.
596 *
597 * @returns VBox status code.
598 *
599 * @param pDbgc Debugger console instance data.
600 * @param pszExpr The expression string.
601 * @param enmCategory The target category for the result.
602 * @param pResult Where to store the result of the expression evaluation.
603 */
604int dbgcEvalSub(PDBGC pDbgc, char *pszExpr, size_t cchExpr, DBGCVARCAT enmCategory, PDBGCVAR pResult)
605{
606 Log2(("dbgcEvalSub: cchExpr=%d pszExpr=%s\n", cchExpr, pszExpr));
607 /*
608 * First we need to remove blanks in both ends.
609 * ASSUMES: There is no quoting unless the entire expression is a string.
610 */
611
612 /* stripping. */
613 while (cchExpr > 0 && RT_C_IS_BLANK(pszExpr[cchExpr - 1]))
614 pszExpr[--cchExpr] = '\0';
615 while (RT_C_IS_BLANK(*pszExpr))
616 pszExpr++, cchExpr--;
617 if (!*pszExpr)
618 return VERR_PARSE_EMPTY_ARGUMENT;
619
620 /* it there is any kind of quoting in the expression, it's string meat. */
621 if (strpbrk(pszExpr, "\"'`"))
622 return dbgcEvalSubString(pDbgc, pszExpr, cchExpr, pResult);
623
624 /*
625 * Check if there are any parenthesis which needs removing.
626 */
627 if (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')')
628 {
629 do
630 {
631 unsigned cPar = 1;
632 char *psz = pszExpr + 1;
633 char ch;
634 while ((ch = *psz) != '\0')
635 {
636 if (ch == '(')
637 cPar++;
638 else if (ch == ')')
639 {
640 if (cPar <= 0)
641 return VERR_PARSE_UNBALANCED_PARENTHESIS;
642 cPar--;
643 if (cPar == 0 && psz[1]) /* If not at end, there's nothing to do. */
644 break;
645 }
646 /* next */
647 psz++;
648 }
649 if (ch)
650 break;
651
652 /* remove the parenthesis. */
653 pszExpr++;
654 cchExpr -= 2;
655 pszExpr[cchExpr] = '\0';
656
657 /* strip blanks. */
658 while (cchExpr > 0 && RT_C_IS_BLANK(pszExpr[cchExpr - 1]))
659 pszExpr[--cchExpr] = '\0';
660 while (RT_C_IS_BLANK(*pszExpr))
661 pszExpr++, cchExpr--;
662 if (!*pszExpr)
663 return VERR_PARSE_EMPTY_ARGUMENT;
664 } while (pszExpr[0] == '(' && pszExpr[cchExpr - 1] == ')');
665 }
666
667 /* tabs to spaces. */
668 char *psz = pszExpr;
669 while ((psz = strchr(psz, '\t')) != NULL)
670 *psz = ' ';
671
672 /*
673 * Now, we need to look for the binary operator with the lowest precedence.
674 *
675 * If there are no operators we're left with a simple expression which we
676 * evaluate with respect to unary operators
677 */
678 char *pszOpSplit = NULL;
679 PCDBGCOP pOpSplit = NULL;
680 unsigned cBinaryOps = 0;
681 unsigned cPar = 0;
682 char ch;
683 char chPrev = ' ';
684 bool fBinary = false;
685 psz = pszExpr;
686
687 while ((ch = *psz) != '\0')
688 {
689 //Log2(("ch=%c cPar=%d fBinary=%d\n", ch, cPar, fBinary));
690 /*
691 * Parenthesis.
692 */
693 if (ch == '(')
694 {
695 cPar++;
696 fBinary = false;
697 }
698 else if (ch == ')')
699 {
700 if (cPar <= 0)
701 return VERR_PARSE_UNBALANCED_PARENTHESIS;
702 cPar--;
703 fBinary = true;
704 }
705 /*
706 * Potential operator.
707 */
708 else if (cPar == 0 && !RT_C_IS_BLANK(ch))
709 {
710 PCDBGCOP pOp = dbgcIsOpChar(ch)
711 ? dbgcOperatorLookup(pDbgc, psz, fBinary, chPrev)
712 : NULL;
713 if (pOp)
714 {
715 /* If not the right kind of operator we've got a syntax error. */
716 if (pOp->fBinary != fBinary)
717 return VERR_PARSE_UNEXPECTED_OPERATOR;
718
719 /*
720 * Update the parse state and skip the operator.
721 */
722 if (!pOpSplit)
723 {
724 pOpSplit = pOp;
725 pszOpSplit = psz;
726 cBinaryOps = fBinary;
727 }
728 else if (fBinary)
729 {
730 cBinaryOps++;
731 if (pOp->iPrecedence >= pOpSplit->iPrecedence)
732 {
733 pOpSplit = pOp;
734 pszOpSplit = psz;
735 }
736 }
737
738 psz += pOp->cchName - 1;
739 fBinary = false;
740 }
741 else
742 fBinary = true;
743 }
744
745 /* next */
746 psz++;
747 chPrev = ch;
748 } /* parse loop. */
749
750
751 /*
752 * Either we found an operator to divide the expression by
753 * or we didn't find any. In the first case it's divide and
754 * conquer. In the latter it's a single expression which
755 * needs dealing with its unary operators if any.
756 */
757 int rc;
758 if ( cBinaryOps
759 && pOpSplit->fBinary)
760 {
761 /* process 1st sub expression. */
762 *pszOpSplit = '\0';
763 DBGCVAR Arg1;
764 rc = dbgcEvalSub(pDbgc, pszExpr, pszOpSplit - pszExpr, pOpSplit->enmCatArg1, &Arg1);
765 if (RT_SUCCESS(rc))
766 {
767 /* process 2nd sub expression. */
768 char *psz2 = pszOpSplit + pOpSplit->cchName;
769 DBGCVAR Arg2;
770 rc = dbgcEvalSub(pDbgc, psz2, cchExpr - (psz2 - pszExpr), pOpSplit->enmCatArg2, &Arg2);
771 if (RT_SUCCESS(rc))
772 /* apply the operator. */
773 rc = pOpSplit->pfnHandlerBinary(pDbgc, &Arg1, &Arg2, pResult);
774 }
775 }
776 else if (cBinaryOps)
777 {
778 /* process sub expression. */
779 pszOpSplit += pOpSplit->cchName;
780 DBGCVAR Arg;
781 rc = dbgcEvalSub(pDbgc, pszOpSplit, cchExpr - (pszOpSplit - pszExpr), pOpSplit->enmCatArg1, &Arg);
782 if (RT_SUCCESS(rc))
783 /* apply the operator. */
784 rc = pOpSplit->pfnHandlerUnary(pDbgc, &Arg, pResult);
785 }
786 else
787 /* plain expression or using unary operators perhaps with parentheses. */
788 rc = dbgcEvalSubUnary(pDbgc, pszExpr, cchExpr, enmCategory, pResult);
789
790 return rc;
791}
792
793
794/**
795 * Parses the arguments of one command.
796 *
797 * @returns VBox statuc code. On parser errors the index of the troublesome
798 * argument is indicated by *pcArg.
799 *
800 * @param pDbgc Debugger console instance data.
801 * @param pCmd Pointer to the command descriptor.
802 * @param pszArg Pointer to the arguments to parse.
803 * @param paArgs Where to store the parsed arguments.
804 * @param cArgs Size of the paArgs array.
805 * @param pcArgs Where to store the number of arguments. In the event
806 * of an error this is (ab)used to store the index of the
807 * offending argument.
808 */
809static int dbgcProcessArguments(PDBGC pDbgc, PCDBGCCMD pCmd, char *pszArgs, PDBGCVAR paArgs, unsigned cArgs, unsigned *pcArgs)
810{
811 Log2(("dbgcProcessArguments: pCmd=%s pszArgs='%s'\n", pCmd->pszCmd, pszArgs));
812
813 /*
814 * Check if we have any argument and if the command takes any.
815 */
816 *pcArgs = 0;
817 /* strip leading blanks. */
818 while (*pszArgs && RT_C_IS_BLANK(*pszArgs))
819 pszArgs++;
820 if (!*pszArgs)
821 {
822 if (!pCmd->cArgsMin)
823 return VINF_SUCCESS;
824 return VERR_PARSE_TOO_FEW_ARGUMENTS;
825 }
826 /** @todo fixme - foo() doesn't work. */
827 if (!pCmd->cArgsMax)
828 return VERR_PARSE_TOO_MANY_ARGUMENTS;
829
830 /*
831 * This is a hack, it's "temporary" and should go away "when" the parser is
832 * modified to match arguments while parsing.
833 */
834 if ( pCmd->cArgsMax == 1
835 && pCmd->cArgsMin == 1
836 && pCmd->cArgDescs == 1
837 && ( pCmd->paArgDescs[0].enmCategory == DBGCVAR_CAT_STRING
838 || pCmd->paArgDescs[0].enmCategory == DBGCVAR_CAT_SYMBOL)
839 && cArgs >= 1)
840 {
841 *pcArgs = 1;
842 RTStrStripR(pszArgs);
843 return dbgcEvalSubString(pDbgc, pszArgs, strlen(pszArgs), &paArgs[0]);
844 }
845
846 /*
847 * The parse loop.
848 */
849 PDBGCVAR pArg0 = &paArgs[0];
850 PDBGCVAR pArg = pArg0;
851 *pcArgs = 0;
852 do
853 {
854 /*
855 * Can we have another argument?
856 */
857 if (*pcArgs >= pCmd->cArgsMax)
858 return VERR_PARSE_TOO_MANY_ARGUMENTS;
859 if (pArg >= &paArgs[cArgs])
860 return VERR_PARSE_ARGUMENT_OVERFLOW;
861
862 /*
863 * Find the end of the argument.
864 */
865 int cPar = 0;
866 char chQuote = '\0';
867 char *pszEnd = NULL;
868 char *psz = pszArgs;
869 char ch;
870 bool fBinary = false;
871 for (;;)
872 {
873 /*
874 * Check for the end.
875 */
876 if ((ch = *psz) == '\0')
877 {
878 if (chQuote)
879 return VERR_PARSE_UNBALANCED_QUOTE;
880 if (cPar)
881 return VERR_PARSE_UNBALANCED_PARENTHESIS;
882 pszEnd = psz;
883 break;
884 }
885 /*
886 * When quoted we ignore everything but the quotation char.
887 * We use the REXX way of escaping the quotation char, i.e. double occurrence.
888 */
889 else if (ch == '\'' || ch == '"' || ch == '`')
890 {
891 if (chQuote)
892 {
893 /* end quote? */
894 if (ch == chQuote)
895 {
896 if (psz[1] == ch)
897 psz++; /* skip the escaped quote char */
898 else
899 chQuote = '\0'; /* end of quoted string. */
900 }
901 }
902 else
903 chQuote = ch; /* open new quote */
904 }
905 /*
906 * Parenthesis can of course be nested.
907 */
908 else if (ch == '(')
909 {
910 cPar++;
911 fBinary = false;
912 }
913 else if (ch == ')')
914 {
915 if (!cPar)
916 return VERR_PARSE_UNBALANCED_PARENTHESIS;
917 cPar--;
918 fBinary = true;
919 }
920 else if (!chQuote && !cPar)
921 {
922 /*
923 * Encountering blanks may mean the end of it all. A binary operator
924 * will force continued parsing.
925 */
926 if (RT_C_IS_BLANK(*psz))
927 {
928 pszEnd = psz++; /* just in case. */
929 while (RT_C_IS_BLANK(*psz))
930 psz++;
931 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
932 if (!pOp || pOp->fBinary != fBinary)
933 break; /* the end. */
934 psz += pOp->cchName;
935 while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */
936 psz++;
937 fBinary = false;
938 continue;
939 }
940
941 /*
942 * Look for operators without a space up front.
943 */
944 if (dbgcIsOpChar(*psz))
945 {
946 PCDBGCOP pOp = dbgcOperatorLookup(pDbgc, psz, fBinary, ' ');
947 if (pOp)
948 {
949 if (pOp->fBinary != fBinary)
950 {
951 pszEnd = psz;
952 /** @todo this is a parsing error really. */
953 break; /* the end. */
954 }
955 psz += pOp->cchName;
956 while (RT_C_IS_BLANK(*psz)) /* skip blanks so we don't get here again */
957 psz++;
958 fBinary = false;
959 continue;
960 }
961 }
962 fBinary = true;
963 }
964
965 /* next char */
966 psz++;
967 }
968 *pszEnd = '\0';
969 /* (psz = next char to process) */
970
971 /*
972 * Parse and evaluate the argument.
973 */
974 int rc = dbgcEvalSub(pDbgc, pszArgs, strlen(pszArgs), DBGCVAR_CAT_ANY, pArg);
975 if (RT_FAILURE(rc))
976 return rc;
977
978 /*
979 * Next.
980 */
981 pArg++;
982 (*pcArgs)++;
983 pszArgs = psz;
984 while (*pszArgs && RT_C_IS_BLANK(*pszArgs))
985 pszArgs++;
986 } while (*pszArgs);
987
988 /*
989 * Match the arguments.
990 */
991 return dbgcEvalSubMatchVars(pDbgc, pCmd->cArgsMin, pCmd->cArgsMax, pCmd->paArgDescs, pCmd->cArgDescs, pArg0, pArg - pArg0);
992}
993
994
995/**
996 * Evaluate one command.
997 *
998 * @returns VBox status code. This is also stored in DBGC::rcCmd.
999 *
1000 * @param pDbgc Debugger console instance data.
1001 * @param pszCmd Pointer to the command.
1002 * @param cchCmd Length of the command.
1003 * @param fNoExecute Indicates that no commands should actually be executed.
1004 *
1005 * @todo Change pszCmd into argc/argv?
1006 */
1007int dbgcEvalCommand(PDBGC pDbgc, char *pszCmd, size_t cchCmd, bool fNoExecute)
1008{
1009 char *pszCmdInput = pszCmd;
1010
1011 /*
1012 * Skip blanks.
1013 */
1014 while (RT_C_IS_BLANK(*pszCmd))
1015 pszCmd++, cchCmd--;
1016
1017 /* external command? */
1018 bool const fExternal = *pszCmd == '.';
1019 if (fExternal)
1020 pszCmd++, cchCmd--;
1021
1022 /*
1023 * Find arguments.
1024 */
1025 char *pszArgs = pszCmd;
1026 while (RT_C_IS_ALNUM(*pszArgs))
1027 pszArgs++;
1028 if (*pszArgs && (!RT_C_IS_BLANK(*pszArgs) || pszArgs == pszCmd))
1029 {
1030 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Syntax error: Invalid command '%s'!\n", pszCmdInput);
1031 return pDbgc->rcCmd = VINF_PARSE_INVALD_COMMAND_NAME;
1032 }
1033
1034 /*
1035 * Find the command.
1036 */
1037 PCDBGCCMD pCmd = dbgcRoutineLookup(pDbgc, pszCmd, pszArgs - pszCmd, fExternal);
1038 if (!pCmd || (pCmd->fFlags & DBGCCMD_FLAGS_FUNCTION))
1039 {
1040 DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Syntax error: Unknown command '%s'!\n", pszCmdInput);
1041 return pDbgc->rcCmd = VINF_PARSE_COMMAND_NOT_FOUND;
1042 }
1043
1044 /*
1045 * Parse arguments (if any).
1046 */
1047 unsigned cArgs;
1048 int rc = dbgcProcessArguments(pDbgc, pCmd, pszArgs, &pDbgc->aArgs[pDbgc->iArg], RT_ELEMENTS(pDbgc->aArgs) - pDbgc->iArg, &cArgs);
1049 if (RT_SUCCESS(rc))
1050 {
1051 AssertMsg(rc == VINF_SUCCESS, ("%Rrc\n", rc));
1052
1053 /*
1054 * Execute the command.
1055 */
1056 if (!fNoExecute)
1057 rc = pCmd->pfnHandler(pCmd, &pDbgc->CmdHlp, pDbgc->pVM, &pDbgc->aArgs[0], cArgs, NULL);
1058 pDbgc->rcCmd = rc;
1059 if (rc == VERR_DBGC_COMMAND_FAILED)
1060 rc = VINF_SUCCESS;
1061 }
1062 else
1063 {
1064 pDbgc->rcCmd = rc;
1065
1066 /* report parse / eval error. */
1067 switch (rc)
1068 {
1069 case VERR_PARSE_TOO_FEW_ARGUMENTS:
1070 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1071 "Syntax error: Too few arguments. Minimum is %d for command '%s'.\n", pCmd->cArgsMin, pCmd->pszCmd);
1072 break;
1073 case VERR_PARSE_TOO_MANY_ARGUMENTS:
1074 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1075 "Syntax error: Too many arguments. Maximum is %d for command '%s'.\n", pCmd->cArgsMax, pCmd->pszCmd);
1076 break;
1077 case VERR_PARSE_ARGUMENT_OVERFLOW:
1078 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1079 "Syntax error: Too many arguments.\n");
1080 break;
1081 case VERR_PARSE_UNBALANCED_QUOTE:
1082 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1083 "Syntax error: Unbalanced quote (argument %d).\n", cArgs);
1084 break;
1085 case VERR_PARSE_UNBALANCED_PARENTHESIS:
1086 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1087 "Syntax error: Unbalanced parenthesis (argument %d).\n", cArgs);
1088 break;
1089 case VERR_PARSE_EMPTY_ARGUMENT:
1090 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1091 "Syntax error: An argument or subargument contains nothing useful (argument %d).\n", cArgs);
1092 break;
1093 case VERR_PARSE_UNEXPECTED_OPERATOR:
1094 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1095 "Syntax error: Invalid operator usage (argument %d).\n", cArgs);
1096 break;
1097 case VERR_PARSE_INVALID_NUMBER:
1098 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1099 "Syntax error: Ivalid numeric value (argument %d). If a string was the intention, then quote it.\n", cArgs);
1100 break;
1101 case VERR_PARSE_NUMBER_TOO_BIG:
1102 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1103 "Error: Numeric overflow (argument %d).\n", cArgs);
1104 break;
1105 case VERR_PARSE_INVALID_OPERATION:
1106 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1107 "Error: Invalid operation attempted (argument %d).\n", cArgs);
1108 break;
1109 case VERR_PARSE_FUNCTION_NOT_FOUND:
1110 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1111 "Error: Function not found (argument %d).\n", cArgs);
1112 break;
1113 case VERR_PARSE_NOT_A_FUNCTION:
1114 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1115 "Error: The function specified is not a function (argument %d).\n", cArgs);
1116 break;
1117 case VERR_PARSE_NO_MEMORY:
1118 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1119 "Error: Out memory in the regular heap! Expect odd stuff to happen...\n", cArgs);
1120 break;
1121 case VERR_PARSE_INCORRECT_ARG_TYPE:
1122 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1123 "Error: Incorrect argument type (argument %d?).\n", cArgs);
1124 break;
1125 case VERR_PARSE_VARIABLE_NOT_FOUND:
1126 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1127 "Error: An undefined variable was referenced (argument %d).\n", cArgs);
1128 break;
1129 case VERR_PARSE_CONVERSION_FAILED:
1130 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1131 "Error: A conversion between two types failed (argument %d).\n", cArgs);
1132 break;
1133 case VERR_PARSE_NOT_IMPLEMENTED:
1134 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1135 "Error: You hit a debugger feature which isn't implemented yet (argument %d).\n", cArgs);
1136 break;
1137 case VERR_PARSE_BAD_RESULT_TYPE:
1138 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1139 "Error: Couldn't satisfy a request for a specific result type (argument %d). (Usually applies to symbols)\n", cArgs);
1140 break;
1141 case VERR_PARSE_WRITEONLY_SYMBOL:
1142 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp,
1143 "Error: Cannot get symbol, it's set only (argument %d).\n", cArgs);
1144 break;
1145
1146 case VERR_DBGC_COMMAND_FAILED:
1147 break;
1148
1149 default:
1150 rc = DBGCCmdHlpPrintf(&pDbgc->CmdHlp, "Error: Unknown error %d!\n", rc);
1151 break;
1152 }
1153 }
1154 return rc;
1155}
1156
Note: See TracBrowser for help on using the repository browser.

© 2024 Oracle Support Privacy / Do Not Sell My Info Terms of Use Trademark Policy Automated Access Etiquette