VirtualBox

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

Last change on this file since 41546 was 41546, checked in by vboxsync, 13 years ago

Debugger hacking.

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

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