X-Git-Url: https://git.xonotic.org/?a=blobdiff_plain;f=prvm_edict.c;h=191197f5fed5ae1c49b591b8d9496a90028a9332;hb=899b85faed94b09a0b8a0738617927f0bba4d6b3;hp=82392bbf8921b8082e0160566c8f5575bdddfcad;hpb=7a1436391f520ba36288eff409fab6019f7cba37;p=xonotic%2Fdarkplaces.git diff --git a/prvm_edict.c b/prvm_edict.c index 82392bbf..191197f5 100644 --- a/prvm_edict.c +++ b/prvm_edict.c @@ -39,6 +39,7 @@ cvar_t prvm_backtraceforwarnings = {0, "prvm_backtraceforwarnings", "0", "print cvar_t prvm_leaktest = {0, "prvm_leaktest", "0", "try to detect memory leaks in strings or entities"}; cvar_t prvm_leaktest_ignore_classnames = {0, "prvm_leaktest_ignore_classnames", "", "classnames of entities to NOT leak check because they are found by find(world, classname, ...) but are actually spawned by QC code (NOT map entities)"}; cvar_t prvm_errordump = {0, "prvm_errordump", "0", "write a savegame on crash to crash-server.dmp"}; +cvar_t prvm_breakpointdump = {0, "prvm_breakpointdump", "0", "write a savegame on breakpoint to breakpoint-server.dmp"}; cvar_t prvm_reuseedicts_startuptime = {0, "prvm_reuseedicts_startuptime", "2", "allows immediate re-use of freed entity slots during start of new level (value in seconds)"}; cvar_t prvm_reuseedicts_neverinsameframe = {0, "prvm_reuseedicts_neverinsameframe", "1", "never allows re-use of freed entity slots during same frame"}; @@ -442,11 +443,11 @@ static char *PRVM_ValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val break; case ev_float: // LordHavoc: changed from %5.1f to %10.4f - dpsnprintf (line, linelength, "%10.4f", val->_float); + dpsnprintf (line, linelength, FLOAT_LOSSLESS_FORMAT, val->_float); break; case ev_vector: // LordHavoc: changed from %5.1f to %10.4f - dpsnprintf (line, linelength, "'%10.4f %10.4f %10.4f'", val->vector[0], val->vector[1], val->vector[2]); + dpsnprintf (line, linelength, "'" VECTOR_LOSSLESS_FORMAT "'", val->vector[0], val->vector[1], val->vector[2]); break; case ev_pointer: dpsnprintf (line, linelength, "pointer"); @@ -512,7 +513,7 @@ char *PRVM_UglyValueString (prvm_prog_t *prog, etype_t type, prvm_eval_t *val, c line[i] = '\0'; break; case ev_entity: - dpsnprintf (line, linelength, "%i", PRVM_NUM_FOR_EDICT(PRVM_PROG_TO_EDICT(val->edict))); + dpsnprintf (line, linelength, "%i", val->edict); break; case ev_function: f = prog->functions + val->function; @@ -1814,12 +1815,18 @@ static void PRVM_PO_Destroy(po_t *po) void PRVM_LeakTest(prvm_prog_t *prog); void PRVM_Prog_Reset(prvm_prog_t *prog) { - PRVM_LeakTest(prog); - prog->reset_cmd(prog); - Mem_FreePool(&prog->progs_mempool); - if(prog->po) - PRVM_PO_Destroy((po_t *) prog->po); + if (prog->loaded) + { + PRVM_LeakTest(prog); + prog->reset_cmd(prog); + Mem_FreePool(&prog->progs_mempool); + if(prog->po) + PRVM_PO_Destroy((po_t *) prog->po); + } memset(prog,0,sizeof(prvm_prog_t)); + prog->break_statement = -1; + prog->watch_global_type = ev_void; + prog->watch_field_type = ev_void; } /* @@ -1875,7 +1882,8 @@ static void PRVM_LoadLNO( prvm_prog_t *prog, const char *progname ) { PRVM_LoadProgs =============== */ -void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, int numrequiredfunc, const char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, prvm_required_field_t *required_global) +static void PRVM_UpdateBreakpoints(prvm_prog_t *prog); +void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, unsigned char * data, fs_offset_t size, int numrequiredfunc, const char **required_func, int numrequiredfields, prvm_required_field_t *required_field, int numrequiredglobals, prvm_required_field_t *required_global) { int i; dprograms_t *dprograms; @@ -1906,7 +1914,13 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, int numrequiredfun Host_LockSession(); // all progs can use the session cvar Crypto_LoadKeys(); // all progs might use the keys at init time - dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize); + if (data) + { + dprograms = (dprograms_t *) data; + filesize = size; + } + else + dprograms = (dprograms_t *)FS_LoadFile (filename, prog->progs_mempool, false, &filesize); if (dprograms == NULL || filesize < (fs_offset_t)sizeof(dprograms_t)) prog->error_cmd("PRVM_LoadProgs: couldn't load %s for %s", filename, prog->name); // TODO bounds check header fields (e.g. numstatements), they must never go behind end of file @@ -1964,7 +1978,10 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, int numrequiredfun // we need to expand the globaldefs and fielddefs to include engine defs prog->globaldefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobaldefs + numrequiredglobals) * sizeof(ddef_t)); - prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace) * sizeof(prvm_vec_t)); + prog->globals.fp = (prvm_vec_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numglobals + requiredglobalspace + 2) * sizeof(prvm_vec_t)); + // + 2 is because of an otherwise occurring overrun in RETURN instruction + // when trying to return the last or second-last global + // (RETURN always returns a vector, there is no RETURN_F instruction) prog->fielddefs = (ddef_t *)Mem_Alloc(prog->progs_mempool, (prog->progs_numfielddefs + numrequiredfields) * sizeof(ddef_t)); // we need to convert the statements to our memory format prog->statements = (mstatement_t *)Mem_Alloc(prog->progs_mempool, prog->progs_numstatements * sizeof(mstatement_t)); @@ -2210,7 +2227,8 @@ void PRVM_Prog_Load(prvm_prog_t *prog, const char * filename, int numrequiredfun } // we're done with the file now - Mem_Free(dprograms); + if(!data) + Mem_Free(dprograms); dprograms = NULL; // check required functions @@ -2388,6 +2406,8 @@ fail: prog->loaded = TRUE; + PRVM_UpdateBreakpoints(prog); + // set flags & ddef_ts in prog prog->flag = 0; @@ -2613,6 +2633,214 @@ static void PRVM_GlobalSet_f(void) PRVM_ED_ParseEpair( prog, NULL, global, Cmd_Argv(3), true ); } +/* +====================== +Break- and Watchpoints +====================== +*/ +typedef struct +{ + char break_statement[256]; + char watch_global[256]; + int watch_edict; + char watch_field[256]; +} +debug_data_t; +static debug_data_t debug_data[PRVM_PROG_MAX]; + +void PRVM_Breakpoint(prvm_prog_t *prog, int stack_index, const char *text) +{ + char vabuf[1024]; + Con_Printf("PRVM_Breakpoint: %s\n", text); + PRVM_PrintState(prog, stack_index); + if (prvm_breakpointdump.integer) + Host_Savegame_to(prog, va(vabuf, sizeof(vabuf), "breakpoint-%s.dmp", prog->name)); +} + +void PRVM_Watchpoint(prvm_prog_t *prog, int stack_index, const char *text, etype_t type, prvm_eval_t *o, prvm_eval_t *n) +{ + size_t sz = sizeof(prvm_vec_t) * ((type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1); + if (memcmp(o, n, sz)) + { + char buf[1024]; + char valuebuf_o[128]; + char valuebuf_n[128]; + PRVM_UglyValueString(prog, type, o, valuebuf_o, sizeof(valuebuf_o)); + PRVM_UglyValueString(prog, type, n, valuebuf_n, sizeof(valuebuf_n)); + dpsnprintf(buf, sizeof(buf), "%s: %s -> %s", text, valuebuf_o, valuebuf_n); + PRVM_Breakpoint(prog, stack_index, buf); + memcpy(o, n, sz); + } +} + +static void PRVM_UpdateBreakpoints(prvm_prog_t *prog) +{ + debug_data_t *debug = &debug_data[prog - prvm_prog_list]; + if (!prog->loaded) + return; + if (debug->break_statement[0]) + { + if (debug->break_statement[0] >= '0' && debug->break_statement[0] <= '9') + { + prog->break_statement = atoi(debug->break_statement); + prog->break_stack_index = 0; + } + else + { + mfunction_t *func; + func = PRVM_ED_FindFunction (prog, debug->break_statement); + if (!func) + { + Con_Printf("%s progs: no function or statement named %s to break on!\n", prog->name, debug->break_statement); + prog->break_statement = -1; + } + else + { + prog->break_statement = func->first_statement; + prog->break_stack_index = 1; + } + } + if (prog->break_statement >= -1) + Con_Printf("%s progs: breakpoint is at statement %d\n", prog->name, prog->break_statement); + } + else + prog->break_statement = -1; + + if (debug->watch_global[0]) + { + ddef_t *global = PRVM_ED_FindGlobal( prog, debug->watch_global ); + if( !global ) + { + Con_Printf( "%s progs: no global named '%s' to watch!\n", prog->name, debug->watch_global ); + prog->watch_global_type = ev_void; + } + else + { + size_t sz = sizeof(prvm_vec_t) * ((global->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1); + prog->watch_global = global->ofs; + prog->watch_global_type = (etype_t)global->type; + memcpy(&prog->watch_global_value, PRVM_GLOBALFIELDVALUE(prog->watch_global), sz); + } + if (prog->watch_global_type != ev_void) + Con_Printf("%s progs: global watchpoint is at global index %d\n", prog->name, prog->watch_global); + } + else + prog->watch_global_type = ev_void; + + if (debug->watch_field[0]) + { + ddef_t *field = PRVM_ED_FindField( prog, debug->watch_field ); + if( !field ) + { + Con_Printf( "%s progs: no field named '%s' to watch!\n", prog->name, debug->watch_field ); + prog->watch_field_type = ev_void; + } + else + { + size_t sz = sizeof(prvm_vec_t) * ((field->type & ~DEF_SAVEGLOBAL) == ev_vector ? 3 : 1); + prog->watch_edict = debug->watch_edict; + prog->watch_field = field->ofs; + prog->watch_field_type = (etype_t)field->type; + if (prog->watch_edict < prog->num_edicts) + memcpy(&prog->watch_edictfield_value, PRVM_EDICTFIELDVALUE(PRVM_EDICT_NUM(prog->watch_edict), prog->watch_field), sz); + else + memset(&prog->watch_edictfield_value, 0, sz); + } + if (prog->watch_edict != ev_void) + Con_Printf("%s progs: edict field watchpoint is at edict %d field index %d\n", prog->name, prog->watch_edict, prog->watch_field); + } + else + prog->watch_field_type = ev_void; +} + +static void PRVM_Breakpoint_f(void) +{ + prvm_prog_t *prog; + + if( Cmd_Argc() == 2 ) { + if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1)))) + return; + { + debug_data_t *debug = &debug_data[prog - prvm_prog_list]; + debug->break_statement[0] = 0; + } + PRVM_UpdateBreakpoints(prog); + return; + } + if( Cmd_Argc() != 3 ) { + Con_Printf( "prvm_breakpoint \n" ); + return; + } + + if (!(prog = PRVM_ProgFromString(Cmd_Argv(1)))) + return; + + { + debug_data_t *debug = &debug_data[prog - prvm_prog_list]; + strlcpy(debug->break_statement, Cmd_Argv(2), sizeof(debug->break_statement)); + } + PRVM_UpdateBreakpoints(prog); +} + +static void PRVM_GlobalWatchpoint_f(void) +{ + prvm_prog_t *prog; + + if( Cmd_Argc() == 2 ) { + if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1)))) + return; + { + debug_data_t *debug = &debug_data[prog - prvm_prog_list]; + debug->watch_global[0] = 0; + } + PRVM_UpdateBreakpoints(prog); + return; + } + if( Cmd_Argc() != 3 ) { + Con_Printf( "prvm_globalwatchpoint \n" ); + return; + } + + if (!(prog = PRVM_ProgFromString(Cmd_Argv(1)))) + return; + + { + debug_data_t *debug = &debug_data[prog - prvm_prog_list]; + strlcpy(debug->watch_global, Cmd_Argv(2), sizeof(debug->watch_global)); + } + PRVM_UpdateBreakpoints(prog); +} + +static void PRVM_EdictWatchpoint_f(void) +{ + prvm_prog_t *prog; + + if( Cmd_Argc() == 2 ) { + if (!(prog = PRVM_FriendlyProgFromString(Cmd_Argv(1)))) + return; + { + debug_data_t *debug = &debug_data[prog - prvm_prog_list]; + debug->watch_field[0] = 0; + } + PRVM_UpdateBreakpoints(prog); + return; + } + if( Cmd_Argc() != 4 ) { + Con_Printf( "prvm_edictwatchpoint \n" ); + return; + } + + if (!(prog = PRVM_ProgFromString(Cmd_Argv(1)))) + return; + + { + debug_data_t *debug = &debug_data[prog - prvm_prog_list]; + debug->watch_edict = atoi(Cmd_Argv(2)); + strlcpy(debug->watch_field, Cmd_Argv(3), sizeof(debug->watch_field)); + } + PRVM_UpdateBreakpoints(prog); +} + /* =============== PRVM_Init @@ -2638,6 +2866,10 @@ void PRVM_Init (void) Cmd_AddCommand ("menu_cmd", PRVM_GameCommand_Menu_f, "calls the menu QC function GameCommand with the supplied string as argument"); Cmd_AddCommand ("sv_cmd", PRVM_GameCommand_Server_f, "calls the server QC function GameCommand with the supplied string as argument"); + Cmd_AddCommand ("prvm_breakpoint", PRVM_Breakpoint_f, "marks a statement or function as breakpoint (when this is executed, a stack trace is printed); to actually halt and investigate state, combine this with a gdb breakpoint on PRVM_Breakpoint, or with prvm_breakpointdump; run with just progs name to clear breakpoint"); + Cmd_AddCommand ("prvm_globalwatchpoint", PRVM_GlobalWatchpoint_f, "marks a global as watchpoint (when this is executed, a stack trace is printed); to actually halt and investigate state, combine this with a gdb breakpoint on PRVM_Breakpoint, or with prvm_breakpointdump; run with just progs name to clear watchpoint"); + Cmd_AddCommand ("prvm_edictwatchpoint", PRVM_EdictWatchpoint_f, "marks an entity field as watchpoint (when this is executed, a stack trace is printed); to actually halt and investigate state, combine this with a gdb breakpoint on PRVM_Breakpoint, or with prvm_breakpointdump; run with just progs name to clear watchpoint"); + Cvar_RegisterVariable (&prvm_language); Cvar_RegisterVariable (&prvm_traceqc); Cvar_RegisterVariable (&prvm_statementprofiling); @@ -2646,6 +2878,7 @@ void PRVM_Init (void) Cvar_RegisterVariable (&prvm_leaktest); Cvar_RegisterVariable (&prvm_leaktest_ignore_classnames); Cvar_RegisterVariable (&prvm_errordump); + Cvar_RegisterVariable (&prvm_breakpointdump); Cvar_RegisterVariable (&prvm_reuseedicts_startuptime); Cvar_RegisterVariable (&prvm_reuseedicts_neverinsameframe); @@ -2662,10 +2895,7 @@ PRVM_InitProg */ void PRVM_Prog_Init(prvm_prog_t *prog) { - if (prog->loaded) - PRVM_Prog_Reset(prog); - - memset(prog, 0, sizeof(prvm_prog_t)); + PRVM_Prog_Reset(prog); prog->leaktest_active = prvm_leaktest.integer != 0; }