| /****************************************************************************** | |
| * Copyright (c) 2005 The Regents of the University of California. | |
| * This material was produced under U.S. Government contract W-7405-ENG-36 | |
| * for Los Alamos National Laboratory, which is operated by the University | |
| * of California for the U.S. Department of Energy. The U.S. Government has | |
| * rights to use, reproduce, and distribute this software. NEITHER THE | |
| * GOVERNMENT NOR THE UNIVERSITY MAKES ANY WARRANTY, EXPRESS OR IMPLIED, OR | |
| * ASSUMES ANY LIABILITY FOR THE USE OF THIS SOFTWARE. If software is modified | |
| * to produce derivative works, such modified software should be clearly | |
| * marked, so as not to confuse it with the version available from LANL. | |
| * | |
| * Additionally, this program and the accompanying materials | |
| * are made available under the terms of the Eclipse Public License v1.0 | |
| * which accompanies this distribution, and is available at | |
| * http://www.eclipse.org/legal/epl-v10.html | |
| * | |
| * LA-CC 04-115 | |
| ******************************************************************************/ | |
| #ifdef __gnu_linux__ | |
| #define _GNU_SOURCE | |
| #endif /* __gnu_linux__ */ | |
| #include "config.h" | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <ctype.h> | |
| #include "gdb.h" | |
| #include "dbg.h" | |
| #include "dbg_error.h" | |
| static int _address_length = 0; | |
| static float _gdb_version = 0.0; | |
| /* | |
| * Send command and wait for a response. | |
| */ | |
| void | |
| SendCommandWait(MISession *session, MICommand *cmd) | |
| { | |
| MISessionSendCommand(session, cmd); | |
| do { | |
| MISessionProgress(session); | |
| if (session->out_fd == -1) { | |
| DEBUG_PRINTS(DEBUG_LEVEL_BACKEND, "------------------- SendCommandWait sess->out_fd = -1\n"); | |
| break; | |
| } | |
| } while (!MICommandCompleted(cmd)); | |
| } | |
| /* | |
| * If this is a refence type, then remove the trailing '&'. This | |
| * is required because 'ptype' is not able to deal with '&' in | |
| * an expression. | |
| */ | |
| static void | |
| FixReferenceType(char *type) | |
| { | |
| char * t = &type[strlen(type)-1]; | |
| while (t >= type) { | |
| if (isspace(*t)) { | |
| *t-- = '\0'; | |
| continue; | |
| } else if (*t == '&') { | |
| *t = '\0'; | |
| } | |
| break; | |
| } | |
| } | |
| /* | |
| * Create a new MI variable and corresponding MIVar object using | |
| * the supplied expression 'expr'. Sets the 'exp' field to the | |
| * expression if it is not already set. | |
| * Returns NULL if the variable can't be created. | |
| */ | |
| MIVar * | |
| CreateMIVar(MISession *session, char *expr) | |
| { | |
| MICommand *cmd; | |
| MIVar *mivar; | |
| cmd = MIVarCreate("-", "*", expr); | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| //DbgSetError(DBGERR_UNKNOWN_VARIABLE, GetLastErrorStr()); | |
| MICommandFree(cmd); | |
| return NULL; | |
| } | |
| mivar = MIGetVarCreateInfo(cmd); | |
| if (mivar->exp == NULL) { | |
| mivar->exp = strdup(expr); | |
| } | |
| FixReferenceType(mivar->type); | |
| MICommandFree(cmd); | |
| return mivar; | |
| } | |
| void | |
| DeleteMIVar(MISession *session, char *mi_name) | |
| { | |
| MICommand *cmd; | |
| cmd = MIVarDelete(mi_name); | |
| SendCommandWait(session, cmd); | |
| MICommandFree(cmd); | |
| } | |
| stackframe * | |
| GetCurrentFrame(MISession *session) | |
| { | |
| stackframe *f; | |
| List * frames; | |
| if (GetStackframes(session, 1, 0, 0, &frames) != DBGRES_OK) | |
| return NULL; | |
| if (EmptyList(frames)) { | |
| DbgSetError(DBGERR_DEBUGGER, "Could not get current stack frame"); | |
| return NULL; | |
| } | |
| SetList(frames); | |
| f = (stackframe *)GetListElement(frames); | |
| DestroyList(frames, NULL); | |
| return f; | |
| } | |
| stackframe * | |
| ConvertMIFrame(MIFrame *f) | |
| { | |
| stackframe * s = NewStackframe(f->level); | |
| if ( f->addr != NULL ) | |
| s->loc.addr = strdup(f->addr); | |
| if ( f->func != NULL ) | |
| s->loc.func = strdup(f->func); | |
| if ( f->file != NULL ) | |
| s->loc.file = strdup(f->file); | |
| s->loc.line = f->line; | |
| return s; | |
| } | |
| breakpoint * | |
| ConvertMIBreakpoint(MIBreakpoint *bpt) | |
| { | |
| breakpoint * bp = NewBreakpoint(bpt->number); | |
| bp->ignore = bpt->ignore; | |
| bp->type = strdup(bpt->type); | |
| bp->hits = bpt->times; | |
| if ( bpt->file != NULL ) | |
| bp->loc.file = strdup(bpt->file); | |
| if ( bpt->func != NULL ) | |
| bp->loc.func = strdup(bpt->func); | |
| if ( bpt->address != NULL ) | |
| bp->loc.addr = strdup(bpt->address); | |
| bp->loc.line = bpt->line; | |
| return bp; | |
| } | |
| List * | |
| GetChangedVariables(MISession *session) | |
| { | |
| MICommand *cmd; | |
| MIList *changes; | |
| List *changedVars; | |
| MIVarChange *var; | |
| cmd = MIVarUpdate("*"); | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| DEBUG_PRINTS(DEBUG_LEVEL_BACKEND, "------------------- GetChangedVariables error\n"); | |
| SetDebugError(cmd); | |
| MICommandFree(cmd); | |
| return NULL; | |
| } | |
| MIGetVarUpdateInfo(cmd, &changes); | |
| MICommandFree(cmd); | |
| changedVars = NewList(); | |
| for (MIListSet(changes); (var = (MIVarChange *)MIListGet(changes)) != NULL;) { | |
| if (var->in_scope == 1) { | |
| AddToList(changedVars, (void *)strdup(var->name)); | |
| } | |
| else { | |
| DeleteMIVar(session, var->name); | |
| } | |
| } | |
| MIListFree(changes, MIVarChangeFree); | |
| return changedVars; | |
| } | |
| int | |
| GetMIInfoDepth(MISession *session) | |
| { | |
| MICommand *cmd; | |
| int depth; | |
| cmd = MIStackInfoDepth(); | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| DEBUG_PRINTS(DEBUG_LEVEL_BACKEND, "------------------- GDBMIStackInfoDepth error\n"); | |
| SetDebugError(cmd); | |
| MICommandFree(cmd); | |
| return -1; | |
| } | |
| depth = MIGetStackInfoDepth(cmd); | |
| MICommandFree(cmd); | |
| return depth; | |
| } | |
| int | |
| GetStackframes(MISession *session, int current, int low, int high, List **flist) | |
| { | |
| MIList * frames; | |
| MIFrame * f; | |
| MICommand * cmd; | |
| if (current) { | |
| if (_gdb_version > 6.3) { | |
| cmd = MIStackInfoFrame(); | |
| } else { | |
| cmd = CLIFrame(); | |
| } | |
| } else { | |
| if (low == 0 && high == 0) { | |
| cmd = MIStackListAllFrames(); | |
| } else { | |
| cmd = MIStackListFrames(low, high); | |
| } | |
| } | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| DEBUG_PRINTS(DEBUG_LEVEL_BACKEND, "------------------- GetStackframes error\n"); | |
| SetDebugError(cmd); | |
| MICommandFree(cmd); | |
| return DBGRES_ERR; | |
| } | |
| if (current) | |
| frames = MIGetFrameInfo(cmd); | |
| else | |
| frames = MIGetStackListFramesInfo(cmd); | |
| MICommandFree(cmd); | |
| if ( frames == NULL ) | |
| { | |
| DbgSetError(DBGERR_DEBUGGER, "Failed to get stack frames from backend"); | |
| return DBGRES_ERR; | |
| } | |
| *flist = NewList(); | |
| for (MIListSet(frames); (f = (MIFrame *)MIListGet(frames)) != NULL; ) { | |
| AddToList(*flist, (void *)ConvertMIFrame(f)); | |
| } | |
| MIListFree(frames, MIFrameFree); | |
| return DBGRES_OK; | |
| } | |
| /* | |
| * This is needed to check for a bug in the Linux x86 GCC 4.1 compiler | |
| * that causes gdb 6.4 and 6.5 to crash under certain conditions. | |
| */ | |
| #define GDB_BUG_2188 __gnu_linux__ && __i386__ && __GNUC__ == 4 && __GNUC_MINOR__ == 1 | |
| #if GDB_BUG_2188 | |
| int | |
| CurrentFrame(MISession *session, int level, char *name) | |
| { | |
| MICommand * cmd; | |
| MIFrame * frame; | |
| List * frames; | |
| int val = 0; | |
| if (_gdb_version > 6.3 && _gdb_version < 6.7) { | |
| cmd = MIStackListFrames(level, level); | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| MICommandFree(cmd); | |
| return val; | |
| } | |
| frames = MIGetStackListFramesInfo(cmd); | |
| //only one frame | |
| SetList(frames); | |
| if ((frame = (MIFrame *)GetListElement(frames)) != NULL) { | |
| if (frame->func != NULL && strncmp(frame->func, name, 4) == 0) { | |
| val = 1; | |
| } | |
| } | |
| DestroyList(frames, MIFrameFree); | |
| } | |
| return val; | |
| } | |
| #endif /* GDB_BUG_2188 */ | |
| char * | |
| GetVarValue(MISession *session, char *var) | |
| { | |
| char * res; | |
| MICommand * cmd = MIVarEvaluateExpression(var); | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| SetDebugError(cmd); | |
| MICommandFree(cmd); | |
| return strdup(""); | |
| } | |
| res = MIGetVarEvaluateExpressionInfo(cmd); | |
| MICommandFree(cmd); | |
| return res; | |
| } | |
| int | |
| GetAddressLength(MISession *session) | |
| { | |
| char * res; | |
| MICommand * cmd; | |
| if (_address_length != 0) { | |
| return _address_length; | |
| } | |
| cmd = MIDataEvaluateExpression("\"sizeof(char *)\""); | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| SetDebugError(cmd); | |
| MICommandFree(cmd); | |
| return 0; | |
| } | |
| res = MIGetDataEvaluateExpressionInfo(cmd); | |
| MICommandFree(cmd); | |
| _address_length = (int)strtol(res, NULL, 10); | |
| free(res); | |
| return _address_length; | |
| } | |
| int | |
| GetSizeOf(MISession *session, char* type) | |
| { | |
| char * res = NULL; | |
| MICommand * cmd = NULL; | |
| int result = 0; | |
| int inputLength = 20 + strlen(type); | |
| char *input = (char*)malloc(inputLength); | |
| strcpy(input, "\"sizeof("); | |
| strcat(input, type); | |
| strcat(input, ")\""); | |
| cmd = MIDataEvaluateExpression(input); | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| SetDebugError(cmd); | |
| MICommandFree(cmd); | |
| return 0; | |
| } | |
| res = MIGetDataEvaluateExpressionInfo(cmd); | |
| MICommandFree(cmd); | |
| result = (int)strtol(res, NULL, 10); | |
| free(input); | |
| free(res); | |
| return result; | |
| } | |
| /* | |
| * Try to find the type of a variable using the 'ptype' | |
| * command. First try using the type field, or if this | |
| * fails, try using the expression 'expr'. | |
| */ | |
| char * | |
| GetPtypeValue(MISession *session, char *expr, MIVar *var) | |
| { | |
| char * type = NULL; | |
| MICommand * cmd; | |
| DEBUG_PRINTF(DEBUG_LEVEL_BACKEND, "---------------------- GetPtypeValue(%s, %s)\n", expr != NULL ? expr : "NULL", var->type); | |
| cmd = CLIPType(var->type); | |
| MICommandSetTimeout(cmd, 1000); | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| if (expr == NULL) { | |
| DEBUG_PRINTS(DEBUG_LEVEL_BACKEND, "---------------------- GetPtypeValue failed and expr was NULL\n"); | |
| return NULL; | |
| } | |
| cmd = CLIPType(expr); | |
| MICommandSetTimeout(cmd, 100); | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| DEBUG_PRINTS(DEBUG_LEVEL_BACKEND, "---------------------- GetPtypeValue failed\n"); | |
| return NULL; | |
| } | |
| } | |
| type = CLIGetPTypeInfo(cmd); | |
| MICommandFree(cmd); | |
| DEBUG_PRINTF(DEBUG_LEVEL_BACKEND, "---------------------- GetPtypeValue returns \"%s\"\n", type); | |
| return type; | |
| } | |
| /* | |
| * Create a variable containing the fields of a class | |
| */ | |
| MIVar * | |
| GetMIVarClassFields(MISession *session, char *name) | |
| { | |
| MICommand * cmd; | |
| MIVar * v = NULL; | |
| v = MIVarNew(); | |
| cmd = MIVarListChildren(name); | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| MICommandFree(cmd); | |
| MIVarFree(v); | |
| return NULL; | |
| } | |
| MIGetVarListChildrenInfo(cmd, v); | |
| MICommandFree(cmd); | |
| v->name = strdup(name); | |
| return v; | |
| } | |
| /* | |
| * Get MI variable details. If 'mivar' is NULL then an MIVar object is created | |
| * with the name 'name', otherwise the details are added to the existing variable. | |
| * | |
| * If 'listChildren' is true, then the variable's children will be | |
| * queried also. If 'listChildren' is false, only 'numchildren' will be supplied. | |
| * | |
| * Returns a new MIVar object or NULL if any commands fail. | |
| */ | |
| MIVar * | |
| GetMIVarDetails(MISession *session, char *name, MIVar *mivar, int listChildren) | |
| { | |
| MICommand * cmd; | |
| MIVar * v = NULL; | |
| if (mivar == NULL || mivar->type == NULL) { | |
| cmd = MIVarInfoType(name); | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| MICommandFree(cmd); | |
| return NULL; | |
| } | |
| v = MIGetVarInfoType(cmd); | |
| MICommandFree(cmd); | |
| if (mivar == NULL) { | |
| mivar = v; | |
| if (mivar->name == NULL) { | |
| mivar->name = strdup(name); | |
| } | |
| } else { | |
| mivar->type = strdup(v->type); | |
| MIVarFree(v); | |
| } | |
| FixReferenceType(mivar->type); | |
| } | |
| /* | |
| * Only fetch child info if we haven't already done it. | |
| */ | |
| if (mivar->children == NULL || mivar->numchild == 0) { | |
| if (listChildren) { | |
| cmd = MIVarListChildren(name); | |
| } else { | |
| cmd = MIVarInfoNumChildren(name); | |
| } | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| MICommandFree(cmd); | |
| if (mivar == v) { | |
| MIVarFree(v); | |
| } | |
| return NULL; | |
| } | |
| if (listChildren) { | |
| MIGetVarListChildrenInfo(cmd, mivar); | |
| } else { | |
| MIGetVarInfoNumChildren(cmd, mivar); | |
| } | |
| MICommandFree(cmd); | |
| } | |
| return mivar; | |
| } | |
| int | |
| GetGDBVersion(MISession *session, float *version) | |
| { | |
| MICommand * cmd; | |
| if (_gdb_version == 0.0) { | |
| cmd = MIGDBVersion(); | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| MICommandFree(cmd); | |
| DbgSetError(DBGERR_DEBUGGER, "Unable to determine gdb version"); | |
| MISessionFree(session); | |
| return -1; | |
| } | |
| _gdb_version = CLIGetGDBVersion(cmd); | |
| MICommandFree(cmd); | |
| } | |
| if (version != NULL) { | |
| *version = _gdb_version; | |
| } | |
| return 0; | |
| } | |
| void | |
| SetDebugError(MICommand * cmd) | |
| { | |
| if (MICommandResultClass(cmd) == MIResultRecordERROR) { | |
| char *err = MICommandResultErrorMessage(cmd); | |
| if (err != NULL) { | |
| DbgSetError(DBGERR_DEBUGGER, err); | |
| free(err); | |
| } else { | |
| DbgSetError(DBGERR_DEBUGGER, "got error from gdb, but no message"); | |
| } | |
| } else { | |
| DbgSetError(DBGERR_DEBUGGER, "bad response from gdb"); | |
| } | |
| } | |
| memoryinfo * | |
| GetMemoryInfo(MISession *session, long offset, char* address, char* format, int wordSize, int rows, int cols, char* asChar) | |
| { | |
| MICommand * cmd; | |
| MIDataReadMemoryInfo * info; | |
| MIMemory * mem; | |
| memoryinfo *meminfo = NULL; | |
| memory * m; | |
| cmd = MIDataReadMemory(offset, address, format, wordSize, rows, cols, asChar); | |
| SendCommandWait(session, cmd); | |
| if (!MICommandResultOK(cmd)) { | |
| SetDebugError(cmd); | |
| MICommandFree(cmd); | |
| return NULL; | |
| } | |
| info = MIGetDataReadMemoryInfo(cmd); | |
| MICommandFree(cmd); | |
| if (info != NULL) { | |
| meminfo = NewMemoryInfo(); | |
| if (info->addr != NULL) { | |
| meminfo->addr = strdup(info->addr); | |
| } | |
| meminfo->nextRow = info->nextRow; | |
| meminfo->prevRow = info->prevRow; | |
| meminfo->nextPage = info->nextPage; | |
| meminfo->prevPage = info->prevPage; | |
| meminfo->numBytes = info->numBytes; | |
| meminfo->totalBytes = info->totalBytes; | |
| if (info->memories != NULL ) { | |
| meminfo->memories = NewList(); | |
| for (MIListSet(info->memories); (mem = (MIMemory *)MIListGet(info->memories)) != NULL;) { | |
| m = NewMemory(); | |
| if (mem->addr != NULL) { | |
| m->addr = strdup(mem->addr); | |
| } | |
| if (mem->ascii != NULL) { | |
| m->ascii = strdup(mem->ascii); | |
| } | |
| if (mem->data != NULL) { | |
| char* d; | |
| m->data = NewList(); | |
| for (MIListSet(mem->data); (d = (char *)MIListGet(mem->data)) != NULL;) { | |
| AddToList(m->data, (void *) strdup(d)); | |
| } | |
| } | |
| AddToList(meminfo->memories, (void *)m); | |
| } | |
| } | |
| } | |
| MIDataReadMemoryInfoFree(info); | |
| return meminfo; | |
| } |