| /****************************************************************************** |
| * 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 <sys/types.h> |
| #include <sys/stat.h> |
| #include <sys/wait.h> |
| |
| #include <stdlib.h> |
| #include <unistd.h> |
| #include <string.h> |
| #include <errno.h> |
| #include <fcntl.h> |
| |
| #include <aif.h> |
| |
| #include "dbg.h" |
| #include "dbg_event.h" |
| #include "backend.h" |
| #include "list.h" |
| |
| #include "MI.h" |
| |
| struct bpentry { |
| int local; |
| int remote; |
| int temp; |
| }; |
| typedef struct bpentry bpentry; |
| |
| struct bpmap { |
| int nels; // number of elements currently in map |
| int size; // total size of map |
| struct bpentry * maps; |
| }; |
| |
| struct varinfo { |
| char * name; //variable name |
| MIVar *mivar; |
| }; |
| typedef struct varinfo varinfo; |
| |
| struct varmap { |
| int nels; // number of elements currently in map |
| int size; // total size of map |
| struct varinfo * maps; |
| }; |
| |
| static double GDB_Version; |
| static int ADDRESS_LENGTH = 0; |
| static MISession * DebugSession; |
| static dbg_event * LastEvent; |
| static void (*EventCallback)(dbg_event *); |
| static int ServerExit; |
| static int Started; |
| static struct bpmap BPMap = { 0, 0, NULL }; |
| #if 0 |
| static struct varmap VARMap = { 0, 0, NULL }; |
| #endif |
| static int (*AsyncFunc)(void *) = NULL; |
| static void * AsyncFuncData; |
| |
| static int GDBMIInit(void (*)(dbg_event *)); |
| static int GDBMIProgress(void); |
| static int GDBMIInterrupt(void); |
| static int GDBMIStartSession(char *, char *, char *, char *, char **, char **, long); |
| static int GDBMISetLineBreakpoint(int, int, int, char *, int, char*, int, int); |
| static int GDBMISetFuncBreakpoint(int, int, int, char *, char *, char*, int, int); |
| static int GDBMIDeleteBreakpoint(int); |
| static int GDBMIEnableBreakpoint(int); |
| static int GDBMIDisableBreakpoint(int); |
| static int GDBMIConditionBreakpoint(int, char *expr); |
| static int GDBMIBreakpointAfter(int, int icount); |
| static int GDBMIWatchpoint(int, char *, int, int, char *, int); |
| static int GDBMIGo(void); |
| static int GDBMIStep(int, int); |
| static int GDBMITerminate(void); |
| static int GDBMIListStackframes(int, int); |
| static int GDBMISetCurrentStackframe(int); |
| static int GDBMIEvaluateExpression(char *); |
| static int GDBMIGetNativeType(char *); |
| static int GDBMIGetLocalVariables(void); |
| static int GDBMIListArguments(int, int); |
| static int GDBMIGetInfoThread(void); |
| static int GDBMISetThreadSelect(int); |
| static int GDBMIStackInfoDepth(void); |
| static int GDBMIDataReadMemory(long, char*, char*, int, int, int, char*); |
| static int GDBMIDataWriteMemory(long, char*, char*, int, char*); |
| static int GDBMIGetGlobalVariables(void); |
| static int GDBCLIListSignals(char*); |
| static int GDBCLISignalInfo(char*); |
| static int GDBCLIHandle(char*); |
| static int GDBMIQuit(void); |
| static int GDBMIDataEvaluateExpression(char*); |
| static int GDBGetPartialAIF(char *, char *, int, int); |
| static int GDBMIVarDelete(char*); |
| |
| static void SendCommandWait(MISession *, MICommand *); |
| static int SetAndCheckBreak(int, int, int, char *, char *, int, int); |
| static int GetStackframes(int, int, int, List **); |
| static int GetAIFVar(char *, AIF **, char **); |
| |
| static AIF * GetAIF(MIVar *, char *, int); |
| static AIF * GetPartialAIF(MIVar *, char *); |
| static void RemoveAllMaps(); |
| |
| dbg_backend_funcs GDBMIBackend = |
| { |
| GDBMIInit, |
| GDBMIProgress, |
| GDBMIInterrupt, |
| GDBMIStartSession, |
| GDBMISetLineBreakpoint, |
| GDBMISetFuncBreakpoint, |
| GDBMIDeleteBreakpoint, |
| GDBMIEnableBreakpoint, |
| GDBMIDisableBreakpoint, |
| GDBMIConditionBreakpoint, |
| GDBMIBreakpointAfter, |
| GDBMIWatchpoint, |
| GDBMIGo, |
| GDBMIStep, |
| GDBMITerminate, |
| GDBMIListStackframes, |
| GDBMISetCurrentStackframe, |
| GDBMIEvaluateExpression, |
| GDBMIGetNativeType, |
| GDBMIGetLocalVariables, |
| GDBMIListArguments, |
| GDBMIGetGlobalVariables, |
| GDBMIGetInfoThread, |
| GDBMISetThreadSelect, |
| GDBMIStackInfoDepth, |
| GDBMIDataReadMemory, |
| GDBMIDataWriteMemory, |
| GDBCLIListSignals, |
| GDBCLISignalInfo, |
| GDBCLIHandle, |
| GDBMIDataEvaluateExpression, |
| GDBGetPartialAIF, |
| GDBMIVarDelete, |
| GDBMIQuit |
| }; |
| |
| #define CHECK_SESSION() \ |
| if (DebugSession == NULL) { \ |
| DbgSetError(DBGERR_NOSESSION, NULL); \ |
| return DBGRES_ERR; \ |
| } |
| |
| #define ERROR_TO_EVENT(e) \ |
| e = NewDbgEvent(DBGEV_ERROR); \ |
| e->dbg_event_u.error_event.error_code = DbgGetError(); \ |
| e->dbg_event_u.error_event.error_msg = strdup(DbgGetErrorStr()) |
| |
| static char * |
| GetLastErrorStr(void) |
| { |
| return MIGetErrorStr(); |
| } |
| |
| static void |
| SaveEvent(dbg_event *e) |
| { |
| if (LastEvent != NULL) |
| FreeDbgEvent(LastEvent); |
| |
| LastEvent = e; |
| } |
| |
| /**** Variable ****/ |
| static MIVar* |
| CreateMIVar(char *name) |
| { |
| MICommand *cmd; |
| MIVar *mivar; |
| |
| cmd = MIVarCreate("-", "*", name); |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| //DbgSetError(DBGERR_UNKNOWN_VARIABLE, GetLastErrorStr()); |
| MICommandFree(cmd); |
| return NULL; |
| } |
| mivar = MIGetVarCreateInfo(cmd); |
| MICommandFree(cmd); |
| return mivar; |
| } |
| |
| static void |
| DeleteMIVar(char *mi_name) |
| { |
| MICommand *cmd; |
| cmd = MIVarDelete(mi_name); |
| SendCommandWait(DebugSession, cmd); |
| MICommandFree(cmd); |
| } |
| |
| static void |
| AddBPMap(int local, int remote, int temp) |
| { |
| int i; |
| struct bpentry * map; |
| |
| if (BPMap.size == 0) { |
| BPMap.maps = (struct bpentry *)malloc(sizeof(struct bpentry) * 100); |
| BPMap.size = 100; |
| |
| for (i = 0; i < BPMap.size; i++) { |
| map = &BPMap.maps[i]; |
| map->remote = map->local = -1; |
| map->temp = 0; |
| } |
| } |
| |
| if (BPMap.nels == BPMap.size) { |
| i = BPMap.size; |
| BPMap.size *= 2; |
| BPMap.maps = (struct bpentry *)realloc(BPMap.maps, sizeof(struct bpentry) * BPMap.size); |
| |
| for (; i < BPMap.size; i++) { |
| map = &BPMap.maps[i]; |
| map->remote = map->local = -1; |
| map->temp = 0; |
| } |
| } |
| |
| for (i = 0; i < BPMap.size; i++) { |
| map = &BPMap.maps[i]; |
| if (map->remote == -1) { |
| map->remote = remote; |
| map->local = local; |
| map->temp = temp; |
| BPMap.nels++; |
| break; |
| } |
| } |
| } |
| |
| static void |
| RemoveBPMap(bpentry *bp) |
| { |
| int i; |
| struct bpentry * map; |
| for (i = 0; i < BPMap.size; i++) { |
| map = &BPMap.maps[i]; |
| if (map == bp) { |
| map->remote = -1; |
| map->local = -1; |
| map->temp = 0; |
| BPMap.nels--; |
| break; |
| } |
| } |
| } |
| |
| static bpentry * |
| FindLocalBP(int local) |
| { |
| int i; |
| struct bpentry * map; |
| for (i = 0; i < BPMap.size; i++) { |
| map = &BPMap.maps[i]; |
| if (map->local == local) { |
| return map; |
| } |
| } |
| return NULL; |
| } |
| |
| static bpentry * |
| FindRemoteBP(int remote) |
| { |
| int i; |
| struct bpentry * map; |
| for (i = 0; i < BPMap.size; i++) { |
| map = &BPMap.maps[i]; |
| if (map->remote == remote) { |
| return map; |
| } |
| } |
| return NULL; |
| } |
| |
| static void |
| RemoveAllBPMap() |
| { |
| int i; |
| struct bpentry * map; |
| int length = BPMap.size; |
| for (i = 0; i < length; i++) { |
| map = &BPMap.maps[i]; |
| if (map == NULL) |
| return; |
| |
| map->remote = -1; |
| map->local = -1; |
| map->temp = 0; |
| BPMap.nels--; |
| } |
| } |
| |
| static stackframe * |
| get_current_frame() |
| { |
| stackframe *f; |
| List * frames; |
| |
| if (GetStackframes(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; |
| } |
| |
| static stackframe * |
| ConvertMIFrameToStackframe(MIFrame *f) |
| { |
| stackframe * s; |
| if (f == NULL) {//by default return current frame |
| return get_current_frame(); |
| } |
| 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; |
| } |
| |
| |
| #if 0 |
| static void |
| AddVARMap(char *name, MIVar *mivar) |
| { |
| int i; |
| struct varinfo *map; |
| |
| if (VARMap.size == 0) { |
| VARMap.maps = (struct varinfo *)malloc(sizeof(struct varinfo) * 100); |
| VARMap.size = 100; |
| |
| for (i=0; i<VARMap.size; i++) { |
| map = &VARMap.maps[i]; |
| map->name = NULL; |
| map->mivar = NULL; |
| } |
| } |
| if (VARMap.nels == VARMap.size) { |
| i = VARMap.size; |
| VARMap.size *= 2; |
| VARMap.maps = (struct varinfo *)realloc(VARMap.maps, sizeof(struct varinfo) * VARMap.size); |
| |
| for (; i<VARMap.size; i++) { |
| map = &VARMap.maps[i]; |
| map->name = NULL; |
| map->mivar = NULL; |
| } |
| } |
| for (i=0; i<VARMap.size; i++) { |
| map = &VARMap.maps[i]; |
| if (map->name == NULL) { |
| map->name = strdup(name); |
| map->mivar = mivar; |
| VARMap.nels++; |
| break; |
| } |
| } |
| } |
| |
| static varinfo * |
| FindVARByName(char *name) |
| { |
| int i; |
| struct varinfo *map; |
| for (i=0; i<VARMap.size; i++) { |
| map = &VARMap.maps[i]; |
| if (map != NULL && strcmp(map->name, name) == 0) { |
| return map; |
| } |
| } |
| return NULL; |
| } |
| |
| static varinfo * |
| FindVARByMIName(char *mi_name) |
| { |
| int i; |
| struct varinfo *map; |
| for (i=0; i<VARMap.size; i++) { |
| map = &VARMap.maps[i]; |
| if (map != NULL && strcmp(map->mivar->name, mi_name) == 0) { |
| return map; |
| } |
| } |
| return NULL; |
| } |
| |
| static void |
| RemoveVARMap(varinfo *map) |
| { |
| if (map != NULL) { |
| if (map->name != NULL) { |
| free(map->name); |
| map->name = NULL; |
| } |
| if (map->mivar != NULL) { |
| DeleteMIVar(map->mivar->name); |
| MIVarFree(map->mivar); |
| map->mivar = NULL; |
| } |
| VARMap.nels--; |
| } |
| } |
| |
| static void |
| RemoveVARMapByName(char *name) |
| { |
| struct varinfo *map; |
| map = FindVARByName(name); |
| RemoveVARMap(map); |
| } |
| |
| static void |
| RemoveVARMapByMIName(char *mi_name) |
| { |
| struct varinfo *map; |
| map = FindVARByMIName(mi_name); |
| RemoveVARMap(map); |
| } |
| |
| static void |
| RemoveAllVARMap() |
| { |
| int i; |
| struct varinfo *map; |
| int length = VARMap.size; |
| for (i = 0; i < length; i++) { |
| map = &VARMap.maps[i]; |
| if (map == NULL) |
| return; |
| |
| RemoveVARMap(map); |
| } |
| } |
| #endif |
| |
| static void |
| RemoveAllMaps() |
| { |
| RemoveAllBPMap(); |
| #if 0 |
| RemoveAllVARMap(); |
| #endif |
| } |
| |
| static 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"); |
| } |
| } |
| static List * |
| GetChangedVariables() |
| { |
| MICommand *cmd; |
| List *changes; |
| List *changedVars; |
| MIVarChange *var; |
| |
| cmd = MIVarUpdate("*"); |
| SendCommandWait(DebugSession, 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 (SetList(changes); (var = (MIVarChange *)GetListElement(changes)) != NULL;) { |
| if (var->in_scope == 1) { |
| AddToList(changedVars, (void *)strdup(var->name)); |
| } |
| else { |
| DeleteMIVar(var->name); |
| } |
| } |
| DestroyList(changes, MIVarChangeFree); |
| return changedVars; |
| } |
| |
| static int |
| get_info_depth() |
| { |
| MICommand *cmd; |
| int depth; |
| |
| cmd = MIStackInfoDepth(); |
| SendCommandWait(DebugSession, 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; |
| } |
| /** |
| * not used |
| * |
| static void |
| set_frame_with_line(stackframe *current_f, int depth) |
| { |
| stackframe *f; |
| List * frames; |
| |
| if (GetStackframes(0, 1, depth, &frames) != DBGRES_OK) { |
| return NULL; |
| } |
| |
| for (SetList(frames); (f = (stackframe *)GetListElement(frames)) != NULL;) { |
| if (f->loc.line > 0) { |
| current_f->level = f->level; |
| current_f->loc.file = strdup(f->loc.file); |
| current_f->loc.func = strdup(f->loc.func); |
| current_f->loc.addr = strdup(f->loc.addr); |
| current_f->loc.line = f->loc.line; |
| break; |
| } |
| } |
| DestroyList(frames, FreeStackframe); |
| } |
| */ |
| |
| /**** aysn stop ****/ |
| static int |
| AsyncStop(void *data) |
| { |
| dbg_event * e; |
| stackframe * frame; |
| bpentry * bpmap; |
| MIEvent * evt = (MIEvent *)data; |
| |
| switch (evt->type) |
| { |
| case MIEventTypeBreakpointHit: |
| bpmap = FindLocalBP(evt->bkptno); |
| if (!bpmap->temp) { |
| e = NewDbgEvent(DBGEV_SUSPEND); |
| e->dbg_event_u.suspend_event.reason = DBGEV_SUSPEND_BPHIT; |
| e->dbg_event_u.suspend_event.ev_u.bpid = bpmap->remote; |
| e->dbg_event_u.suspend_event.thread_id = evt->threadId; |
| e->dbg_event_u.suspend_event.frame = NULL; |
| e->dbg_event_u.suspend_event.depth = get_info_depth(); |
| e->dbg_event_u.suspend_event.changed_vars = GetChangedVariables(); |
| break; |
| } |
| /* else must be a temporary breakpoint drop through... */ |
| RemoveBPMap(bpmap); |
| |
| case MIEventTypeSuspended: |
| frame = ConvertMIFrameToStackframe(evt->frame); |
| if (frame == NULL) { |
| ERROR_TO_EVENT(e); |
| } |
| else { |
| e = NewDbgEvent(DBGEV_SUSPEND); |
| e->dbg_event_u.suspend_event.reason = DBGEV_SUSPEND_INT; |
| e->dbg_event_u.suspend_event.thread_id = evt->threadId; |
| e->dbg_event_u.suspend_event.frame = frame; |
| e->dbg_event_u.suspend_event.depth = get_info_depth(); |
| e->dbg_event_u.suspend_event.changed_vars = GetChangedVariables(); |
| } |
| break; |
| |
| case MIEventTypeFunctionFinished: |
| case MIEventTypeSteppingRange: |
| frame = ConvertMIFrameToStackframe(evt->frame); |
| if (frame == NULL) { |
| ERROR_TO_EVENT(e); |
| } |
| else { |
| e = NewDbgEvent(DBGEV_SUSPEND); |
| e->dbg_event_u.suspend_event.reason = DBGEV_SUSPEND_STEP; |
| e->dbg_event_u.suspend_event.thread_id = evt->threadId; |
| e->dbg_event_u.suspend_event.frame = frame; |
| e->dbg_event_u.suspend_event.depth = get_info_depth(); |
| e->dbg_event_u.suspend_event.changed_vars = GetChangedVariables(); |
| } |
| break; |
| |
| case MIEventTypeSignal: |
| frame = ConvertMIFrameToStackframe(evt->frame); |
| if (frame == NULL) { |
| ERROR_TO_EVENT(e); |
| } |
| else { |
| e = NewDbgEvent(DBGEV_SUSPEND); |
| e->dbg_event_u.suspend_event.reason = DBGEV_SUSPEND_SIGNAL; |
| e->dbg_event_u.suspend_event.ev_u.sig = NewSignalInfo(); |
| e->dbg_event_u.suspend_event.ev_u.sig->name = strdup(evt->sigName); |
| e->dbg_event_u.suspend_event.ev_u.sig->desc = strdup(evt->sigMeaning); |
| e->dbg_event_u.suspend_event.thread_id = evt->threadId; |
| e->dbg_event_u.suspend_event.frame = frame; |
| e->dbg_event_u.suspend_event.depth = get_info_depth(); |
| e->dbg_event_u.suspend_event.changed_vars = GetChangedVariables(); |
| } |
| break; |
| |
| case MIEventTypeInferiorSignalExit: |
| e = NewDbgEvent(DBGEV_EXIT); |
| e->dbg_event_u.exit_event.reason = DBGEV_EXIT_SIGNAL; |
| e->dbg_event_u.exit_event.ev_u.sig = NewSignalInfo(); |
| e->dbg_event_u.exit_event.ev_u.sig->name = strdup(evt->sigName); |
| e->dbg_event_u.exit_event.ev_u.sig->desc = strdup(evt->sigMeaning); |
| //RemoveAllMaps(); |
| break; |
| |
| case MIEventTypeInferiorExit: |
| e = NewDbgEvent(DBGEV_EXIT); |
| e->dbg_event_u.exit_event.reason = DBGEV_EXIT_NORMAL; |
| e->dbg_event_u.exit_event.ev_u.exit_status = evt->code; |
| //RemoveAllMaps(); |
| break; |
| |
| default: |
| DbgSetError(DBGERR_DEBUGGER, "Unknown reason for stopping"); |
| return DBGRES_ERR; |
| } |
| MIEventFree(evt); |
| |
| if (EventCallback != NULL) |
| EventCallback(e); |
| |
| FreeDbgEvent(e); |
| return DBGRES_OK; |
| } |
| |
| /* |
| ** AsyncCallback is called by mi_get_response() when an async response is |
| ** detected. It can't issue any gdb commands or there's a potential |
| ** for deadlock. If commands need to be issues (e.g. to obtain |
| ** current stack frame, they must be called from the main select |
| ** loop using the AsyncFunc() mechanism. |
| */ |
| static void |
| AsyncCallback(MIEvent *evt) |
| { |
| AsyncFunc = AsyncStop; |
| AsyncFuncData = (void *)evt; |
| } |
| |
| /* |
| * Initialize GDB |
| */ |
| static int |
| GDBMIInit(void (*event_callback)(dbg_event *)) |
| { |
| EventCallback = event_callback; |
| DebugSession = NULL; |
| LastEvent = NULL; |
| GDB_Version = -1.0; |
| ServerExit = 0; |
| |
| signal(SIGTERM, SIG_IGN); |
| signal(SIGHUP, SIG_IGN); |
| signal(SIGINT, SIG_IGN); |
| |
| return DBGRES_OK; |
| } |
| |
| #if 0 |
| static int |
| timeout_cb(void *data) |
| { |
| return 0; |
| } |
| #endif |
| |
| /* |
| * Send command and wait for immediate response. |
| */ |
| static void |
| SendCommandWait(MISession *sess, MICommand *cmd) |
| { |
| MISessionSendCommand(sess, cmd); |
| MIOutput *output = MIOutputNew(); |
| do { |
| MISessionProgress(sess, output); |
| if (sess->out_fd == -1) { |
| DEBUG_PRINTS(DEBUG_LEVEL_BACKEND, "------------------- SendCommandWait sess->out_fd = -1\n"); |
| break; |
| } |
| } while (!MISessionCommandCompleted(sess)); |
| } |
| |
| /* |
| * Start GDB session |
| */ |
| static int |
| GDBMIStartSession(char *gdb_path, char *prog, char *path, char *work_dir, char **args, char **env, long timeout) |
| { |
| char * prog_path; |
| char ** e; |
| struct stat st; |
| MICommand * cmd; |
| MISession * sess; |
| |
| if (DebugSession != NULL) { |
| DbgSetError(DBGERR_SESSION, NULL); |
| return DBGRES_ERR; |
| } |
| |
| /* |
| * see if we can find the gdb executable |
| */ |
| if (*gdb_path == '/') { |
| if (stat(gdb_path, &st) < 0 || !S_ISREG(st.st_mode)) { |
| DbgSetError(DBGERR_NOBACKEND, gdb_path); |
| return DBGRES_ERR; |
| } |
| } |
| |
| if (*work_dir != '\0' && chdir(work_dir) < 0) { |
| DbgSetError(DBGERR_CHDIR, work_dir); |
| return DBGRES_ERR; |
| } |
| |
| if (path != NULL) |
| asprintf(&prog_path, "%s/%s", path, prog); |
| else |
| prog_path = strdup(prog); |
| |
| if (access(prog_path, R_OK) < 0) { |
| DbgSetError(DBGERR_NOFILEDIR, prog_path); |
| free(prog_path); |
| return DBGRES_ERR; |
| } |
| |
| sess = MISessionNew(); |
| |
| #ifdef DEBUG |
| MISessionSetDebug(TEST_DEBUG_LEVEL(DEBUG_LEVEL_BACKEND)); |
| #endif /* DEBUG */ |
| |
| MISessionSetTimeout(sess, 0, timeout); |
| |
| MISessionSetGDBPath(sess, gdb_path); |
| |
| if (MISessionStartLocal(sess, prog_path) < 0) { |
| DbgSetError(DBGERR_DEBUGGER, GetLastErrorStr()); |
| MISessionFree(sess); |
| free(prog_path); |
| return DBGRES_ERR; |
| } |
| |
| free(prog_path); |
| |
| if (*args != NULL) { |
| cmd = MIExecArguments(args); |
| SendCommandWait(sess, cmd); |
| MICommandFree(cmd); |
| } |
| |
| for (e = env; e != NULL && *e != NULL; e++) { |
| cmd = MIGDBSet("environment", *e); |
| SendCommandWait(sess, cmd); |
| MICommandFree(cmd); |
| } |
| |
| cmd = MIGDBSet("confirm", "off"); |
| SendCommandWait(sess, cmd); |
| MICommandFree(cmd); |
| |
| MISessionRegisterEventCallback(sess, AsyncCallback); |
| |
| DebugSession = sess; |
| |
| cmd = MIGDBVersion(); |
| SendCommandWait(sess, cmd); |
| if (MICommandResultOK(cmd)) { |
| GDB_Version = CLIGetGDBVersion(cmd); |
| DEBUG_PRINTF(DEBUG_LEVEL_BACKEND, "------------------- gdb version: %f\n", GDB_Version); |
| } |
| MICommandFree(cmd); |
| |
| Started = 0; |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| |
| return DBGRES_OK; |
| } |
| |
| /* |
| * Progress gdb commands. |
| */ |
| static int |
| GDBMIProgress(void) |
| { |
| dbg_event * e; |
| |
| /* |
| * Check for existing events |
| */ |
| if (LastEvent != NULL) { |
| if (EventCallback != NULL) { |
| EventCallback(LastEvent); |
| } |
| |
| if (ServerExit && LastEvent->event_id == DBGEV_OK) { |
| if (DebugSession != NULL) { |
| MISessionFree(DebugSession); |
| DebugSession = NULL; |
| } |
| } |
| |
| FreeDbgEvent(LastEvent); |
| LastEvent = NULL; |
| return 0; |
| } |
| |
| if (DebugSession != NULL) { |
| if (MISessionProgress(DebugSession, MIOutputNew()) < 0) { |
| MISessionFree(DebugSession); |
| DebugSession = NULL; |
| DbgSetError(DBGERR_DEBUGGER, GetLastErrorStr()); |
| ERROR_TO_EVENT(e); |
| SaveEvent(e); |
| return 0; |
| } |
| |
| /* |
| * Do any extra async functions. We can call gdbmi safely here |
| */ |
| if (AsyncFunc != NULL) { |
| AsyncFunc(AsyncFuncData); |
| AsyncFunc = NULL; |
| return 0; |
| } |
| } |
| |
| return 0; |
| } |
| |
| /* |
| ** Set breakpoint at specified line. |
| */ |
| static int |
| GDBMISetLineBreakpoint(int bpid, int isTemp, int isHard, char *file, int line, char *condition, int ignoreCount, int tid) |
| { |
| int res; |
| char * where; |
| |
| CHECK_SESSION(); |
| |
| if (file == NULL || *file == '\0') |
| asprintf(&where, "%d", line); |
| else |
| asprintf(&where, "%s:%d", file, line); |
| |
| res = SetAndCheckBreak(bpid, isTemp, isHard, where, condition, ignoreCount, tid); |
| |
| free(where); |
| |
| return res; |
| } |
| |
| /* |
| ** Set breakpoint at start of specified function. |
| */ |
| static int |
| GDBMISetFuncBreakpoint(int bpid, int isTemp, int isHard, char *file, char *func, char *condition, int ignoreCount, int tid) |
| { |
| int res; |
| char * where; |
| |
| CHECK_SESSION(); |
| |
| if (file == NULL || *file == '\0') |
| asprintf(&where, "%s", func); |
| else |
| asprintf(&where, "%s:%s", file, func); |
| |
| res = SetAndCheckBreak(bpid, isTemp, isHard, where, condition, ignoreCount, tid); |
| |
| free(where); |
| |
| return res; |
| } |
| |
| /* |
| ** Check that breakpoint command has succeded and |
| ** extract appropriate information. Returns breakpoint |
| ** id in bid. Adds to breakpoint list if necessary. |
| */ |
| static int |
| SetAndCheckBreak(int bpid, int isTemp, int isHard, char *where, char *condition, int ignoreCount, int tid) |
| { |
| dbg_event * e; |
| MIBreakpoint * bpt; |
| MICommand * cmd; |
| List * bpts; |
| breakpoint * bp; |
| |
| if (condition != NULL && strlen(condition) == 0) |
| condition = NULL; |
| |
| cmd = MIBreakInsert(isTemp, isHard, condition, ignoreCount, where, tid); |
| SendCommandWait(DebugSession, cmd); |
| |
| if (!MICommandResultOK(cmd)) { |
| DbgSetError(DBGERR_NOFILE, GetLastErrorStr()); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| bpts = MIBreakpointGetBreakInsertInfo(cmd); |
| MICommandFree(cmd); |
| |
| if (bpts == NULL) { |
| DbgSetError(DBGERR_NOFILE, where); |
| //DbgSetError(DBGERR_NOFILE, "error getting breakpoint information"); |
| return DBGRES_ERR; |
| } |
| |
| SetList(bpts); |
| bpt = (MIBreakpoint *)GetListElement(bpts); |
| |
| AddBPMap(bpt->number, bpid, isTemp); |
| |
| //if the type is temporary, no need to send bpt set event |
| if (isTemp) { |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| DestroyList(bpts, MIBreakpointFree); |
| return DBGRES_OK; |
| } |
| |
| 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; |
| |
| e = NewDbgEvent(DBGEV_BPSET); |
| e->dbg_event_u.bpset_event.bpid = bpid; |
| e->dbg_event_u.bpset_event.bp = bp; |
| SaveEvent(e); |
| |
| DestroyList(bpts, MIBreakpointFree); |
| return DBGRES_OK; |
| } |
| |
| /* |
| ** Delete a breakpoint. |
| */ |
| static int |
| GDBMIDeleteBreakpoint(int bpid) |
| { |
| bpentry * bp; |
| char * bpstr; |
| MICommand * cmd; |
| |
| CHECK_SESSION(); |
| |
| if ((bp = FindRemoteBP(bpid)) == NULL) { |
| asprintf(&bpstr, "%d", bpid); |
| DbgSetError(DBGERR_NOBP, bpstr); |
| free(bpstr); |
| return DBGRES_ERR; |
| } |
| |
| cmd = MIBreakDelete(1, &bp->local); |
| SendCommandWait(DebugSession, cmd); |
| |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| |
| RemoveBPMap(bp); |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| |
| return DBGRES_OK; |
| } |
| |
| /* |
| ** Enable a breakpoint. |
| */ |
| static int |
| GDBMIEnableBreakpoint(int bpid) |
| { |
| bpentry * bp; |
| char * bpstr; |
| MICommand * cmd; |
| |
| CHECK_SESSION(); |
| |
| if ((bp = FindRemoteBP(bpid)) == NULL) { |
| asprintf(&bpstr, "%d", bpid); |
| DbgSetError(DBGERR_NOBP, bpstr); |
| free(bpstr); |
| return DBGRES_ERR; |
| } |
| |
| cmd = MIBreakEnable(1, &bp->local); |
| SendCommandWait(DebugSession, cmd); |
| |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| return DBGRES_OK; |
| } |
| |
| /* |
| ** Disable a breakpoint. |
| */ |
| static int |
| GDBMIDisableBreakpoint(int bpid) |
| { |
| bpentry * bp; |
| char * bpstr; |
| MICommand * cmd; |
| |
| CHECK_SESSION(); |
| |
| if ((bp = FindRemoteBP(bpid)) == NULL) { |
| asprintf(&bpstr, "%d", bpid); |
| DbgSetError(DBGERR_NOBP, bpstr); |
| free(bpstr); |
| return DBGRES_ERR; |
| } |
| |
| cmd = MIBreakDisable(1, &bp->local); |
| SendCommandWait(DebugSession, cmd); |
| |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| return DBGRES_OK; |
| } |
| |
| /* |
| ** Condition a breakpoint. |
| */ |
| static int |
| GDBMIConditionBreakpoint(int bpid, char *expr) |
| { |
| bpentry * bp; |
| char * bpstr; |
| MICommand * cmd; |
| |
| CHECK_SESSION(); |
| |
| if ((bp = FindRemoteBP(bpid)) == NULL) { |
| asprintf(&bpstr, "%d", bpid); |
| DbgSetError(DBGERR_NOBP, bpstr); |
| free(bpstr); |
| return DBGRES_ERR; |
| } |
| |
| cmd = MIBreakCondition(1, &bp->local, expr); |
| SendCommandWait(DebugSession, cmd); |
| |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| return DBGRES_OK; |
| } |
| |
| /* |
| ** breakpoint after. |
| */ |
| static int |
| GDBMIBreakpointAfter(int bpid, int icount) |
| { |
| bpentry * bp; |
| char * bpstr; |
| MICommand * cmd; |
| |
| CHECK_SESSION(); |
| |
| if ((bp = FindRemoteBP(bpid)) == NULL) { |
| asprintf(&bpstr, "%d", bpid); |
| DbgSetError(DBGERR_NOBP, bpstr); |
| free(bpstr); |
| return DBGRES_ERR; |
| } |
| |
| cmd = MIBreakAfter(1, &bp->local, icount); |
| SendCommandWait(DebugSession, cmd); |
| |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| return DBGRES_OK; |
| } |
| |
| /* |
| * Set watch point |
| */ |
| static int |
| GDBMIWatchpoint(int bpid, char *expr, int isAccess, int isRead, char *condition, int ignoreCount) |
| { |
| dbg_event * e; |
| MIBreakpoint * bpt; |
| MICommand * cmd; |
| List * bpts; |
| breakpoint * bp; |
| |
| if (condition != NULL && strlen(condition) == 0) |
| condition = NULL; |
| |
| cmd = MIBreakWatch(expr, isAccess, isRead); |
| SendCommandWait(DebugSession, cmd); |
| |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| |
| bpts = MIBreakpointGetBreakInsertInfo(cmd); |
| MICommandFree(cmd); |
| |
| if (bpts == NULL) { |
| DbgSetError(DBGERR_DEBUGGER, "error getting breakpoint information"); |
| return DBGRES_ERR; |
| } |
| |
| SetList(bpts); |
| bpt = (MIBreakpoint *)GetListElement(bpts); |
| |
| AddBPMap(bpt->number, bpid, 0); //0 is not temp?? |
| |
| bp = NewBreakpoint(bpt->number); |
| |
| bp->ignore = bpt->ignore; |
| bp->type = strdup(bpt->type); |
| bp->hits = bpt->times; |
| |
| if (condition != NULL) { |
| GDBMIConditionBreakpoint(bpid, condition); |
| } |
| if (ignoreCount > 0) { |
| GDBMIBreakpointAfter(bpid, ignoreCount); |
| } |
| |
| e = NewDbgEvent(DBGEV_BPSET); |
| e->dbg_event_u.bpset_event.bpid = bpid; |
| e->dbg_event_u.bpset_event.bp = bp; |
| SaveEvent(e); |
| |
| DestroyList(bpts, MIBreakpointFree); |
| |
| return DBGRES_OK; |
| } |
| |
| /* |
| ** Start/continue executing program. |
| */ |
| static int |
| GDBMIGo(void) |
| { |
| MICommand * cmd; |
| |
| CHECK_SESSION(); |
| |
| if (Started) |
| cmd = MIExecContinue(); |
| else { |
| cmd = MIExecRun(); |
| Started = 1; |
| } |
| |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| |
| MICommandFree(cmd); |
| |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| |
| return DBGRES_OK; |
| } |
| |
| /* |
| * Execute count statements. |
| * |
| * type step kind |
| * 0 enter function calls |
| * 1 do not enter function calls |
| * 2 step out of function (count ignored) |
| */ |
| static int |
| GDBMIStep(int count, int type) |
| { |
| MICommand * cmd; |
| |
| CHECK_SESSION(); |
| |
| switch ( type ) { |
| case 0: |
| cmd = MIExecStep(count); |
| break; |
| |
| case 1: |
| cmd = MIExecNext(count); |
| break; |
| |
| case 2: |
| cmd = MIExecFinish(); |
| break; |
| |
| default: |
| DbgSetError(DBGERR_DEBUGGER, "Unknown step type"); |
| return DBGRES_ERR; |
| } |
| |
| SendCommandWait(DebugSession, cmd); |
| |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| |
| MICommandFree(cmd); |
| |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| |
| return DBGRES_OK; |
| } |
| |
| /* |
| ** Terminate program execution. |
| */ |
| static int |
| GDBMITerminate(void) |
| { |
| MICommand * cmd; |
| |
| CHECK_SESSION(); |
| |
| cmd = MICommandNew("kill", MIResultRecordDONE); |
| |
| SendCommandWait(DebugSession, cmd); |
| |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| |
| MICommandFree(cmd); |
| |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| |
| return DBGRES_OK; |
| } |
| |
| /* |
| ** Interrupt an executing program. |
| */ |
| static int |
| GDBMIInterrupt(void) |
| { |
| MICommand * cmd; |
| |
| CHECK_SESSION(); |
| |
| /* |
| * Don't do anything if there's an event pending or the |
| * target is not running. |
| * |
| if (LastEvent != NULL || !gmi_exec_interrupt(MIHandle)) |
| return DBGRES_OK;*/ |
| |
| /* |
| * Must check async here due to broken MI implementation. AsyncCallback will |
| * be called inside gmi_exec_interrupt(). |
| * |
| if (AsyncFunc != NULL) { |
| AsyncFunc(AsyncFuncData); |
| AsyncFunc = NULL; |
| }*/ |
| |
| /* |
| * Ignore error if target is not running |
| */ |
| |
| cmd = MIExecInterrupt(); |
| |
| SendCommandWait(DebugSession, cmd); |
| |
| MICommandFree(cmd); |
| |
| return DBGRES_OK; |
| } |
| |
| /* |
| ** Move up or down count stack frames. |
| */ |
| static int |
| GDBMISetCurrentStackframe(int level) |
| { |
| MICommand * cmd; |
| |
| CHECK_SESSION(); |
| |
| cmd = MIStackSelectFrame(level); |
| |
| SendCommandWait(DebugSession, cmd); |
| |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| |
| MICommandFree(cmd); |
| |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| |
| return DBGRES_OK; |
| } |
| |
| static int |
| GetStackframes(int current, int low, int high, List **flist) |
| { |
| List * frames; |
| MIFrame * f; |
| MICommand * cmd; |
| stackframe * s; |
| |
| //checking gdb version |
| 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(DebugSession, 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 (SetList(frames); (f = (MIFrame *)GetListElement(frames)) != NULL; ) { |
| s = ConvertMIFrameToStackframe(f); |
| AddToList(*flist, (void *)s); |
| } |
| DestroyList(frames, MIFrameFree); |
| return DBGRES_OK; |
| } |
| |
| /* |
| ** List current or all stack frames. |
| */ |
| static int |
| GDBMIListStackframes(int low, int high) |
| { |
| dbg_event * e; |
| List * frames; |
| |
| CHECK_SESSION(); |
| |
| if (GetStackframes(0, low, high, &frames) != DBGRES_OK) |
| return DBGRES_ERR; |
| |
| e = NewDbgEvent(DBGEV_FRAMES); |
| e->dbg_event_u.list = frames; |
| SaveEvent(e); |
| |
| return DBGRES_OK; |
| } |
| |
| #ifdef notdef |
| struct mi_aif_struct |
| { |
| char *fds; |
| char *data; |
| }; |
| typedef struct mi_aif_struct mi_aif; |
| |
| mi_aif * |
| mi_alloc_aif(void) |
| { |
| return (mi_aif *)mi_calloc1(sizeof(mi_aif)); |
| } |
| |
| mi_aif * |
| mi_parse_aif(mi_results *c) |
| { |
| mi_aif *res = mi_alloc_aif(); |
| |
| if ( res ) |
| { |
| while ( c ) |
| { |
| if ( c->type == t_const ) |
| { |
| if ( strcmp(c->var, "fds") == 0 ) |
| res->fds = c->v.cstr; |
| else if ( strcmp(c->var, "data") == 0 ) |
| res->data = c->v.cstr; |
| } |
| c = c->next; |
| } |
| } |
| |
| return res; |
| } |
| |
| mi_aif * |
| mi_res_aif(mi_h *h) |
| { |
| mi_results *r = mi_res_done_var(MIHandle, "aif"); |
| mi_aif *a = NULL; |
| |
| if (r && r->type == t_tuple) |
| a = mi_parse_aif(r->v.rs); |
| mi_free_results(r); |
| return a; |
| } |
| |
| mi_aif * |
| gmi_aif_evaluate_expression(mi_h *h, char *exp) |
| { |
| mi_send(h, "-aif-evaluate-expression \"%s\"\n", exp); |
| return mi_res_aif(h); |
| } |
| |
| int |
| DumpBinaryValue(MISession *sess, char *exp, char *file) |
| { |
| MICommand * cmd; |
| |
| cmd = MICommandNew("dump binary value", MIResultRecordDONE); |
| MICommandAddOption(cmd, file, exp); |
| |
| SendCommandWait(DebugSession, cmd); |
| |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return -1; |
| } |
| MICommandFree(cmd); |
| return 0; |
| } |
| #endif |
| |
| /* |
| ** List local variables. |
| */ |
| static int |
| GDBMIGetLocalVariables(void) |
| { |
| dbg_event * e; |
| MICommand * cmd; |
| MIArg * arg; |
| List * args; |
| |
| CHECK_SESSION(); |
| |
| cmd = MIStackListLocals(0); |
| |
| SendCommandWait(DebugSession, cmd); |
| |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| |
| args = MIGetStackListLocalsInfo(cmd); |
| |
| MICommandFree(cmd); |
| |
| e = NewDbgEvent(DBGEV_VARS); |
| e->dbg_event_u.list = NewList(); |
| |
| for (SetList(args); (arg = (MIArg *)GetListElement(args)) != NULL; ) { |
| AddToList(e->dbg_event_u.list, (void *)strdup(arg->name)); |
| } |
| |
| DestroyList(args, MIArgFree); |
| |
| SaveEvent(e); |
| |
| 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 |
| static int |
| CurrentFrame(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(DebugSession, 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 */ |
| |
| /* |
| ** List arguments. |
| */ |
| static int |
| GDBMIListArguments(int low, int high) |
| { |
| dbg_event * e; |
| MICommand * cmd; |
| MIArg * arg; |
| MIFrame * frame; |
| List * frames; |
| |
| CHECK_SESSION(); |
| |
| cmd = MIStackListArguments(0, low, high); |
| |
| SendCommandWait(DebugSession, cmd); |
| |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| |
| frames = MIGetStackListArgumentsInfo(cmd); |
| |
| MICommandFree(cmd); |
| |
| e = NewDbgEvent(DBGEV_ARGS); |
| e->dbg_event_u.list = NewList(); |
| |
| /* |
| * Just look at first frame - we should only get |
| * one anyway... |
| * ****** If frame is more than one, no arg needs ***** |
| */ |
| SetList(frames); |
| if ((frame = (MIFrame *)GetListElement(frames)) != NULL) { |
| #if GDB_BUG_2188 |
| if (!CurrentFrame(frame->level, "main")) { |
| #endif /* GDB_BUG_2188 */ |
| for (SetList(frame->args); (arg = (MIArg *)GetListElement(frame->args)) != NULL; ) |
| AddToList(e->dbg_event_u.list, (void *)strdup(arg->name)); |
| #if GDB_BUG_2188 |
| } |
| #endif /* GDB_BUG_2188 */ |
| } |
| DestroyList(frames, MIFrameFree); |
| SaveEvent(e); |
| |
| return DBGRES_OK; |
| } |
| |
| /* |
| ** List global variables. |
| */ |
| static int |
| GDBMIGetGlobalVariables(void) |
| { |
| CHECK_SESSION(); |
| |
| DbgSetError(DBGERR_NOTIMP, NULL); |
| return DBGRES_ERR; |
| } |
| |
| /* |
| ** Quit debugger. |
| */ |
| static int |
| GDBMIQuit(void) |
| { |
| MICommand * cmd; |
| |
| if (DebugSession != NULL) { |
| cmd = MIGDBExit(); |
| SendCommandWait(DebugSession, cmd); |
| MICommandFree(cmd); |
| } |
| RemoveAllMaps(); |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| ServerExit++; |
| |
| return DBGRES_OK; |
| } |
| |
| static int |
| GDBMIGetInfoThread(void) |
| { |
| MICommand * cmd; |
| dbg_event * e; |
| char * tid; |
| CLIInfoThreadsInfo * info; |
| |
| CHECK_SESSION(); |
| |
| cmd = CLIInfoThreads(); |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| DEBUG_PRINTS(DEBUG_LEVEL_BACKEND, "------------------- GDBMIGetInfoThread error\n"); |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| info = CLIGetInfoThreadsInfo(cmd); |
| MICommandFree(cmd); |
| |
| e = NewDbgEvent(DBGEV_THREADS); |
| e->dbg_event_u.threads_event.thread_id = info->current_thread_id; |
| e->dbg_event_u.threads_event.list = NewList(); |
| for (SetList(info->thread_ids); (tid = (char *)GetListElement(info->thread_ids)) != NULL;) { |
| AddToList(e->dbg_event_u.threads_event.list, (void *)strdup(tid)); |
| } |
| |
| DestroyList(info->thread_ids, free); |
| free(info); |
| SaveEvent(e); |
| |
| return DBGRES_OK; |
| } |
| |
| static int |
| GDBMISetThreadSelect(int threadNum) |
| { |
| MICommand * cmd; |
| dbg_event * e; |
| MIThreadSelectInfo * info; |
| //MIFrame *f; |
| stackframe *s = NULL; |
| |
| CHECK_SESSION(); |
| |
| cmd = MIThreadSelect(threadNum); |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| info = MISetThreadSelectInfo(cmd); |
| MICommandFree(cmd); |
| |
| if (info->frame != NULL) { |
| s = ConvertMIFrameToStackframe(info->frame); |
| } |
| e = NewDbgEvent(DBGEV_THREAD_SELECT); |
| e->dbg_event_u.thread_select_event.thread_id = info->current_thread_id; |
| e->dbg_event_u.thread_select_event.frame = s; |
| |
| MIFrameFree(info->frame); |
| free(info); |
| SaveEvent(e); |
| |
| return DBGRES_OK; |
| } |
| |
| static int |
| GDBMIStackInfoDepth() |
| { |
| int depth; |
| dbg_event * e; |
| |
| CHECK_SESSION(); |
| |
| if ((depth = get_info_depth()) == -1) { |
| return DBGRES_ERR; |
| } |
| e = NewDbgEvent(DBGEV_STACK_DEPTH); |
| e->dbg_event_u.stack_depth = depth; |
| SaveEvent(e); |
| return DBGRES_OK; |
| } |
| |
| static int |
| GDBMIDataReadMemory(long offset, char* address, char* format, int wordSize, int rows, int cols, char* asChar) |
| { |
| MICommand * cmd; |
| dbg_event * e; |
| MIDataReadMemoryInfo * info; |
| MIMemory * mem; |
| memoryinfo *meminfo = NULL; |
| memory * m; |
| |
| CHECK_SESSION(); |
| |
| cmd = MIDataReadMemory(offset, address, format, wordSize, rows, cols, asChar); |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| info = MIGetDataReadMemoryInfo(cmd); |
| MICommandFree(cmd); |
| |
| e = NewDbgEvent(DBGEV_DATAR_MEM); |
| 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 (SetList(info->memories); (mem = (MIMemory *)GetListElement(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 (SetList(mem->data); (d = (char *)GetListElement(mem->data)) != NULL;) { |
| AddToList(m->data, (void *) strdup(d)); |
| } |
| } |
| AddToList(meminfo->memories, (void *)m); |
| } |
| } |
| } |
| e->dbg_event_u.meminfo = meminfo; |
| MIDataReadMemoryInfoFree(info); |
| |
| SaveEvent(e); |
| return DBGRES_OK; |
| } |
| |
| static int |
| GDBMIDataWriteMemory(long offset, char* address, char* format, int wordSize, char* value) |
| { |
| MICommand * cmd; |
| |
| CHECK_SESSION(); |
| |
| //DEBUG_PRINTS(DEBUG_LEVEL_BACKEND, "----- gdbmi_sevrer: GDBMIDataWriteMemory called ---------\n"); |
| cmd = MIDataWriteMemory(offset, address, format, wordSize, value); |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| MICommandFree(cmd); |
| //TODO |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| |
| return DBGRES_OK; |
| } |
| |
| static int |
| GDBCLIListSignals(char* name) |
| { |
| MICommand * cmd; |
| List *signals; |
| dbg_event * e; |
| |
| CHECK_SESSION(); |
| |
| cmd = CLIListSignals(name); |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| CLIGetSigHandleList(cmd, &signals); |
| MICommandFree(cmd); |
| |
| e = NewDbgEvent(DBGEV_SIGNALS); |
| e->dbg_event_u.list = signals; |
| SaveEvent(e); |
| |
| return DBGRES_OK; |
| } |
| |
| static int |
| GDBCLISignalInfo(char* arg) |
| { |
| MICommand * cmd; |
| |
| CHECK_SESSION(); |
| |
| cmd = CLISignalInfo(arg); |
| MICommandRegisterCallback(cmd, ProcessCLIResultRecord, DebugSession); |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| MICommandFree(cmd); |
| return DBGRES_OK; |
| } |
| |
| static int |
| GDBCLIHandle(char *arg) |
| { |
| MICommand *cmd; |
| |
| CHECK_SESSION(); |
| |
| cmd = CLIHandle(arg); |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return DBGRES_ERR; |
| } |
| MICommandFree(cmd); |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| return DBGRES_OK; |
| } |
| |
| static int |
| GDBMIDataEvaluateExpression(char *arg) |
| { |
| MICommand *cmd; |
| dbg_event * e; |
| char *res = NULL; |
| |
| CHECK_SESSION(); |
| |
| cmd = MIDataEvaluateExpression(arg); |
| SendCommandWait(DebugSession, cmd); |
| |
| if (MICommandResultOK(cmd)) { |
| res = MIGetDataEvaluateExpressionInfo(cmd); |
| } |
| if (res == NULL) { |
| res = strdup("No value found."); |
| } |
| MICommandFree(cmd); |
| |
| e = NewDbgEvent(DBGEV_DATA_EVA_EX); |
| e->dbg_event_u.data_expression = res; |
| SaveEvent(e); |
| |
| return DBGRES_OK; |
| } |
| |
| /* |
| ** Evaluate the expression exp. |
| */ |
| static int |
| GDBMIEvaluateExpression(char *exp) |
| { |
| char * type; |
| AIF * a; |
| dbg_event * e; |
| |
| if (GetAIFVar(exp, &a, &type) != DBGRES_OK) |
| return DBGRES_ERR; |
| |
| e = NewDbgEvent(DBGEV_DATA); |
| e->dbg_event_u.data_event.data = a; |
| e->dbg_event_u.data_event.type_desc = type; |
| SaveEvent(e); |
| |
| return DBGRES_OK; |
| } |
| /* |
| ** Find native type of variable. |
| */ |
| static int |
| GDBMIGetNativeType(char *var) |
| { |
| dbg_event * e; |
| AIF * a; |
| char * type; |
| |
| CHECK_SESSION(); |
| |
| if (GetAIFVar(var, &a, &type) != DBGRES_OK) |
| return DBGRES_ERR; |
| |
| e = NewDbgEvent(DBGEV_TYPE); |
| e->dbg_event_u.type_desc = type; |
| |
| SaveEvent(e); |
| AIFFree(a); |
| return DBGRES_OK; |
| } |
| |
| static int |
| GetAIFVar(char *var, AIF **val, char **type) |
| { |
| AIF * res; |
| MIVar *mivar; |
| |
| mivar = CreateMIVar(var); |
| if (mivar == NULL) { |
| DbgSetError(DBGERR_UNKNOWN_VARIABLE, var); |
| return DBGRES_ERR; |
| } |
| |
| if ((res = GetAIF(mivar, var, 0)) == NULL) { |
| DbgSetError(DBGERR_UNKNOWN_TYPE, mivar->type); |
| DeleteMIVar(mivar->name); |
| MIVarFree(mivar); |
| return DBGRES_ERR; |
| } |
| *type = strdup(mivar->type); |
| *val = res; |
| |
| DeleteMIVar(mivar->name); |
| MIVarFree(mivar); |
| |
| return DBGRES_OK; |
| } |
| |
| static char * |
| GetVarValue(char *var) |
| { |
| char * res; |
| MICommand * cmd = MIVarEvaluateExpression(var); |
| |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return ""; |
| } |
| res = MIGetVarEvaluateExpressionInfo(cmd); |
| MICommandFree(cmd); |
| return res; |
| } |
| |
| static int |
| GetAddressLength() |
| { |
| if (ADDRESS_LENGTH != 0) { |
| return ADDRESS_LENGTH; |
| } |
| char * res; |
| MICommand * cmd = MIDataEvaluateExpression("\"sizeof(char *)\""); |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| SetDebugError(cmd); |
| MICommandFree(cmd); |
| return 0; |
| } |
| res = MIGetDataEvaluateExpressionInfo(cmd); |
| MICommandFree(cmd); |
| |
| ADDRESS_LENGTH = (int)strtol(res, NULL, 10); |
| return ADDRESS_LENGTH; |
| } |
| |
| static char * |
| GetPtypeValue(char *exp) |
| { |
| char * type = NULL; |
| MICommand* cmd = CLIPType(exp); |
| SendCommandWait(DebugSession, cmd); |
| type = CLIGetPTypeInfo(cmd); |
| MICommandFree(cmd); |
| return type; |
| } |
| |
| /******************************************************** |
| * TYPE CONVERSION |
| ********************************************************/ |
| #define T_OTHER 0 |
| #define T_CHAR 1 |
| #define T_SHORT 2 |
| #define T_USHORT 3 |
| #define T_INT 4 |
| #define T_UINT 5 |
| #define T_LONG 6 |
| #define T_ULONG 7 |
| #define T_LONGLONG 8 |
| #define T_ULONGLONG 9 |
| #define T_FLOAT 10 |
| #define T_DOUBLE 11 |
| #define T_STRING 12 |
| #define T_BOOLEAN 13 |
| |
| #define T_CHAR_PTR 14 |
| #define T_FUNCTION 15 |
| #define T_VOID_PTR 16 |
| #define T_UNION 17 |
| #define T_ENUM 18 |
| #define T_ARRAY 19 |
| #define T_STRUCT 20 |
| #define T_POINTER 21 |
| |
| static int |
| get_simple_type(char *type) |
| { |
| char *t = NULL; |
| int id; |
| int len = strlen(type); |
| |
| if (type[len - 1] == ')') { // function |
| return T_FUNCTION; |
| } |
| if (strncmp(type, "void *", 6) == 0) { // void pointer |
| return T_VOID_PTR; |
| } |
| if (strncmp(type, "enum", 4) == 0) { // enum |
| return T_ENUM; |
| } |
| |
| //check modifiers |
| if (strncmp(type, "const volatile", 14) == 0) |
| t = strdup(&type[15]); //+ 1 remove whitespeace |
| else if (strncmp(type, "volatile", 8) == 0) |
| t = strdup(&type[9]); //+ 1 remove whitespeace |
| else if (strncmp(type, "const", 5) == 0) |
| t = strdup(&type[6]); //+ 1 remove whitespeace |
| else |
| t = strdup(type); |
| |
| if (strncmp(t, "char *", 6) == 0) |
| id = T_CHAR_PTR; |
| else if (strncmp(t, "char", 4) == 0) |
| id = T_CHAR; |
| else if (strncmp(t, "unsigned char", 13) == 0) |
| id = T_CHAR; |
| else if (strncmp(t, "short int", 9) == 0 || strncmp(t, "int2", 4) == 0) |
| id = T_SHORT; |
| else if (strncmp(t, "short unsigned int", 18) == 0) |
| id = T_USHORT; |
| else if (strncmp(t, "int", 3) == 0 || strncmp(t, "int4", 4) == 0) |
| id = T_INT; |
| else if (strncmp(t, "unsigned int", 12) == 0) |
| id = T_UINT; |
| else if (strncmp(t, "long int", 8) == 0 || strncmp(t, "int8", 4) == 0) |
| id = T_LONG; |
| else if (strncmp(t, "long unsigned int", 17) == 0) |
| id = T_ULONG; |
| #ifdef CC_HAS_LONG_LONG |
| else if (strncmp(t, "long long int", 13) == 0 || strncmp(t, "real*16", 7) == 0) |
| id = T_LONGLONG; |
| else if (strncmp(t, "long long unsigned int", 22) == 0) |
| id = T_ULONGLONG; |
| #endif /* CC_HAS_LONG_LONG */ |
| else if (strncmp(t, "long", 4) == 0 || strncmp(t, "real*4", 6) == 0) |
| id = T_LONG; |
| else if (strncmp(t, "float", 5) == 0 || strncmp(t, "real*8", 6) == 0) |
| id = T_FLOAT; |
| else if (strncmp(t, "double", 6) == 0) |
| id = T_DOUBLE; |
| else if (strncmp(t, "string", 6) == 0) |
| id = T_STRING; |
| else if (strncmp(t, "logical4", 8) == 0) |
| id = T_BOOLEAN; |
| else |
| id = T_OTHER; |
| |
| free(t); |
| return id; |
| } |
| |
| static int |
| get_complex_type(char *type) |
| { |
| int len = strlen(type); |
| switch (type[len - 1]) { |
| case ']': |
| return T_ARRAY; |
| case '*': |
| if (type[len - 2] == '*') //pointer pointer |
| return T_POINTER; |
| if (strncmp(type, "char", 4) == 0) //char pointer |
| return T_CHAR_PTR; |
| return T_POINTER; //normal pointer |
| default: |
| if (strncmp(type, "union", 5) == 0) |
| return T_UNION; |
| |
| if (strncmp(type, "struct", 6) == 0) |
| return T_STRUCT; |
| |
| return T_OTHER; |
| } |
| } |
| |
| static AIF* |
| GetPrimitiveAIF(int id, char *res) |
| { |
| char *pch; |
| |
| if (res == NULL) { |
| return NULL; |
| } |
| switch (id) { |
| case T_STRING: |
| case T_CHAR: |
| if ((pch = strchr(res, ' ')) != NULL) { |
| pch++; |
| if (*pch == '\'') { //character |
| pch--; |
| *pch = '\0'; |
| return CharToAIF((char)strtol(res, NULL, 10)); |
| } |
| else { //string |
| return StringToAIF(pch); |
| } |
| } |
| else { |
| return CharToAIF((char)strtol(res, NULL, 10)); |
| } |
| break; |
| case T_SHORT: |
| return ShortToAIF((short)strtol(res, NULL, 10)); |
| case T_USHORT: |
| return UnsignedShortToAIF((unsigned short)strtoul(res, NULL, 10)); |
| case T_INT: |
| return IntToAIF((int)strtol(res, NULL, 10)); |
| case T_UINT: |
| return UnsignedIntToAIF((unsigned int)strtoul(res, NULL, 10)); |
| case T_LONG: |
| return LongToAIF(strtol(res, NULL, 10)); |
| case T_ULONG: |
| return UnsignedLongToAIF((unsigned long)strtoul(res, NULL, 10)); |
| #ifdef CC_HAS_LONG_LONG |
| case T_LONGLONG: |
| return LongLongToAIF(strtoll(res, NULL)); |
| case T_ULONGLONG: |
| return UnsignedLongLongToAIF((unsigned long long)strtoull(res, NULL)); |
| #endif /* CC_HAS_LONG_LONG */ |
| case T_FLOAT: |
| return FloatToAIF((float)strtod(res, NULL)); |
| case T_DOUBLE: |
| return DoubleToAIF(strtod(res, NULL)); |
| default://other type |
| return VoidToAIF(0, 0); |
| } |
| } |
| |
| static AIF * |
| GetAIFPointer(char *addr, AIF *i) |
| { |
| AIF *ac; |
| AIF *a; |
| char *pch; |
| |
| if (addr == NULL) { |
| ac = VoidToAIF(0, 0); |
| } |
| else { |
| if ((pch = strchr(addr, ' ')) != NULL) { |
| *pch = '\0'; |
| } |
| addr += 2; //skip 0x |
| ac = AddressToAIF(addr, GetAddressLength()); |
| } |
| a = PointerToAIF(ac, i); |
| AIFFree(ac); |
| return a; |
| } |
| |
| static AIF * |
| GetCharPointerAIF(char *res) |
| { |
| char *pch; |
| char *val; |
| AIF *a; |
| AIF *ac; |
| |
| if ((pch = strchr(res, ' ')) != NULL) { |
| val = strdup(pch+1); |
| *pch = '\0'; |
| |
| res += 2; //skip 0x |
| ac = AddressToAIF(res, GetAddressLength()); |
| a = CharPointerToAIF(ac, val); |
| free(val); |
| AIFFree(ac); |
| return a; |
| } |
| return VoidToAIF(0, 0); |
| } |
| |
| static AIF * |
| GetSimpleAIF(MIVar *var, char *exp) |
| { |
| AIF *a = NULL; |
| AIF *ac; |
| char *pt; |
| |
| int id = get_simple_type(var->type); |
| if (id == T_OTHER) { |
| pt = GetPtypeValue(var->type); |
| if (pt != NULL) { |
| var->type = strdup(pt); |
| id = get_simple_type(var->type); |
| free(pt); |
| } |
| } |
| switch (id) { |
| case T_FUNCTION: |
| return MakeAIF("&/is4", exp); |
| case T_VOID_PTR: |
| ac = VoidToAIF(0, 0); |
| a = GetAIFPointer(GetVarValue(var->name), ac); |
| AIFFree(ac); |
| return a; |
| case T_ENUM: |
| return (var->type[4] == ' ') ? EmptyEnumToAIF(&var->type[5]) : EmptyEnumToAIF(NULL); |
| case T_OTHER: |
| if (exp == NULL) |
| return NULL; |
| |
| return GetSimpleAIF(var, NULL); |
| default: |
| return GetPrimitiveAIF(id, GetVarValue(var->name)); |
| } |
| } |
| |
| static AIF* |
| GetNamedAIF(AIF *a, int named) |
| { |
| if (FDSType(AIF_FORMAT(a)) != AIF_NAME) { |
| return NameAIF(a, named); |
| } |
| return a; |
| } |
| |
| static AIF * |
| GetStructAIF(MIVar *var, int named) |
| { |
| int i; |
| MIVar * v; |
| AIF * a; |
| AIF * ac; |
| char *pch; |
| char *struct_name = strdup(var->type); |
| named++; |
| |
| if ((pch = strchr(struct_name, ' ')) != NULL) { |
| pch++; |
| a = EmptyStructToAIF(pch); |
| } |
| else { |
| a = EmptyStructToAIF(struct_name); |
| } |
| free(struct_name); |
| |
| for (i=0; i<var->numchild; i++) { |
| v = var->children[i]; |
| //check whether child contains parent |
| if (strcmp(var->type, v->type) == 0 && strcmp(var->name, v->name)) { |
| a = GetNamedAIF(a, named); |
| ac = AIFNull(a); |
| } |
| else { |
| if ((ac = GetAIF(v, v->exp, named)) == NULL) { |
| AIFFree(a); |
| return NULL; |
| } |
| } |
| AIFAddFieldToStruct(a, v->exp, ac); |
| } |
| return a; |
| } |
| |
| static AIF * |
| GetUnionAIF(MIVar *var, int named) |
| { |
| int i; |
| MIVar * v; |
| AIF * a; |
| AIF * ac; |
| char *union_name = NULL; |
| named++; |
| |
| if (var->type[5] == ' ') { |
| union_name = strdup(&var->type[6]); |
| } |
| a = EmptyUnionToAIF(union_name); |
| if (union_name != NULL) |
| free(union_name); |
| |
| for (i=0; i<var->numchild; i++) { |
| v = var->children[i]; |
| //check whether child contains parent |
| if (strcmp(var->type, v->type) == 0 && strcmp(var->name, v->name)) { |
| a = GetNamedAIF(a, named); |
| ac = AIFNull(a); |
| } |
| else { |
| if ((ac = GetAIF(v, v->exp, named)) == NULL) { |
| AIFFree(a); |
| return NULL; |
| } |
| } |
| AIFAddFieldToUnion(a, v->exp, AIF_FORMAT(ac)); |
| |
| //Set the union value |
| if (i == var->numchild - 1) |
| AIFSetUnion(a, v->exp, ac); |
| |
| AIFFree(ac); |
| } |
| return a; |
| } |
| |
| static AIF * |
| GetArrayAIF(MIVar *var, int named) |
| { |
| int i; |
| MIVar * v; |
| AIF * a = NULL; |
| AIF * ac; |
| |
| for (i = 0; i < var->numchild; i++) { |
| v = var->children[i]; |
| if ((ac = GetAIF(v, v->exp, named)) == NULL) { |
| return NULL; |
| } |
| if (a == NULL) |
| a = EmptyArrayToAIF(0, var->numchild-1, ac); |
| AIFAddArrayElement(a, i, ac); |
| AIFFree(ac); |
| } |
| return a; |
| } |
| |
| static AIF * |
| GetPointerAIF(MIVar *var, int named) |
| { |
| AIF *ac; |
| AIF *a; |
| int id; |
| |
| var->type[strlen(var->type) - 1] = '\0'; //remove pointer |
| while (var->type[strlen(var->type) - 1] == ' ') {//remove whilespace |
| var->type[strlen(var->type) - 1] = '\0'; |
| } |
| |
| id = get_complex_type(var->type); |
| switch (id) { |
| case T_POINTER: |
| ac = GetAIF(var->children[0], var->children[0]->exp, named); |
| break; |
| case T_CHAR_PTR: |
| return GetCharPointerAIF(GetVarValue(var->children[0]->name)); |
| case T_UNION: |
| ac = GetUnionAIF(var, named); |
| break; |
| default: |
| if (var->numchild == 1) { |
| ac = GetAIF(var->children[0], var->children[0]->exp, named); |
| } |
| else { |
| ac = GetStructAIF(var, named); |
| } |
| break; |
| } |
| |
| if (ac == NULL) { |
| ac = VoidToAIF(0, 0); |
| } |
| a = GetAIFPointer(GetVarValue(var->name), ac); |
| AIFFree(ac); |
| return a; |
| } |
| |
| static AIF * |
| GetComplexAIF(MIVar *var, char *exp, int named) |
| { |
| int id = get_complex_type(var->type); |
| switch (id) { |
| case T_ARRAY: |
| return GetArrayAIF(var, named); |
| case T_CHAR_PTR: |
| return GetCharPointerAIF(GetVarValue(var->name)); |
| case T_POINTER: |
| return GetPointerAIF(var, named); |
| case T_UNION: |
| return GetUnionAIF(var, named); |
| default://struct |
| return GetStructAIF(var, named); |
| } |
| } |
| |
| static AIF * |
| GetAIF(MIVar *var, char *exp, int named) |
| { |
| MICommand *cmd; |
| |
| if (strcmp(var->type, "<text variable, no debug info>") == 0) { |
| DbgSetError(DBGERR_NOSYMS, ""); |
| return NULL; |
| } |
| if (var->numchild == 0) { //simple type |
| return GetSimpleAIF(var, exp); |
| } |
| //complex type |
| cmd = MIVarListChildren(var->name); |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(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"); |
| } |
| MICommandFree(cmd); |
| return NULL; |
| } |
| MIGetVarListChildrenInfo(var, cmd); |
| MICommandFree(cmd); |
| |
| return GetComplexAIF(var, exp, named); |
| } |
| |
| /*************************** PARTIAL AIF ***************************/ |
| static AIF * |
| GetPartialArrayAIF(MIVar *var) |
| { |
| AIF *a = NULL; |
| AIF *ac; |
| int i; |
| char *pch; |
| char *pt; |
| int id; |
| |
| if (var->children == NULL) { |
| pch = strchr(var->type, '['); |
| *pch = '\0'; |
| while (var->type[strlen(var->type) - 1] == ' ') {//remove whilespace |
| var->type[strlen(var->type) - 1] = '\0'; |
| } |
| id = get_simple_type(var->type); |
| if (id == T_OTHER) { |
| pt = GetPtypeValue(var->type); |
| if (pt != NULL) { |
| var->type = strdup(pt); |
| id = get_simple_type(var->type); |
| free(pt); |
| } |
| } |
| ac = GetPrimitiveAIF(id, ""); |
| a = EmptyArrayToAIF(0, var->numchild-1, ac); |
| AIFFree(ac); |
| } |
| else { |
| for (i=0; i<var->numchild; i++) { |
| ac = GetPartialAIF(var->children[i], NULL); |
| if (a == NULL) { |
| a = EmptyArrayToAIF(0, var->numchild-1, ac); |
| } |
| AIFAddArrayElement(a, i, ac); |
| AIFFree(ac); |
| } |
| } |
| return a; |
| } |
| |
| static AIF * |
| GetPartialStructAIF(MIVar *var) |
| { |
| AIF *ac; |
| AIF *a; |
| int i; |
| char *pch; |
| char *struct_name = strdup(var->type); |
| |
| if ((pch = strchr(struct_name, ' ')) != NULL) { |
| pch++; |
| a = EmptyStructToAIF(pch); |
| } |
| else { |
| a = EmptyStructToAIF(struct_name); |
| } |
| free(struct_name); |
| |
| if (var->children != NULL) { |
| for (i=0; i<var->numchild; i++) { |
| ac = GetPartialAIF(var->children[i], var->children[i]->exp); |
| AIFAddFieldToStruct(a, var->children[i]->exp, ac); |
| AIFFree(ac); |
| } |
| } |
| return a; |
| } |
| |
| static AIF * |
| GetPartialUnionAIF(MIVar *var) |
| { |
| AIF *ac; |
| AIF *a; |
| int i; |
| char *union_name = NULL; |
| |
| if (var->type[5] == ' ') { |
| union_name = strdup(&var->type[6]); |
| } |
| a = EmptyUnionToAIF(union_name); |
| if (union_name != NULL) |
| free(union_name); |
| |
| if (var->children != NULL) { |
| for (i=0; i<var->numchild; i++) { |
| ac = GetPartialAIF(var->children[i], var->children[i]->exp); |
| AIFAddFieldToUnion(a, var->children[i]->exp, AIF_FORMAT(ac)); |
| if (i == var->numchild - 1) { |
| AIFSetUnion(a, var->children[i]->exp, ac); |
| } |
| AIFFree(ac); |
| } |
| } |
| return a; |
| } |
| |
| static AIF * |
| GetPartialPointerAIF(MIVar *var) |
| { |
| AIF *ac; |
| AIF *a; |
| int id; |
| |
| if (var->children != NULL) { |
| var->type[strlen(var->type) - 1] = '\0'; //remove pointer |
| while (var->type[strlen(var->type) - 1] == ' ') {//remove whilespace |
| var->type[strlen(var->type) - 1] = '\0'; |
| } |
| |
| id = get_complex_type(var->type); |
| switch (id) { |
| case T_CHAR_PTR: |
| //replace miname |
| var->name = strdup(var->children[0]->exp); |
| a = GetCharPointerAIF(GetVarValue(var->children[0]->name)); |
| break; |
| case T_POINTER: |
| //replace miname |
| var->name = strdup(var->children[0]->exp); |
| ac = VoidToAIF(0, 0); |
| a = GetAIFPointer(GetVarValue(var->children[0]->name), ac); |
| AIFFree(ac); |
| break; |
| case T_UNION: |
| a = GetPartialUnionAIF(var); |
| break; |
| default: |
| if (var->numchild == 1) { |
| //replace miname |
| var->name = strdup(var->children[0]->exp); |
| a = GetPartialAIF(var->children[0], var->children[0]->exp); |
| } |
| else { |
| a = GetPartialStructAIF(var); |
| } |
| break; |
| } |
| } |
| else { |
| id = get_complex_type(var->type); |
| switch (id) { |
| case T_CHAR_PTR: |
| a = GetCharPointerAIF(GetVarValue(var->name)); |
| break; |
| default: |
| ac = VoidToAIF(0, 0); |
| a = GetAIFPointer(GetVarValue(var->name), ac); |
| AIFFree(ac); |
| break; |
| } |
| } |
| return a; |
| } |
| |
| static AIF * |
| GetPartialComplexAIF(MIVar *var, char *exp) |
| { |
| char *pt; |
| int id = get_complex_type(var->type); |
| if (id == T_OTHER) { |
| pt = GetPtypeValue(var->type); |
| if (pt != NULL) { |
| var->type = pt; |
| id = get_complex_type(var->type); |
| } |
| } |
| switch (id) { |
| case T_ARRAY: |
| return GetPartialArrayAIF(var); |
| case T_CHAR_PTR: |
| return GetCharPointerAIF(GetVarValue(var->name)); |
| case T_POINTER: |
| return GetPartialPointerAIF(var); |
| case T_UNION: |
| return GetPartialUnionAIF(var); |
| default://struct |
| return GetPartialStructAIF(var); |
| } |
| } |
| |
| static AIF * |
| GetPartialAIF(MIVar *var, char *exp) |
| { |
| if (strcmp(var->type, "<text variable, no debug info>") == 0) { |
| DbgSetError(DBGERR_NOSYMS, ""); |
| return NULL; |
| } |
| if (var->numchild == 0) { |
| return GetSimpleAIF(var, exp); |
| } |
| return GetPartialComplexAIF(var, exp); |
| } |
| |
| static MIVar * |
| GetChildrenMIVar(char *mivar_name, MIVar *mivar, int showParentType) |
| { |
| MICommand *cmd; |
| |
| if (showParentType || mivar == NULL) { |
| cmd = MIVarInfoType(mivar_name); |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| MICommandFree(cmd); |
| return NULL; |
| } |
| mivar = MIGetVarInfoType(cmd); |
| mivar->name = strdup(mivar_name); |
| MICommandFree(cmd); |
| } |
| cmd = MIVarListChildren(mivar_name); |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| MICommandFree(cmd); |
| return NULL; |
| } |
| MIGetVarListChildrenInfo(mivar, cmd); |
| MICommandFree(cmd); |
| return mivar; |
| } |
| |
| static MIVar * |
| GetMIVarDetails(char *name) |
| { |
| MICommand *cmd; |
| MIVar *mivar; |
| |
| cmd = MIVarInfoType(name); |
| SendCommandWait(DebugSession, cmd); |
| if (!MICommandResultOK(cmd)) { |
| MICommandFree(cmd); |
| return NULL; |
| } |
| mivar = MIGetVarInfoType(cmd); |
| mivar->name = strdup(name); |
| MICommandFree(cmd); |
| |
| cmd = MIVarInfoNumChildren(name); |
| SendCommandWait(DebugSession, cmd); |
| MIGetVarInfoNumChildren(cmd, mivar); |
| MICommandFree(cmd); |
| return mivar; |
| } |
| |
| static int |
| GDBGetPartialAIF(char* name, char* key, int listChildren, int express) |
| { |
| MIVar *mivar; |
| AIF *a; |
| dbg_event * e; |
| char* var_name; |
| |
| CHECK_SESSION(); |
| |
| //key is first perference if it exists |
| var_name = (key == NULL || strlen(key) == 0)?name:key; |
| |
| if (express) { |
| mivar = GetMIVarDetails(var_name); |
| } |
| else { |
| //@ - for partial array |
| if (!listChildren || strchr(var_name, '@') != NULL) { |
| mivar = CreateMIVar(var_name); |
| if (listChildren) { |
| mivar = GetChildrenMIVar(mivar->name, mivar, 0); |
| } |
| } |
| else { |
| mivar = GetChildrenMIVar(var_name, NULL, 1); |
| } |
| } |
| |
| if (mivar == NULL) { |
| //try again with original variable name |
| var_name = name; |
| if ((mivar = CreateMIVar(var_name)) == NULL) { |
| DbgSetError(DBGERR_UNKNOWN_VARIABLE, var_name); |
| return DBGRES_ERR; |
| } |
| } |
| if ((a = GetPartialAIF(mivar, var_name)) == NULL) { |
| DbgSetError(DBGERR_UNKNOWN_TYPE, mivar->type); |
| MIVarFree(mivar); |
| return DBGRES_ERR; |
| } |
| |
| DEBUG_PRINTF(DEBUG_LEVEL_BACKEND, "---------------------- GDBGetPartialAIF found key: %s, format: %s\n", mivar->name, AIF_FORMAT(a)); |
| |
| e = NewDbgEvent(DBGEV_PARTIAL_AIF); |
| e->dbg_event_u.partial_aif_event.data = a; |
| e->dbg_event_u.partial_aif_event.type_desc = strdup(mivar->type); |
| e->dbg_event_u.partial_aif_event.name = strdup(mivar->name); |
| |
| MIVarFree(mivar); |
| SaveEvent(e); |
| |
| return DBGRES_OK; |
| } |
| |
| static int |
| GDBMIVarDelete(char *name) |
| { |
| CHECK_SESSION(); |
| DeleteMIVar(name); |
| SaveEvent(NewDbgEvent(DBGEV_OK)); |
| return DBGRES_OK; |
| } |
| |
| #if 0 |
| char tohex[] = {'0', '1', '2', '3', '4', '5', '6', '7', |
| '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'}; |
| |
| static int |
| GDBMIBuildAIFVar(char *var, char *type, char *file, AIF **aif) |
| { |
| int n; |
| int fd; |
| char * data; |
| char * ap; |
| char * bp; |
| char buf[BUFSIZ]; |
| struct stat sb; |
| |
| if ( stat(file, &sb) < 0 ) |
| { |
| DbgSetError(DBGERR_DEBUGGER, (char *)strerror(errno)); |
| return DBGRES_ERR; |
| } |
| |
| if ( FDSType(type) == AIF_FUNCTION ) |
| { |
| /* |
| ** Data is function name |
| */ |
| ap = data = malloc(strlen(var) * 2 + 1); |
| |
| for ( bp = var ; *bp != '\0' ; bp++ ) |
| { |
| *ap++ = tohex[(*bp >> 4) & 0xf]; |
| *ap++ = tohex[*bp & 0xf]; |
| } |
| |
| *ap++ = '\0'; |
| } |
| else |
| { |
| if ( (fd = open(file, O_RDONLY)) < 0 ) |
| { |
| DbgSetError(DBGERR_DEBUGGER, (char *)strerror(errno)); |
| return DBGRES_ERR; |
| } |
| |
| ap = data = malloc(sb.st_size * 2 + 1); |
| |
| while ((n = read(fd, buf, BUFSIZ)) > 0) |
| { |
| bp = buf; |
| |
| while ( n-- > 0 ) |
| { |
| *ap++ = tohex[(*bp >> 4) & 0xf]; |
| *ap++ = tohex[*bp++ & 0xf]; |
| } |
| } |
| |
| *ap++ = '\0'; |
| |
| (void)close(fd); |
| } |
| |
| if ( FDSType(type) == AIF_POINTER ) |
| { |
| /* |
| * Need to add marker to data |
| */ |
| ap = malloc(sb.st_size * 2 + 3); |
| *ap++ = '0'; |
| *ap++ = '1'; // normal pointer |
| memcpy(ap, data, sb.st_size * 2 + 1); |
| data = ap; |
| } |
| |
| if ( (*aif = AsciiToAIF(type, data)) == NULL ) |
| { |
| DbgSetError(DBGERR_DEBUGGER, AIFErrorStr()); |
| return DBGRES_ERR; |
| } |
| |
| return DBGRES_OK; |
| } |
| #endif |